pinme-auth
Use when a PinMe project (Worker TypeScript) needs to integrate user authentication — creating email/password users, verifying id_tokens, querying user info,…
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.
$ npx -y skills add glitternetwork/pinme --skill pinme-llm --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/pinme-llmContext 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.
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.
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.
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`.
---
**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();
}---
**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
{
"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.
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 ?? '';
}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.
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: awaitPinMe 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.
Repo: glitternetwork/pinme
Use when a PinMe project (Worker TypeScript) needs to integrate user authentication — creating email/password users, verifying id_tokens, querying user info,…
Use this skill when a PinMe project (Worker TypeScript) needs to integrate email sending (send_email). Guides AI to generate correct Worker TS code.
Use when a PinMe Cloudflare Worker needs R2 object storage, including secure file or image upload, streaming download, metadata lookup, deletion, listing,…
Use this skill when the user wants to share, publish, or upload a static result through PinMe, especially by generating a static HTML share page for a PinMe…
Use when generating, modifying, or reviewing PinMe Worker (Cloudflare Worker TypeScript) code that accepts payments through UniwebPay — payment links,…
Use this skill when the user mentions "pinme", or needs to upload files, store to IPFS, create/publish/deploy websites or full-stack services (including…