{"_id":"@doany-ai/ai-sdk","_rev":"2-45fc2d991cb4f352ffc31569dc004cac","name":"@doany-ai/ai-sdk","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@doany-ai/ai-sdk","version":"0.1.0","keywords":["doany","ai","gateway","cloudflare-workers","llm","openai","sdk"],"license":"UNLICENSED","_id":"@doany-ai/ai-sdk@0.1.0","maintainers":[{"name":"kalvinrc9","email":"kalvinrc9@gmail.com"},{"name":"loner233","email":"yinjiakang@gmail.com"}],"homepage":"https://github.com/InceptionsAI/doany-ai-app/tree/main/doany-ai-sdk#readme","bugs":{"url":"https://github.com/InceptionsAI/doany-ai-app/issues"},"dist":{"shasum":"2fe334c5bedab994bf521d20a468bb71b961f6d8","tarball":"https://registry.npmjs.org/@doany-ai/ai-sdk/-/ai-sdk-0.1.0.tgz","fileCount":9,"integrity":"sha512-uofa3LLkQ8ZyGFWoWegrllgIUSN/vLrRrDS+PxWcf/22iDuNGCht34AWFLxs/HnApCMP1ajQBeoaOBjHSUZ6VQ==","signatures":[{"sig":"MEYCIQD22MwEiPgimt6tzwJ3QXl1skJhDrOLrrDV1wDx68dz3wIhAJXz1uU6cM62hwJkn21TVBxDhXZCuEBtdAsZtkM+Mci6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":148441},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"pnpm run build"},"_npmUser":{"name":"loner233","email":"yinjiakang@gmail.com"},"repository":{"url":"git+https://github.com/InceptionsAI/doany-ai-app.git","type":"git","directory":"doany-ai-sdk"},"_npmVersion":"11.17.0","description":"Client SDK for doany-deployed sites to call the doany API Gateway with their site token (sk). Cloudflare Workers friendly.","directories":{},"sideEffects":false,"_nodeVersion":"26.4.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/ai-sdk_0.1.0_1783652674643_0.6048980875455447","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@doany-ai/ai-sdk","version":"0.1.1","description":"Client SDK for doany-deployed sites to call the doany API Gateway with their site token (sk). Cloudflare Workers friendly.","type":"module","license":"UNLICENSED","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","engines":{"node":">=18"},"repository":{"type":"git","url":"git+https://github.com/InceptionsAI/doany-ai-app.git","directory":"doany-ai-sdk"},"homepage":"https://github.com/InceptionsAI/doany-ai-app/tree/main/doany-ai-sdk#readme","bugs":{"url":"https://github.com/InceptionsAI/doany-ai-app/issues"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"pnpm run build"},"keywords":["doany","ai","gateway","cloudflare-workers","llm","openai","sdk"],"devDependencies":{"tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^2.1.8"},"publishConfig":{"access":"public"},"_id":"@doany-ai/ai-sdk@0.1.1","_nodeVersion":"26.4.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-7lz1MLXnggdOgbcqwi5KytEUcz5VYi7nY1V+rBFH9U/x6l/T5NLm0vn+ZAL67gdLOP7WGQdvc31Q5HNCzN735A==","shasum":"49b1d4f28a470f2f8173165e35dec08e9f5cdd64","tarball":"https://registry.npmjs.org/@doany-ai/ai-sdk/-/ai-sdk-0.1.1.tgz","fileCount":9,"unpackedSize":142462,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEcJ5C6WrHHPSPmLDGsLr7uIym6AnuSl7CqUw6K+cu1jAiA6r4j9b2wjsrW8t58GuPo/rgBxc1KHJaYOJY5JhrH1Hw=="}]},"_npmUser":{"name":"loner233","email":"yinjiakang@gmail.com"},"directories":{},"maintainers":[{"name":"kalvinrc9","email":"kalvinrc9@gmail.com"},{"name":"loner233","email":"yinjiakang@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-sdk_0.1.1_1783654939914_0.29488780618119326"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-10T03:04:34.555Z","modified":"2026-07-10T03:42:20.186Z","0.1.0":"2026-07-10T03:04:34.800Z","0.1.1":"2026-07-10T03:42:20.076Z"},"bugs":{"url":"https://github.com/InceptionsAI/doany-ai-app/issues"},"license":"UNLICENSED","homepage":"https://github.com/InceptionsAI/doany-ai-app/tree/main/doany-ai-sdk#readme","keywords":["doany","ai","gateway","cloudflare-workers","llm","openai","sdk"],"repository":{"type":"git","url":"git+https://github.com/InceptionsAI/doany-ai-app.git","directory":"doany-ai-sdk"},"description":"Client SDK for doany-deployed sites to call the doany API Gateway with their site token (sk). Cloudflare Workers friendly.","maintainers":[{"name":"kalvinrc9","email":"kalvinrc9@gmail.com"},{"name":"loner233","email":"yinjiakang@gmail.com"}],"readme":"# @doany-ai/ai-sdk\n\nClient SDK for **doany-deployed sites** to call the [doany API Gateway](../doany-ai-gateway) with their site token (`sk`). Cloudflare Workers friendly (`fetch` + Web Crypto only — no `node:` builtins), zero runtime dependencies.\n\nThe gateway holds every AI supplier key (LLM providers, RunComfy image models, …). A site never sees them: it authenticates with the `sk` that the deploy plane injects as `DOANY_SITE_TOKEN`, and this SDK does the rest.\n\n```bash\npnpm add @doany-ai/ai-sdk   # npm i / yarn add\n```\n\n## Quick start (Cloudflare Worker)\n\n```ts\nimport { DoanyAI } from '@doany-ai/ai-sdk'\n\nexport default {\n  async fetch(req: Request, env: Env) {\n    const ai = DoanyAI.fromEnv(env) // reads env.DOANY_SITE_TOKEN + env.DOANY_GW_URL\n\n    const res = await ai.chat.completions.create({\n      model: 'gpt-5.5',\n      messages: [{ role: 'user', content: 'Write a one-line slogan.' }],\n    })\n    return Response.json(res)\n  },\n}\n```\n\n`fromEnv` reads the bindings the doany deploy plane injects. To be explicit:\n\n```ts\nconst ai = new DoanyAI({\n  token: env.DOANY_SITE_TOKEN,\n  baseUrl: env.DOANY_GW_URL,      // defaults to https://gw.doany.ai\n  endUserId: session.userId,      // optional default attribution (see below)\n})\n```\n\n## Surface\n\nOnly the endpoints a site token can reach:\n\n| Call | Gateway |\n|---|---|\n| `ai.chat.completions.create(body, opts?)` | `POST /v1/llm/chat/completions` (OpenAI-compatible) |\n| `ai.responses.create(body, opts?)` | `POST /v1/llm/responses` (OpenAI Responses) |\n| `ai.images.generate(body, opts?)` · `ai.images.get(id)` · `ai.images.generateAndWait(...)` | `POST /v1/images/generate`, `GET /v1/images/{id}` |\n| `ai.jobs.get(id)` · `ai.jobs.list(...)` · `ai.jobs.cancel(id)` | `GET/DELETE /v1/jobs/*` |\n| `ai.raw(method, path, body?, opts?)` | escape hatch → raw `Response` |\n\nSearch / reddit / resources / template presign are agent-only on the gateway and are intentionally **not** exposed here. Email (`/v1/email/send`) is reachable with a site token, but it is not an AI capability, so it is out of scope for this SDK.\n\n## Streaming (LLM)\n\n```ts\nconst stream = await ai.chat.completions.create({ model: 'gpt-5.5', messages, stream: true })\nfor await (const chunk of stream) {\n  write(chunk.choices[0]?.delta?.content ?? '')\n}\n```\n\nTo pipe the gateway's SSE straight to the browser, skip parsing and forward the raw response:\n\n```ts\nconst upstream = await ai.raw('POST', '/v1/llm/chat/completions', { model, messages, stream: true }, { stream: true })\nreturn new Response(upstream.body, { headers: { 'content-type': 'text/event-stream' } })\n```\n\n## Images\n\n```ts\n// submit + poll to completion\nconst job = await ai.images.generateAndWait(\n  { input: { prompt: 'a mountain bike on a cliff at sunset', aspect_ratio: '16:9' } },\n  { pollMs: 3000, waitMs: 180_000 },\n)\nconst url = job.output?.image // RunComfy URL\n\n// or drive the polling yourself\nconst submitted = await ai.images.generate({ input: { prompt: '…' } })\nconst status = await ai.images.get(submitted.id)\n```\n\n## Idempotency (safe retries)\n\n`images.generate` is side-effecting. The SDK **auto-attaches an `Idempotency-Key`** when you don't supply one, so a network-retried call never double-charges. Provide your own to make \"the same logical action\" explicit:\n\n```ts\nawait ai.images.generate(body, { idempotencyKey: `hero:${page.id}` })\n```\n\nA replay surfaces `result.idempotentReplay === true`.\n\n## End-user attribution\n\nPass the site's logged-in user id and it lands on the gateway usage row (`metadata.end_user_id`) for per-user metering — it is **attribution only, never auth**:\n\n```ts\nnew DoanyAI({ token, endUserId: user.id })          // client-wide default\nawait ai.chat.completions.create(body, { endUserId: user.id }) // per call\n```\n\n## Errors\n\nEverything throws a typed `DoanyError` subclass with `status`, `code`, `hint?`, and `requestId`:\n\n```ts\nimport { DoanyRateLimitError, DoanyQuotaError, DoanyError } from '@doany-ai/ai-sdk'\n\ntry {\n  await ai.images.generate(body)\n} catch (e) {\n  if (e instanceof DoanyQuotaError) { /* per-project quota; e.retryAfter */ }\n  else if (e instanceof DoanyRateLimitError) { /* back off e.retryAfter */ }\n  else if (e instanceof DoanyError) { console.error(e.code, e.hint, e.requestId) }\n}\n```\n\nSubclasses: `DoanyAuthError` (401/403), `DoanyNotFoundError` (404), `DoanyValidationError` (400/422), `DoanyConflictError` (409), `DoanyRateLimitError` / `DoanyQuotaError` (429), `DoanyUpstreamError` (502), `DoanyGatewayNotConfiguredError` (503), `DoanyTimeoutError`, `DoanyConnectionError`, `DoanyConfigError`, `DoanyAbortError`.\n\n## Request correlation\n\nEvery result and error carries `.requestId` (the gateway's `X-Request-Id`). Use it to find the call in GCP Log Explorer: `jsonPayload.request_id=\"<id>\"` under `logName=doany-gateway-{int,prod}`.\n\n## Retries & timeouts\n\n- Auto-retries (default `maxRetries: 2`, exp backoff + jitter): network errors, `429` (honors `Retry-After`), and retry-safe `5xx` (GET or calls carrying an Idempotency-Key). A non-idempotent LLM `5xx` is **not** retried — the gateway already fails over across providers internally.\n- Timeouts default per operation (LLM ~305s, others ~30s); override with `opts.timeoutMs` and cancel with `opts.signal`.\n\n## Options\n\n```ts\nnew DoanyAI({ token, baseUrl?, endUserId?, maxRetries?, timeoutMs?, fetch?, defaultHeaders? })\n// per call:\nai.<x>.<m>(body, { endUserId?, idempotencyKey?, requestId?, signal?, timeoutMs?, maxRetries?, headers? })\n```\n\n## Compatibility\n\nTargets Cloudflare Workers; also runs on Node 18+ / modern browsers / other edge runtimes (anything with global `fetch`). SDK major tracks the gateway's `/v1`.\n\nSee [`DESIGN.md`](./DESIGN.md) for the full design.\n","readmeFilename":"README.md"}