@justin06lee/yagami 0.4.1 → 0.5.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/server/app.ts","../src/server/config.ts"],"sourcesContent":["import { serve, type ServerType } from \"@hono/node-server\";\nimport { YagamiEngine } from \"./core/engine.js\";\nimport { SessionCache } from \"./core/sessionCache.js\";\nimport { createApp } from \"./server/app.js\";\nimport {\n loadConfig,\n sessionCachePath,\n type YagamiConfig,\n} from \"./server/config.js\";\nimport { VERSION } from \"./version.js\";\n\nexport interface RunningServer {\n server: ServerType;\n engine: YagamiEngine;\n sessionCache: SessionCache;\n config: YagamiConfig;\n url: string;\n close(): Promise<void>;\n}\n\nexport interface StartOptions extends Partial<YagamiConfig> {\n /** Sink for one-line request logs (default: console.log). Pass null to disable. */\n log?: ((line: string) => void) | null;\n}\n\n/**\n * Start a yagami server. Overrides are merged over the loaded config\n * (~/.config/yagami/config.json plus YAGAMI_* env vars).\n */\nexport async function startYagami(overrides: StartOptions = {}): Promise<RunningServer> {\n const { log, ...configOverrides } = overrides;\n const config: YagamiConfig = { ...loadConfig(), ...definedProps(configOverrides) };\n if (config.apiKeys.length === 0) {\n throw new Error(\n \"no API keys configured — run `yagami keygen` (or set YAGAMI_API_KEY) so the endpoint isn't unauthenticated\",\n );\n }\n\n const sessionCache = new SessionCache({ persistPath: sessionCachePath() });\n const engine = new YagamiEngine({\n ...(config.providers ? { providerConfig: config.providers } : {}),\n ...(config.defaultProvider ? { defaultProvider: config.defaultProvider } : {}),\n ...(config.claudePath ? { claudePath: config.claudePath } : {}),\n ...(config.claudeConfigDir ? { claudeConfigDir: config.claudeConfigDir } : {}),\n ...(config.defaultModel ? { defaultModel: config.defaultModel } : {}),\n sessionCache,\n appName: \"yagami\",\n });\n\n const app = createApp({\n engine,\n apiKeys: config.apiKeys,\n cors: config.cors,\n version: VERSION,\n ...(log === null ? {} : { log: log ?? ((line: string) => console.log(line)) }),\n });\n\n const server = await new Promise<ServerType>((resolve) => {\n const s = serve({ fetch: app.fetch, hostname: config.host, port: config.port }, () => resolve(s));\n });\n\n const address = server.address();\n const port = typeof address === \"object\" && address ? address.port : config.port;\n\n return {\n server,\n engine,\n sessionCache,\n config: { ...config, port },\n url: `http://${config.host}:${port}`,\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()));\n }),\n };\n}\n\nfunction definedProps<T extends object>(obj: T): Partial<T> {\n return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)) as Partial<T>;\n}\n\nexport { createApp } from \"./server/app.js\";\nexport type { AppOptions, EngineLike } from \"./server/app.js\";\nexport {\n loadConfig,\n loadFileConfig,\n saveConfig,\n generateApiKey,\n configFilePath,\n sessionCachePath,\n serverStatePath,\n logFilePath,\n readServerState,\n writeServerState,\n clearServerState,\n isProcessAlive,\n yagamiConfigDir,\n type YagamiConfig,\n type ServerState,\n} from \"./server/config.js\";\n","import { createHash, randomUUID, timingSafeEqual } from \"node:crypto\";\nimport { Hono } from \"hono\";\nimport { cors } from \"hono/cors\";\nimport { streamSSE } from \"hono/streaming\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport { ApiError, type MessagesRequest } from \"../core/types.js\";\nimport { toApiError } from \"../core/errors.js\";\nimport type { CompleteResult, EngineModel, StreamOptions, StreamStart } from \"../core/engine.js\";\nimport {\n ChatChunkTranslator,\n chatToMessagesRequest,\n modelListBody,\n openAiErrorBody,\n toChatCompletion,\n type ChatCompletionsRequest,\n} from \"../core/openai.js\";\n\n/** What the app needs from an engine — lets tests inject a fake. */\nexport interface EngineLike {\n /** Executable of the default provider. */\n executable: string;\n defaultProviderId: string;\n providerIds: string[];\n complete(req: MessagesRequest): Promise<CompleteResult>;\n stream(req: MessagesRequest, opts?: StreamOptions): StreamStart;\n listModels(): Promise<EngineModel[]>;\n}\n\nexport interface AppOptions {\n engine: EngineLike;\n apiKeys: string[];\n cors?: boolean;\n version?: string;\n /** Sink for one-line request logs; omit to disable request logging. */\n log?: (line: string) => void;\n}\n\n/** Served by GET /v1/models only when probing the CLI fails. */\nconst FALLBACK_MODELS: EngineModel[] = [\n \"claude-fable-5\",\n \"claude-opus-5\",\n \"claude-sonnet-5\",\n \"claude-haiku-4-5-20251001\",\n].map((id) => ({ id, display_name: id }));\n\nfunction safeEqual(a: string, b: string): boolean {\n const ha = createHash(\"sha256\").update(a).digest();\n const hb = createHash(\"sha256\").update(b).digest();\n return timingSafeEqual(ha, hb);\n}\n\nfunction errorBody(type: ApiError[\"type\"], message: string) {\n return { type: \"error\" as const, error: { type, message } };\n}\n\nfunction requestLine(\n path: string,\n status: number,\n model: string,\n startedAt: number,\n extra: { cost?: number; session?: string; stream?: boolean; error?: string } = {},\n): string {\n const parts = [\n new Date().toISOString(),\n `POST ${path} ${status}`,\n `model=${model}`,\n `${((Date.now() - startedAt) / 1000).toFixed(1)}s`,\n ];\n if (extra.stream) parts.push(\"stream\");\n if (extra.cost !== undefined) parts.push(`cost=$${extra.cost.toFixed(6)}`);\n if (extra.session) parts.push(`session=${extra.session}`);\n if (extra.error) parts.push(`error=${extra.error}`);\n return parts.join(\" \");\n}\n\nexport function createApp(options: AppOptions): Hono {\n const { engine, apiKeys, log } = options;\n const app = new Hono();\n const stats = { startedAt: Date.now(), requests: 0, totalCostUsd: 0 };\n\n if (options.cors) app.use(\"*\", cors());\n\n app.use(\"*\", async (c, next) => {\n c.header(\"request-id\", `req_${randomUUID().replace(/-/g, \"\")}`);\n await next();\n });\n\n app.get(\"/healthz\", (c) =>\n c.json({\n ok: true,\n service: \"yagami\",\n version: options.version,\n provider: engine.defaultProviderId,\n providers: engine.providerIds,\n executable: engine.executable,\n uptime_s: Math.round((Date.now() - stats.startedAt) / 1000),\n requests: stats.requests,\n total_cost_usd: stats.totalCostUsd,\n }),\n );\n\n app.use(\"/v1/*\", async (c, next) => {\n const header = c.req.header(\"x-api-key\") ?? c.req.header(\"authorization\")?.replace(/^Bearer\\s+/i, \"\");\n const ok = header != null && apiKeys.some((key) => safeEqual(key, header));\n if (!ok) {\n const err = new ApiError(401, \"authentication_error\", \"invalid API key (x-api-key or Authorization: Bearer)\");\n // The chat-completions path answers in the OpenAI error shape.\n return c.req.path.startsWith(\"/v1/chat\")\n ? c.json(openAiErrorBody(err), 401)\n : c.json(err.toBody(), 401);\n }\n await next();\n });\n\n // Served in a merged shape: Anthropic fields + OpenAI fields per model, so\n // both SDKs' models.list() parse it.\n app.get(\"/v1/models\", async (c) => {\n let models = FALLBACK_MODELS;\n let source = \"fallback\";\n try {\n const probed = await engine.listModels();\n if (probed.length > 0) {\n models = probed;\n source = \"engine\";\n }\n } catch {\n // engine unavailable or slow — the static list keeps clients working\n }\n c.header(\"x-yagami-models-source\", source);\n return c.json(modelListBody(models));\n });\n\n app.post(\"/v1/messages\", async (c) => {\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json(errorBody(\"invalid_request_error\", \"request body must be valid JSON\"), 400);\n }\n const req = body as MessagesRequest;\n const startedAt = Date.now();\n const model = typeof req.model === \"string\" ? req.model : \"(default)\";\n stats.requests += 1;\n\n try {\n if (req.stream === true) {\n const abortController = new AbortController();\n const { ignored, provider, events } = engine.stream(req, {\n signal: abortController.signal,\n onResult: (info) => {\n if (info.costUsd !== undefined) stats.totalCostUsd += info.costUsd;\n log?.(\n requestLine(\"/v1/messages\", 200, model, startedAt, {\n stream: true,\n ...(info.costUsd !== undefined ? { cost: info.costUsd } : {}),\n ...(info.sessionId ? { session: info.sessionId } : {}),\n }),\n );\n },\n });\n c.header(\"x-yagami-provider\", provider);\n if (ignored.length > 0) c.header(\"x-yagami-ignored\", ignored.join(\",\"));\n return streamSSE(c, async (stream) => {\n stream.onAbort(() => abortController.abort());\n for await (const ev of events) {\n await stream.writeSSE({ event: ev.event, data: JSON.stringify(ev.data) });\n }\n });\n }\n\n const result = await engine.complete(req);\n if (result.costUsd !== undefined) stats.totalCostUsd += result.costUsd;\n log?.(\n requestLine(\"/v1/messages\", 200, model, startedAt, {\n ...(result.costUsd !== undefined ? { cost: result.costUsd } : {}),\n ...(result.sessionId ? { session: result.sessionId } : {}),\n }),\n );\n c.header(\"x-yagami-provider\", result.provider);\n if (result.ignored.length > 0) c.header(\"x-yagami-ignored\", result.ignored.join(\",\"));\n if (result.costUsd !== undefined) c.header(\"x-yagami-cost-usd\", result.costUsd.toFixed(6));\n if (result.sessionId) c.header(\"x-yagami-session\", result.sessionId);\n return c.json(result.response);\n } catch (err) {\n const apiErr = toApiError(err);\n log?.(requestLine(\"/v1/messages\", apiErr.status, model, startedAt, { error: apiErr.type }));\n return c.json(apiErr.toBody(), apiErr.status as ContentfulStatusCode);\n }\n });\n\n // OpenAI dialect: the same engine behind Chat Completions shapes, so apps\n // that expect an OpenAI base URL + API key work against the same key.\n app.post(\"/v1/chat/completions\", async (c) => {\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json(openAiErrorBody(new ApiError(400, \"invalid_request_error\", \"request body must be valid JSON\")), 400);\n }\n const startedAt = Date.now();\n const chatReq = body as ChatCompletionsRequest;\n const model = typeof chatReq?.model === \"string\" ? chatReq.model : \"(default)\";\n stats.requests += 1;\n\n try {\n const { req, extraIgnored, includeUsage } = chatToMessagesRequest(chatReq);\n\n if (req.stream === true) {\n const abortController = new AbortController();\n const { ignored, provider, events } = engine.stream(req, {\n signal: abortController.signal,\n onResult: (info) => {\n if (info.costUsd !== undefined) stats.totalCostUsd += info.costUsd;\n log?.(\n requestLine(\"/v1/chat/completions\", 200, model, startedAt, {\n stream: true,\n ...(info.costUsd !== undefined ? { cost: info.costUsd } : {}),\n ...(info.sessionId ? { session: info.sessionId } : {}),\n }),\n );\n },\n });\n c.header(\"x-yagami-provider\", provider);\n const allIgnored = [...ignored, ...extraIgnored];\n if (allIgnored.length > 0) c.header(\"x-yagami-ignored\", allIgnored.join(\",\"));\n const translator = new ChatChunkTranslator(includeUsage);\n return streamSSE(c, async (stream) => {\n stream.onAbort(() => abortController.abort());\n for await (const ev of events) {\n for (const chunk of translator.push(ev)) {\n await stream.writeSSE({ data: JSON.stringify(chunk) });\n }\n }\n if (!translator.errored) await stream.writeSSE({ data: \"[DONE]\" });\n });\n }\n\n const result = await engine.complete(req);\n if (result.costUsd !== undefined) stats.totalCostUsd += result.costUsd;\n log?.(\n requestLine(\"/v1/chat/completions\", 200, model, startedAt, {\n ...(result.costUsd !== undefined ? { cost: result.costUsd } : {}),\n ...(result.sessionId ? { session: result.sessionId } : {}),\n }),\n );\n c.header(\"x-yagami-provider\", result.provider);\n const allIgnored = [...result.ignored, ...extraIgnored];\n if (allIgnored.length > 0) c.header(\"x-yagami-ignored\", allIgnored.join(\",\"));\n if (result.costUsd !== undefined) c.header(\"x-yagami-cost-usd\", result.costUsd.toFixed(6));\n if (result.sessionId) c.header(\"x-yagami-session\", result.sessionId);\n return c.json(toChatCompletion(result.response));\n } catch (err) {\n const apiErr = toApiError(err);\n log?.(requestLine(\"/v1/chat/completions\", apiErr.status, model, startedAt, { error: apiErr.type }));\n return c.json(openAiErrorBody(apiErr), apiErr.status as ContentfulStatusCode);\n }\n });\n\n app.notFound((c) =>\n c.json(errorBody(\"not_found_error\", `no route for ${c.req.method} ${c.req.path}`), 404),\n );\n\n app.onError((err, c) => {\n const apiErr = toApiError(err);\n return c.json(apiErr.toBody(), apiErr.status as ContentfulStatusCode);\n });\n\n return app;\n}\n","import { randomBytes } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { yagamiConfigDir } from \"../core/hostConfig.js\";\nimport type { ProviderConfigEntry } from \"../core/providers/registry.js\";\n\nexport { yagamiConfigDir } from \"../core/hostConfig.js\";\n\nexport interface YagamiConfig {\n host: string;\n port: number;\n apiKeys: string[];\n /** @deprecated Use providers.claude.path. */\n claudePath?: string;\n /** @deprecated Use providers.claude.configDir. */\n claudeConfigDir?: string;\n defaultModel?: string;\n cors?: boolean;\n /** Provider used for bare model ids (default: claude). */\n defaultProvider?: string;\n /** Per-provider settings, keyed by provider id. */\n providers?: Record<string, ProviderConfigEntry>;\n}\n\nexport const DEFAULT_CONFIG: YagamiConfig = {\n host: \"127.0.0.1\",\n port: 8787,\n apiKeys: [],\n};\n\nexport function configFilePath(): string {\n return path.join(yagamiConfigDir(), \"config.json\");\n}\n\nexport function sessionCachePath(): string {\n return path.join(yagamiConfigDir(), \"sessions.json\");\n}\n\nexport function serverStatePath(): string {\n return path.join(yagamiConfigDir(), \"server.json\");\n}\n\nexport function logFilePath(): string {\n return path.join(yagamiConfigDir(), \"yagami.log\");\n}\n\n/** What a running server records about itself for `stop`/`status`. */\nexport interface ServerState {\n pid: number;\n host: string;\n port: number;\n url: string;\n startedAt: string;\n version: string;\n log?: string;\n}\n\nexport function readServerState(): ServerState | undefined {\n try {\n const state = JSON.parse(fs.readFileSync(serverStatePath(), \"utf8\")) as ServerState;\n return typeof state?.pid === \"number\" ? state : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function writeServerState(state: ServerState): void {\n fs.mkdirSync(yagamiConfigDir(), { recursive: true, mode: 0o700 });\n fs.writeFileSync(serverStatePath(), `${JSON.stringify(state, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/** Remove the state file; with `pid`, only if it still belongs to that pid. */\nexport function clearServerState(pid?: number): void {\n try {\n if (pid !== undefined && readServerState()?.pid !== pid) return;\n fs.unlinkSync(serverStatePath());\n } catch {\n // already gone\n }\n}\n\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Config as stored on disk, without env overrides (safe to save back). */\nexport function loadFileConfig(): YagamiConfig {\n let fromFile: Partial<YagamiConfig> = {};\n try {\n fromFile = JSON.parse(fs.readFileSync(configFilePath(), \"utf8\")) as Partial<YagamiConfig>;\n } catch {\n // no config file yet\n }\n return {\n ...DEFAULT_CONFIG,\n ...fromFile,\n apiKeys: Array.isArray(fromFile.apiKeys) ? fromFile.apiKeys.filter((k) => typeof k === \"string\") : [],\n };\n}\n\n/** File config plus environment overrides. */\nexport function loadConfig(): YagamiConfig {\n const cfg = loadFileConfig();\n const env = process.env;\n if (env[\"YAGAMI_HOST\"]) cfg.host = env[\"YAGAMI_HOST\"];\n if (env[\"YAGAMI_PORT\"] && Number.isFinite(Number(env[\"YAGAMI_PORT\"]))) {\n cfg.port = Number(env[\"YAGAMI_PORT\"]);\n }\n if (env[\"YAGAMI_API_KEY\"] && !cfg.apiKeys.includes(env[\"YAGAMI_API_KEY\"])) {\n cfg.apiKeys.push(env[\"YAGAMI_API_KEY\"]);\n }\n if (env[\"YAGAMI_CLAUDE_PATH\"]) cfg.claudePath = env[\"YAGAMI_CLAUDE_PATH\"];\n if (env[\"YAGAMI_DEFAULT_MODEL\"]) cfg.defaultModel = env[\"YAGAMI_DEFAULT_MODEL\"];\n if (env[\"YAGAMI_PROVIDER\"]) cfg.defaultProvider = env[\"YAGAMI_PROVIDER\"];\n return cfg;\n}\n\nexport function saveConfig(cfg: YagamiConfig): string {\n const dir = yagamiConfigDir();\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n const file = configFilePath();\n fs.writeFileSync(file, `${JSON.stringify(cfg, null, 2)}\\n`, { mode: 0o600 });\n return file;\n}\n\nexport function generateApiKey(): string {\n return `ygm_${randomBytes(24).toString(\"hex\")}`;\n}\n\nexport function maskKey(key: string): string {\n return key.length <= 12 ? key : `${key.slice(0, 12)}…`;\n}\n"],"mappings":"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","names":["path","allIgnored"]}
package/dist/cli.js CHANGED
@@ -13,13 +13,13 @@ import {
13
13
  sessionCachePath,
14
14
  startYagami,
15
15
  writeServerState
16
- } from "./chunk-M5UHR273.js";
16
+ } from "./chunk-GPX47OIO.js";
17
17
  import {
18
18
  VERSION,
19
19
  YagamiEngine,
20
20
  createProvider,
21
21
  detectProviders
22
- } from "./chunk-ASS6MJ7C.js";
22
+ } from "./chunk-2UG5CR5X.js";
23
23
 
24
24
  // src/cli.ts
25
25
  import { spawn } from "child_process";
@@ -28,7 +28,7 @@ import * as path from "path";
28
28
  import { Command } from "commander";
29
29
  var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
30
30
  var program = new Command();
31
- program.name("yagami").description("Anthropic-compatible API served by your signed-in Claude Code CLI").version(VERSION);
31
+ program.name("yagami").description("Anthropic- and OpenAI-compatible API served by your signed-in coding-agent CLIs").version(VERSION);
32
32
  program.command("start", { isDefault: true }).description("start the yagami server").option("-p, --port <port>", "port to listen on").option("-H, --host <host>", "host to bind (default 127.0.0.1)").option("--claude <path>", "path to the claude executable").option("--provider <id>", "default provider for bare model ids (claude, codex, opencode, gemini, \u2026)").option("--cors", "enable permissive CORS (for browser clients)").option("--daemon", "run in the background (managed with `yagami stop`/`yagami status`)").option("--log <file>", "log file for --daemon mode (default ~/.config/yagami/yagami.log)").action(async (opts) => {
33
33
  const fileConfig = loadFileConfig();
34
34
  let freshKey;
@@ -78,17 +78,18 @@ program.command("start", { isDefault: true }).description("start the yagami serv
78
78
  console.log(` config ${configFilePath()}`);
79
79
  if (freshKey) {
80
80
  console.log(` api key ${freshKey}`);
81
- console.log(" (newly generated and saved \u2014 copy it now, it is shown in full only once)");
81
+ console.log(" (newly generated and saved \u2014 `yagami key` prints it again)");
82
82
  } else {
83
- console.log(` api keys ${running.config.apiKeys.map(maskKey).join(", ")}`);
83
+ console.log(` api keys ${running.config.apiKeys.map(maskKey).join(", ")} (\`yagami key\` prints them in full)`);
84
84
  }
85
85
  if (!["127.0.0.1", "localhost", "::1"].includes(running.config.host)) {
86
86
  console.log(
87
87
  ` \u26A0 bound to ${running.config.host} \u2014 reachable beyond this machine. Only do this on a network you trust.`
88
88
  );
89
89
  }
90
- console.log("\nPoint any Anthropic SDK at it:");
91
- console.log(` baseURL: "${running.url}" apiKey: <your yagami key>`);
90
+ console.log("\nConnect apps \u2014 either dialect, same key (`yagami key` prints ready-to-paste env exports):");
91
+ console.log(` Anthropic apps baseURL ${running.url} (ANTHROPIC_BASE_URL / ANTHROPIC_API_KEY)`);
92
+ console.log(` OpenAI apps baseURL ${running.url}/v1 (OPENAI_BASE_URL / OPENAI_API_KEY)`);
92
93
  } catch (err) {
93
94
  console.error(`yagami: ${err instanceof Error ? err.message : String(err)}`);
94
95
  process.exitCode = 1;
@@ -132,8 +133,9 @@ async function startDaemon(opts, freshKey) {
132
133
  console.log(` log ${logPath}`);
133
134
  if (freshKey) {
134
135
  console.log(` key ${freshKey}`);
135
- console.log(" (newly generated and saved \u2014 copy it now, it is shown in full only once)");
136
+ console.log(" (newly generated and saved \u2014 `yagami key` prints it again)");
136
137
  }
138
+ console.log(" `yagami key` prints the URL, key, and env exports for client apps");
137
139
  return;
138
140
  }
139
141
  await sleep(200);
@@ -194,6 +196,29 @@ program.command("keygen").description("generate an API key and add it to the con
194
196
  console.log(key);
195
197
  console.error(`saved to ${file} (${cfg.apiKeys.length} key${cfg.apiKeys.length === 1 ? "" : "s"} total)`);
196
198
  });
199
+ program.command("key").description("print the server URL, API key, and ready-to-paste env exports for client apps").action(() => {
200
+ const cfg = loadConfig();
201
+ const state = readServerState();
202
+ const live = state !== void 0 && isProcessAlive(state.pid);
203
+ const url = live ? state.url : `http://${cfg.host}:${cfg.port}`;
204
+ if (cfg.apiKeys.length === 0) {
205
+ console.error("no API keys configured \u2014 run `yagami start` (generates one) or `yagami keygen`");
206
+ process.exitCode = 1;
207
+ return;
208
+ }
209
+ const key = cfg.apiKeys[cfg.apiKeys.length - 1];
210
+ console.log(`url ${url}${live ? "" : " (server not running \u2014 this is where `yagami start` will listen)"}`);
211
+ console.log(`key ${key}`);
212
+ if (cfg.apiKeys.length > 1) {
213
+ console.log(` (newest of ${cfg.apiKeys.length} keys in ${configFilePath()})`);
214
+ }
215
+ console.log("\nAnthropic-dialect apps:");
216
+ console.log(` export ANTHROPIC_BASE_URL=${url}`);
217
+ console.log(` export ANTHROPIC_API_KEY=${key}`);
218
+ console.log("\nOpenAI-dialect apps:");
219
+ console.log(` export OPENAI_BASE_URL=${url}/v1`);
220
+ console.log(` export OPENAI_API_KEY=${key}`);
221
+ });
197
222
  program.command("doctor").description("check which coding-agent CLIs yagami can drive and whether they work").option("--live", "send one real (tiny) completion through the default provider").option("--provider <id>", "provider to use for --live (default: config/claude)").action(async (opts) => {
198
223
  let failed = false;
199
224
  const cfg = loadConfig();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { Command } from \"commander\";\nimport { YagamiEngine } from \"./core/engine.js\";\nimport { ClaudeProvider } from \"./core/providers/claude.js\";\nimport { createProvider, detectProviders } from \"./core/providers/registry.js\";\nimport { startYagami } from \"./server.js\";\nimport {\n clearServerState,\n configFilePath,\n generateApiKey,\n isProcessAlive,\n loadConfig,\n loadFileConfig,\n logFilePath,\n maskKey,\n readServerState,\n saveConfig,\n sessionCachePath,\n writeServerState,\n} from \"./server/config.js\";\nimport { VERSION } from \"./version.js\";\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst program = new Command();\n\nprogram\n .name(\"yagami\")\n .description(\"Anthropic-compatible API served by your signed-in Claude Code CLI\")\n .version(VERSION);\n\ninterface StartFlags {\n port?: string;\n host?: string;\n claude?: string;\n provider?: string;\n cors?: boolean;\n daemon?: boolean;\n log?: string;\n}\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"start the yagami server\")\n .option(\"-p, --port <port>\", \"port to listen on\")\n .option(\"-H, --host <host>\", \"host to bind (default 127.0.0.1)\")\n .option(\"--claude <path>\", \"path to the claude executable\")\n .option(\"--provider <id>\", \"default provider for bare model ids (claude, codex, opencode, gemini, …)\")\n .option(\"--cors\", \"enable permissive CORS (for browser clients)\")\n .option(\"--daemon\", \"run in the background (managed with `yagami stop`/`yagami status`)\")\n .option(\"--log <file>\", \"log file for --daemon mode (default ~/.config/yagami/yagami.log)\")\n .action(async (opts: StartFlags) => {\n // First run: generate a key automatically so the endpoint is never open.\n const fileConfig = loadFileConfig();\n let freshKey: string | undefined;\n if (fileConfig.apiKeys.length === 0 && !process.env[\"YAGAMI_API_KEY\"]) {\n freshKey = generateApiKey();\n fileConfig.apiKeys.push(freshKey);\n saveConfig(fileConfig);\n }\n\n if (opts.daemon) {\n await startDaemon(opts, freshKey);\n return;\n }\n\n try {\n const running = await startYagami({\n port: opts.port !== undefined ? Number(opts.port) : undefined,\n host: opts.host,\n claudePath: opts.claude,\n defaultProvider: opts.provider,\n cors: opts.cors,\n });\n\n writeServerState({\n pid: process.pid,\n host: running.config.host,\n port: running.config.port,\n url: running.url,\n startedAt: new Date().toISOString(),\n version: VERSION,\n ...(process.env[\"YAGAMI_LOG_FILE\"] ? { log: process.env[\"YAGAMI_LOG_FILE\"] } : {}),\n });\n const shutdown = () => {\n running.sessionCache.persistNow();\n clearServerState(process.pid);\n void running.close().finally(() => process.exit(0));\n // Don't hang on a stuck in-flight response.\n setTimeout(() => process.exit(0), 3000).unref?.();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n const engine = running.engine;\n const version = await engine.defaultProvider.version();\n const others = engine.providerIds.filter((id) => id !== engine.defaultProviderId);\n console.log(`yagami v${VERSION}`);\n console.log(` listening ${running.url}`);\n console.log(` provider ${engine.defaultProviderId} — ${engine.executable}${version ? ` (${version})` : \"\"}`);\n console.log(\n ` also ${others.length > 0 ? `${others.join(\", \")} (use model \"<provider>:<model>\")` : \"no other harness CLIs found — see `yagami doctor`\"}`,\n );\n console.log(` config ${configFilePath()}`);\n if (freshKey) {\n console.log(` api key ${freshKey}`);\n console.log(\" (newly generated and saved — copy it now, it is shown in full only once)\");\n } else {\n console.log(` api keys ${running.config.apiKeys.map(maskKey).join(\", \")}`);\n }\n if (![\"127.0.0.1\", \"localhost\", \"::1\"].includes(running.config.host)) {\n console.log(\n ` ⚠ bound to ${running.config.host} — reachable beyond this machine. Only do this on a network you trust.`,\n );\n }\n console.log(\"\\nPoint any Anthropic SDK at it:\");\n console.log(` baseURL: \"${running.url}\" apiKey: <your yagami key>`);\n } catch (err) {\n console.error(`yagami: ${err instanceof Error ? err.message : String(err)}`);\n process.exitCode = 1;\n }\n });\n\nasync function startDaemon(opts: StartFlags, freshKey: string | undefined): Promise<void> {\n const existing = readServerState();\n if (existing && isProcessAlive(existing.pid)) {\n console.error(`yagami is already running (pid ${existing.pid}, ${existing.url}) — \\`yagami stop\\` first`);\n process.exitCode = 1;\n return;\n }\n clearServerState();\n\n const logPath = opts.log ? path.resolve(opts.log) : logFilePath();\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n const fd = fs.openSync(logPath, \"a\");\n const args = [process.argv[1]!, \"start\"];\n if (opts.port !== undefined) args.push(\"-p\", opts.port);\n if (opts.host !== undefined) args.push(\"-H\", opts.host);\n if (opts.claude !== undefined) args.push(\"--claude\", opts.claude);\n if (opts.provider !== undefined) args.push(\"--provider\", opts.provider);\n if (opts.cors) args.push(\"--cors\");\n\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: [\"ignore\", fd, fd],\n env: { ...process.env, YAGAMI_LOG_FILE: logPath },\n });\n fs.closeSync(fd);\n let exitCode: number | null | undefined;\n child.on(\"exit\", (code) => {\n exitCode = code;\n });\n child.unref();\n\n const deadline = Date.now() + 15_000;\n while (Date.now() < deadline && exitCode === undefined) {\n const state = readServerState();\n if (state && state.pid === child.pid) {\n console.log(`yagami v${VERSION} running in the background`);\n console.log(` pid ${child.pid}`);\n console.log(` url ${state.url}`);\n console.log(` log ${logPath}`);\n if (freshKey) {\n console.log(` key ${freshKey}`);\n console.log(\" (newly generated and saved — copy it now, it is shown in full only once)\");\n }\n return;\n }\n await sleep(200);\n }\n console.error(\n exitCode !== undefined\n ? `yagami exited immediately (code ${exitCode}) — see ${logPath}`\n : `yagami did not report ready within 15s — see ${logPath}`,\n );\n process.exitCode = 1;\n}\n\nprogram\n .command(\"stop\")\n .description(\"stop a running yagami server\")\n .action(async () => {\n const state = readServerState();\n if (!state || !isProcessAlive(state.pid)) {\n if (state) clearServerState();\n console.log(\"yagami is not running\");\n return;\n }\n process.kill(state.pid, \"SIGTERM\");\n const deadline = Date.now() + 5_000;\n while (Date.now() < deadline) {\n if (!isProcessAlive(state.pid)) {\n clearServerState();\n console.log(`stopped yagami (pid ${state.pid})`);\n return;\n }\n await sleep(100);\n }\n console.error(`yagami (pid ${state.pid}) did not exit within 5s`);\n process.exitCode = 1;\n });\n\nprogram\n .command(\"status\")\n .description(\"show whether yagami is running, plus request/cost totals\")\n .action(async () => {\n const state = readServerState();\n if (!state || !isProcessAlive(state.pid)) {\n if (state) clearServerState();\n console.log(\"yagami is not running\");\n process.exitCode = 1;\n return;\n }\n console.log(`yagami running (pid ${state.pid})`);\n console.log(` url ${state.url}`);\n console.log(` since ${state.startedAt}`);\n if (state.log) console.log(` log ${state.log}`);\n try {\n const res = await fetch(`${state.url}/healthz`, { signal: AbortSignal.timeout(3000) });\n const body = (await res.json()) as {\n version?: string;\n claude?: string;\n requests?: number;\n total_cost_usd?: number;\n };\n console.log(` version ${body.version ?? \"?\"}`);\n console.log(` claude ${body.claude ?? \"?\"}`);\n console.log(` requests ${body.requests ?? 0}`);\n console.log(` cost $${(body.total_cost_usd ?? 0).toFixed(4)} (would-be API cost since start)`);\n } catch {\n console.log(` healthz unreachable — process is alive but ${state.url} is not answering`);\n }\n });\n\nprogram\n .command(\"keygen\")\n .description(\"generate an API key and add it to the config\")\n .action(() => {\n const cfg = loadFileConfig();\n const key = generateApiKey();\n cfg.apiKeys.push(key);\n const file = saveConfig(cfg);\n console.log(key);\n console.error(`saved to ${file} (${cfg.apiKeys.length} key${cfg.apiKeys.length === 1 ? \"\" : \"s\"} total)`);\n });\n\nprogram\n .command(\"doctor\")\n .description(\"check which coding-agent CLIs yagami can drive and whether they work\")\n .option(\"--live\", \"send one real (tiny) completion through the default provider\")\n .option(\"--provider <id>\", \"provider to use for --live (default: config/claude)\")\n .action(async (opts: { live?: boolean; provider?: string }) => {\n let failed = false;\n const cfg = loadConfig();\n const providerConfig = { ...cfg.providers };\n if (cfg.claudePath || cfg.claudeConfigDir) {\n providerConfig[\"claude\"] = {\n ...providerConfig[\"claude\"],\n ...(cfg.claudePath ? { path: cfg.claudePath } : {}),\n ...(cfg.claudeConfigDir ? { configDir: cfg.claudeConfigDir } : {}),\n };\n }\n const defaultProvider = opts.provider ?? cfg.defaultProvider ?? \"claude\";\n\n console.log(`node ${process.version}`);\n console.log(`config ${configFilePath()}${fs.existsSync(configFilePath()) ? \"\" : \" (not created yet)\"}`);\n console.log(`api keys ${cfg.apiKeys.length === 0 ? \"none — run `yagami keygen`\" : cfg.apiKeys.map(maskKey).join(\", \")}`);\n console.log(`bind ${cfg.host}:${cfg.port}`);\n console.log(`sessions ${sessionCachePath()}${fs.existsSync(sessionCachePath()) ? \"\" : \" (empty)\"}`);\n const state = readServerState();\n console.log(\n `server ${state && isProcessAlive(state.pid) ? `running (pid ${state.pid}, ${state.url})` : \"not running\"}`,\n );\n\n console.log(\"\\nproviders (model ids route as \\\"<provider>:<model>\\\"; bare ids go to the default)\");\n const detected = detectProviders(providerConfig);\n const installed = detected.filter((d) => d.installed);\n for (const d of detected) {\n const marker = d.id === defaultProvider ? \"*\" : \" \";\n if (!d.installed) {\n if (presetIsNiche(d.id)) continue; // keep the list readable\n console.log(` ${marker} ${d.id.padEnd(11)} not installed — ${d.installHint}`);\n continue;\n }\n let version: string | undefined;\n try {\n version = await createProvider(d.id, providerConfig[d.id] ?? {}, {}).version();\n } catch (err) {\n version = `✗ ${err instanceof Error ? err.message : String(err)}`;\n }\n console.log(` ${marker} ${d.id.padEnd(11)} ${d.path}${version ? ` (${version})` : \"\"}`);\n }\n const hidden = detected.filter((d) => !d.installed && presetIsNiche(d.id)).length;\n if (hidden > 0) console.log(` … ${hidden} more ACP presets not installed (see README for the full list)`);\n if (!installed.some((d) => d.id === defaultProvider)) {\n failed = true;\n console.log(` ✗ default provider \"${defaultProvider}\" is not installed`);\n }\n\n if (installed.some((d) => d.id === \"claude\")) {\n try {\n const claude = createProvider(\"claude\", providerConfig[\"claude\"] ?? {}, {}) as ClaudeProvider;\n const skew = await claude.versionSkew();\n if (skew) {\n console.log(`\\nagent sdk ${skew.sdkVersion} ↔ claude ${skew.binaryVersion} — ${skew.inSync ? \"in sync\" : `⚠ ${skew.note}`}`);\n }\n } catch {\n // skew check is advisory\n }\n }\n\n if (opts.live && !failed) {\n console.log(`\\nlive check: sending one tiny completion through ${defaultProvider}…`);\n try {\n const engine = new YagamiEngine({ providerConfig, defaultProvider, ...(cfg.defaultModel ? { defaultModel: cfg.defaultModel } : {}) });\n const started = Date.now();\n const result = await engine.complete({\n messages: [{ role: \"user\", content: \"Reply with exactly: pong\" }],\n max_tokens: 32,\n });\n const text = result.response.content\n .filter((b) => b.type === \"text\")\n .map((b) => b[\"text\"])\n .join(\"\");\n console.log(` reply ${JSON.stringify(text)}`);\n console.log(` model ${result.response.model}`);\n console.log(` latency ${((Date.now() - started) / 1000).toFixed(1)}s`);\n if (result.costUsd !== undefined) console.log(` cost $${result.costUsd.toFixed(6)}`);\n } catch (err) {\n failed = true;\n console.log(` ✗ ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (failed) process.exitCode = 1;\n });\n\n/** Presets most people won't have; hidden from doctor unless installed. */\nfunction presetIsNiche(id: string): boolean {\n return ![\"claude\", \"codex\", \"opencode\", \"gemini\", \"copilot\", \"cursor\", \"qwen\", \"goose\", \"kimi\"].includes(id);\n}\n\nprogram\n .command(\"models\")\n .description(\"list models across every installed provider (ids are ready to paste into requests)\")\n .option(\"--provider <id>\", \"only this provider\")\n .action(async (opts: { provider?: string }) => {\n const cfg = loadConfig();\n try {\n const engine = new YagamiEngine({\n ...(cfg.providers ? { providerConfig: cfg.providers } : {}),\n ...(cfg.defaultProvider ? { defaultProvider: cfg.defaultProvider } : {}),\n });\n const models = await engine.listModels();\n const byProvider = new Map<string, typeof models>();\n for (const m of models) {\n if (opts.provider && m.provider !== opts.provider) continue;\n if (!m.id.includes(\":\")) continue; // print the qualified form once\n const list = byProvider.get(m.provider ?? \"?\") ?? [];\n list.push(m);\n byProvider.set(m.provider ?? \"?\", list);\n }\n for (const [provider, list] of byProvider) {\n console.log(`${provider}${provider === engine.defaultProviderId ? \" (default — bare ids work too)\" : \"\"}`);\n for (const m of list) {\n console.log(` ${m.id.padEnd(40)} ${m.display_name}${m.resolved_model ? ` → ${m.resolved_model}` : \"\"}`);\n }\n }\n if (byProvider.size === 0) console.log(\"no models reported — run `yagami doctor`\");\n } catch (err) {\n console.error(`yagami: ${err instanceof Error ? err.message : String(err)}`);\n process.exitCode = 1;\n }\n });\n\nawait program.parseAsync(process.argv);\n"],"mappings":"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","names":["resolve"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { spawn, spawnSync } from \"node:child_process\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { Command } from \"commander\";\nimport { YagamiEngine } from \"./core/engine.js\";\nimport { ClaudeProvider } from \"./core/providers/claude.js\";\nimport { createProvider, detectProviders } from \"./core/providers/registry.js\";\nimport { startYagami } from \"./server.js\";\nimport {\n clearServerState,\n configFilePath,\n generateApiKey,\n isProcessAlive,\n loadConfig,\n loadFileConfig,\n logFilePath,\n maskKey,\n readServerState,\n saveConfig,\n sessionCachePath,\n writeServerState,\n} from \"./server/config.js\";\nimport { VERSION } from \"./version.js\";\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst program = new Command();\n\nprogram\n .name(\"yagami\")\n .description(\"Anthropic- and OpenAI-compatible API served by your signed-in coding-agent CLIs\")\n .version(VERSION);\n\ninterface StartFlags {\n port?: string;\n host?: string;\n claude?: string;\n provider?: string;\n cors?: boolean;\n daemon?: boolean;\n log?: string;\n}\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"start the yagami server\")\n .option(\"-p, --port <port>\", \"port to listen on\")\n .option(\"-H, --host <host>\", \"host to bind (default 127.0.0.1)\")\n .option(\"--claude <path>\", \"path to the claude executable\")\n .option(\"--provider <id>\", \"default provider for bare model ids (claude, codex, opencode, gemini, …)\")\n .option(\"--cors\", \"enable permissive CORS (for browser clients)\")\n .option(\"--daemon\", \"run in the background (managed with `yagami stop`/`yagami status`)\")\n .option(\"--log <file>\", \"log file for --daemon mode (default ~/.config/yagami/yagami.log)\")\n .action(async (opts: StartFlags) => {\n // First run: generate a key automatically so the endpoint is never open.\n const fileConfig = loadFileConfig();\n let freshKey: string | undefined;\n if (fileConfig.apiKeys.length === 0 && !process.env[\"YAGAMI_API_KEY\"]) {\n freshKey = generateApiKey();\n fileConfig.apiKeys.push(freshKey);\n saveConfig(fileConfig);\n }\n\n if (opts.daemon) {\n await startDaemon(opts, freshKey);\n return;\n }\n\n try {\n const running = await startYagami({\n port: opts.port !== undefined ? Number(opts.port) : undefined,\n host: opts.host,\n claudePath: opts.claude,\n defaultProvider: opts.provider,\n cors: opts.cors,\n });\n\n writeServerState({\n pid: process.pid,\n host: running.config.host,\n port: running.config.port,\n url: running.url,\n startedAt: new Date().toISOString(),\n version: VERSION,\n ...(process.env[\"YAGAMI_LOG_FILE\"] ? { log: process.env[\"YAGAMI_LOG_FILE\"] } : {}),\n });\n const shutdown = () => {\n running.sessionCache.persistNow();\n clearServerState(process.pid);\n void running.close().finally(() => process.exit(0));\n // Don't hang on a stuck in-flight response.\n setTimeout(() => process.exit(0), 3000).unref?.();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n const engine = running.engine;\n const version = await engine.defaultProvider.version();\n const others = engine.providerIds.filter((id) => id !== engine.defaultProviderId);\n console.log(`yagami v${VERSION}`);\n console.log(` listening ${running.url}`);\n console.log(` provider ${engine.defaultProviderId} — ${engine.executable}${version ? ` (${version})` : \"\"}`);\n console.log(\n ` also ${others.length > 0 ? `${others.join(\", \")} (use model \"<provider>:<model>\")` : \"no other harness CLIs found — see `yagami doctor`\"}`,\n );\n console.log(` config ${configFilePath()}`);\n if (freshKey) {\n console.log(` api key ${freshKey}`);\n console.log(\" (newly generated and saved — `yagami key` prints it again)\");\n } else {\n console.log(` api keys ${running.config.apiKeys.map(maskKey).join(\", \")} (\\`yagami key\\` prints them in full)`);\n }\n if (![\"127.0.0.1\", \"localhost\", \"::1\"].includes(running.config.host)) {\n console.log(\n ` ⚠ bound to ${running.config.host} — reachable beyond this machine. Only do this on a network you trust.`,\n );\n }\n console.log(\"\\nConnect apps — either dialect, same key (`yagami key` prints ready-to-paste env exports):\");\n console.log(` Anthropic apps baseURL ${running.url} (ANTHROPIC_BASE_URL / ANTHROPIC_API_KEY)`);\n console.log(` OpenAI apps baseURL ${running.url}/v1 (OPENAI_BASE_URL / OPENAI_API_KEY)`);\n } catch (err) {\n console.error(`yagami: ${err instanceof Error ? err.message : String(err)}`);\n process.exitCode = 1;\n }\n });\n\nasync function startDaemon(opts: StartFlags, freshKey: string | undefined): Promise<void> {\n const existing = readServerState();\n if (existing && isProcessAlive(existing.pid)) {\n console.error(`yagami is already running (pid ${existing.pid}, ${existing.url}) — \\`yagami stop\\` first`);\n process.exitCode = 1;\n return;\n }\n clearServerState();\n\n const logPath = opts.log ? path.resolve(opts.log) : logFilePath();\n fs.mkdirSync(path.dirname(logPath), { recursive: true });\n const fd = fs.openSync(logPath, \"a\");\n const args = [process.argv[1]!, \"start\"];\n if (opts.port !== undefined) args.push(\"-p\", opts.port);\n if (opts.host !== undefined) args.push(\"-H\", opts.host);\n if (opts.claude !== undefined) args.push(\"--claude\", opts.claude);\n if (opts.provider !== undefined) args.push(\"--provider\", opts.provider);\n if (opts.cors) args.push(\"--cors\");\n\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: [\"ignore\", fd, fd],\n env: { ...process.env, YAGAMI_LOG_FILE: logPath },\n });\n fs.closeSync(fd);\n let exitCode: number | null | undefined;\n child.on(\"exit\", (code) => {\n exitCode = code;\n });\n child.unref();\n\n const deadline = Date.now() + 15_000;\n while (Date.now() < deadline && exitCode === undefined) {\n const state = readServerState();\n if (state && state.pid === child.pid) {\n console.log(`yagami v${VERSION} running in the background`);\n console.log(` pid ${child.pid}`);\n console.log(` url ${state.url}`);\n console.log(` log ${logPath}`);\n if (freshKey) {\n console.log(` key ${freshKey}`);\n console.log(\" (newly generated and saved — `yagami key` prints it again)\");\n }\n console.log(\" `yagami key` prints the URL, key, and env exports for client apps\");\n return;\n }\n await sleep(200);\n }\n console.error(\n exitCode !== undefined\n ? `yagami exited immediately (code ${exitCode}) — see ${logPath}`\n : `yagami did not report ready within 15s — see ${logPath}`,\n );\n process.exitCode = 1;\n}\n\nprogram\n .command(\"stop\")\n .description(\"stop a running yagami server\")\n .action(async () => {\n const state = readServerState();\n if (!state || !isProcessAlive(state.pid)) {\n if (state) clearServerState();\n console.log(\"yagami is not running\");\n return;\n }\n process.kill(state.pid, \"SIGTERM\");\n const deadline = Date.now() + 5_000;\n while (Date.now() < deadline) {\n if (!isProcessAlive(state.pid)) {\n clearServerState();\n console.log(`stopped yagami (pid ${state.pid})`);\n return;\n }\n await sleep(100);\n }\n console.error(`yagami (pid ${state.pid}) did not exit within 5s`);\n process.exitCode = 1;\n });\n\nprogram\n .command(\"status\")\n .description(\"show whether yagami is running, plus request/cost totals\")\n .action(async () => {\n const state = readServerState();\n if (!state || !isProcessAlive(state.pid)) {\n if (state) clearServerState();\n console.log(\"yagami is not running\");\n process.exitCode = 1;\n return;\n }\n console.log(`yagami running (pid ${state.pid})`);\n console.log(` url ${state.url}`);\n console.log(` since ${state.startedAt}`);\n if (state.log) console.log(` log ${state.log}`);\n try {\n const res = await fetch(`${state.url}/healthz`, { signal: AbortSignal.timeout(3000) });\n const body = (await res.json()) as {\n version?: string;\n claude?: string;\n requests?: number;\n total_cost_usd?: number;\n };\n console.log(` version ${body.version ?? \"?\"}`);\n console.log(` claude ${body.claude ?? \"?\"}`);\n console.log(` requests ${body.requests ?? 0}`);\n console.log(` cost $${(body.total_cost_usd ?? 0).toFixed(4)} (would-be API cost since start)`);\n } catch {\n console.log(` healthz unreachable — process is alive but ${state.url} is not answering`);\n }\n });\n\nprogram\n .command(\"keygen\")\n .description(\"generate an API key and add it to the config\")\n .action(() => {\n const cfg = loadFileConfig();\n const key = generateApiKey();\n cfg.apiKeys.push(key);\n const file = saveConfig(cfg);\n console.log(key);\n console.error(`saved to ${file} (${cfg.apiKeys.length} key${cfg.apiKeys.length === 1 ? \"\" : \"s\"} total)`);\n });\n\nprogram\n .command(\"key\")\n .description(\"print the server URL, API key, and ready-to-paste env exports for client apps\")\n .action(() => {\n const cfg = loadConfig();\n const state = readServerState();\n const live = state !== undefined && isProcessAlive(state.pid);\n const url = live ? state.url : `http://${cfg.host}:${cfg.port}`;\n if (cfg.apiKeys.length === 0) {\n console.error(\"no API keys configured — run `yagami start` (generates one) or `yagami keygen`\");\n process.exitCode = 1;\n return;\n }\n const key = cfg.apiKeys[cfg.apiKeys.length - 1]!;\n console.log(`url ${url}${live ? \"\" : \" (server not running — this is where `yagami start` will listen)\"}`);\n console.log(`key ${key}`);\n if (cfg.apiKeys.length > 1) {\n console.log(` (newest of ${cfg.apiKeys.length} keys in ${configFilePath()})`);\n }\n console.log(\"\\nAnthropic-dialect apps:\");\n console.log(` export ANTHROPIC_BASE_URL=${url}`);\n console.log(` export ANTHROPIC_API_KEY=${key}`);\n console.log(\"\\nOpenAI-dialect apps:\");\n console.log(` export OPENAI_BASE_URL=${url}/v1`);\n console.log(` export OPENAI_API_KEY=${key}`);\n });\n\nprogram\n .command(\"doctor\")\n .description(\"check which coding-agent CLIs yagami can drive and whether they work\")\n .option(\"--live\", \"send one real (tiny) completion through the default provider\")\n .option(\"--provider <id>\", \"provider to use for --live (default: config/claude)\")\n .action(async (opts: { live?: boolean; provider?: string }) => {\n let failed = false;\n const cfg = loadConfig();\n const providerConfig = { ...cfg.providers };\n if (cfg.claudePath || cfg.claudeConfigDir) {\n providerConfig[\"claude\"] = {\n ...providerConfig[\"claude\"],\n ...(cfg.claudePath ? { path: cfg.claudePath } : {}),\n ...(cfg.claudeConfigDir ? { configDir: cfg.claudeConfigDir } : {}),\n };\n }\n const defaultProvider = opts.provider ?? cfg.defaultProvider ?? \"claude\";\n\n console.log(`node ${process.version}`);\n console.log(`config ${configFilePath()}${fs.existsSync(configFilePath()) ? \"\" : \" (not created yet)\"}`);\n console.log(`api keys ${cfg.apiKeys.length === 0 ? \"none — run `yagami keygen`\" : cfg.apiKeys.map(maskKey).join(\", \")}`);\n console.log(`bind ${cfg.host}:${cfg.port}`);\n console.log(`sessions ${sessionCachePath()}${fs.existsSync(sessionCachePath()) ? \"\" : \" (empty)\"}`);\n const state = readServerState();\n console.log(\n `server ${state && isProcessAlive(state.pid) ? `running (pid ${state.pid}, ${state.url})` : \"not running\"}`,\n );\n\n console.log(\"\\nproviders (model ids route as \\\"<provider>:<model>\\\"; bare ids go to the default)\");\n const detected = detectProviders(providerConfig);\n const installed = detected.filter((d) => d.installed);\n for (const d of detected) {\n const marker = d.id === defaultProvider ? \"*\" : \" \";\n if (!d.installed) {\n if (presetIsNiche(d.id)) continue; // keep the list readable\n console.log(` ${marker} ${d.id.padEnd(11)} not installed — ${d.installHint}`);\n continue;\n }\n let version: string | undefined;\n try {\n version = await createProvider(d.id, providerConfig[d.id] ?? {}, {}).version();\n } catch (err) {\n version = `✗ ${err instanceof Error ? err.message : String(err)}`;\n }\n console.log(` ${marker} ${d.id.padEnd(11)} ${d.path}${version ? ` (${version})` : \"\"}`);\n }\n const hidden = detected.filter((d) => !d.installed && presetIsNiche(d.id)).length;\n if (hidden > 0) console.log(` … ${hidden} more ACP presets not installed (see README for the full list)`);\n if (!installed.some((d) => d.id === defaultProvider)) {\n failed = true;\n console.log(` ✗ default provider \"${defaultProvider}\" is not installed`);\n }\n\n if (installed.some((d) => d.id === \"claude\")) {\n try {\n const claude = createProvider(\"claude\", providerConfig[\"claude\"] ?? {}, {}) as ClaudeProvider;\n const skew = await claude.versionSkew();\n if (skew) {\n console.log(`\\nagent sdk ${skew.sdkVersion} ↔ claude ${skew.binaryVersion} — ${skew.inSync ? \"in sync\" : `⚠ ${skew.note}`}`);\n }\n } catch {\n // skew check is advisory\n }\n }\n\n if (opts.live && !failed) {\n console.log(`\\nlive check: sending one tiny completion through ${defaultProvider}…`);\n try {\n const engine = new YagamiEngine({ providerConfig, defaultProvider, ...(cfg.defaultModel ? { defaultModel: cfg.defaultModel } : {}) });\n const started = Date.now();\n const result = await engine.complete({\n messages: [{ role: \"user\", content: \"Reply with exactly: pong\" }],\n max_tokens: 32,\n });\n const text = result.response.content\n .filter((b) => b.type === \"text\")\n .map((b) => b[\"text\"])\n .join(\"\");\n console.log(` reply ${JSON.stringify(text)}`);\n console.log(` model ${result.response.model}`);\n console.log(` latency ${((Date.now() - started) / 1000).toFixed(1)}s`);\n if (result.costUsd !== undefined) console.log(` cost $${result.costUsd.toFixed(6)}`);\n } catch (err) {\n failed = true;\n console.log(` ✗ ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (failed) process.exitCode = 1;\n });\n\n/** Presets most people won't have; hidden from doctor unless installed. */\nfunction presetIsNiche(id: string): boolean {\n return ![\"claude\", \"codex\", \"opencode\", \"gemini\", \"copilot\", \"cursor\", \"qwen\", \"goose\", \"kimi\"].includes(id);\n}\n\nprogram\n .command(\"models\")\n .description(\"list models across every installed provider (ids are ready to paste into requests)\")\n .option(\"--provider <id>\", \"only this provider\")\n .action(async (opts: { provider?: string }) => {\n const cfg = loadConfig();\n try {\n const engine = new YagamiEngine({\n ...(cfg.providers ? { providerConfig: cfg.providers } : {}),\n ...(cfg.defaultProvider ? { defaultProvider: cfg.defaultProvider } : {}),\n });\n const models = await engine.listModels();\n const byProvider = new Map<string, typeof models>();\n for (const m of models) {\n if (opts.provider && m.provider !== opts.provider) continue;\n if (!m.id.includes(\":\")) continue; // print the qualified form once\n const list = byProvider.get(m.provider ?? \"?\") ?? [];\n list.push(m);\n byProvider.set(m.provider ?? \"?\", list);\n }\n for (const [provider, list] of byProvider) {\n console.log(`${provider}${provider === engine.defaultProviderId ? \" (default — bare ids work too)\" : \"\"}`);\n for (const m of list) {\n console.log(` ${m.id.padEnd(40)} ${m.display_name}${m.resolved_model ? ` → ${m.resolved_model}` : \"\"}`);\n }\n }\n if (byProvider.size === 0) console.log(\"no models reported — run `yagami doctor`\");\n } catch (err) {\n console.error(`yagami: ${err instanceof Error ? err.message : String(err)}`);\n process.exitCode = 1;\n }\n });\n\nawait program.parseAsync(process.argv);\n"],"mappings":"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","names":["resolve"]}
@@ -378,4 +378,23 @@ declare class YagamiEngine {
378
378
  private attemptStream;
379
379
  }
380
380
 
381
- export { ApiError as A, presetFor as B, CodexProvider as C, type DetectedProvider as D, type EngineModel as E, qualifiedModel as F, type LoadedProviders as L, type MessageParam as M, type Provider as P, SessionCache as S, type TurnRequest as T, type Usage as U, YagamiEngine as Y, type ProviderCapabilities as a, type TurnEvent as b, type ApiErrorType as c, type CodexProviderOptions as d, type CodexSandboxMode as e, type CompleteResult as f, type ContentBlock as g, type ContentBlockParam as h, type EngineOptions as i, type MessagesRequest as j, type MessagesResponse as k, type ModelRef as l, PROVIDER_PRESETS as m, type ProviderConfigEntry as n, type ProviderKind as o, type ProviderPreset as p, type SessionCacheOptions as q, type SseEvent as r, type StreamOptions as s, type StreamResultInfo as t, type StreamStart as u, type SystemParam as v, createProvider as w, detectProviders as x, loadProviders as y, parseModelRef as z };
381
+ /**
382
+ * The engine-relevant slice of the host's yagami config
383
+ * (~/.config/yagami/config.json + YAGAMI_* env) — what library mode reads so
384
+ * an embedded `Yagami` client automatically matches the `yagami` binary on
385
+ * the same machine: same providers, same paths, same defaults. Server-only
386
+ * fields (host, port, apiKeys) are ignored here on purpose.
387
+ */
388
+
389
+ declare function yagamiConfigDir(): string;
390
+ /** Engine options derivable from the host machine's yagami config. */
391
+ interface HostEngineConfig {
392
+ defaultProvider?: string;
393
+ defaultModel?: string;
394
+ providerConfig?: Record<string, ProviderConfigEntry>;
395
+ claudePath?: string;
396
+ claudeConfigDir?: string;
397
+ }
398
+ declare function loadHostEngineConfig(): HostEngineConfig;
399
+
400
+ export { ApiError as A, parseModelRef as B, CodexProvider as C, type DetectedProvider as D, type EngineModel as E, presetFor as F, qualifiedModel as G, type HostEngineConfig as H, yagamiConfigDir as I, type LoadedProviders as L, type MessagesRequest as M, type Provider as P, type SseEvent as S, type TurnRequest as T, type Usage as U, YagamiEngine as Y, type MessagesResponse as a, type EngineOptions as b, type ProviderCapabilities as c, type TurnEvent as d, type ApiErrorType as e, type CodexProviderOptions as f, type CodexSandboxMode as g, type CompleteResult as h, type ContentBlock as i, type ContentBlockParam as j, type MessageParam as k, type ModelRef as l, PROVIDER_PRESETS as m, type ProviderConfigEntry as n, type ProviderKind as o, type ProviderPreset as p, SessionCache as q, type SessionCacheOptions as r, type StreamOptions as s, type StreamResultInfo as t, type StreamStart as u, type SystemParam as v, createProvider as w, detectProviders as x, loadHostEngineConfig as y, loadProviders as z };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,204 @@
1
- import { A as ApiError, P as Provider, a as ProviderCapabilities, T as TurnRequest, b as TurnEvent, E as EngineModel } from './engine-pmCK3S7z.js';
2
- export { c as ApiErrorType, C as CodexProvider, d as CodexProviderOptions, e as CodexSandboxMode, f as CompleteResult, g as ContentBlock, h as ContentBlockParam, D as DetectedProvider, i as EngineOptions, L as LoadedProviders, M as MessageParam, j as MessagesRequest, k as MessagesResponse, l as ModelRef, m as PROVIDER_PRESETS, n as ProviderConfigEntry, o as ProviderKind, p as ProviderPreset, S as SessionCache, q as SessionCacheOptions, r as SseEvent, s as StreamOptions, t as StreamResultInfo, u as StreamStart, v as SystemParam, U as Usage, Y as YagamiEngine, w as createProvider, x as detectProviders, y as loadProviders, z as parseModelRef, B as presetFor, F as qualifiedModel } from './engine-pmCK3S7z.js';
1
+ import { S as SseEvent, M as MessagesRequest, E as EngineModel, A as ApiError, a as MessagesResponse, Y as YagamiEngine, b as EngineOptions, P as Provider, c as ProviderCapabilities, T as TurnRequest, d as TurnEvent } from './hostConfig-Bzr9JB8R.js';
2
+ export { e as ApiErrorType, C as CodexProvider, f as CodexProviderOptions, g as CodexSandboxMode, h as CompleteResult, i as ContentBlock, j as ContentBlockParam, D as DetectedProvider, H as HostEngineConfig, L as LoadedProviders, k as MessageParam, l as ModelRef, m as PROVIDER_PRESETS, n as ProviderConfigEntry, o as ProviderKind, p as ProviderPreset, q as SessionCache, r as SessionCacheOptions, s as StreamOptions, t as StreamResultInfo, u as StreamStart, v as SystemParam, U as Usage, w as createProvider, x as detectProviders, y as loadHostEngineConfig, z as loadProviders, B as parseModelRef, F as presetFor, G as qualifiedModel, I as yagamiConfigDir } from './hostConfig-Bzr9JB8R.js';
3
3
  import { Options, SDKUserMessage, Query, SettingSource, PermissionUpdate, CanUseTool, PermissionResult, SDKMessage } from '@anthropic-ai/claude-agent-sdk';
4
4
  export { Options as AgentOptions, CanUseTool, PermissionMode, Query, SDKMessage, SDKUserMessage } from '@anthropic-ai/claude-agent-sdk';
5
5
  import { ClientSideConnection, InitializeResponse, SessionNotification, RequestPermissionRequest, RequestPermissionResponse } from '@agentclientprotocol/sdk';
6
6
 
7
+ /**
8
+ * OpenAI Chat Completions dialect: translation to and from the Anthropic
9
+ * Messages shapes the engine speaks. Pure functions — used by both the HTTP
10
+ * server (`POST /v1/chat/completions`) and the library client
11
+ * (`yagami.chat.completions.create`).
12
+ */
13
+
14
+ interface ChatMessageParam {
15
+ role: string;
16
+ content?: string | null | Array<{
17
+ type: string;
18
+ [key: string]: unknown;
19
+ }>;
20
+ [key: string]: unknown;
21
+ }
22
+ interface ChatCompletionsRequest {
23
+ model?: string;
24
+ messages: ChatMessageParam[];
25
+ stream?: boolean;
26
+ stream_options?: {
27
+ include_usage?: boolean;
28
+ };
29
+ max_tokens?: number;
30
+ max_completion_tokens?: number;
31
+ temperature?: number;
32
+ top_p?: number;
33
+ stop?: string | string[];
34
+ n?: number;
35
+ reasoning_effort?: string;
36
+ tools?: unknown;
37
+ tool_choice?: unknown;
38
+ functions?: unknown;
39
+ function_call?: unknown;
40
+ [key: string]: unknown;
41
+ }
42
+ interface ChatCompletionChoice {
43
+ index: number;
44
+ message: {
45
+ role: "assistant";
46
+ content: string;
47
+ /** Thinking output (DeepSeek-style extension; no standard OpenAI field exists). */
48
+ reasoning_content?: string;
49
+ refusal: null;
50
+ };
51
+ finish_reason: string;
52
+ logprobs: null;
53
+ }
54
+ interface ChatCompletion {
55
+ id: string;
56
+ object: "chat.completion";
57
+ created: number;
58
+ model: string;
59
+ choices: ChatCompletionChoice[];
60
+ usage: OpenAiUsage;
61
+ }
62
+ interface OpenAiUsage {
63
+ prompt_tokens: number;
64
+ completion_tokens: number;
65
+ total_tokens: number;
66
+ }
67
+ interface ChatCompletionChunk {
68
+ id: string;
69
+ object: "chat.completion.chunk";
70
+ created: number;
71
+ model: string;
72
+ choices: Array<{
73
+ index: number;
74
+ delta: {
75
+ role?: "assistant";
76
+ content?: string;
77
+ reasoning_content?: string;
78
+ };
79
+ finish_reason: string | null;
80
+ }>;
81
+ usage?: OpenAiUsage;
82
+ }
83
+ interface TranslatedChatRequest {
84
+ req: MessagesRequest;
85
+ /** OpenAI params yagami accepted but cannot honor (adds to x-yagami-ignored). */
86
+ extraIgnored: string[];
87
+ /** `stream_options.include_usage` — emit the trailing usage chunk. */
88
+ includeUsage: boolean;
89
+ }
90
+ /**
91
+ * Translate an OpenAI Chat Completions request into the Anthropic Messages
92
+ * request the engine runs. Tool calling is rejected (yagami is
93
+ * completions-only by design); knobs no CLI engine exposes are collected
94
+ * into `extraIgnored` instead of failing the request.
95
+ */
96
+ declare function chatToMessagesRequest(body: ChatCompletionsRequest): TranslatedChatRequest;
97
+ /** Anthropic Messages response → OpenAI chat.completion. */
98
+ declare function toChatCompletion(resp: MessagesResponse): ChatCompletion;
99
+ /** OpenAI-shaped error body (the OpenAI dialect's counterpart of ApiError.toBody). */
100
+ declare function openAiErrorBody(err: ApiError): {
101
+ error: {
102
+ message: string;
103
+ type: string;
104
+ param: null;
105
+ code: null;
106
+ };
107
+ };
108
+ /**
109
+ * Re-emits an Anthropic SSE event sequence as OpenAI chat.completion.chunk
110
+ * objects. Feed every engine event through `push`; the caller appends the
111
+ * `[DONE]` sentinel (HTTP mode) unless `errored`.
112
+ */
113
+ declare class ChatChunkTranslator {
114
+ private readonly includeUsage;
115
+ private id;
116
+ private model;
117
+ private created;
118
+ private stopReason;
119
+ private usage;
120
+ /** Set when the engine reported an error mid-stream (no [DONE] after). */
121
+ errored: boolean;
122
+ constructor(includeUsage: boolean);
123
+ private chunk;
124
+ /** Translate one engine SSE event into zero or more OpenAI chunk payloads. */
125
+ push(ev: SseEvent): unknown[];
126
+ }
127
+ /**
128
+ * Model list body served by GET /v1/models: Anthropic fields and OpenAI
129
+ * fields on the same objects, so both SDKs' `models.list()` parse it.
130
+ */
131
+ declare function modelListBody(models: EngineModel[]): {
132
+ object: "list";
133
+ data: Array<Record<string, unknown>>;
134
+ has_more: false;
135
+ first_id?: string;
136
+ last_id?: string;
137
+ };
138
+
139
+ /**
140
+ * `Yagami` — the zero-config library client. `new Yagami()` needs no URL and
141
+ * no API key: it finds the coding-agent CLIs already installed and signed in
142
+ * on this machine (the same T3-Code trick the server does) and mirrors the
143
+ * Anthropic and OpenAI SDK surfaces on top of them, so an app written
144
+ * against either SDK shape drops in with nothing to configure.
145
+ *
146
+ * By default it also reads the host's yagami config
147
+ * (~/.config/yagami/config.json), so an embedded client and the `yagami`
148
+ * binary on the same machine agree on providers, paths, and defaults.
149
+ */
150
+
151
+ interface YagamiOptions extends EngineOptions {
152
+ /**
153
+ * Merge the host machine's yagami config (providers, defaults) under any
154
+ * explicit options, so library and binary stay in sync. Default true;
155
+ * ignored when explicit `providers` instances are passed.
156
+ */
157
+ syncHostConfig?: boolean;
158
+ }
159
+ /** An Anthropic stream event's payload (`message_start`, `content_block_delta`, …). */
160
+ interface MessageStreamEvent {
161
+ type: string;
162
+ [key: string]: unknown;
163
+ }
164
+ interface YagamiMessages {
165
+ /** Anthropic-SDK-shaped: non-stream resolves to the message; `stream: true` yields stream events. */
166
+ create(req: MessagesRequest & {
167
+ stream: true;
168
+ }): AsyncGenerator<MessageStreamEvent, void, undefined>;
169
+ create(req: MessagesRequest & {
170
+ stream?: false | undefined;
171
+ }): Promise<MessagesResponse>;
172
+ /** Always-streaming variant (the SDK's `messages.stream`). */
173
+ stream(req: MessagesRequest): AsyncGenerator<MessageStreamEvent, void, undefined>;
174
+ }
175
+ interface YagamiChatCompletions {
176
+ /** OpenAI-SDK-shaped: non-stream resolves to a chat.completion; `stream: true` yields chunks. */
177
+ create(req: ChatCompletionsRequest & {
178
+ stream: true;
179
+ }): AsyncGenerator<ChatCompletionChunk, void, undefined>;
180
+ create(req: ChatCompletionsRequest & {
181
+ stream?: false | undefined;
182
+ }): Promise<ChatCompletion>;
183
+ }
184
+ declare class Yagami {
185
+ /** The underlying engine, for anything beyond the SDK-shaped surface. */
186
+ readonly engine: YagamiEngine;
187
+ constructor(options?: YagamiOptions);
188
+ readonly messages: YagamiMessages;
189
+ readonly chat: {
190
+ completions: YagamiChatCompletions;
191
+ };
192
+ readonly models: {
193
+ /** Both SDK shapes at once (Anthropic + OpenAI model-list fields). */
194
+ list: () => Promise<ReturnType<typeof modelListBody>>;
195
+ /** The engine's raw model list. */
196
+ raw: () => Promise<EngineModel[]>;
197
+ };
198
+ private streamMessageEvents;
199
+ private streamChatChunks;
200
+ }
201
+
7
202
  interface ClaudeSessionOptions {
8
203
  /** Path to the `claude` binary. Auto-resolved when omitted. */
9
204
  claudePath?: string;
@@ -323,6 +518,6 @@ declare function resolveExecutable(providerId: string, name: string, installHint
323
518
  */
324
519
  declare function resolveClaudeExecutable(explicit?: string): string;
325
520
 
326
- declare const VERSION = "0.4.1";
521
+ declare const VERSION = "0.5.0";
327
522
 
328
- export { type AcpConnection, AcpProvider, type AcpProviderOptions, AgentSession, type AgentSessionOptions, ApiError, AuthRequiredError, ClaudeProvider, type ClaudeProviderOptions, type ClaudeSessionOptions, EngineModel, type Parity, PermissionAdapter, type PermissionAdapterOptions, type PermissionDecision, type PermissionHandler, type PermissionRequest, Provider, ProviderCapabilities, ProviderError, ProviderNotInstalledError, TurnEvent, TurnRequest, VERSION, type VersionSkew, YagamiError, claudeCodeSession, findExecutable, resolveClaudeExecutable, resolveExecutable, settingSourcesFor, startAgentSession, toApiError };
523
+ export { type AcpConnection, AcpProvider, type AcpProviderOptions, AgentSession, type AgentSessionOptions, ApiError, AuthRequiredError, ChatChunkTranslator, type ChatCompletion, type ChatCompletionChunk, type ChatCompletionsRequest, type ChatMessageParam, ClaudeProvider, type ClaudeProviderOptions, type ClaudeSessionOptions, EngineModel, EngineOptions, type MessageStreamEvent, MessagesRequest, MessagesResponse, type OpenAiUsage, type Parity, PermissionAdapter, type PermissionAdapterOptions, type PermissionDecision, type PermissionHandler, type PermissionRequest, Provider, ProviderCapabilities, ProviderError, ProviderNotInstalledError, SseEvent, type TranslatedChatRequest, TurnEvent, TurnRequest, VERSION, type VersionSkew, Yagami, type YagamiChatCompletions, YagamiEngine, YagamiError, type YagamiMessages, type YagamiOptions, chatToMessagesRequest, claudeCodeSession, findExecutable, modelListBody, openAiErrorBody, resolveClaudeExecutable, resolveExecutable, settingSourcesFor, startAgentSession, toApiError, toChatCompletion };