Skip to content
Deployment
Skill

/pinme-llm

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.

From plugin
pinme
3.7k7 skills
Install
$ npx -y skills add glitternetwork/pinme --skill pinme-llm --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/pinme-llm

Context preview

The summary Claude sees to decide when to auto-load this skill.

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.

SKILL.md

pinme-llm.SKILL.md
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:

// 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`.

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

{
  "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

PinMe does not provide a raw search endpoint. To search the web, pass OpenRouter's `openrouter:web_search` server tool to `chat/completions`; the model decides whether and when to search.

Always set `max_results` and `max_total_results` to keep search volume and cost bounded.

async function searchWithLLM(env: Env, query: string): Promise<string> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const resp = await fetch(
    `${baseUrl}/api/v1/chat/completions?project_name=${encodeURIComponent(env.PROJECT_NAME)}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': env.API_KEY,
      },
      body: JSON.stringify({
        model: 'openai/gpt-5.2',
        messages: [{ role: 'user', content: query }],
        tools: [
          {
            type: 'openrouter:web_search',
            parameters: {
              engine: 'auto',
              max_results: 5,
              max_total_results: 10,
            },
          },
        ],
      }),
    },
  );

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

  const data = await resp.json() as { choices: Array<{ message?: { content?: string } }> };
  return data.choices[0]?.message?.content ?? '';
}

Response Format

Successful requests return OpenRouter's raw response body.

**Non-streaming Success (200):**

{
  "id": "chatcmpl-...",
  "choices": [{ "message": { "role": "assistant", "content": "Hello!" }, "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15 }
}

**Streaming Success (200):** SSE format

data: {"choices":[{"delta":{"content":"Hello"}}]}
data: {"choices":[{"delta":{"content":" there"}}]}
data: [DONE]

**Errors:**

| HTTP Status | Meaning | data.error Example | |-------------|---------|-------------------| | 401 | API Key missing, invalid, or mismatched with project_name | `"X-API-Key header is required"` / `"Invalid API key"` / `"Invalid API key or project name"` | | 400 | project_name missing or OpenRouter key not configured | `"project_name is required"` / `"LLM service not configured for this project"` | | 403 | LLM balance insufficient or disabled | `"Insufficient balance, please recharge to continue using LLM service"` | | 413 | Request body exceeds 1MB | `"Request body too large (max 1MB)"` | | 500 | Proxy failed before upstream request | `"Failed to build request"` | | 502 | LLM service unavailable | `"LLM service unavailable"` |

If OpenRouter receives the request and returns a 4xx/5xx, PinMe passes through OpenRouter's status, headers, and response body instead of wrapping it.

Worker Example Code — Non-streaming

async function callLLM(
  env: Env,
  messages: Array<{ role: string; content: string }>,
  model = 'openai/gpt-4o-mini',
): Promise<{ content: string; error?: string }> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const resp = await fetch(
    `${baseUrl}/api/v1/chat/completions?project_name=${encodeURIComponent(env.PROJECT_NAME)}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': env.API_KEY,
      },
      body: JSON.stringify({ model, messages }),
    },
  );

  if (!resp.ok) {
    return { content: '', error: await
Read more
Ships withpinme

PinMe is a zero-config deployment CLI focused on one-command creation and deployment for full-stack projects. It lets you quickly set up and launch a complete project with an integrated frontend, Worker backend, and database, without tedious configuration.

Get the whole plugin
Stats
3,742
Stars
276
Forks
Active
Maintenance
TypeScript
Language
MIT
License
3d ago
Last commit
1y ago
Created

Repo: glitternetwork/pinme

Other skills on pinme.