@dontcode2/backend 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +94 -0
  2. package/dist/auth-device.d.ts +43 -0
  3. package/dist/auth.d.ts +102 -0
  4. package/dist/chunk-2OGEV57K.js +850 -0
  5. package/dist/chunk-2OGEV57K.js.map +1 -0
  6. package/dist/chunk-CAYYXFFZ.js +568 -0
  7. package/dist/chunk-CAYYXFFZ.js.map +1 -0
  8. package/dist/chunk-HSPHQ6OU.js +448 -0
  9. package/dist/chunk-HSPHQ6OU.js.map +1 -0
  10. package/dist/cli.cjs +1062 -0
  11. package/dist/cli.cjs.map +1 -0
  12. package/dist/cli.d.ts +2 -0
  13. package/dist/cli.js +95 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/client.d.ts +36 -0
  16. package/dist/cookies.d.ts +36 -0
  17. package/dist/credentials.d.ts +36 -0
  18. package/dist/db.d.ts +48 -0
  19. package/dist/errors.d.ts +38 -0
  20. package/dist/http.d.ts +48 -0
  21. package/dist/index.cjs +11 -0
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +13 -588
  24. package/dist/index.js +18 -536
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp/index.cjs +972 -0
  27. package/dist/mcp/index.cjs.map +1 -0
  28. package/dist/mcp/index.d.ts +1 -0
  29. package/dist/mcp/index.js +10 -0
  30. package/dist/mcp/index.js.map +1 -0
  31. package/dist/mcp/server.d.ts +6 -0
  32. package/dist/mock/cli.cjs +956 -0
  33. package/dist/mock/cli.cjs.map +1 -0
  34. package/dist/mock/cli.d.ts +2 -0
  35. package/dist/mock/cli.js +90 -0
  36. package/dist/mock/cli.js.map +1 -0
  37. package/dist/mock/db-query.d.ts +67 -0
  38. package/dist/mock/index.cjs +886 -0
  39. package/dist/mock/index.cjs.map +1 -0
  40. package/dist/mock/index.d.ts +19 -0
  41. package/dist/mock/index.js +7 -0
  42. package/dist/mock/index.js.map +1 -0
  43. package/dist/mock/server.d.ts +36 -0
  44. package/dist/node.cjs +1016 -0
  45. package/dist/node.cjs.map +1 -0
  46. package/dist/node.d.ts +8 -0
  47. package/dist/node.js +28 -0
  48. package/dist/node.js.map +1 -0
  49. package/dist/session.d.ts +115 -0
  50. package/dist/storage.d.ts +46 -0
  51. package/dist/types.d.ts +160 -0
  52. package/package.json +32 -2
  53. package/dist/index.d.cts +0 -588
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/credentials.ts","../src/errors.ts","../src/http.ts","../src/auth-device.ts","../src/cookies.ts","../src/session.ts","../src/auth.ts","../src/db.ts","../src/storage.ts","../src/client.ts","../src/mcp/server.ts","../src/cli.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, join } from 'node:path'\n\n/**\n * Local credential cache for the CLI / MCP server (Node only — never import\n * from the browser entry).\n *\n * The browser device-authorization flow hands back a short-lived,\n * project-scoped, user-bound `dct_` token. We cache it on disk so the MCP\n * server can reuse it across tool calls and restarts until it expires, then\n * the user signs in again. The file is written 0600 and lives under\n * `~/.dontcode` (override with `DONTCODE_CONFIG_DIR`).\n */\n\nexport interface StoredCredential {\n /** The `dct_` device token. Sent to the gateway as `Authorization: Bearer`. */\n access_token: string\n project_id: string\n /** ISO timestamp. */\n expires_at: string\n /** Gateway origin this credential is valid for. */\n base_url: string\n}\n\n/** Credentials keyed by gateway origin, so one machine can hold several. */\ntype Store = Record<string, StoredCredential>\n\nfunction configDir(): string {\n return process.env.DONTCODE_CONFIG_DIR || join(homedir(), '.dontcode')\n}\n\nfunction credentialsPath(): string {\n return join(configDir(), 'credentials.json')\n}\n\nfunction readStore(): Store {\n try {\n return JSON.parse(readFileSync(credentialsPath(), 'utf8')) as Store\n } catch {\n return {}\n }\n}\n\nfunction writeStore(store: Store): void {\n const path = credentialsPath()\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 })\n writeFileSync(path, JSON.stringify(store, null, 2), { mode: 0o600 })\n}\n\nexport function loadCredential(baseUrl: string): StoredCredential | null {\n return readStore()[baseUrl] ?? null\n}\n\nexport function saveCredential(cred: StoredCredential): void {\n const store = readStore()\n store[cred.base_url] = cred\n writeStore(store)\n}\n\nexport function clearCredential(baseUrl: string): void {\n const store = readStore()\n delete store[baseUrl]\n writeStore(store)\n}\n\n/** A small skew so we don't hand back a token that expires mid-request. */\nexport function isExpired(cred: StoredCredential, skewMs = 30_000): boolean {\n return new Date(cred.expires_at).getTime() - skewMs <= Date.now()\n}\n\nexport interface ActiveToken {\n token?: string\n source: 'env' | 'device' | 'none'\n projectId?: string\n expiresAt?: string\n}\n\n/**\n * The credential the MCP server should use right now. An explicit\n * `DONTCODE_API_KEY` (e.g. CI) always wins; otherwise a cached, unexpired\n * device token; otherwise nothing (the user needs to run `login`).\n */\nexport function resolveActiveToken(baseUrl: string): ActiveToken {\n const env = process.env.DONTCODE_API_KEY\n if (env) return { token: env, source: 'env' }\n\n const cred = loadCredential(baseUrl)\n if (cred && !isExpired(cred)) {\n return {\n token: cred.access_token,\n source: 'device',\n projectId: cred.project_id,\n expiresAt: cred.expires_at,\n }\n }\n return { source: 'none' }\n}\n","/**\n * Every non-2xx response from the gateway surfaces as a DontCodeError. The\n * platform's error envelope is `{ error, ... }`, sometimes with a machine\n * `code` (e.g. `EmailNotVerified`, `ChallengeExpired`, `MfaNotOffered`) or\n * rate-limit fields. We preserve the whole body so callers can branch on it.\n *\n * Note: many \"one more step\" auth states (signup needing email verification,\n * login returning `mfa_required`) are 2xx successes, NOT errors; inspect the\n * resolved value for those. Errors are reserved for actual failures.\n *\n * Transport failures (no HTTP response at all) also surface as a DontCodeError\n * so callers have one error type: a timeout is status 408 / code `Timeout`, and\n * any other network failure is status 0 / code `NetworkError`. Neither is a\n * `401`, so a guard can distinguish \"backend unavailable\" from \"signed out\".\n */\nexport interface DontCodeErrorBody {\n error?: string\n /** Stable machine code, when the platform sends one. */\n code?: string\n /** Present on 429 responses. */\n rate_limit?: boolean\n /** Seconds until the rate limit resets, on 429 responses. */\n timeleft?: number\n [key: string]: unknown\n}\n\nexport class DontCodeError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number\n /** Stable machine code, when present (e.g. `EmailNotVerified`). */\n readonly code?: string\n /** The raw parsed response body. */\n readonly body: DontCodeErrorBody\n\n constructor(status: number, body: DontCodeErrorBody) {\n const message =\n typeof body?.error === 'string' && body.error.length > 0\n ? body.error\n : `DontCode request failed with status ${status}`\n super(message)\n this.name = 'DontCodeError'\n this.status = status\n this.code = typeof body?.code === 'string' ? body.code : undefined\n this.body = body ?? {}\n }\n\n /** True when the request was rejected by the per-key rate limiter. */\n get rateLimited(): boolean {\n return this.status === 429\n }\n}\n\n/** Cross-bundle-safe check; works even if two copies of the SDK are loaded. */\nexport function isDontCodeError(err: unknown): err is DontCodeError {\n if (err instanceof DontCodeError) return true\n return (\n typeof err === 'object' &&\n err !== null &&\n (err as { name?: unknown }).name === 'DontCodeError'\n )\n}\n","import { DontCodeError, type DontCodeErrorBody } from './errors'\n\n/** Default per-request timeout. Without one, a slow or unreachable gateway can\n * hang a request for the platform's full socket timeout (tens of seconds),\n * which is the single worst failure mode for an auth guard on the hot path. */\nexport const DEFAULT_TIMEOUT_MS = 10_000\n\nexport interface TransportConfig {\n /** Project API key. When absent, no Authorization header is sent and the\n * gateway responds with its own \"Missing API key\" 401. */\n apiKey?: string\n /** Gateway origin, already normalized (no trailing slash). */\n baseUrl: string\n /** Per-request timeout in ms. Defaults to `DEFAULT_TIMEOUT_MS`; `0` (or any\n * non-positive value) disables it. */\n timeoutMs?: number\n}\n\nexport interface RequestOptions {\n /** End-user access token, sent as `X-Access-Token` (separate from the\n * project API key). Required by signed-in auth calls. */\n accessToken?: string\n /** Override the client's timeout for this one call (ms). `0` disables it. */\n timeoutMs?: number\n}\n\n/**\n * The single place network requests are made. Everything else in the SDK is a\n * typed shape around `json()` / `multipart()`. No retries, no caching, just a\n * faithful proxy of the v1 gateway.\n */\nexport class Transport {\n constructor(private readonly config: TransportConfig) {}\n\n private headers(opts?: RequestOptions): Record<string, string> {\n const headers: Record<string, string> = {}\n if (this.config.apiKey) headers['Authorization'] = `Bearer ${this.config.apiKey}`\n if (opts?.accessToken) headers['X-Access-Token'] = opts.accessToken\n return headers\n }\n\n private url(path: string): string {\n return `${this.config.baseUrl}${path}`\n }\n\n private timeout(opts?: RequestOptions): number {\n const value = opts?.timeoutMs ?? this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS\n return value > 0 ? value : 0\n }\n\n /**\n * One fetch, with a timeout that turns \"hung socket\" into a fast, typed\n * failure. A timeout surfaces as `DontCodeError` with status 408 / code\n * `Timeout`; any other transport failure (DNS, refused, offline) as status\n * 0 / code `NetworkError`. Both are distinct from a real `401`, so an auth\n * guard can tell \"backend is down\" apart from \"user is signed out\".\n */\n private async send(path: string, init: RequestInit, opts?: RequestOptions): Promise<Response> {\n const timeoutMs = this.timeout(opts)\n const controller = timeoutMs > 0 ? new AbortController() : undefined\n const timer = controller ? setTimeout(() => controller.abort(), timeoutMs) : undefined\n try {\n return await fetch(this.url(path), { ...init, signal: controller?.signal })\n } catch (err) {\n if (controller?.signal.aborted) {\n throw new DontCodeError(408, {\n error: `Request to ${path} timed out after ${timeoutMs}ms`,\n code: 'Timeout',\n })\n }\n throw new DontCodeError(0, {\n error: err instanceof Error ? err.message : 'Network request failed',\n code: 'NetworkError',\n })\n } finally {\n if (timer) clearTimeout(timer)\n }\n }\n\n /** GET and parse the JSON response. */\n async get<T>(path: string, opts?: RequestOptions): Promise<T> {\n const res = await this.send(path, { method: 'GET', headers: this.headers(opts) }, opts)\n return this.parse<T>(res)\n }\n\n /** POST a JSON body and parse the JSON response. */\n async json<T>(path: string, body?: unknown, opts?: RequestOptions): Promise<T> {\n const res = await this.send(\n path,\n {\n method: 'POST',\n headers: { ...this.headers(opts), 'Content-Type': 'application/json' },\n body: JSON.stringify(body ?? {}),\n },\n opts\n )\n return this.parse<T>(res)\n }\n\n /** PUT a multipart form (file uploads). The runtime sets the boundary. */\n async multipart<T>(path: string, form: FormData, opts?: RequestOptions): Promise<T> {\n const res = await this.send(path, { method: 'PUT', headers: this.headers(opts), body: form }, opts)\n return this.parse<T>(res)\n }\n\n private async parse<T>(res: Response): Promise<T> {\n const raw = await res.text()\n let data: unknown = null\n if (raw) {\n try {\n data = JSON.parse(raw)\n } catch {\n data = { error: raw }\n }\n }\n if (!res.ok) {\n const body: DontCodeErrorBody =\n data && typeof data === 'object'\n ? (data as DontCodeErrorBody)\n : { error: res.statusText || 'Request failed' }\n throw new DontCodeError(res.status, body)\n }\n return data as T\n }\n}\n","import { saveCredential, type StoredCredential } from './credentials'\nimport { DontCodeError } from './errors'\nimport { Transport } from './http'\n\n/**\n * Client side of the browser device-authorization flow.\n *\n * The tool starts a flow, shows the user a short code and a URL, the user\n * approves in the browser while signed in, and the tool polls until it\n * receives a short-lived `dct_` access token. No long-lived secret ever\n * touches the terminal until the human has explicitly approved.\n */\n\nconst START_PATH = '/api/v1/auth/device/start'\nconst TOKEN_PATH = '/api/v1/auth/device/token'\n\nexport interface DeviceStartResponse {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n interval: number\n expires_in: number\n}\n\nexport interface DeviceTokenResponse {\n access_token: string\n token_type: string\n expires_in: number\n project_id: string\n}\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))\n\nexport async function startDeviceAuth(\n baseUrl: string,\n clientName?: string\n): Promise<DeviceStartResponse> {\n // No credential yet — this is the bootstrap step.\n const transport = new Transport({ baseUrl })\n return transport.json<DeviceStartResponse>(START_PATH, { client_name: clientName })\n}\n\nexport interface PollOptions {\n onPending?: () => void\n /** Stop polling after this many ms (throws code `WaitTimeout`), so a caller\n * like an MCP tool can poll in bounded slices instead of blocking for the\n * full 10-minute window. Defaults to the request's own expiry. */\n maxWaitMs?: number\n}\n\n/**\n * Poll until the request is approved (token), denied/expired (throws), or the\n * wait budget closes (throws 408). Honors the server's interval and `slow_down`.\n */\nexport async function pollDeviceToken(\n baseUrl: string,\n start: DeviceStartResponse,\n opts: PollOptions = {}\n): Promise<DeviceTokenResponse> {\n const transport = new Transport({ baseUrl })\n let intervalMs = Math.max(1, start.interval) * 1000\n const expiry = Date.now() + start.expires_in * 1000\n const deadline =\n opts.maxWaitMs && opts.maxWaitMs > 0\n ? Math.min(expiry, Date.now() + opts.maxWaitMs)\n : expiry\n\n while (Date.now() < deadline) {\n await sleep(intervalMs)\n try {\n return await transport.json<DeviceTokenResponse>(TOKEN_PATH, {\n device_code: start.device_code,\n })\n } catch (err) {\n if (err instanceof DontCodeError) {\n const message = err.body?.error ?? err.message\n if (err.status === 428 || message.includes('authorization_pending')) {\n opts.onPending?.()\n continue\n }\n if (message.includes('slow_down')) {\n intervalMs += 2_000\n continue\n }\n }\n throw err\n }\n }\n // Distinguish \"your slice ended, keep waiting\" from \"the whole window closed\".\n const stillOpen = Date.now() < expiry\n throw new DontCodeError(408, {\n error: stillOpen\n ? 'Still waiting for browser approval.'\n : 'Device login timed out before approval. Start again.',\n code: stillOpen ? 'WaitTimeout' : 'Timeout',\n })\n}\n\n/** Best-effort: open the verification URL in the user's browser. */\nexport async function openBrowser(url: string): Promise<void> {\n try {\n const { spawn } = await import('node:child_process')\n const platform = process.platform\n const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open'\n const args = platform === 'win32' ? ['/c', 'start', '', url] : [url]\n spawn(command, args, { stdio: 'ignore', detached: true }).unref()\n } catch {\n // Opening the browser is a convenience; the URL is also printed.\n }\n}\n\nexport interface LoginOptions {\n baseUrl: string\n clientName?: string\n /** Open the browser automatically. Default true. */\n open?: boolean\n /** Where human-facing prompts go. Default: no-op. */\n log?: (message: string) => void\n}\n\n/**\n * Run the full device flow and cache the resulting credential. Returns the\n * stored credential (token, project, expiry).\n */\nexport async function login(options: LoginOptions): Promise<StoredCredential> {\n const log = options.log ?? (() => {})\n\n const start = await startDeviceAuth(options.baseUrl, options.clientName)\n log(\n `\\nOpen this URL to connect:\\n ${start.verification_uri_complete}\\n\\n` +\n `Confirm this code matches:\\n ${start.user_code}\\n\\nWaiting for approval...\\n`\n )\n if (options.open !== false) await openBrowser(start.verification_uri_complete)\n\n const token = await pollDeviceToken(options.baseUrl, start)\n const cred: StoredCredential = {\n access_token: token.access_token,\n project_id: token.project_id,\n expires_at: new Date(Date.now() + token.expires_in * 1000).toISOString(),\n base_url: options.baseUrl,\n }\n saveCredential(cred)\n return cred\n}\n","/**\n * Cookie helpers, framework-agnostic by design. They return strings: a\n * `Set-Cookie` header value to write, or a parsed token to read. Your framework\n * applies them (`headers.append('Set-Cookie', …)`, SvelteKit `cookies.set`, a\n * Next `Response` cookie, etc.). The SDK never owns a request or response.\n *\n * Defaults match how DontCode's own apps store the session: an httpOnly cookie\n * so JavaScript can't read the token, `Secure`, `SameSite=Lax`, path `/`, and a\n * 7-day max age. Cross-site setups (your app and the gateway on different sites)\n * need `sameSite: 'none'`, which forces `Secure` on.\n */\n\n/** Default cookie name for the end-user access token. */\nexport const DEFAULT_SESSION_COOKIE_NAME = 'dc_access_token'\n\n/** One week, in seconds. Matches the default session lifetime DontCode issues. */\nconst DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 7\n\nexport interface SessionCookieOptions {\n /** Cookie name. Default `dc_access_token`. */\n name?: string\n /** Lifetime in seconds. Default one week. Pass the token's `ExpiresIn` to\n * keep the cookie and the token in lockstep. */\n maxAge?: number\n /** Default `/`. */\n path?: string\n domain?: string\n /** Default `true`. */\n secure?: boolean\n /** Default `true`. Keep the token unreadable from client JavaScript. */\n httpOnly?: boolean\n /** Default `'lax'`. Use `'none'` for cross-site (it forces `Secure`). */\n sameSite?: 'lax' | 'strict' | 'none'\n}\n\nfunction serialize(name: string, value: string, options: SessionCookieOptions, maxAge: number): string {\n const sameSite = options.sameSite ?? 'lax'\n // SameSite=None is meaningless without Secure (browsers drop it), so force\n // Secure on there; otherwise it defaults on and the caller can opt out.\n const secure = sameSite === 'none' ? true : (options.secure ?? true)\n const httpOnly = options.httpOnly ?? true\n const path = options.path ?? '/'\n\n const parts = [`${name}=${encodeURIComponent(value)}`, `Path=${path}`, `Max-Age=${maxAge}`]\n if (options.domain) parts.push(`Domain=${options.domain}`)\n parts.push(`SameSite=${sameSite.charAt(0).toUpperCase()}${sameSite.slice(1)}`)\n if (httpOnly) parts.push('HttpOnly')\n if (secure) parts.push('Secure')\n return parts.join('; ')\n}\n\n/** Build a `Set-Cookie` value that stores the access token. */\nexport function serializeSessionCookie(token: string, options: SessionCookieOptions = {}): string {\n const name = options.name ?? DEFAULT_SESSION_COOKIE_NAME\n const maxAge = options.maxAge ?? DEFAULT_MAX_AGE_SECONDS\n return serialize(name, token, options, maxAge)\n}\n\n/** Build a `Set-Cookie` value that clears the access token (logout). */\nexport function clearSessionCookie(options: SessionCookieOptions = {}): string {\n const name = options.name ?? DEFAULT_SESSION_COOKIE_NAME\n return serialize(name, '', options, 0)\n}\n\n/** Read the access token out of a `Cookie` request header, or `null`. Pass the\n * raw header string (`name=value; name2=value2`). */\nexport function readSessionToken(\n cookieHeader: string | null | undefined,\n name: string = DEFAULT_SESSION_COOKIE_NAME\n): string | null {\n if (!cookieHeader) return null\n for (const pair of cookieHeader.split(';')) {\n const eq = pair.indexOf('=')\n if (eq === -1) continue\n if (pair.slice(0, eq).trim() !== name) continue\n const raw = pair.slice(eq + 1).trim()\n if (!raw) return null\n try {\n return decodeURIComponent(raw)\n } catch {\n return raw\n }\n }\n return null\n}\n","import { isDontCodeError } from './errors'\nimport type { AuthApi } from './auth'\nimport type { CurrentUser } from './types'\n\n/**\n * Session helpers, framework-agnostic by design. They never touch a request or\n * response object; they take a token (or a cookie header) and return plain\n * data, so they slot into any framework's guard (Next middleware, SvelteKit\n * hooks, Express, a worker) without an adapter.\n *\n * The point of the module: an auth guard must not make a network round-trip on\n * every navigation, or a slow gateway stalls the page and a swallowed timeout\n * reads as \"signed out\". So there are two modes:\n *\n * - optimistic — decode the token locally (no signature check, no network)\n * and trust its claims for routing. Instant. Used for the common gate.\n * - verified — call the gateway's `me` once, cache the result for a short\n * TTL, and hard-timeout the request. Used for sensitive actions.\n *\n * The trade-offs (no signature verification, revocation lag) are documented on\n * `getSession` and in the public BYOC docs; read them before choosing a mode.\n */\n\n/** A JWT payload decoded WITHOUT verifying its signature. Trust accordingly. */\nexport interface DecodedSession {\n /** Subject — the user id. */\n sub: string\n email?: string\n role?: string\n claims?: Record<string, unknown>\n /** Expiry, seconds since the epoch (standard JWT `exp`). */\n exp?: number\n /** Issued-at, seconds since the epoch (standard JWT `iat`). */\n iat?: number\n [key: string]: unknown\n}\n\nexport type SessionStatus =\n /** A usable session: a present, unexpired token (verified or optimistic). */\n | 'active'\n /** The token is present but past its `exp`. */\n | 'expired'\n /** No token, or an unparseable one. */\n | 'anonymous'\n /** Verified mode could not reach the gateway (timeout/network/5xx). The\n * optimistically-decoded `user` is still returned so the caller can choose\n * to fail open during an outage instead of logging everyone out. */\n | 'unavailable'\n\nexport interface SessionResult {\n status: SessionStatus\n /** The signed-in user, or `null` when anonymous/expired. In `verified` mode\n * this came from the gateway; in `optimistic` (or `unavailable`) it was\n * decoded from the token's own claims. */\n user: CurrentUser | null\n /** True only when `user` was confirmed by a gateway `me` call this request\n * (or from a fresh cache entry of one). False for optimistic decodes. */\n verified: boolean\n /** The token's `exp` (seconds since epoch), when present. */\n expiresAt?: number\n}\n\nexport interface GetSessionInput {\n accessToken: string\n /** `optimistic` (default): decode locally, zero network. `verified`: confirm\n * against the gateway with caching + a hard timeout. */\n mode?: 'optimistic' | 'verified'\n}\n\n/** A place to cache verified sessions. Swap the default in-memory store for a\n * shared one (Redis, KV) when running multiple instances. */\nexport interface SessionCache {\n get(token: string): SessionResult | undefined\n set(token: string, value: SessionResult, ttlMs: number): void\n delete?(token: string): void\n}\n\nexport interface SessionOptions {\n /** Cache for verified sessions. Defaults to a per-process `InMemorySessionCache`. */\n cache?: SessionCache\n /** How long a verified session stays cached. Default 60_000 (60s). Keep it\n * short: a cached session can outlive a server-side revocation by up to\n * this long. */\n ttlMs?: number\n /** Timeout for the `me` call made by `verified` mode (ms). Default 5_000. */\n verifyTimeoutMs?: number\n}\n\nconst DEFAULT_TTL_MS = 60_000\nconst DEFAULT_VERIFY_TIMEOUT_MS = 5_000\n\n/** Default cache: a `Map` with per-entry TTL. Lives for the life of the process\n * (and is shared across requests on a reused serverless instance). */\nexport class InMemorySessionCache implements SessionCache {\n private readonly store = new Map<string, { value: SessionResult; expiresAtMs: number }>()\n\n get(token: string): SessionResult | undefined {\n const hit = this.store.get(token)\n if (!hit) return undefined\n if (Date.now() >= hit.expiresAtMs) {\n this.store.delete(token)\n return undefined\n }\n return hit.value\n }\n\n set(token: string, value: SessionResult, ttlMs: number): void {\n this.store.set(token, { value, expiresAtMs: Date.now() + ttlMs })\n }\n\n delete(token: string): void {\n this.store.delete(token)\n }\n}\n\nfunction base64UrlDecode(segment: string): string {\n const base64 = segment.replace(/-/g, '+').replace(/_/g, '/')\n const padded = base64.length % 4 === 0 ? base64 : base64 + '='.repeat(4 - (base64.length % 4))\n if (typeof atob === 'function') {\n const binary = atob(padded)\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0))\n return new TextDecoder().decode(bytes)\n }\n // Node without a global atob (older runtimes); Buffer is always present there.\n return Buffer.from(padded, 'base64').toString('utf8')\n}\n\n/**\n * Decode a JWT access token's payload WITHOUT verifying its signature, or\n * return `null` if it is not a parseable JWT. This is deliberately cheap and\n * offline; it is not proof the token is genuine. DontCode tokens are signed\n * with a secret the gateway never shares, so the only authority on a token is\n * the gateway's `me` endpoint — use `getSession({ mode: 'verified' })` when you\n * need that authority.\n */\nexport function decodeAccessToken(token: string): DecodedSession | null {\n if (!token || typeof token !== 'string') return null\n const parts = token.split('.')\n if (parts.length < 2) return null\n try {\n const payload = JSON.parse(base64UrlDecode(parts[1])) as Record<string, unknown>\n if (!payload || typeof payload.sub !== 'string') return null\n return payload as DecodedSession\n } catch {\n return null\n }\n}\n\n/** True when a token (or already-decoded payload) is past its `exp`. A token\n * with no `exp` is treated as not expired (the caller cannot prove otherwise\n * offline). `skewSeconds` widens the window to absorb clock drift. */\nexport function isSessionExpired(\n input: string | DecodedSession | null,\n opts: { skewSeconds?: number } = {}\n): boolean {\n const decoded = typeof input === 'string' ? decodeAccessToken(input) : input\n if (!decoded || typeof decoded.exp !== 'number') return false\n const nowSeconds = Date.now() / 1000\n return nowSeconds >= decoded.exp - (opts.skewSeconds ?? 0)\n}\n\nfunction userFromClaims(decoded: DecodedSession): CurrentUser {\n return {\n id: decoded.sub,\n email: typeof decoded.email === 'string' ? decoded.email : '',\n role: decoded.role,\n claims: decoded.claims,\n }\n}\n\n/**\n * Resolves access tokens into sessions for `AuthApi`. Holds the verified-session\n * cache and timeout policy. Not exported directly; reach it via\n * `client.auth.getSession` / `client.auth.sessionFromCookies`.\n */\nexport class SessionVerifier {\n private readonly cache: SessionCache\n private readonly ttlMs: number\n private readonly verifyTimeoutMs: number\n\n constructor(\n private readonly auth: AuthApi,\n options: SessionOptions = {}\n ) {\n this.cache = options.cache ?? new InMemorySessionCache()\n this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS\n this.verifyTimeoutMs = options.verifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS\n }\n\n async getSession({ accessToken, mode = 'optimistic' }: GetSessionInput): Promise<SessionResult> {\n const decoded = decodeAccessToken(accessToken)\n if (!decoded) return { status: 'anonymous', user: null, verified: false }\n if (isSessionExpired(decoded)) {\n return { status: 'expired', user: null, verified: false, expiresAt: decoded.exp }\n }\n\n const optimistic: SessionResult = {\n status: 'active',\n user: userFromClaims(decoded),\n verified: false,\n expiresAt: decoded.exp,\n }\n if (mode === 'optimistic') return optimistic\n\n const cached = this.cache.get(accessToken)\n if (cached) return cached\n\n try {\n const { user } = await this.auth.me({\n accessToken,\n timeoutMs: this.verifyTimeoutMs,\n })\n const result: SessionResult = user\n ? { status: 'active', user, verified: true, expiresAt: decoded.exp }\n : { status: 'anonymous', user: null, verified: true }\n this.cache.set(accessToken, result, this.ttlMs)\n return result\n } catch (err) {\n // A real 401 means the gateway rejected the token: the user is out.\n if (isDontCodeError(err) && err.status === 401) {\n return { status: 'anonymous', user: null, verified: true }\n }\n // Timeout / network / 5xx: the backend is unreachable, not a verdict\n // on the user. Hand back the optimistic session marked unavailable so\n // the caller decides whether to fail open. Not cached.\n return { ...optimistic, status: 'unavailable' }\n }\n }\n}\n","import { Transport } from './http'\nimport { readSessionToken } from './cookies'\nimport {\n SessionVerifier,\n decodeAccessToken,\n type DecodedSession,\n type GetSessionInput,\n type SessionOptions,\n type SessionResult,\n} from './session'\nimport type {\n ForgotPasswordInput,\n LoginInput,\n LoginResult,\n MeResult,\n MfaChallengeInput,\n MfaDisableInput,\n MfaEnrollConfirmInput,\n MfaEnrollResult,\n ResetPasswordInput,\n SignupInput,\n SignupResult,\n SimpleResult,\n VerifyEmailInput,\n} from './types'\n\nconst AUTH_BASE = '/api/v1/auth'\n\n/** Shape of `GET /api/v1/info`: validates the credential and reports what it\n * can do. For device tokens, capabilities follow the signed-in user's role. */\nexport interface InfoResult {\n project: { id: string; name: string | null }\n credential: {\n type: 'api_key' | 'device'\n role: string | null\n user_id: string | null\n }\n capabilities: Record<string, boolean>\n}\n\n/**\n * MFA is per-user and opt-in. `enroll`/`enrollConfirm`/`disable` act as the\n * signed-in user, so they need the end-user access token. `challenge` does\n * not; it completes a login that returned `mfa_required`, exchanging the\n * short-lived challenge token for real session tokens.\n */\nexport class MfaApi {\n constructor(private readonly transport: Transport) {}\n\n /** Complete an MFA login. Pass the `challenge_token` from `login`, plus\n * either the authenticator `code` or a `recoveryCode`. */\n challenge(input: MfaChallengeInput): Promise<LoginResult> {\n return this.transport.json<LoginResult>(`${AUTH_BASE}/mfa/challenge`, {\n challenge_token: input.challengeToken,\n code: input.code,\n recovery_code: input.recoveryCode,\n })\n }\n\n /** Begin enrollment. Render the returned `otpauth_url` as a QR code.\n * Enrollment stays pending until `enrollConfirm`. */\n enroll(input: { accessToken: string }): Promise<MfaEnrollResult> {\n return this.transport.json<MfaEnrollResult>(\n `${AUTH_BASE}/mfa/enroll`,\n {},\n { accessToken: input.accessToken }\n )\n }\n\n /** Confirm enrollment with the first authenticator code. The returned\n * `recovery_codes` are shown once and never again. */\n enrollConfirm(input: MfaEnrollConfirmInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(\n `${AUTH_BASE}/mfa/enroll/confirm`,\n { code: input.code },\n { accessToken: input.accessToken }\n )\n }\n\n /** Turn MFA off. Proves possession of the second factor via `code` or\n * `recoveryCode`. */\n disable(input: MfaDisableInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(\n `${AUTH_BASE}/mfa/disable`,\n { code: input.code, recovery_code: input.recoveryCode },\n { accessToken: input.accessToken }\n )\n }\n}\n\n/**\n * Fronts DontCode Auth with the same shapes as the gateway. Two behaviours are\n * project settings (not API flags) and your code must handle both states:\n * email verification (signup may not return tokens) and MFA (login may be two\n * steps). Branch on the resolved value; never assume one round-trip.\n */\nexport class AuthApi {\n readonly mfa: MfaApi\n private readonly sessions: SessionVerifier\n\n constructor(\n private readonly transport: Transport,\n sessionOptions?: SessionOptions\n ) {\n this.mfa = new MfaApi(transport)\n this.sessions = new SessionVerifier(this, sessionOptions)\n }\n\n /** Create an account. If the project requires email verification the\n * response has `verification_required: true` and NO tokens; collect a\n * code and call `verifyEmail`, then `login`. */\n signup(input: SignupInput): Promise<SignupResult> {\n return this.transport.json<SignupResult>(`${AUTH_BASE}/signup`, {\n email: input.email,\n password: input.password,\n name: input.name,\n role: input.role,\n })\n }\n\n /** Authenticate. Branch on `mfa_required`: when true you hold only a\n * challenge (finish via `mfa.challenge`); otherwise `tokens` is your\n * session. A 403 `EmailNotVerified` means the email step isn't done. */\n login(input: LoginInput): Promise<LoginResult> {\n return this.transport.json<LoginResult>(`${AUTH_BASE}/login`, {\n email: input.email,\n password: input.password,\n })\n }\n\n /** Validate the current credential (API key or device token) and report the\n * project, the caller's role, and which capabilities that role grants.\n * Backs the MCP \"is my session still good\" check. */\n info(): Promise<InfoResult> {\n return this.transport.get<InfoResult>('/api/v1/info')\n }\n\n /** Resolve the signed-in user from their access token, or `{ user: null }`.\n * This is a network round-trip; for a per-navigation guard prefer\n * `getSession`, which can answer offline and caches verified results. */\n me(input: { accessToken: string; timeoutMs?: number }): Promise<MeResult> {\n return this.transport.json<MeResult>(\n `${AUTH_BASE}/me`,\n {},\n { accessToken: input.accessToken, timeoutMs: input.timeoutMs }\n )\n }\n\n /**\n * Resolve an access token into a session for a route guard, the one call\n * that replaces \"hit `me` on every navigation\". Two modes:\n *\n * - `'optimistic'` (default): decode the token locally and trust its\n * claims. Zero network, zero stall. The right default for gating page\n * loads. It does NOT verify the signature and will not notice a\n * server-side revocation until the token's own `exp`.\n * - `'verified'`: confirm against the gateway's `me`, cached for a short\n * TTL with a hard timeout. Use it before sensitive actions. On a\n * timeout/outage it returns `status: 'unavailable'` with the optimistic\n * user, so you choose whether to fail open rather than the SDK guessing.\n *\n * See the BYOC docs (\"Sessions\") for the full reasoning and best practices.\n */\n getSession(input: GetSessionInput): Promise<SessionResult> {\n return this.sessions.getSession(input)\n }\n\n /** Read the access token from a `Cookie` request header and resolve it, in\n * one call. `name` defaults to `dc_access_token`. Returns the anonymous\n * session when no cookie is present. */\n sessionFromCookies(\n cookieHeader: string | null | undefined,\n options: { mode?: GetSessionInput['mode']; cookieName?: string } = {}\n ): Promise<SessionResult> {\n const token = readSessionToken(cookieHeader, options.cookieName)\n if (!token) return Promise.resolve({ status: 'anonymous', user: null, verified: false })\n return this.sessions.getSession({ accessToken: token, mode: options.mode })\n }\n\n /** Decode an access token's claims locally without a network call or any\n * signature check. Convenience re-export of `decodeAccessToken`. */\n decodeToken(token: string): DecodedSession | null {\n return decodeAccessToken(token)\n }\n\n /** Confirm the 6-digit code emailed at signup. */\n verifyEmail(input: VerifyEmailInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(`${AUTH_BASE}/verify-email`, {\n code: input.code,\n email: input.email,\n })\n }\n\n forgotPassword(input: ForgotPasswordInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(`${AUTH_BASE}/forgot-password`, {\n email: input.email,\n })\n }\n\n resetPassword(input: ResetPasswordInput): Promise<SimpleResult> {\n return this.transport.json<SimpleResult>(`${AUTH_BASE}/reset-password`, {\n code: input.code,\n password: input.password,\n email: input.email,\n })\n }\n}\n","import { Transport } from './http'\nimport type {\n DeleteInput,\n MigrateInput,\n MigrateResult,\n QueryOptions,\n UpdateInput,\n} from './types'\n\nconst DB_PATH = '/api/v1/db'\nconst MIGRATE_PATH = '/api/v1/db/migrate'\n\n/** The gateway wraps every DB result in `{ data }`; we unwrap it. */\ninterface DbEnvelope<T> {\n data: T\n}\n\n/**\n * A handle to one table. Structured queries only; there is no raw-SQL escape\n * hatch (schema changes go through `db.migrate`). `update` and `delete`\n * require a `where` clause server-side, so the types make it mandatory.\n */\nexport class TableQuery {\n constructor(\n private readonly transport: Transport,\n private readonly tableName: string\n ) {}\n\n private async run<T>(operation: string, options: unknown): Promise<T> {\n const res = await this.transport.json<DbEnvelope<T>>(DB_PATH, {\n operation,\n tableName: this.tableName,\n options,\n })\n return res.data\n }\n\n /** Rows matching the query (max 1000 per call). */\n find<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T[]> {\n return this.run<T[]>('find', options)\n }\n\n /** Alias of `find`. */\n findMany<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T[]> {\n return this.run<T[]>('findMany', options)\n }\n\n /** The first matching row, or `null`. */\n findFirst<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T | null> {\n return this.run<T | null>('findFirst', options)\n }\n\n /** Alias of `findFirst`. */\n findOne<T = Record<string, unknown>>(options: QueryOptions = {}): Promise<T | null> {\n return this.run<T | null>('findOne', options)\n }\n\n /** Insert one row. Returns `{ id }`. Unique/FK conflicts throw a 409\n * DontCodeError, the supported idempotency signal. */\n insert(data: Record<string, unknown>): Promise<{ id: unknown }> {\n return this.run<{ id: unknown }>('insert', { data })\n }\n\n /** Update rows matching `where`. Returns `{ count }`. */\n update(input: UpdateInput): Promise<{ count: number }> {\n return this.run<{ count: number }>('update', { where: input.where, data: input.data })\n }\n\n /** Delete rows matching `where`. Returns `{ count }`. */\n delete(input: DeleteInput): Promise<{ count: number }> {\n return this.run<{ count: number }>('delete', { where: input.where })\n }\n\n /** Count matching rows. */\n count(options: Pick<QueryOptions, 'where'> = {}): Promise<number> {\n return this.run<number>('count', options)\n }\n}\n\n/**\n * `db.users.find()` and `db('users').find()` both work; the bracket/callable\n * form is there for table names that aren't valid identifiers. `db.migrate()`\n * applies schema DDL (the one place migrations enter from outside).\n */\nexport type DbClient = {\n readonly [tableName: string]: TableQuery\n} & {\n (tableName: string): TableQuery\n migrate(input: MigrateInput): Promise<MigrateResult>\n}\n\nexport function createDb(transport: Transport): DbClient {\n const table = (tableName: string): TableQuery => new TableQuery(transport, tableName)\n const migrate = (input: MigrateInput): Promise<MigrateResult> =>\n transport.json<MigrateResult>(MIGRATE_PATH, { sql: input.sql })\n\n return new Proxy(table, {\n get(target, prop, receiver) {\n if (prop === 'migrate') return migrate\n // Don't manufacture a TableQuery for symbols or promise-unwrapping\n // probes (`then`) or the function's own members.\n if (typeof prop !== 'string' || prop === 'then' || prop in target) {\n return Reflect.get(target, prop, receiver)\n }\n return new TableQuery(transport, prop)\n },\n apply(_target, _thisArg, args: [string]) {\n return table(args[0])\n },\n }) as unknown as DbClient\n}\n","import { Transport } from './http'\nimport type {\n DownloadResult,\n ListResult,\n PresignResult,\n StorageBucket,\n StorageObject,\n TemporaryUrlResult,\n UploadBody,\n} from './types'\n\nconst STORAGE_PATH = '/api/v1/storage'\n\nconst DEFAULT_CONTENT_TYPE = 'application/octet-stream'\n\n/** Normalize whatever the caller hands us into a Blob for multipart upload. */\nfunction toBlob(body: UploadBody, contentType: string): Blob {\n if (body instanceof Blob) return body\n if (typeof body === 'string') return new Blob([body], { type: contentType })\n if (body instanceof ArrayBuffer) return new Blob([body], { type: contentType })\n if (ArrayBuffer.isView(body)) {\n return new Blob([body as unknown as BlobPart], { type: contentType })\n }\n throw new TypeError('upload expects a Blob, ArrayBuffer, typed array, or string')\n}\n\nfunction fileName(path: string): string {\n return path.split('/').filter(Boolean).pop() ?? path\n}\n\n/** Operations available on both buckets. */\nexport class BucketClient {\n constructor(\n protected readonly transport: Transport,\n protected readonly bucket: StorageBucket\n ) {}\n\n protected op<T>(operation: string, params: Record<string, unknown> = {}): Promise<T> {\n return this.transport.json<T>(STORAGE_PATH, { operation, bucket: this.bucket, ...params })\n }\n\n /** List objects under `prefix`. */\n list(prefix?: string): Promise<ListResult> {\n return this.op<ListResult>('list', { prefix })\n }\n\n /** Delete one or more objects. Returns `{ deleted }`. */\n remove(paths: string[]): Promise<{ deleted: number }> {\n return this.op<{ deleted: number }>('remove', { paths })\n }\n\n /** Move/rename an object within the bucket. */\n move(from: string, to: string): Promise<{ object: StorageObject }> {\n return this.op<{ object: StorageObject }>('move', { from, to })\n }\n\n createFolder(path: string): Promise<{ created: string }> {\n return this.op<{ created: string }>('createFolder', { path })\n }\n\n /** Download an object inline (≤ 8 MB). Use `getTemporaryUrl` for larger files. */\n download(path: string): Promise<DownloadResult> {\n return this.op<DownloadResult>('download', { path })\n }\n\n /** A short-lived signed URL (default 300s, max 7 days). */\n getTemporaryUrl(path: string, expiresIn?: number): Promise<TemporaryUrlResult> {\n return this.op<TemporaryUrlResult>('getTemporaryUrl', { path, expiresIn })\n }\n\n /** A presigned PUT URL for direct, large uploads (≤ no inline limit). */\n presignUpload(path: string, contentType?: string): Promise<PresignResult> {\n return this.op<PresignResult>('presignUpload', { path, contentType })\n }\n\n /** Upload bytes directly (≤ 100 MB). For larger files, `presignUpload`\n * then PUT to the returned URL yourself. */\n upload(\n path: string,\n body: UploadBody,\n contentType: string = DEFAULT_CONTENT_TYPE\n ): Promise<{ object: StorageObject }> {\n const form = new FormData()\n form.append('file', toBlob(body, contentType), fileName(path))\n form.append('bucket', this.bucket)\n form.append('path', path)\n form.append('contentType', contentType)\n return this.transport.multipart<{ object: StorageObject }>(STORAGE_PATH, form)\n }\n}\n\n/** The public bucket additionally exposes stable public URLs. */\nexport class PublicBucketClient extends BucketClient {\n constructor(transport: Transport) {\n super(transport, 'public')\n }\n\n /** The permanent public URL for an object. */\n getUrl(path: string): Promise<{ url: string }> {\n return this.op<{ url: string }>('getUrl', { path })\n }\n}\n\nexport interface StorageClient {\n public: PublicBucketClient\n private: BucketClient\n}\n\nexport function createStorage(transport: Transport): StorageClient {\n return {\n public: new PublicBucketClient(transport),\n private: new BucketClient(transport, 'private'),\n }\n}\n","import { AuthApi } from './auth'\nimport { createDb, type DbClient } from './db'\nimport { Transport } from './http'\nimport type { SessionOptions } from './session'\nimport { createStorage, type StorageClient } from './storage'\n\nconst DEFAULT_BASE_URL = 'https://backend.dontcode.co'\n\nexport interface DontCodeClientOptions {\n /** Project API key (`dc_…`). Defaults to `process.env.DONTCODE_API_KEY`.\n * If neither is set, requests fail naturally with the gateway's\n * \"Missing API key\" 401. */\n apiKey?: string\n /** Gateway origin. Defaults to `process.env.DONTCODE_API_URL`, then to\n * `https://backend.dontcode.co`. */\n baseUrl?: string\n /** Per-request network timeout in ms. Defaults to 10_000; `0` disables it.\n * Without one, a slow gateway can hang a request for the full socket\n * timeout, the worst case for an auth guard. */\n timeoutMs?: number\n /** Caching + timeout policy for `auth.getSession` / `auth.sessionFromCookies`. */\n session?: SessionOptions\n}\n\nexport interface DontCodeClient {\n auth: AuthApi\n db: DbClient\n storage: StorageClient\n}\n\n/** Read an env var without assuming `process` exists (e.g. in the browser). */\nfunction fromEnv(name: string): string | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined\n return process.env[name]\n}\n\n/**\n * Create a DontCode backend client. A thin, typed proxy over the v1 HTTP\n * gateway: auth, database, and storage. The API key scopes every request to\n * a single project; there is nothing else to configure.\n *\n * ```ts\n * import { dontcode } from '@dontcode2/backend'\n * const client = dontcode() // reads DONTCODE_API_KEY\n * await client.auth.signup({ email, password, role: 'editor' })\n * ```\n */\nexport function dontcode(options: DontCodeClientOptions = {}): DontCodeClient {\n const apiKey = options.apiKey ?? fromEnv('DONTCODE_API_KEY')\n const baseUrl = (options.baseUrl ?? fromEnv('DONTCODE_API_URL') ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n )\n\n const transport = new Transport({ apiKey, baseUrl, timeoutMs: options.timeoutMs })\n\n return {\n auth: new AuthApi(transport, options.session),\n db: createDb(transport),\n storage: createStorage(transport),\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport {\n login as runLogin,\n openBrowser,\n pollDeviceToken,\n startDeviceAuth,\n type DeviceStartResponse,\n} from '../auth-device'\nimport { dontcode, type DontCodeClient } from '../client'\nimport {\n clearCredential,\n resolveActiveToken,\n saveCredential,\n} from '../credentials'\nimport { isDontCodeError } from '../errors'\n\n/**\n * DontCode Backend MCP server (stdio).\n *\n * Exposes the v1 gateway to an AI agent (Claude Code and others) as tools:\n * sign in by browser, query and mutate the project database, run migrations,\n * manage storage, and check the current session. Every tool is a thin call\n * onto the public SDK, so the agent can only do what the gateway allows — and,\n * for device-token sessions, only what the signed-in user's project role\n * allows.\n *\n * IMPORTANT: stdout is the MCP transport. All human-facing logging goes to\n * stderr (console.error), never console.log.\n */\n\nconst SERVER_NAME = 'dontcode-backend'\nconst SERVER_VERSION = '0.3.0'\n\nfunction baseUrl(): string {\n return (process.env.DONTCODE_API_URL || 'https://backend.dontcode.co').replace(/\\/+$/, '')\n}\n\n/** A login flow waiting to be polled by `auth_wait`, kept off the wire. */\nlet pendingFlow: DeviceStartResponse | null = null\n\nfunction text(value: unknown) {\n const body = typeof value === 'string' ? value : JSON.stringify(value, null, 2)\n return { content: [{ type: 'text' as const, text: body }] }\n}\n\nfunction failure(message: string) {\n return { content: [{ type: 'text' as const, text: message }], isError: true }\n}\n\n/** Turn SDK errors into agent-friendly guidance without leaking internals. */\nfunction describeError(err: unknown): string {\n if (isDontCodeError(err)) {\n if (err.status === 401) {\n return 'Not signed in or the session expired. Use the `auth_login` tool, approve in the browser, then `auth_wait`.'\n }\n if (err.status === 403) {\n return `Your project role does not allow that. (${err.message})`\n }\n if (err.rateLimited) {\n return `Rate limited. ${err.message}`\n }\n return err.message\n }\n return err instanceof Error ? err.message : 'Unknown error'\n}\n\nfunction requireClient(): DontCodeClient {\n const active = resolveActiveToken(baseUrl())\n if (!active.token) {\n throw new Error(\n 'Not signed in. Use the `auth_login` tool first (or set DONTCODE_API_KEY for non-interactive use).'\n )\n }\n return dontcode({ apiKey: active.token, baseUrl: baseUrl() })\n}\n\n/** Run a tool body, mapping thrown errors to an MCP error result. */\nasync function run(fn: () => Promise<unknown>) {\n try {\n return text(await fn())\n } catch (err) {\n return failure(describeError(err))\n }\n}\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION })\n\n // The MCP SDK infers each tool's argument type from its `inputSchema`\n // through deep conditional types. Across this many tools that inference\n // exhausts tsc's heap during declaration emit (TS2589). The gateway\n // re-validates every call, so these handlers never rely on the inferred\n // types — register through a thin wrapper that keeps inference shallow.\n const tool = (\n name: string,\n config: {\n title: string\n description: string\n inputSchema?: z.ZodRawShape\n annotations?: Record<string, boolean>\n },\n handler: (args: Record<string, any>) => Promise<unknown>\n ) => server.registerTool(name, config as never, handler as never)\n\n // --- Authentication ----------------------------------------------------\n\n tool(\n 'auth_login',\n {\n title: 'Sign in to DontCode',\n description:\n 'Start a browser sign-in. Returns a URL and a short code; tell the user to open the URL, confirm the code matches, pick a project, and approve. Then call `auth_wait`. Not needed if DONTCODE_API_KEY is set.',\n inputSchema: {\n client_name: z\n .string()\n .optional()\n .describe('Label shown to the user on the approval screen, e.g. \"Claude Code\".'),\n },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ client_name }) =>\n run(async () => {\n const start = await startDeviceAuth(baseUrl(), client_name ?? 'Claude Code (MCP)')\n pendingFlow = start\n await openBrowser(start.verification_uri_complete)\n return {\n message:\n 'Ask the user to open this URL, confirm the code, choose a project, and approve. Then call auth_wait.',\n verification_uri: start.verification_uri_complete,\n user_code: start.user_code,\n expires_in_seconds: start.expires_in,\n }\n })\n )\n\n tool(\n 'auth_wait',\n {\n title: 'Wait for sign-in approval',\n description:\n 'Poll for the result of `auth_login`. Returns connected once the user approves in the browser, or asks you to call it again if still pending. Safe to call repeatedly.',\n inputSchema: {},\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async () =>\n run(async () => {\n if (!pendingFlow) {\n return { status: 'no_login_in_progress', hint: 'Call auth_login first.' }\n }\n try {\n const token = await pollDeviceToken(baseUrl(), pendingFlow, {\n maxWaitMs: 50_000,\n })\n saveCredential({\n access_token: token.access_token,\n project_id: token.project_id,\n expires_at: new Date(Date.now() + token.expires_in * 1000).toISOString(),\n base_url: baseUrl(),\n })\n pendingFlow = null\n return { status: 'connected', project_id: token.project_id }\n } catch (err) {\n if (isDontCodeError(err) && err.code === 'WaitTimeout') {\n return {\n status: 'pending',\n hint: 'Still waiting for approval. Ask the user to approve, then call auth_wait again.',\n }\n }\n pendingFlow = null\n throw err\n }\n })\n )\n\n tool(\n 'auth_status',\n {\n title: 'Check the current session',\n description:\n 'Validate the current credential and report the project, your role, and what you are allowed to do.',\n inputSchema: {},\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async () =>\n run(async () => {\n const active = resolveActiveToken(baseUrl())\n if (!active.token) {\n return { signed_in: false, hint: 'Use auth_login to sign in.' }\n }\n const client = dontcode({ apiKey: active.token, baseUrl: baseUrl() })\n // /api/v1/info is a thin GET; reuse the transport via a raw call.\n const info = await client.auth.info()\n return { signed_in: true, source: active.source, ...info }\n })\n )\n\n tool(\n 'auth_logout',\n {\n title: 'Forget the cached session',\n description: 'Remove the locally cached device token for this gateway.',\n inputSchema: {},\n annotations: { readOnlyHint: false, destructiveHint: true },\n },\n async () =>\n run(async () => {\n clearCredential(baseUrl())\n pendingFlow = null\n return { ok: true }\n })\n )\n\n // --- Database ----------------------------------------------------------\n\n tool(\n 'db_query',\n {\n title: 'Query the database',\n description:\n 'Read rows from a table with a structured query (no raw SQL). Supports where/select/orderBy/limit/offset and count.',\n inputSchema: {\n table: z.string(),\n operation: z\n .enum(['find', 'findMany', 'findFirst', 'findOne', 'count'])\n .default('find'),\n where: z.record(z.string(), z.any()).optional(),\n select: z.array(z.string()).optional(),\n orderBy: z.record(z.string(), z.enum(['asc', 'desc'])).optional(),\n limit: z.number().int().positive().max(1000).optional(),\n offset: z.number().int().nonnegative().optional(),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ table, operation, where, select, orderBy, limit, offset }) =>\n run(async () => {\n const t = requireClient().db(table)\n if (operation === 'count') return { count: await t.count({ where }) }\n const options = { where, select, orderBy, limit, offset }\n if (operation === 'findFirst' || operation === 'findOne') {\n return { row: await t.findFirst(options) }\n }\n return { rows: await t.find(options) }\n })\n )\n\n tool(\n 'db_insert',\n {\n title: 'Insert a row',\n description: 'Insert one row into a table. Returns the new row id.',\n inputSchema: { table: z.string(), data: z.record(z.string(), z.any()) },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ table, data }) => run(async () => requireClient().db(table).insert(data))\n )\n\n tool(\n 'db_update',\n {\n title: 'Update rows',\n description: 'Update rows matching a where clause. Returns the number of rows changed.',\n inputSchema: {\n table: z.string(),\n where: z.record(z.string(), z.any()),\n data: z.record(z.string(), z.any()),\n },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ table, where, data }) =>\n run(async () => requireClient().db(table).update({ where, data }))\n )\n\n tool(\n 'db_delete',\n {\n title: 'Delete rows',\n description:\n 'Delete rows matching a where clause. Destructive: confirm with the user before calling. Returns the number of rows deleted.',\n inputSchema: { table: z.string(), where: z.record(z.string(), z.any()) },\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n },\n async ({ table, where }) => run(async () => requireClient().db(table).delete({ where }))\n )\n\n tool(\n 'db_migrate',\n {\n title: 'Run a schema migration',\n description:\n 'Apply DDL (CREATE/ALTER/DROP TABLE, indexes, etc.) to the project database. Destructive and schema-shaping: confirm with the user, and note it needs an admin/owner role on device-token sessions.',\n inputSchema: { sql: z.string() },\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n },\n async ({ sql }) => run(async () => requireClient().db.migrate({ sql }))\n )\n\n // --- Storage -----------------------------------------------------------\n\n const bucketArg = z.enum(['public', 'private']).default('private')\n const bucketOf = (client: DontCodeClient, bucket: 'public' | 'private') =>\n bucket === 'public' ? client.storage.public : client.storage.private\n\n tool(\n 'storage_list',\n {\n title: 'List files',\n description: 'List objects in a storage bucket, optionally under a prefix.',\n inputSchema: { bucket: bucketArg, prefix: z.string().optional() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ bucket, prefix }) =>\n run(async () => bucketOf(requireClient(), bucket).list(prefix))\n )\n\n tool(\n 'storage_get_url',\n {\n title: 'Get a public URL',\n description: 'Get the permanent public URL for an object in the public bucket.',\n inputSchema: { path: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ path }) => run(async () => requireClient().storage.public.getUrl(path))\n )\n\n tool(\n 'storage_temporary_url',\n {\n title: 'Get a temporary URL',\n description: 'Get a short-lived signed URL for an object (default 300s, max 7 days).',\n inputSchema: {\n bucket: bucketArg,\n path: z.string(),\n expires_in: z.number().int().positive().optional(),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ bucket, path, expires_in }) =>\n run(async () => bucketOf(requireClient(), bucket).getTemporaryUrl(path, expires_in))\n )\n\n tool(\n 'storage_upload',\n {\n title: 'Upload a text file',\n description:\n 'Upload UTF-8 text content to a path. For binary or large files, use storage_temporary_url + a direct PUT instead.',\n inputSchema: {\n bucket: bucketArg,\n path: z.string(),\n content: z.string(),\n content_type: z.string().optional(),\n },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ bucket, path, content, content_type }) =>\n run(async () =>\n bucketOf(requireClient(), bucket).upload(\n path,\n content,\n content_type ?? 'text/plain'\n )\n )\n )\n\n tool(\n 'storage_remove',\n {\n title: 'Delete files',\n description: 'Delete one or more objects. Destructive: confirm with the user.',\n inputSchema: { bucket: bucketArg, paths: z.array(z.string()).min(1) },\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },\n },\n async ({ bucket, paths }) => run(async () => bucketOf(requireClient(), bucket).remove(paths))\n )\n\n tool(\n 'storage_move',\n {\n title: 'Move or rename a file',\n description: 'Move/rename an object within a bucket.',\n inputSchema: { bucket: bucketArg, from: z.string(), to: z.string() },\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n async ({ bucket, from, to }) =>\n run(async () => bucketOf(requireClient(), bucket).move(from, to))\n )\n\n return server\n}\n\n/** Entry point used by the `dontcode mcp` CLI command. */\nexport async function startMcpServer(): Promise<void> {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n console.error(`[${SERVER_NAME}] MCP server ready on stdio (gateway: ${baseUrl()})`)\n}\n\n// Re-export so a programmatic caller can drive the device flow too.\nexport { runLogin as login }\n","#!/usr/bin/env node\n/**\n * `dontcode` — the developer CLI for DontCode Backend.\n *\n * dontcode mcp # run the MCP server on stdio (for Claude Code etc.)\n * dontcode login # sign in via the browser, cache a device token\n * dontcode logout # forget the cached token\n * dontcode status # show the current session\n *\n * The gateway origin comes from DONTCODE_API_URL (default https://backend.dontcode.co).\n * For non-interactive use, set DONTCODE_API_KEY and skip `login` entirely.\n */\nimport { login } from './auth-device'\nimport { clearCredential, resolveActiveToken } from './credentials'\nimport { dontcode } from './client'\nimport { isDontCodeError } from './errors'\nimport { startMcpServer } from './mcp/server'\n\nfunction baseUrl(): string {\n return (process.env.DONTCODE_API_URL || 'https://backend.dontcode.co').replace(/\\/+$/, '')\n}\n\nconst HELP = `dontcode — developer CLI for DontCode Backend\n\nUsage: dontcode <command>\n\nCommands:\n mcp Run the MCP server on stdio (configure this in your AI tool)\n login Sign in through the browser and cache a short-lived token\n logout Remove the cached token for the current gateway\n status Show the current session (project, role, capabilities)\n help Show this help\n\nEnvironment:\n DONTCODE_API_URL Gateway origin (default https://backend.dontcode.co)\n DONTCODE_API_KEY A dc_ project key for non-interactive use (skips login)\n DONTCODE_CONFIG_DIR Where the cached token lives (default ~/.dontcode)\n`\n\nasync function cmdLogin(): Promise<void> {\n const cred = await login({\n baseUrl: baseUrl(),\n clientName: 'dontcode CLI',\n log: (m) => process.stderr.write(m),\n })\n process.stderr.write(\n `\\nSigned in. Project ${cred.project_id}, token valid until ${cred.expires_at}.\\n`\n )\n}\n\nasync function cmdStatus(): Promise<void> {\n const active = resolveActiveToken(baseUrl())\n if (!active.token) {\n process.stdout.write('Not signed in. Run `dontcode login`.\\n')\n return\n }\n try {\n const info = await dontcode({ apiKey: active.token, baseUrl: baseUrl() }).auth.info()\n process.stdout.write(\n JSON.stringify({ source: active.source, ...info }, null, 2) + '\\n'\n )\n } catch (err) {\n const message = isDontCodeError(err) ? err.message : String(err)\n process.stdout.write(`Session invalid: ${message}\\n`)\n }\n}\n\nasync function main(): Promise<void> {\n const command = process.argv[2]\n switch (command) {\n case 'mcp':\n await startMcpServer()\n break\n case 'login':\n await cmdLogin()\n break\n case 'logout':\n clearCredential(baseUrl())\n process.stdout.write('Signed out.\\n')\n break\n case 'status':\n await cmdStatus()\n break\n case undefined:\n case 'help':\n case '-h':\n case '--help':\n process.stdout.write(HELP)\n break\n default:\n process.stderr.write(`Unknown command: ${command}\\n\\n${HELP}`)\n process.exit(1)\n }\n}\n\nmain().catch((err) => {\n process.stderr.write((err instanceof Error ? err.message : String(err)) + '\\n')\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qBAAuD;AACvD,qBAAwB;AACxB,uBAA8B;AA0B9B,SAAS,YAAoB;AACzB,SAAO,QAAQ,IAAI,2BAAuB,2BAAK,wBAAQ,GAAG,WAAW;AACzE;AAEA,SAAS,kBAA0B;AAC/B,aAAO,uBAAK,UAAU,GAAG,kBAAkB;AAC/C;AAEA,SAAS,YAAmB;AACxB,MAAI;AACA,WAAO,KAAK,UAAM,6BAAa,gBAAgB,GAAG,MAAM,CAAC;AAAA,EAC7D,QAAQ;AACJ,WAAO,CAAC;AAAA,EACZ;AACJ;AAEA,SAAS,WAAW,OAAoB;AACpC,QAAM,OAAO,gBAAgB;AAC7B,oCAAU,0BAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACzD,oCAAc,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AACvE;AAEO,SAAS,eAAeA,UAA0C;AACrE,SAAO,UAAU,EAAEA,QAAO,KAAK;AACnC;AAEO,SAAS,eAAe,MAA8B;AACzD,QAAM,QAAQ,UAAU;AACxB,QAAM,KAAK,QAAQ,IAAI;AACvB,aAAW,KAAK;AACpB;AAEO,SAAS,gBAAgBA,UAAuB;AACnD,QAAM,QAAQ,UAAU;AACxB,SAAO,MAAMA,QAAO;AACpB,aAAW,KAAK;AACpB;AAGO,SAAS,UAAU,MAAwB,SAAS,KAAiB;AACxE,SAAO,IAAI,KAAK,KAAK,UAAU,EAAE,QAAQ,IAAI,UAAU,KAAK,IAAI;AACpE;AAcO,SAAS,mBAAmBA,UAA8B;AAC7D,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,IAAK,QAAO,EAAE,OAAO,KAAK,QAAQ,MAAM;AAE5C,QAAM,OAAO,eAAeA,QAAO;AACnC,MAAI,QAAQ,CAAC,UAAU,IAAI,GAAG;AAC1B,WAAO;AAAA,MACH,OAAO,KAAK;AAAA,MACZ,QAAQ;AAAA,MACR,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,IACpB;AAAA,EACJ;AACA,SAAO,EAAE,QAAQ,OAAO;AAC5B;;;ACvEO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAQrC,YAAY,QAAgB,MAAyB;AACjD,UAAM,UACF,OAAO,MAAM,UAAU,YAAY,KAAK,MAAM,SAAS,IACjD,KAAK,QACL,uCAAuC,MAAM;AACvD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO,OAAO,MAAM,SAAS,WAAW,KAAK,OAAO;AACzD,SAAK,OAAO,QAAQ,CAAC;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,cAAuB;AACvB,WAAO,KAAK,WAAW;AAAA,EAC3B;AACJ;AAGO,SAAS,gBAAgB,KAAoC;AAChE,MAAI,eAAe,cAAe,QAAO;AACzC,SACI,OAAO,QAAQ,YACf,QAAQ,QACP,IAA2B,SAAS;AAE7C;;;ACvDO,IAAM,qBAAqB;AA0B3B,IAAM,YAAN,MAAgB;AAAA,EACnB,YAA6B,QAAyB;AAAzB;AAAA,EAA0B;AAAA,EAE/C,QAAQ,MAA+C;AAC3D,UAAM,UAAkC,CAAC;AACzC,QAAI,KAAK,OAAO,OAAQ,SAAQ,eAAe,IAAI,UAAU,KAAK,OAAO,MAAM;AAC/E,QAAI,MAAM,YAAa,SAAQ,gBAAgB,IAAI,KAAK;AACxD,WAAO;AAAA,EACX;AAAA,EAEQ,IAAI,MAAsB;AAC9B,WAAO,GAAG,KAAK,OAAO,OAAO,GAAG,IAAI;AAAA,EACxC;AAAA,EAEQ,QAAQ,MAA+B;AAC3C,UAAM,QAAQ,MAAM,aAAa,KAAK,OAAO,aAAa;AAC1D,WAAO,QAAQ,IAAI,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,KAAK,MAAc,MAAmB,MAA0C;AAC1F,UAAM,YAAY,KAAK,QAAQ,IAAI;AACnC,UAAM,aAAa,YAAY,IAAI,IAAI,gBAAgB,IAAI;AAC3D,UAAM,QAAQ,aAAa,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS,IAAI;AAC7E,QAAI;AACA,aAAO,MAAM,MAAM,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,MAAM,QAAQ,YAAY,OAAO,CAAC;AAAA,IAC9E,SAAS,KAAK;AACV,UAAI,YAAY,OAAO,SAAS;AAC5B,cAAM,IAAI,cAAc,KAAK;AAAA,UACzB,OAAO,cAAc,IAAI,oBAAoB,SAAS;AAAA,UACtD,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AACA,YAAM,IAAI,cAAc,GAAG;AAAA,QACvB,OAAO,eAAe,QAAQ,IAAI,UAAU;AAAA,QAC5C,MAAM;AAAA,MACV,CAAC;AAAA,IACL,UAAE;AACE,UAAI,MAAO,cAAa,KAAK;AAAA,IACjC;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,IAAO,MAAc,MAAmC;AAC1D,UAAM,MAAM,MAAM,KAAK,KAAK,MAAM,EAAE,QAAQ,OAAO,SAAS,KAAK,QAAQ,IAAI,EAAE,GAAG,IAAI;AACtF,WAAO,KAAK,MAAS,GAAG;AAAA,EAC5B;AAAA;AAAA,EAGA,MAAM,KAAQ,MAAc,MAAgB,MAAmC;AAC3E,UAAM,MAAM,MAAM,KAAK;AAAA,MACnB;AAAA,MACA;AAAA,QACI,QAAQ;AAAA,QACR,SAAS,EAAE,GAAG,KAAK,QAAQ,IAAI,GAAG,gBAAgB,mBAAmB;AAAA,QACrE,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,MACnC;AAAA,MACA;AAAA,IACJ;AACA,WAAO,KAAK,MAAS,GAAG;AAAA,EAC5B;AAAA;AAAA,EAGA,MAAM,UAAa,MAAc,MAAgB,MAAmC;AAChF,UAAM,MAAM,MAAM,KAAK,KAAK,MAAM,EAAE,QAAQ,OAAO,SAAS,KAAK,QAAQ,IAAI,GAAG,MAAM,KAAK,GAAG,IAAI;AAClG,WAAO,KAAK,MAAS,GAAG;AAAA,EAC5B;AAAA,EAEA,MAAc,MAAS,KAA2B;AAC9C,UAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,QAAI,OAAgB;AACpB,QAAI,KAAK;AACL,UAAI;AACA,eAAO,KAAK,MAAM,GAAG;AAAA,MACzB,QAAQ;AACJ,eAAO,EAAE,OAAO,IAAI;AAAA,MACxB;AAAA,IACJ;AACA,QAAI,CAAC,IAAI,IAAI;AACT,YAAM,OACF,QAAQ,OAAO,SAAS,WACjB,OACD,EAAE,OAAO,IAAI,cAAc,iBAAiB;AACtD,YAAM,IAAI,cAAc,IAAI,QAAQ,IAAI;AAAA,IAC5C;AACA,WAAO;AAAA,EACX;AACJ;;;AC/GA,IAAM,aAAa;AACnB,IAAM,aAAa;AAkBnB,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAEpF,eAAsB,gBAClBC,UACA,YAC4B;AAE5B,QAAM,YAAY,IAAI,UAAU,EAAE,SAAAA,SAAQ,CAAC;AAC3C,SAAO,UAAU,KAA0B,YAAY,EAAE,aAAa,WAAW,CAAC;AACtF;AAcA,eAAsB,gBAClBA,UACA,OACA,OAAoB,CAAC,GACO;AAC5B,QAAM,YAAY,IAAI,UAAU,EAAE,SAAAA,SAAQ,CAAC;AAC3C,MAAI,aAAa,KAAK,IAAI,GAAG,MAAM,QAAQ,IAAI;AAC/C,QAAM,SAAS,KAAK,IAAI,IAAI,MAAM,aAAa;AAC/C,QAAM,WACF,KAAK,aAAa,KAAK,YAAY,IAC7B,KAAK,IAAI,QAAQ,KAAK,IAAI,IAAI,KAAK,SAAS,IAC5C;AAEV,SAAO,KAAK,IAAI,IAAI,UAAU;AAC1B,UAAM,MAAM,UAAU;AACtB,QAAI;AACA,aAAO,MAAM,UAAU,KAA0B,YAAY;AAAA,QACzD,aAAa,MAAM;AAAA,MACvB,CAAC;AAAA,IACL,SAAS,KAAK;AACV,UAAI,eAAe,eAAe;AAC9B,cAAM,UAAU,IAAI,MAAM,SAAS,IAAI;AACvC,YAAI,IAAI,WAAW,OAAO,QAAQ,SAAS,uBAAuB,GAAG;AACjE,eAAK,YAAY;AACjB;AAAA,QACJ;AACA,YAAI,QAAQ,SAAS,WAAW,GAAG;AAC/B,wBAAc;AACd;AAAA,QACJ;AAAA,MACJ;AACA,YAAM;AAAA,IACV;AAAA,EACJ;AAEA,QAAM,YAAY,KAAK,IAAI,IAAI;AAC/B,QAAM,IAAI,cAAc,KAAK;AAAA,IACzB,OAAO,YACD,wCACA;AAAA,IACN,MAAM,YAAY,gBAAgB;AAAA,EACtC,CAAC;AACL;AAGA,eAAsB,YAAY,KAA4B;AAC1D,MAAI;AACA,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,eAAoB;AACnD,UAAM,WAAW,QAAQ;AACzB,UAAM,UAAU,aAAa,WAAW,SAAS,aAAa,UAAU,QAAQ;AAChF,UAAM,OAAO,aAAa,UAAU,CAAC,MAAM,SAAS,IAAI,GAAG,IAAI,CAAC,GAAG;AACnE,UAAM,SAAS,MAAM,EAAE,OAAO,UAAU,UAAU,KAAK,CAAC,EAAE,MAAM;AAAA,EACpE,QAAQ;AAAA,EAER;AACJ;AAeA,eAAsB,MAAM,SAAkD;AAC1E,QAAM,MAAM,QAAQ,QAAQ,MAAM;AAAA,EAAC;AAEnC,QAAM,QAAQ,MAAM,gBAAgB,QAAQ,SAAS,QAAQ,UAAU;AACvE;AAAA,IACI;AAAA;AAAA,IAAkC,MAAM,yBAAyB;AAAA;AAAA;AAAA,IAC5B,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA,EACxD;AACA,MAAI,QAAQ,SAAS,MAAO,OAAM,YAAY,MAAM,yBAAyB;AAE7E,QAAM,QAAQ,MAAM,gBAAgB,QAAQ,SAAS,KAAK;AAC1D,QAAM,OAAyB;AAAA,IAC3B,cAAc,MAAM;AAAA,IACpB,YAAY,MAAM;AAAA,IAClB,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,aAAa,GAAI,EAAE,YAAY;AAAA,IACvE,UAAU,QAAQ;AAAA,EACtB;AACA,iBAAe,IAAI;AACnB,SAAO;AACX;;;ACnIO,IAAM,8BAA8B;AAG3C,IAAM,0BAA0B,KAAK,KAAK,KAAK;AAkDxC,SAAS,iBACZ,cACA,OAAe,6BACF;AACb,MAAI,CAAC,aAAc,QAAO;AAC1B,aAAW,QAAQ,aAAa,MAAM,GAAG,GAAG;AACxC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,OAAO,GAAI;AACf,QAAI,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK,MAAM,KAAM;AACvC,UAAM,MAAM,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACpC,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI;AACA,aAAO,mBAAmB,GAAG;AAAA,IACjC,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;;;ACIA,IAAM,iBAAiB;AACvB,IAAM,4BAA4B;AAI3B,IAAM,uBAAN,MAAmD;AAAA,EAAnD;AACH,SAAiB,QAAQ,oBAAI,IAA2D;AAAA;AAAA,EAExF,IAAI,OAA0C;AAC1C,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK;AAChC,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,KAAK,IAAI,KAAK,IAAI,aAAa;AAC/B,WAAK,MAAM,OAAO,KAAK;AACvB,aAAO;AAAA,IACX;AACA,WAAO,IAAI;AAAA,EACf;AAAA,EAEA,IAAI,OAAe,OAAsB,OAAqB;AAC1D,SAAK,MAAM,IAAI,OAAO,EAAE,OAAO,aAAa,KAAK,IAAI,IAAI,MAAM,CAAC;AAAA,EACpE;AAAA,EAEA,OAAO,OAAqB;AACxB,SAAK,MAAM,OAAO,KAAK;AAAA,EAC3B;AACJ;AAEA,SAAS,gBAAgB,SAAyB;AAC9C,QAAM,SAAS,QAAQ,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC3D,QAAM,SAAS,OAAO,SAAS,MAAM,IAAI,SAAS,SAAS,IAAI,OAAO,IAAK,OAAO,SAAS,CAAE;AAC7F,MAAI,OAAO,SAAS,YAAY;AAC5B,UAAM,SAAS,KAAK,MAAM;AAC1B,UAAM,QAAQ,WAAW,KAAK,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC5D,WAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,EACzC;AAEA,SAAO,OAAO,KAAK,QAAQ,QAAQ,EAAE,SAAS,MAAM;AACxD;AAUO,SAAS,kBAAkB,OAAsC;AACpE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,MAAI;AACA,UAAM,UAAU,KAAK,MAAM,gBAAgB,MAAM,CAAC,CAAC,CAAC;AACpD,QAAI,CAAC,WAAW,OAAO,QAAQ,QAAQ,SAAU,QAAO;AACxD,WAAO;AAAA,EACX,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAKO,SAAS,iBACZ,OACA,OAAiC,CAAC,GAC3B;AACP,QAAM,UAAU,OAAO,UAAU,WAAW,kBAAkB,KAAK,IAAI;AACvE,MAAI,CAAC,WAAW,OAAO,QAAQ,QAAQ,SAAU,QAAO;AACxD,QAAM,aAAa,KAAK,IAAI,IAAI;AAChC,SAAO,cAAc,QAAQ,OAAO,KAAK,eAAe;AAC5D;AAEA,SAAS,eAAe,SAAsC;AAC1D,SAAO;AAAA,IACH,IAAI,QAAQ;AAAA,IACZ,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,QAAQ;AAAA,IAC3D,MAAM,QAAQ;AAAA,IACd,QAAQ,QAAQ;AAAA,EACpB;AACJ;AAOO,IAAM,kBAAN,MAAsB;AAAA,EAKzB,YACqB,MACjB,UAA0B,CAAC,GAC7B;AAFmB;AAGjB,SAAK,QAAQ,QAAQ,SAAS,IAAI,qBAAqB;AACvD,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,kBAAkB,QAAQ,mBAAmB;AAAA,EACtD;AAAA,EAEA,MAAM,WAAW,EAAE,aAAa,OAAO,aAAa,GAA4C;AAC5F,UAAM,UAAU,kBAAkB,WAAW;AAC7C,QAAI,CAAC,QAAS,QAAO,EAAE,QAAQ,aAAa,MAAM,MAAM,UAAU,MAAM;AACxE,QAAI,iBAAiB,OAAO,GAAG;AAC3B,aAAO,EAAE,QAAQ,WAAW,MAAM,MAAM,UAAU,OAAO,WAAW,QAAQ,IAAI;AAAA,IACpF;AAEA,UAAM,aAA4B;AAAA,MAC9B,QAAQ;AAAA,MACR,MAAM,eAAe,OAAO;AAAA,MAC5B,UAAU;AAAA,MACV,WAAW,QAAQ;AAAA,IACvB;AACA,QAAI,SAAS,aAAc,QAAO;AAElC,UAAM,SAAS,KAAK,MAAM,IAAI,WAAW;AACzC,QAAI,OAAQ,QAAO;AAEnB,QAAI;AACA,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK,GAAG;AAAA,QAChC;AAAA,QACA,WAAW,KAAK;AAAA,MACpB,CAAC;AACD,YAAM,SAAwB,OACxB,EAAE,QAAQ,UAAU,MAAM,UAAU,MAAM,WAAW,QAAQ,IAAI,IACjE,EAAE,QAAQ,aAAa,MAAM,MAAM,UAAU,KAAK;AACxD,WAAK,MAAM,IAAI,aAAa,QAAQ,KAAK,KAAK;AAC9C,aAAO;AAAA,IACX,SAAS,KAAK;AAEV,UAAI,gBAAgB,GAAG,KAAK,IAAI,WAAW,KAAK;AAC5C,eAAO,EAAE,QAAQ,aAAa,MAAM,MAAM,UAAU,KAAK;AAAA,MAC7D;AAIA,aAAO,EAAE,GAAG,YAAY,QAAQ,cAAc;AAAA,IAClD;AAAA,EACJ;AACJ;;;AC1MA,IAAM,YAAY;AAoBX,IAAM,SAAN,MAAa;AAAA,EAChB,YAA6B,WAAsB;AAAtB;AAAA,EAAuB;AAAA;AAAA;AAAA,EAIpD,UAAU,OAAgD;AACtD,WAAO,KAAK,UAAU,KAAkB,GAAG,SAAS,kBAAkB;AAAA,MAClE,iBAAiB,MAAM;AAAA,MACvB,MAAM,MAAM;AAAA,MACZ,eAAe,MAAM;AAAA,IACzB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA,EAIA,OAAO,OAA0D;AAC7D,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,CAAC;AAAA,MACD,EAAE,aAAa,MAAM,YAAY;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA;AAAA,EAIA,cAAc,OAAqD;AAC/D,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,EAAE,MAAM,MAAM,KAAK;AAAA,MACnB,EAAE,aAAa,MAAM,YAAY;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA;AAAA,EAIA,QAAQ,OAA+C;AACnD,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,EAAE,MAAM,MAAM,MAAM,eAAe,MAAM,aAAa;AAAA,MACtD,EAAE,aAAa,MAAM,YAAY;AAAA,IACrC;AAAA,EACJ;AACJ;AAQO,IAAM,UAAN,MAAc;AAAA,EAIjB,YACqB,WACjB,gBACF;AAFmB;AAGjB,SAAK,MAAM,IAAI,OAAO,SAAS;AAC/B,SAAK,WAAW,IAAI,gBAAgB,MAAM,cAAc;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OAA2C;AAC9C,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,WAAW;AAAA,MAC5D,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,IAChB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAyC;AAC3C,WAAO,KAAK,UAAU,KAAkB,GAAG,SAAS,UAAU;AAAA,MAC1D,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,IACpB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,OAA4B;AACxB,WAAO,KAAK,UAAU,IAAgB,cAAc;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,GAAG,OAAuE;AACtE,WAAO,KAAK,UAAU;AAAA,MAClB,GAAG,SAAS;AAAA,MACZ,CAAC;AAAA,MACD,EAAE,aAAa,MAAM,aAAa,WAAW,MAAM,UAAU;AAAA,IACjE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,WAAW,OAAgD;AACvD,WAAO,KAAK,SAAS,WAAW,KAAK;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,mBACI,cACA,UAAmE,CAAC,GAC9C;AACtB,UAAM,QAAQ,iBAAiB,cAAc,QAAQ,UAAU;AAC/D,QAAI,CAAC,MAAO,QAAO,QAAQ,QAAQ,EAAE,QAAQ,aAAa,MAAM,MAAM,UAAU,MAAM,CAAC;AACvF,WAAO,KAAK,SAAS,WAAW,EAAE,aAAa,OAAO,MAAM,QAAQ,KAAK,CAAC;AAAA,EAC9E;AAAA;AAAA;AAAA,EAIA,YAAY,OAAsC;AAC9C,WAAO,kBAAkB,KAAK;AAAA,EAClC;AAAA;AAAA,EAGA,YAAY,OAAgD;AACxD,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,iBAAiB;AAAA,MAClE,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,IACjB,CAAC;AAAA,EACL;AAAA,EAEA,eAAe,OAAmD;AAC9D,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,oBAAoB;AAAA,MACrE,OAAO,MAAM;AAAA,IACjB,CAAC;AAAA,EACL;AAAA,EAEA,cAAc,OAAkD;AAC5D,WAAO,KAAK,UAAU,KAAmB,GAAG,SAAS,mBAAmB;AAAA,MACpE,MAAM,MAAM;AAAA,MACZ,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,IACjB,CAAC;AAAA,EACL;AACJ;;;ACrMA,IAAM,UAAU;AAChB,IAAM,eAAe;AAYd,IAAM,aAAN,MAAiB;AAAA,EACpB,YACqB,WACA,WACnB;AAFmB;AACA;AAAA,EAClB;AAAA,EAEH,MAAc,IAAO,WAAmB,SAA8B;AAClE,UAAM,MAAM,MAAM,KAAK,UAAU,KAAoB,SAAS;AAAA,MAC1D;AAAA,MACA,WAAW,KAAK;AAAA,MAChB;AAAA,IACJ,CAAC;AACD,WAAO,IAAI;AAAA,EACf;AAAA;AAAA,EAGA,KAAkC,UAAwB,CAAC,GAAiB;AACxE,WAAO,KAAK,IAAS,QAAQ,OAAO;AAAA,EACxC;AAAA;AAAA,EAGA,SAAsC,UAAwB,CAAC,GAAiB;AAC5E,WAAO,KAAK,IAAS,YAAY,OAAO;AAAA,EAC5C;AAAA;AAAA,EAGA,UAAuC,UAAwB,CAAC,GAAsB;AAClF,WAAO,KAAK,IAAc,aAAa,OAAO;AAAA,EAClD;AAAA;AAAA,EAGA,QAAqC,UAAwB,CAAC,GAAsB;AAChF,WAAO,KAAK,IAAc,WAAW,OAAO;AAAA,EAChD;AAAA;AAAA;AAAA,EAIA,OAAO,MAAyD;AAC5D,WAAO,KAAK,IAAqB,UAAU,EAAE,KAAK,CAAC;AAAA,EACvD;AAAA;AAAA,EAGA,OAAO,OAAgD;AACnD,WAAO,KAAK,IAAuB,UAAU,EAAE,OAAO,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC;AAAA,EACzF;AAAA;AAAA,EAGA,OAAO,OAAgD;AACnD,WAAO,KAAK,IAAuB,UAAU,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,EACvE;AAAA;AAAA,EAGA,MAAM,UAAuC,CAAC,GAAoB;AAC9D,WAAO,KAAK,IAAY,SAAS,OAAO;AAAA,EAC5C;AACJ;AAcO,SAAS,SAAS,WAAgC;AACrD,QAAM,QAAQ,CAAC,cAAkC,IAAI,WAAW,WAAW,SAAS;AACpF,QAAM,UAAU,CAAC,UACb,UAAU,KAAoB,cAAc,EAAE,KAAK,MAAM,IAAI,CAAC;AAElE,SAAO,IAAI,MAAM,OAAO;AAAA,IACpB,IAAI,QAAQ,MAAM,UAAU;AACxB,UAAI,SAAS,UAAW,QAAO;AAG/B,UAAI,OAAO,SAAS,YAAY,SAAS,UAAU,QAAQ,QAAQ;AAC/D,eAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAAA,MAC7C;AACA,aAAO,IAAI,WAAW,WAAW,IAAI;AAAA,IACzC;AAAA,IACA,MAAM,SAAS,UAAU,MAAgB;AACrC,aAAO,MAAM,KAAK,CAAC,CAAC;AAAA,IACxB;AAAA,EACJ,CAAC;AACL;;;ACnGA,IAAM,eAAe;AAErB,IAAM,uBAAuB;AAG7B,SAAS,OAAO,MAAkB,aAA2B;AACzD,MAAI,gBAAgB,KAAM,QAAO;AACjC,MAAI,OAAO,SAAS,SAAU,QAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAC3E,MAAI,gBAAgB,YAAa,QAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAC9E,MAAI,YAAY,OAAO,IAAI,GAAG;AAC1B,WAAO,IAAI,KAAK,CAAC,IAA2B,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,EACxE;AACA,QAAM,IAAI,UAAU,4DAA4D;AACpF;AAEA,SAAS,SAAS,MAAsB;AACpC,SAAO,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AACpD;AAGO,IAAM,eAAN,MAAmB;AAAA,EACtB,YACuB,WACA,QACrB;AAFqB;AACA;AAAA,EACpB;AAAA,EAEO,GAAM,WAAmB,SAAkC,CAAC,GAAe;AACjF,WAAO,KAAK,UAAU,KAAQ,cAAc,EAAE,WAAW,QAAQ,KAAK,QAAQ,GAAG,OAAO,CAAC;AAAA,EAC7F;AAAA;AAAA,EAGA,KAAK,QAAsC;AACvC,WAAO,KAAK,GAAe,QAAQ,EAAE,OAAO,CAAC;AAAA,EACjD;AAAA;AAAA,EAGA,OAAO,OAA+C;AAClD,WAAO,KAAK,GAAwB,UAAU,EAAE,MAAM,CAAC;AAAA,EAC3D;AAAA;AAAA,EAGA,KAAK,MAAc,IAAgD;AAC/D,WAAO,KAAK,GAA8B,QAAQ,EAAE,MAAM,GAAG,CAAC;AAAA,EAClE;AAAA,EAEA,aAAa,MAA4C;AACrD,WAAO,KAAK,GAAwB,gBAAgB,EAAE,KAAK,CAAC;AAAA,EAChE;AAAA;AAAA,EAGA,SAAS,MAAuC;AAC5C,WAAO,KAAK,GAAmB,YAAY,EAAE,KAAK,CAAC;AAAA,EACvD;AAAA;AAAA,EAGA,gBAAgB,MAAc,WAAiD;AAC3E,WAAO,KAAK,GAAuB,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAAA,EAC7E;AAAA;AAAA,EAGA,cAAc,MAAc,aAA8C;AACtE,WAAO,KAAK,GAAkB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAAA,EACxE;AAAA;AAAA;AAAA,EAIA,OACI,MACA,MACA,cAAsB,sBACY;AAClC,UAAM,OAAO,IAAI,SAAS;AAC1B,SAAK,OAAO,QAAQ,OAAO,MAAM,WAAW,GAAG,SAAS,IAAI,CAAC;AAC7D,SAAK,OAAO,UAAU,KAAK,MAAM;AACjC,SAAK,OAAO,QAAQ,IAAI;AACxB,SAAK,OAAO,eAAe,WAAW;AACtC,WAAO,KAAK,UAAU,UAAqC,cAAc,IAAI;AAAA,EACjF;AACJ;AAGO,IAAM,qBAAN,cAAiC,aAAa;AAAA,EACjD,YAAY,WAAsB;AAC9B,UAAM,WAAW,QAAQ;AAAA,EAC7B;AAAA;AAAA,EAGA,OAAO,MAAwC;AAC3C,WAAO,KAAK,GAAoB,UAAU,EAAE,KAAK,CAAC;AAAA,EACtD;AACJ;AAOO,SAAS,cAAc,WAAqC;AAC/D,SAAO;AAAA,IACH,QAAQ,IAAI,mBAAmB,SAAS;AAAA,IACxC,SAAS,IAAI,aAAa,WAAW,SAAS;AAAA,EAClD;AACJ;;;AC3GA,IAAM,mBAAmB;AAyBzB,SAAS,QAAQ,MAAkC;AAC/C,MAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,IAAK,QAAO;AAC3D,SAAO,QAAQ,IAAI,IAAI;AAC3B;AAaO,SAAS,SAAS,UAAiC,CAAC,GAAmB;AAC1E,QAAM,SAAS,QAAQ,UAAU,QAAQ,kBAAkB;AAC3D,QAAMC,YAAW,QAAQ,WAAW,QAAQ,kBAAkB,KAAK,kBAAkB;AAAA,IACjF;AAAA,IACA;AAAA,EACJ;AAEA,QAAM,YAAY,IAAI,UAAU,EAAE,QAAQ,SAAAA,UAAS,WAAW,QAAQ,UAAU,CAAC;AAEjF,SAAO;AAAA,IACH,MAAM,IAAI,QAAQ,WAAW,QAAQ,OAAO;AAAA,IAC5C,IAAI,SAAS,SAAS;AAAA,IACtB,SAAS,cAAc,SAAS;AAAA,EACpC;AACJ;;;AC7DA,iBAA0B;AAC1B,mBAAqC;AACrC,iBAAkB;AA8BlB,IAAM,cAAc;AACpB,IAAM,iBAAiB;AAEvB,SAAS,UAAkB;AACvB,UAAQ,QAAQ,IAAI,oBAAoB,+BAA+B,QAAQ,QAAQ,EAAE;AAC7F;AAGA,IAAI,cAA0C;AAE9C,SAAS,KAAK,OAAgB;AAC1B,QAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,OAAO,MAAM,CAAC;AAC9E,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,KAAK,CAAC,EAAE;AAC9D;AAEA,SAAS,QAAQ,SAAiB;AAC9B,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AAChF;AAGA,SAAS,cAAc,KAAsB;AACzC,MAAI,gBAAgB,GAAG,GAAG;AACtB,QAAI,IAAI,WAAW,KAAK;AACpB,aAAO;AAAA,IACX;AACA,QAAI,IAAI,WAAW,KAAK;AACpB,aAAO,2CAA2C,IAAI,OAAO;AAAA,IACjE;AACA,QAAI,IAAI,aAAa;AACjB,aAAO,iBAAiB,IAAI,OAAO;AAAA,IACvC;AACA,WAAO,IAAI;AAAA,EACf;AACA,SAAO,eAAe,QAAQ,IAAI,UAAU;AAChD;AAEA,SAAS,gBAAgC;AACrC,QAAM,SAAS,mBAAmB,QAAQ,CAAC;AAC3C,MAAI,CAAC,OAAO,OAAO;AACf,UAAM,IAAI;AAAA,MACN;AAAA,IACJ;AAAA,EACJ;AACA,SAAO,SAAS,EAAE,QAAQ,OAAO,OAAO,SAAS,QAAQ,EAAE,CAAC;AAChE;AAGA,eAAe,IAAI,IAA4B;AAC3C,MAAI;AACA,WAAO,KAAK,MAAM,GAAG,CAAC;AAAA,EAC1B,SAAS,KAAK;AACV,WAAO,QAAQ,cAAc,GAAG,CAAC;AAAA,EACrC;AACJ;AAEO,SAAS,kBAA6B;AACzC,QAAM,SAAS,IAAI,qBAAU,EAAE,MAAM,aAAa,SAAS,eAAe,CAAC;AAO3E,QAAM,OAAO,CACT,MACA,QAMA,YACC,OAAO,aAAa,MAAM,QAAiB,OAAgB;AAIhE;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa;AAAA,QACT,aAAa,aACR,OAAO,EACP,SAAS,EACT,SAAS,qEAAqE;AAAA,MACvF;AAAA,MACA,aAAa,EAAE,cAAc,OAAO,eAAe,KAAK;AAAA,IAC5D;AAAA,IACA,OAAO,EAAE,YAAY,MACjB,IAAI,YAAY;AACZ,YAAM,QAAQ,MAAM,gBAAgB,QAAQ,GAAG,eAAe,mBAAmB;AACjF,oBAAc;AACd,YAAM,YAAY,MAAM,yBAAyB;AACjD,aAAO;AAAA,QACH,SACI;AAAA,QACJ,kBAAkB,MAAM;AAAA,QACxB,WAAW,MAAM;AAAA,QACjB,oBAAoB,MAAM;AAAA,MAC9B;AAAA,IACJ,CAAC;AAAA,EACT;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa,CAAC;AAAA,MACd,aAAa,EAAE,cAAc,OAAO,eAAe,KAAK;AAAA,IAC5D;AAAA,IACA,YACI,IAAI,YAAY;AACZ,UAAI,CAAC,aAAa;AACd,eAAO,EAAE,QAAQ,wBAAwB,MAAM,yBAAyB;AAAA,MAC5E;AACA,UAAI;AACA,cAAM,QAAQ,MAAM,gBAAgB,QAAQ,GAAG,aAAa;AAAA,UACxD,WAAW;AAAA,QACf,CAAC;AACD,uBAAe;AAAA,UACX,cAAc,MAAM;AAAA,UACpB,YAAY,MAAM;AAAA,UAClB,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,aAAa,GAAI,EAAE,YAAY;AAAA,UACvE,UAAU,QAAQ;AAAA,QACtB,CAAC;AACD,sBAAc;AACd,eAAO,EAAE,QAAQ,aAAa,YAAY,MAAM,WAAW;AAAA,MAC/D,SAAS,KAAK;AACV,YAAI,gBAAgB,GAAG,KAAK,IAAI,SAAS,eAAe;AACpD,iBAAO;AAAA,YACH,QAAQ;AAAA,YACR,MAAM;AAAA,UACV;AAAA,QACJ;AACA,sBAAc;AACd,cAAM;AAAA,MACV;AAAA,IACJ,CAAC;AAAA,EACT;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa,CAAC;AAAA,MACd,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,IAC3D;AAAA,IACA,YACI,IAAI,YAAY;AACZ,YAAM,SAAS,mBAAmB,QAAQ,CAAC;AAC3C,UAAI,CAAC,OAAO,OAAO;AACf,eAAO,EAAE,WAAW,OAAO,MAAM,6BAA6B;AAAA,MAClE;AACA,YAAM,SAAS,SAAS,EAAE,QAAQ,OAAO,OAAO,SAAS,QAAQ,EAAE,CAAC;AAEpE,YAAM,OAAO,MAAM,OAAO,KAAK,KAAK;AACpC,aAAO,EAAE,WAAW,MAAM,QAAQ,OAAO,QAAQ,GAAG,KAAK;AAAA,IAC7D,CAAC;AAAA,EACT;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,CAAC;AAAA,MACd,aAAa,EAAE,cAAc,OAAO,iBAAiB,KAAK;AAAA,IAC9D;AAAA,IACA,YACI,IAAI,YAAY;AACZ,sBAAgB,QAAQ,CAAC;AACzB,oBAAc;AACd,aAAO,EAAE,IAAI,KAAK;AAAA,IACtB,CAAC;AAAA,EACT;AAIA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa;AAAA,QACT,OAAO,aAAE,OAAO;AAAA,QAChB,WAAW,aACN,KAAK,CAAC,QAAQ,YAAY,aAAa,WAAW,OAAO,CAAC,EAC1D,QAAQ,MAAM;AAAA,QACnB,OAAO,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC,EAAE,SAAS;AAAA,QAC9C,QAAQ,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,QACrC,SAAS,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC,EAAE,SAAS;AAAA,QAChE,OAAO,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAI,EAAE,SAAS;AAAA,QACtD,QAAQ,aAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAAA,MACpD;AAAA,MACA,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,IAC3D;AAAA,IACA,OAAO,EAAE,OAAO,WAAW,OAAO,QAAQ,SAAS,OAAO,OAAO,MAC7D,IAAI,YAAY;AACZ,YAAM,IAAI,cAAc,EAAE,GAAG,KAAK;AAClC,UAAI,cAAc,QAAS,QAAO,EAAE,OAAO,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;AACpE,YAAM,UAAU,EAAE,OAAO,QAAQ,SAAS,OAAO,OAAO;AACxD,UAAI,cAAc,eAAe,cAAc,WAAW;AACtD,eAAO,EAAE,KAAK,MAAM,EAAE,UAAU,OAAO,EAAE;AAAA,MAC7C;AACA,aAAO,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,EAAE;AAAA,IACzC,CAAC;AAAA,EACT;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,OAAO,aAAE,OAAO,GAAG,MAAM,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC,EAAE;AAAA,MACtE,aAAa,EAAE,cAAc,OAAO,eAAe,KAAK;AAAA,IAC5D;AAAA,IACA,OAAO,EAAE,OAAO,KAAK,MAAM,IAAI,YAAY,cAAc,EAAE,GAAG,KAAK,EAAE,OAAO,IAAI,CAAC;AAAA,EACrF;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACT,OAAO,aAAE,OAAO;AAAA,QAChB,OAAO,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC;AAAA,QACnC,MAAM,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC;AAAA,MACtC;AAAA,MACA,aAAa,EAAE,cAAc,OAAO,eAAe,KAAK;AAAA,IAC5D;AAAA,IACA,OAAO,EAAE,OAAO,OAAO,KAAK,MACxB,IAAI,YAAY,cAAc,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,CAAC,CAAC;AAAA,EACzE;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa,EAAE,OAAO,aAAE,OAAO,GAAG,OAAO,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC,EAAE;AAAA,MACvE,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,eAAe,KAAK;AAAA,IACnF;AAAA,IACA,OAAO,EAAE,OAAO,MAAM,MAAM,IAAI,YAAY,cAAc,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAAA,EAC3F;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa,EAAE,KAAK,aAAE,OAAO,EAAE;AAAA,MAC/B,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,eAAe,KAAK;AAAA,IACnF;AAAA,IACA,OAAO,EAAE,IAAI,MAAM,IAAI,YAAY,cAAc,EAAE,GAAG,QAAQ,EAAE,IAAI,CAAC,CAAC;AAAA,EAC1E;AAIA,QAAM,YAAY,aAAE,KAAK,CAAC,UAAU,SAAS,CAAC,EAAE,QAAQ,SAAS;AACjE,QAAM,WAAW,CAAC,QAAwB,WACtC,WAAW,WAAW,OAAO,QAAQ,SAAS,OAAO,QAAQ;AAEjE;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,QAAQ,WAAW,QAAQ,aAAE,OAAO,EAAE,SAAS,EAAE;AAAA,MAChE,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,IAC3D;AAAA,IACA,OAAO,EAAE,QAAQ,OAAO,MACpB,IAAI,YAAY,SAAS,cAAc,GAAG,MAAM,EAAE,KAAK,MAAM,CAAC;AAAA,EACtE;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,MAAM,aAAE,OAAO,EAAE;AAAA,MAChC,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,IAC3D;AAAA,IACA,OAAO,EAAE,KAAK,MAAM,IAAI,YAAY,cAAc,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,EACnF;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACT,QAAQ;AAAA,QACR,MAAM,aAAE,OAAO;AAAA,QACf,YAAY,aAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA,MACrD;AAAA,MACA,aAAa,EAAE,cAAc,MAAM,eAAe,KAAK;AAAA,IAC3D;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,WAAW,MAC9B,IAAI,YAAY,SAAS,cAAc,GAAG,MAAM,EAAE,gBAAgB,MAAM,UAAU,CAAC;AAAA,EAC3F;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aACI;AAAA,MACJ,aAAa;AAAA,QACT,QAAQ;AAAA,QACR,MAAM,aAAE,OAAO;AAAA,QACf,SAAS,aAAE,OAAO;AAAA,QAClB,cAAc,aAAE,OAAO,EAAE,SAAS;AAAA,MACtC;AAAA,MACA,aAAa,EAAE,cAAc,OAAO,eAAe,KAAK;AAAA,IAC5D;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,SAAS,aAAa,MACzC;AAAA,MAAI,YACA,SAAS,cAAc,GAAG,MAAM,EAAE;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,MACpB;AAAA,IACJ;AAAA,EACR;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,QAAQ,WAAW,OAAO,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE;AAAA,MACpE,aAAa,EAAE,cAAc,OAAO,iBAAiB,MAAM,eAAe,KAAK;AAAA,IACnF;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,MAAM,IAAI,YAAY,SAAS,cAAc,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EAChG;AAEA;AAAA,IACI;AAAA,IACA;AAAA,MACI,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,QAAQ,WAAW,MAAM,aAAE,OAAO,GAAG,IAAI,aAAE,OAAO,EAAE;AAAA,MACnE,aAAa,EAAE,cAAc,OAAO,eAAe,KAAK;AAAA,IAC5D;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,GAAG,MACtB,IAAI,YAAY,SAAS,cAAc,GAAG,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;AAAA,EACxE;AAEA,SAAO;AACX;AAGA,eAAsB,iBAAgC;AAClD,QAAM,SAAS,gBAAgB;AAC/B,QAAM,YAAY,IAAI,kCAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,UAAQ,MAAM,IAAI,WAAW,yCAAyC,QAAQ,CAAC,GAAG;AACtF;;;AC7XA,SAASC,WAAkB;AACvB,UAAQ,QAAQ,IAAI,oBAAoB,+BAA+B,QAAQ,QAAQ,EAAE;AAC7F;AAEA,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBb,eAAe,WAA0B;AACrC,QAAM,OAAO,MAAM,MAAM;AAAA,IACrB,SAASA,SAAQ;AAAA,IACjB,YAAY;AAAA,IACZ,KAAK,CAAC,MAAM,QAAQ,OAAO,MAAM,CAAC;AAAA,EACtC,CAAC;AACD,UAAQ,OAAO;AAAA,IACX;AAAA,qBAAwB,KAAK,UAAU,uBAAuB,KAAK,UAAU;AAAA;AAAA,EACjF;AACJ;AAEA,eAAe,YAA2B;AACtC,QAAM,SAAS,mBAAmBA,SAAQ,CAAC;AAC3C,MAAI,CAAC,OAAO,OAAO;AACf,YAAQ,OAAO,MAAM,wCAAwC;AAC7D;AAAA,EACJ;AACA,MAAI;AACA,UAAM,OAAO,MAAM,SAAS,EAAE,QAAQ,OAAO,OAAO,SAASA,SAAQ,EAAE,CAAC,EAAE,KAAK,KAAK;AACpF,YAAQ,OAAO;AAAA,MACX,KAAK,UAAU,EAAE,QAAQ,OAAO,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC,IAAI;AAAA,IAClE;AAAA,EACJ,SAAS,KAAK;AACV,UAAM,UAAU,gBAAgB,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,oBAAoB,OAAO;AAAA,CAAI;AAAA,EACxD;AACJ;AAEA,eAAe,OAAsB;AACjC,QAAM,UAAU,QAAQ,KAAK,CAAC;AAC9B,UAAQ,SAAS;AAAA,IACb,KAAK;AACD,YAAM,eAAe;AACrB;AAAA,IACJ,KAAK;AACD,YAAM,SAAS;AACf;AAAA,IACJ,KAAK;AACD,sBAAgBA,SAAQ,CAAC;AACzB,cAAQ,OAAO,MAAM,eAAe;AACpC;AAAA,IACJ,KAAK;AACD,YAAM,UAAU;AAChB;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,cAAQ,OAAO,MAAM,IAAI;AACzB;AAAA,IACJ;AACI,cAAQ,OAAO,MAAM,oBAAoB,OAAO;AAAA;AAAA,EAAO,IAAI,EAAE;AAC7D,cAAQ,KAAK,CAAC;AAAA,EACtB;AACJ;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AAClB,UAAQ,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KAAK,IAAI;AAC9E,UAAQ,KAAK,CAAC;AAClB,CAAC;","names":["baseUrl","baseUrl","baseUrl","baseUrl"]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ clearCredential,
4
+ login,
5
+ resolveActiveToken,
6
+ startMcpServer
7
+ } from "./chunk-HSPHQ6OU.js";
8
+ import {
9
+ dontcode,
10
+ isDontCodeError
11
+ } from "./chunk-CAYYXFFZ.js";
12
+
13
+ // src/cli.ts
14
+ function baseUrl() {
15
+ return (process.env.DONTCODE_API_URL || "https://backend.dontcode.co").replace(/\/+$/, "");
16
+ }
17
+ var HELP = `dontcode \u2014 developer CLI for DontCode Backend
18
+
19
+ Usage: dontcode <command>
20
+
21
+ Commands:
22
+ mcp Run the MCP server on stdio (configure this in your AI tool)
23
+ login Sign in through the browser and cache a short-lived token
24
+ logout Remove the cached token for the current gateway
25
+ status Show the current session (project, role, capabilities)
26
+ help Show this help
27
+
28
+ Environment:
29
+ DONTCODE_API_URL Gateway origin (default https://backend.dontcode.co)
30
+ DONTCODE_API_KEY A dc_ project key for non-interactive use (skips login)
31
+ DONTCODE_CONFIG_DIR Where the cached token lives (default ~/.dontcode)
32
+ `;
33
+ async function cmdLogin() {
34
+ const cred = await login({
35
+ baseUrl: baseUrl(),
36
+ clientName: "dontcode CLI",
37
+ log: (m) => process.stderr.write(m)
38
+ });
39
+ process.stderr.write(
40
+ `
41
+ Signed in. Project ${cred.project_id}, token valid until ${cred.expires_at}.
42
+ `
43
+ );
44
+ }
45
+ async function cmdStatus() {
46
+ const active = resolveActiveToken(baseUrl());
47
+ if (!active.token) {
48
+ process.stdout.write("Not signed in. Run `dontcode login`.\n");
49
+ return;
50
+ }
51
+ try {
52
+ const info = await dontcode({ apiKey: active.token, baseUrl: baseUrl() }).auth.info();
53
+ process.stdout.write(
54
+ JSON.stringify({ source: active.source, ...info }, null, 2) + "\n"
55
+ );
56
+ } catch (err) {
57
+ const message = isDontCodeError(err) ? err.message : String(err);
58
+ process.stdout.write(`Session invalid: ${message}
59
+ `);
60
+ }
61
+ }
62
+ async function main() {
63
+ const command = process.argv[2];
64
+ switch (command) {
65
+ case "mcp":
66
+ await startMcpServer();
67
+ break;
68
+ case "login":
69
+ await cmdLogin();
70
+ break;
71
+ case "logout":
72
+ clearCredential(baseUrl());
73
+ process.stdout.write("Signed out.\n");
74
+ break;
75
+ case "status":
76
+ await cmdStatus();
77
+ break;
78
+ case void 0:
79
+ case "help":
80
+ case "-h":
81
+ case "--help":
82
+ process.stdout.write(HELP);
83
+ break;
84
+ default:
85
+ process.stderr.write(`Unknown command: ${command}
86
+
87
+ ${HELP}`);
88
+ process.exit(1);
89
+ }
90
+ }
91
+ main().catch((err) => {
92
+ process.stderr.write((err instanceof Error ? err.message : String(err)) + "\n");
93
+ process.exit(1);
94
+ });
95
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `dontcode` — the developer CLI for DontCode Backend.\n *\n * dontcode mcp # run the MCP server on stdio (for Claude Code etc.)\n * dontcode login # sign in via the browser, cache a device token\n * dontcode logout # forget the cached token\n * dontcode status # show the current session\n *\n * The gateway origin comes from DONTCODE_API_URL (default https://backend.dontcode.co).\n * For non-interactive use, set DONTCODE_API_KEY and skip `login` entirely.\n */\nimport { login } from './auth-device'\nimport { clearCredential, resolveActiveToken } from './credentials'\nimport { dontcode } from './client'\nimport { isDontCodeError } from './errors'\nimport { startMcpServer } from './mcp/server'\n\nfunction baseUrl(): string {\n return (process.env.DONTCODE_API_URL || 'https://backend.dontcode.co').replace(/\\/+$/, '')\n}\n\nconst HELP = `dontcode — developer CLI for DontCode Backend\n\nUsage: dontcode <command>\n\nCommands:\n mcp Run the MCP server on stdio (configure this in your AI tool)\n login Sign in through the browser and cache a short-lived token\n logout Remove the cached token for the current gateway\n status Show the current session (project, role, capabilities)\n help Show this help\n\nEnvironment:\n DONTCODE_API_URL Gateway origin (default https://backend.dontcode.co)\n DONTCODE_API_KEY A dc_ project key for non-interactive use (skips login)\n DONTCODE_CONFIG_DIR Where the cached token lives (default ~/.dontcode)\n`\n\nasync function cmdLogin(): Promise<void> {\n const cred = await login({\n baseUrl: baseUrl(),\n clientName: 'dontcode CLI',\n log: (m) => process.stderr.write(m),\n })\n process.stderr.write(\n `\\nSigned in. Project ${cred.project_id}, token valid until ${cred.expires_at}.\\n`\n )\n}\n\nasync function cmdStatus(): Promise<void> {\n const active = resolveActiveToken(baseUrl())\n if (!active.token) {\n process.stdout.write('Not signed in. Run `dontcode login`.\\n')\n return\n }\n try {\n const info = await dontcode({ apiKey: active.token, baseUrl: baseUrl() }).auth.info()\n process.stdout.write(\n JSON.stringify({ source: active.source, ...info }, null, 2) + '\\n'\n )\n } catch (err) {\n const message = isDontCodeError(err) ? err.message : String(err)\n process.stdout.write(`Session invalid: ${message}\\n`)\n }\n}\n\nasync function main(): Promise<void> {\n const command = process.argv[2]\n switch (command) {\n case 'mcp':\n await startMcpServer()\n break\n case 'login':\n await cmdLogin()\n break\n case 'logout':\n clearCredential(baseUrl())\n process.stdout.write('Signed out.\\n')\n break\n case 'status':\n await cmdStatus()\n break\n case undefined:\n case 'help':\n case '-h':\n case '--help':\n process.stdout.write(HELP)\n break\n default:\n process.stderr.write(`Unknown command: ${command}\\n\\n${HELP}`)\n process.exit(1)\n }\n}\n\nmain().catch((err) => {\n process.stderr.write((err instanceof Error ? err.message : String(err)) + '\\n')\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;;;;AAkBA,SAAS,UAAkB;AACvB,UAAQ,QAAQ,IAAI,oBAAoB,+BAA+B,QAAQ,QAAQ,EAAE;AAC7F;AAEA,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBb,eAAe,WAA0B;AACrC,QAAM,OAAO,MAAM,MAAM;AAAA,IACrB,SAAS,QAAQ;AAAA,IACjB,YAAY;AAAA,IACZ,KAAK,CAAC,MAAM,QAAQ,OAAO,MAAM,CAAC;AAAA,EACtC,CAAC;AACD,UAAQ,OAAO;AAAA,IACX;AAAA,qBAAwB,KAAK,UAAU,uBAAuB,KAAK,UAAU;AAAA;AAAA,EACjF;AACJ;AAEA,eAAe,YAA2B;AACtC,QAAM,SAAS,mBAAmB,QAAQ,CAAC;AAC3C,MAAI,CAAC,OAAO,OAAO;AACf,YAAQ,OAAO,MAAM,wCAAwC;AAC7D;AAAA,EACJ;AACA,MAAI;AACA,UAAM,OAAO,MAAM,SAAS,EAAE,QAAQ,OAAO,OAAO,SAAS,QAAQ,EAAE,CAAC,EAAE,KAAK,KAAK;AACpF,YAAQ,OAAO;AAAA,MACX,KAAK,UAAU,EAAE,QAAQ,OAAO,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC,IAAI;AAAA,IAClE;AAAA,EACJ,SAAS,KAAK;AACV,UAAM,UAAU,gBAAgB,GAAG,IAAI,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,oBAAoB,OAAO;AAAA,CAAI;AAAA,EACxD;AACJ;AAEA,eAAe,OAAsB;AACjC,QAAM,UAAU,QAAQ,KAAK,CAAC;AAC9B,UAAQ,SAAS;AAAA,IACb,KAAK;AACD,YAAM,eAAe;AACrB;AAAA,IACJ,KAAK;AACD,YAAM,SAAS;AACf;AAAA,IACJ,KAAK;AACD,sBAAgB,QAAQ,CAAC;AACzB,cAAQ,OAAO,MAAM,eAAe;AACpC;AAAA,IACJ,KAAK;AACD,YAAM,UAAU;AAChB;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,cAAQ,OAAO,MAAM,IAAI;AACzB;AAAA,IACJ;AACI,cAAQ,OAAO,MAAM,oBAAoB,OAAO;AAAA;AAAA,EAAO,IAAI,EAAE;AAC7D,cAAQ,KAAK,CAAC;AAAA,EACtB;AACJ;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AAClB,UAAQ,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KAAK,IAAI;AAC9E,UAAQ,KAAK,CAAC;AAClB,CAAC;","names":[]}
@@ -0,0 +1,36 @@
1
+ import { AuthApi } from './auth';
2
+ import { type DbClient } from './db';
3
+ import type { SessionOptions } from './session';
4
+ import { type StorageClient } from './storage';
5
+ export interface DontCodeClientOptions {
6
+ /** Project API key (`dc_…`). Defaults to `process.env.DONTCODE_API_KEY`.
7
+ * If neither is set, requests fail naturally with the gateway's
8
+ * "Missing API key" 401. */
9
+ apiKey?: string;
10
+ /** Gateway origin. Defaults to `process.env.DONTCODE_API_URL`, then to
11
+ * `https://backend.dontcode.co`. */
12
+ baseUrl?: string;
13
+ /** Per-request network timeout in ms. Defaults to 10_000; `0` disables it.
14
+ * Without one, a slow gateway can hang a request for the full socket
15
+ * timeout, the worst case for an auth guard. */
16
+ timeoutMs?: number;
17
+ /** Caching + timeout policy for `auth.getSession` / `auth.sessionFromCookies`. */
18
+ session?: SessionOptions;
19
+ }
20
+ export interface DontCodeClient {
21
+ auth: AuthApi;
22
+ db: DbClient;
23
+ storage: StorageClient;
24
+ }
25
+ /**
26
+ * Create a DontCode backend client. A thin, typed proxy over the v1 HTTP
27
+ * gateway: auth, database, and storage. The API key scopes every request to
28
+ * a single project; there is nothing else to configure.
29
+ *
30
+ * ```ts
31
+ * import { dontcode } from '@dontcode2/backend'
32
+ * const client = dontcode() // reads DONTCODE_API_KEY
33
+ * await client.auth.signup({ email, password, role: 'editor' })
34
+ * ```
35
+ */
36
+ export declare function dontcode(options?: DontCodeClientOptions): DontCodeClient;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Cookie helpers, framework-agnostic by design. They return strings: a
3
+ * `Set-Cookie` header value to write, or a parsed token to read. Your framework
4
+ * applies them (`headers.append('Set-Cookie', …)`, SvelteKit `cookies.set`, a
5
+ * Next `Response` cookie, etc.). The SDK never owns a request or response.
6
+ *
7
+ * Defaults match how DontCode's own apps store the session: an httpOnly cookie
8
+ * so JavaScript can't read the token, `Secure`, `SameSite=Lax`, path `/`, and a
9
+ * 7-day max age. Cross-site setups (your app and the gateway on different sites)
10
+ * need `sameSite: 'none'`, which forces `Secure` on.
11
+ */
12
+ /** Default cookie name for the end-user access token. */
13
+ export declare const DEFAULT_SESSION_COOKIE_NAME = "dc_access_token";
14
+ export interface SessionCookieOptions {
15
+ /** Cookie name. Default `dc_access_token`. */
16
+ name?: string;
17
+ /** Lifetime in seconds. Default one week. Pass the token's `ExpiresIn` to
18
+ * keep the cookie and the token in lockstep. */
19
+ maxAge?: number;
20
+ /** Default `/`. */
21
+ path?: string;
22
+ domain?: string;
23
+ /** Default `true`. */
24
+ secure?: boolean;
25
+ /** Default `true`. Keep the token unreadable from client JavaScript. */
26
+ httpOnly?: boolean;
27
+ /** Default `'lax'`. Use `'none'` for cross-site (it forces `Secure`). */
28
+ sameSite?: 'lax' | 'strict' | 'none';
29
+ }
30
+ /** Build a `Set-Cookie` value that stores the access token. */
31
+ export declare function serializeSessionCookie(token: string, options?: SessionCookieOptions): string;
32
+ /** Build a `Set-Cookie` value that clears the access token (logout). */
33
+ export declare function clearSessionCookie(options?: SessionCookieOptions): string;
34
+ /** Read the access token out of a `Cookie` request header, or `null`. Pass the
35
+ * raw header string (`name=value; name2=value2`). */
36
+ export declare function readSessionToken(cookieHeader: string | null | undefined, name?: string): string | null;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Local credential cache for the CLI / MCP server (Node only — never import
3
+ * from the browser entry).
4
+ *
5
+ * The browser device-authorization flow hands back a short-lived,
6
+ * project-scoped, user-bound `dct_` token. We cache it on disk so the MCP
7
+ * server can reuse it across tool calls and restarts until it expires, then
8
+ * the user signs in again. The file is written 0600 and lives under
9
+ * `~/.dontcode` (override with `DONTCODE_CONFIG_DIR`).
10
+ */
11
+ export interface StoredCredential {
12
+ /** The `dct_` device token. Sent to the gateway as `Authorization: Bearer`. */
13
+ access_token: string;
14
+ project_id: string;
15
+ /** ISO timestamp. */
16
+ expires_at: string;
17
+ /** Gateway origin this credential is valid for. */
18
+ base_url: string;
19
+ }
20
+ export declare function loadCredential(baseUrl: string): StoredCredential | null;
21
+ export declare function saveCredential(cred: StoredCredential): void;
22
+ export declare function clearCredential(baseUrl: string): void;
23
+ /** A small skew so we don't hand back a token that expires mid-request. */
24
+ export declare function isExpired(cred: StoredCredential, skewMs?: number): boolean;
25
+ export interface ActiveToken {
26
+ token?: string;
27
+ source: 'env' | 'device' | 'none';
28
+ projectId?: string;
29
+ expiresAt?: string;
30
+ }
31
+ /**
32
+ * The credential the MCP server should use right now. An explicit
33
+ * `DONTCODE_API_KEY` (e.g. CI) always wins; otherwise a cached, unexpired
34
+ * device token; otherwise nothing (the user needs to run `login`).
35
+ */
36
+ export declare function resolveActiveToken(baseUrl: string): ActiveToken;
package/dist/db.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { Transport } from './http';
2
+ import type { DeleteInput, MigrateInput, MigrateResult, QueryOptions, UpdateInput } from './types';
3
+ /**
4
+ * A handle to one table. Structured queries only; there is no raw-SQL escape
5
+ * hatch (schema changes go through `db.migrate`). `update` and `delete`
6
+ * require a `where` clause server-side, so the types make it mandatory.
7
+ */
8
+ export declare class TableQuery {
9
+ private readonly transport;
10
+ private readonly tableName;
11
+ constructor(transport: Transport, tableName: string);
12
+ private run;
13
+ /** Rows matching the query (max 1000 per call). */
14
+ find<T = Record<string, unknown>>(options?: QueryOptions): Promise<T[]>;
15
+ /** Alias of `find`. */
16
+ findMany<T = Record<string, unknown>>(options?: QueryOptions): Promise<T[]>;
17
+ /** The first matching row, or `null`. */
18
+ findFirst<T = Record<string, unknown>>(options?: QueryOptions): Promise<T | null>;
19
+ /** Alias of `findFirst`. */
20
+ findOne<T = Record<string, unknown>>(options?: QueryOptions): Promise<T | null>;
21
+ /** Insert one row. Returns `{ id }`. Unique/FK conflicts throw a 409
22
+ * DontCodeError, the supported idempotency signal. */
23
+ insert(data: Record<string, unknown>): Promise<{
24
+ id: unknown;
25
+ }>;
26
+ /** Update rows matching `where`. Returns `{ count }`. */
27
+ update(input: UpdateInput): Promise<{
28
+ count: number;
29
+ }>;
30
+ /** Delete rows matching `where`. Returns `{ count }`. */
31
+ delete(input: DeleteInput): Promise<{
32
+ count: number;
33
+ }>;
34
+ /** Count matching rows. */
35
+ count(options?: Pick<QueryOptions, 'where'>): Promise<number>;
36
+ }
37
+ /**
38
+ * `db.users.find()` and `db('users').find()` both work; the bracket/callable
39
+ * form is there for table names that aren't valid identifiers. `db.migrate()`
40
+ * applies schema DDL (the one place migrations enter from outside).
41
+ */
42
+ export type DbClient = {
43
+ readonly [tableName: string]: TableQuery;
44
+ } & {
45
+ (tableName: string): TableQuery;
46
+ migrate(input: MigrateInput): Promise<MigrateResult>;
47
+ };
48
+ export declare function createDb(transport: Transport): DbClient;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Every non-2xx response from the gateway surfaces as a DontCodeError. The
3
+ * platform's error envelope is `{ error, ... }`, sometimes with a machine
4
+ * `code` (e.g. `EmailNotVerified`, `ChallengeExpired`, `MfaNotOffered`) or
5
+ * rate-limit fields. We preserve the whole body so callers can branch on it.
6
+ *
7
+ * Note: many "one more step" auth states (signup needing email verification,
8
+ * login returning `mfa_required`) are 2xx successes, NOT errors; inspect the
9
+ * resolved value for those. Errors are reserved for actual failures.
10
+ *
11
+ * Transport failures (no HTTP response at all) also surface as a DontCodeError
12
+ * so callers have one error type: a timeout is status 408 / code `Timeout`, and
13
+ * any other network failure is status 0 / code `NetworkError`. Neither is a
14
+ * `401`, so a guard can distinguish "backend unavailable" from "signed out".
15
+ */
16
+ export interface DontCodeErrorBody {
17
+ error?: string;
18
+ /** Stable machine code, when the platform sends one. */
19
+ code?: string;
20
+ /** Present on 429 responses. */
21
+ rate_limit?: boolean;
22
+ /** Seconds until the rate limit resets, on 429 responses. */
23
+ timeleft?: number;
24
+ [key: string]: unknown;
25
+ }
26
+ export declare class DontCodeError extends Error {
27
+ /** HTTP status code of the failing response. */
28
+ readonly status: number;
29
+ /** Stable machine code, when present (e.g. `EmailNotVerified`). */
30
+ readonly code?: string;
31
+ /** The raw parsed response body. */
32
+ readonly body: DontCodeErrorBody;
33
+ constructor(status: number, body: DontCodeErrorBody);
34
+ /** True when the request was rejected by the per-key rate limiter. */
35
+ get rateLimited(): boolean;
36
+ }
37
+ /** Cross-bundle-safe check; works even if two copies of the SDK are loaded. */
38
+ export declare function isDontCodeError(err: unknown): err is DontCodeError;
package/dist/http.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ /** Default per-request timeout. Without one, a slow or unreachable gateway can
2
+ * hang a request for the platform's full socket timeout (tens of seconds),
3
+ * which is the single worst failure mode for an auth guard on the hot path. */
4
+ export declare const DEFAULT_TIMEOUT_MS = 10000;
5
+ export interface TransportConfig {
6
+ /** Project API key. When absent, no Authorization header is sent and the
7
+ * gateway responds with its own "Missing API key" 401. */
8
+ apiKey?: string;
9
+ /** Gateway origin, already normalized (no trailing slash). */
10
+ baseUrl: string;
11
+ /** Per-request timeout in ms. Defaults to `DEFAULT_TIMEOUT_MS`; `0` (or any
12
+ * non-positive value) disables it. */
13
+ timeoutMs?: number;
14
+ }
15
+ export interface RequestOptions {
16
+ /** End-user access token, sent as `X-Access-Token` (separate from the
17
+ * project API key). Required by signed-in auth calls. */
18
+ accessToken?: string;
19
+ /** Override the client's timeout for this one call (ms). `0` disables it. */
20
+ timeoutMs?: number;
21
+ }
22
+ /**
23
+ * The single place network requests are made. Everything else in the SDK is a
24
+ * typed shape around `json()` / `multipart()`. No retries, no caching, just a
25
+ * faithful proxy of the v1 gateway.
26
+ */
27
+ export declare class Transport {
28
+ private readonly config;
29
+ constructor(config: TransportConfig);
30
+ private headers;
31
+ private url;
32
+ private timeout;
33
+ /**
34
+ * One fetch, with a timeout that turns "hung socket" into a fast, typed
35
+ * failure. A timeout surfaces as `DontCodeError` with status 408 / code
36
+ * `Timeout`; any other transport failure (DNS, refused, offline) as status
37
+ * 0 / code `NetworkError`. Both are distinct from a real `401`, so an auth
38
+ * guard can tell "backend is down" apart from "user is signed out".
39
+ */
40
+ private send;
41
+ /** GET and parse the JSON response. */
42
+ get<T>(path: string, opts?: RequestOptions): Promise<T>;
43
+ /** POST a JSON body and parse the JSON response. */
44
+ json<T>(path: string, body?: unknown, opts?: RequestOptions): Promise<T>;
45
+ /** PUT a multipart form (file uploads). The runtime sets the boundary. */
46
+ multipart<T>(path: string, form: FormData, opts?: RequestOptions): Promise<T>;
47
+ private parse;
48
+ }
package/dist/index.cjs CHANGED
@@ -267,6 +267,12 @@ var AuthApi = class {
267
267
  password: input.password
268
268
  });
269
269
  }
270
+ /** Validate the current credential (API key or device token) and report the
271
+ * project, the caller's role, and which capabilities that role grants.
272
+ * Backs the MCP "is my session still good" check. */
273
+ info() {
274
+ return this.transport.get("/api/v1/info");
275
+ }
270
276
  /** Resolve the signed-in user from their access token, or `{ user: null }`.
271
277
  * This is a network round-trip; for a per-navigation guard prefer
272
278
  * `getSession`, which can answer offline and caches verified results. */
@@ -443,6 +449,11 @@ var Transport = class {
443
449
  if (timer) clearTimeout(timer);
444
450
  }
445
451
  }
452
+ /** GET and parse the JSON response. */
453
+ async get(path, opts) {
454
+ const res = await this.send(path, { method: "GET", headers: this.headers(opts) }, opts);
455
+ return this.parse(res);
456
+ }
446
457
  /** POST a JSON body and parse the JSON response. */
447
458
  async json(path, body, opts) {
448
459
  const res = await this.send(