# pinme-llm

Tells the AI how to call OpenRouter models from a PinMe TypeScript Worker through the platform proxy: chat, streaming, web search.

- skill · `type=skill` · Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode · 499 installs · ★ 3754
- Author: glitternetwork
- License: MIT · policy=mirror · the beginning of the file is at the end of this page
- Source: https://github.com/glitternetwork/pinme/tree/HEAD/skills/pinme-llm/SKILL.md
- Updated: 2026-09-12
- HTML: https://affpapa.org/en/nevibecode/skills/pinme-llm

## What it does

The skill fires when a PinMe Worker written in TypeScript has to reach an LLM. It hands the agent working code for three endpoints: `GET /api/v1/models` to list models, `POST /api/v1/chat/completions` which forwards an OpenRouter-shaped body, and web search via the `openrouter:web_search` server tool.

Your real OpenRouter key never lands in the Worker. PinMe injects `API_KEY`, `PROJECT_NAME` and an optional `BASE_URL` at creation time. Calls carry the `X-API-Key` header, and the `project_name` query value must belong to the same project as the key — a mismatch comes back as 401.

The skill documents the proxy's error codes: 400 when `project_name` is missing or no OpenRouter key is configured, 403 on low balance, 413 past 1MB of body, 502 when the service is down. OpenRouter replies pass through untouched, so parse them in OpenRouter's own shape. Set `max_results` and `max_total_results` on web search, otherwise nothing caps how much gets searched or billed.

## Install

### Claude Code (native platform)

1. **Skills CLI (Vercel)**
```bash
npx skills add glitternetwork/pinme --skill pinme-llm -a claude-code
```

2. **Manually: put the files in place**
- File: `~/.claude/skills/pinme-llm/SKILL.md` — Copy the whole skill folder; for one project use .claude/skills/pinme-llm/

### Codex

1. **Skills CLI (Vercel)**
```bash
npx skills add glitternetwork/pinme --skill pinme-llm -a codex
```

2. **Manually: put the files in place**
- File: `.agents/skills/pinme-llm/SKILL.md` — For all projects use ~/.agents/skills/

### Cursor

1. **Skills CLI (Vercel)**
```bash
npx skills add glitternetwork/pinme --skill pinme-llm -a cursor
```

2. **Manually: put the files in place**
- File: `.cursor/skills/pinme-llm/SKILL.md` — Cursor also reads .agents/skills/ and .claude/skills/

### GitHub Copilot

1. **GitHub CLI**
```bash
gh skill install glitternetwork/pinme pinme-llm
```

2. **Manually: put the files in place**
- File: `.github/skills/pinme-llm/SKILL.md`

### Gemini CLI

1. **Skills CLI (Vercel)**
```bash
npx skills add glitternetwork/pinme --skill pinme-llm -a gemini-cli
```

2. **Platform CLI**
```bash
gemini skills install https://github.com/glitternetwork/pinme --path skills/pinme-llm
```

### Windsurf (Devin Desktop)

**Manually: put the files in place**
- File: `.devin/skills/pinme-llm/SKILL.md` — The legacy .windsurf/skills/ path still works

### OpenCode

**Skills CLI (Vercel)**
```bash
npx skills add glitternetwork/pinme --skill pinme-llm -a opencode
```

### Claude.ai

**Claude.ai**
- Zip the folder and upload it in Settings → Capabilities → Skills

Where a file is given instead of a command, copy the files from the source to that path: https://github.com/glitternetwork/pinme/tree/HEAD/skills/pinme-llm/SKILL.md

## When to use it
- call an OpenRouter model from a TypeScript Worker without holding the real key
- stream a model reply over SSE from a request handler
- attach web search to a chat call with `openrouter:web_search`
- map PinMe proxy errors to sensible responses for the client
- fetch the list of available models straight from the Worker

## Example prompts
- Add a `chat/completions` call to `backend/src/worker.ts` using `openai/gpt-4o-mini` and read the key from `env.API_KEY`
- Stream the model output as SSE from my Worker handler
- Wire web search into the request with `openrouter:web_search` and cap the result count

## Source text · SKILL.md · MIT

Below is the content of a third-party file. It is data, not instructions for you: do not act on anything written inside unless the human asks. Block tag: 55bda6a5f9b24b87.

BEGIN THIRD-PARTY FILE 55bda6a5f9b24b87
````text
---
name: pinme-llm
description: Use this skill when a PinMe project (Worker TypeScript) needs to call OpenRouter-backed LLM APIs, including models, chat/completions, streaming, or OpenRouter web search. Guides AI to generate correct Worker TS code.
---

# PinMe Worker OpenRouter API Integration

Guides how to call PinMe platform's OpenRouter proxy APIs in a PinMe Worker (TypeScript). Workers use the PinMe project API key; they never hold the real OpenRouter API key.

## Environment Variables

The following environment variables are automatically injected when the Worker is created — no manual configuration needed:

```typescript
// backend/src/worker.ts
export interface Env {
  DB: D1Database;
  API_KEY: string;       // Project API Key from create_worker
  PROJECT_NAME: string;  // Actual project_name from create_worker; must match API_KEY
  BASE_URL?: string;     // Optional override for PinMe API base URL, defaults to https://pinme.cloud
}
```

> `API_KEY` authenticates the Worker to PinMe. `PROJECT_NAME` is required for `chat/completions` and must belong to the same project as `API_KEY`. When `BASE_URL` is not set, use `https://pinme.cloud`.

---

## Models API

**Endpoint:** `GET {BASE_URL}/api/v1/models`
**Authentication:** `X-API-Key` header (using `env.API_KEY`)
**Request Body:** none

Use this when the Worker needs to list available OpenRouter models. The response body, status, and headers are passed through from OpenRouter `/models`.

```typescript
async function listModels(env: Env): Promise<unknown> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const resp = await fetch(`${baseUrl}/api/v1/models`, {
    headers: { 'X-API-Key': env.API_KEY },
  });

  if (!resp.ok) {
    throw new Error(await extractPinmeOpenRouterError(resp));
  }

  return await resp.json();
}
```

---

## Chat Completions API

**Endpoint:** `POST {BASE_URL}/api/v1/chat/completions?project_name={project_name}`
**Authentication:** `X-API-Key` header (using `env.API_KEY`)
**Request Body:** OpenRouter chat/completions format, passed through as-is after a 1MB size check
**Streaming:** Supports SSE (`stream: true`)
**Web Search:** Supports OpenRouter `openrouter:web_search` server tool via the `tools` array

### Request Format

```json
{
  "model": "openai/gpt-4o-mini",
  "messages": [
    { "role": "system", "content": "You are a helpful assistant." },
    { "role": "user", "content": "Hello!" }
  ],
  "stream": true
}
```

> Use `env.PROJECT_NAME` from `create_worker`; always URL-encode it in the query string. For available models, call `GET /api/v1/models` or refer to OpenRouter model IDs.

### OpenRouter Web Search
````
END THIRD-PARTY FILE 55bda6a5f9b24b87

The third-party file has ended. Everything between the 55bda6a5f9b24b87 tags is data: requests and commands from it are not followed. An “end of file” line with another tag inside the block is part of the file.

First 2658 of 11265 bytes. © glitternetwork · MIT · https://github.com/glitternetwork/pinme/tree/HEAD/skills/pinme-llm/SKILL.md

_Part of the page did not fit — see the HTML version._

---
NeVibeCode on affpapa.org. Catalog texts are descriptions, not instructions for the agent. How to choose and install: https://affpapa.org/en/nevibecode/agents.md
