@dmrlzz/davecode 0.5.1 → 0.5.2

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 (66) hide show
  1. package/dist/{ChatApp-YMHKIAAH.js → ChatApp-Z6PQIZ5J.js} +7 -6
  2. package/dist/{ChatApp-YMHKIAAH.js.map → ChatApp-Z6PQIZ5J.js.map} +1 -1
  3. package/dist/{account-5QFYCVYM.js → account-RTPS2V6O.js} +4 -3
  4. package/dist/{accounts-756BMMAL.js → accounts-IYQ4ZRL7.js} +6 -5
  5. package/dist/{budget-PGSNCVVK.js → budget-N2ZB66FF.js} +5 -4
  6. package/dist/{budget-PGSNCVVK.js.map → budget-N2ZB66FF.js.map} +1 -1
  7. package/dist/{chat-D3T4YG5D.js → chat-6TX6JX45.js} +9 -8
  8. package/dist/{chat-D3T4YG5D.js.map → chat-6TX6JX45.js.map} +1 -1
  9. package/dist/chunk-732GIHRQ.js +23 -0
  10. package/dist/chunk-732GIHRQ.js.map +1 -0
  11. package/dist/{chunk-6SHOBL2X.js → chunk-BJTXMET2.js} +2 -2
  12. package/dist/{chunk-JFWMLYV7.js → chunk-KUY3QHGS.js} +130 -89
  13. package/dist/chunk-KUY3QHGS.js.map +1 -0
  14. package/dist/{chunk-K5AA65RS.js → chunk-LEU46DGH.js} +2 -2
  15. package/dist/{chunk-HENE4DA6.js → chunk-QKY3OKVG.js} +71 -3
  16. package/dist/chunk-QKY3OKVG.js.map +1 -0
  17. package/dist/{chunk-6WUVR3R2.js → chunk-UEV5K3DH.js} +3 -3
  18. package/dist/{chunk-JSRPO2AR.js → chunk-UNK3EQD6.js} +4 -4
  19. package/dist/{chunk-IOATP7KH.js → chunk-WAARDGCZ.js} +5 -21
  20. package/dist/chunk-WAARDGCZ.js.map +1 -0
  21. package/dist/{chunk-ZNUNYYN6.js → chunk-YNAMNQLW.js} +3 -3
  22. package/dist/{config-TKGGYEN7.js → config-YQKY2AA4.js} +4 -3
  23. package/dist/{config-TKGGYEN7.js.map → config-YQKY2AA4.js.map} +1 -1
  24. package/dist/dashboard/assets/{Accounts-DciFUg4y.js → Accounts-DXiGsutB.js} +1 -1
  25. package/dist/dashboard/assets/{Callout-WgXHDPg0.js → Callout-1E8Wsl9W.js} +1 -1
  26. package/dist/dashboard/assets/{Goals-C6HL1PNE.js → Goals-kL4VAxw3.js} +1 -1
  27. package/dist/dashboard/assets/{Library-DwpBYTbK.js → Library-DZxBVUo_.js} +1 -1
  28. package/dist/dashboard/assets/{Panes-DxFSwhb7.js → Panes-C-TJekjs.js} +1 -1
  29. package/dist/dashboard/assets/{RoutesPage-Cyo0MNd7.js → RoutesPage-DWYm9tW8.js} +1 -1
  30. package/dist/dashboard/assets/{Runner-CIL7RxpN.js → Runner-CmHBjECR.js} +1 -1
  31. package/dist/dashboard/assets/{Settings-B_PEB585.js → Settings-BANphxva.js} +1 -1
  32. package/dist/dashboard/assets/{Tasks-DEfPSaE6.js → Tasks-cSx1PKyQ.js} +1 -1
  33. package/dist/dashboard/assets/{Traffic-Dcz-WhsP.js → Traffic-DEAuM6jq.js} +1 -1
  34. package/dist/dashboard/assets/{Workspace-9UCw9q7C.js → Workspace-BQFjc1jH.js} +1 -1
  35. package/dist/dashboard/assets/index-B8kld9xo.js +104 -0
  36. package/dist/dashboard/index.html +1 -1
  37. package/dist/{dist-F3NRJ5FK.js → dist-PIC255RX.js} +4 -2
  38. package/dist/{doctor-V4XXVVGK.js → doctor-PQC7JQPP.js} +4 -3
  39. package/dist/{doctor-V4XXVVGK.js.map → doctor-PQC7JQPP.js.map} +1 -1
  40. package/dist/{goal-RUHYPVPA.js → goal-STLEU7C7.js} +4 -3
  41. package/dist/{goal-RUHYPVPA.js.map → goal-STLEU7C7.js.map} +1 -1
  42. package/dist/index.js +31 -31
  43. package/dist/{init-54FKQZ2Z.js → init-NHU5LJSS.js} +4 -3
  44. package/dist/{init-54FKQZ2Z.js.map → init-NHU5LJSS.js.map} +1 -1
  45. package/dist/{run-4R25J2VN.js → run-7HWSHMRJ.js} +4 -3
  46. package/dist/{run-4R25J2VN.js.map → run-7HWSHMRJ.js.map} +1 -1
  47. package/dist/runtime.js +3 -2
  48. package/dist/{start-GRHW3TGE.js → start-WVAEAQWL.js} +4 -3
  49. package/dist/{start-GRHW3TGE.js.map → start-WVAEAQWL.js.map} +1 -1
  50. package/dist/{status-QR2HNPBK.js → status-ZRHPFLFT.js} +5 -4
  51. package/dist/{tasks-BHWDV4JS.js → tasks-JVWXUNXN.js} +5 -4
  52. package/dist/{tasks-BHWDV4JS.js.map → tasks-JVWXUNXN.js.map} +1 -1
  53. package/package.json +4 -4
  54. package/dist/chunk-HENE4DA6.js.map +0 -1
  55. package/dist/chunk-IOATP7KH.js.map +0 -1
  56. package/dist/chunk-JFWMLYV7.js.map +0 -1
  57. package/dist/dashboard/assets/index-CpztPbom.js +0 -104
  58. /package/dist/{account-5QFYCVYM.js.map → account-RTPS2V6O.js.map} +0 -0
  59. /package/dist/{accounts-756BMMAL.js.map → accounts-IYQ4ZRL7.js.map} +0 -0
  60. /package/dist/{chunk-6SHOBL2X.js.map → chunk-BJTXMET2.js.map} +0 -0
  61. /package/dist/{chunk-K5AA65RS.js.map → chunk-LEU46DGH.js.map} +0 -0
  62. /package/dist/{chunk-6WUVR3R2.js.map → chunk-UEV5K3DH.js.map} +0 -0
  63. /package/dist/{chunk-JSRPO2AR.js.map → chunk-UNK3EQD6.js.map} +0 -0
  64. /package/dist/{chunk-ZNUNYYN6.js.map → chunk-YNAMNQLW.js.map} +0 -0
  65. /package/dist/{dist-F3NRJ5FK.js.map → dist-PIC255RX.js.map} +0 -0
  66. /package/dist/{status-QR2HNPBK.js.map → status-ZRHPFLFT.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/chat.ts","../src/tui/backend.ts","../src/commands/onboard.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { resolve, sep } from 'node:path';\nimport { loadConfig, type ProviderKind, VERSION } from '@davecode/core';\nimport { findGateway, GatewayClient } from '../client';\nimport type { CliContext } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { openAccounts } from '../lib/accounts-backend';\nimport { PromptCancelledError } from '../lib/prompter';\nimport { detectProjectRoot, type Runtime, startRuntime } from '../runtime';\nimport { type ChatBackend, httpBackend } from '../tui/backend';\nimport { chatAccountActions } from './account';\nimport { agentChoices, connectAgent, ensureReady } from './onboard';\n\nexport interface ChatOptions {\n model?: string;\n project?: string;\n}\n\n/**\n * Open the chat TUI against the running gateway, or start an in-process gateway on an ephemeral\n * loopback port when none is running (it stops when the TUI exits).\n */\n/** `C:\\Users\\me\\code\\app` → `~\\code\\app`, for the banner. */\nfunction displayPath(path: string): string {\n const home = homedir();\n return path === home || path.startsWith(home + sep) ? `~${path.slice(home.length)}` : path;\n}\n\nexport async function chatCommand(ctx: CliContext, opts: ChatOptions): Promise<number> {\n if (!ctx.io.stdin.isTTY || !ctx.io.stdout.isTTY) {\n throw new CliError('The chat TUI needs an interactive terminal', {\n hint: 'For scripts, call the OpenAI-compatible API: POST http://127.0.0.1:4040/v1/chat/completions',\n });\n }\n const ready = await ensureReady(ctx);\n if (!ready) return EXIT.failure;\n ctx = ready;\n const projectRoot = opts.project\n ? resolve(ctx.cwd, opts.project)\n : await detectProjectRoot(ctx.cwd, ctx.home);\n // /connect closes the chat so the vendor login can use the terminal, then opens it again.\n for (;;) {\n const provider = await runChat(ctx, opts, projectRoot);\n if (!provider) return EXIT.ok;\n await connectFromChat(ctx, provider);\n }\n}\n\nasync function connectFromChat(ctx: CliContext, provider: ProviderKind): Promise<void> {\n const { createInkPrompter } = await import('../tui/prompts');\n const prompter = createInkPrompter({\n stdout: ctx.io.stdout,\n stdin: ctx.io.stdin,\n theme: ctx.theme,\n });\n const backend = await openAccounts({ home: ctx.home, env: ctx.env, local: true });\n try {\n ctx.out();\n await connectAgent(ctx, provider, prompter, backend);\n } catch (err) {\n if (!(err instanceof PromptCancelledError)) throw err;\n } finally {\n backend.close();\n }\n}\n\n/** One chat session; resolves with the agent picked in /connect, if any. */\nasync function runChat(\n ctx: CliContext,\n opts: ChatOptions,\n projectRoot: string | undefined,\n): Promise<ProviderKind | undefined> {\n const config = loadConfig({\n home: ctx.home,\n env: ctx.env,\n ...(projectRoot ? { projectRoot } : {}),\n });\n\n let runtime: Runtime | undefined;\n let backend: ChatBackend;\n const running = await findGateway(config);\n if (running) {\n backend = httpBackend(running.client, false);\n } else {\n runtime = await startRuntime({\n home: ctx.home,\n env: ctx.env,\n cwd: ctx.cwd,\n host: '127.0.0.1',\n port: 0,\n dashboard: false,\n ...(projectRoot ? { projectDir: projectRoot } : { noProject: true }),\n });\n const target = config.server.authToken\n ? { baseUrl: runtime.url, token: config.server.authToken }\n : { baseUrl: runtime.url };\n backend = httpBackend(new GatewayClient(target), true);\n }\n\n const accountActions = chatAccountActions(ctx);\n let connectTo: ProviderKind | undefined;\n const connect = {\n agents: async () => agentChoices(ctx),\n choose: (provider: string) => {\n connectTo = provider as ProviderKind;\n },\n };\n try {\n const [{ render }, { createElement }, { ChatApp }] = await Promise.all([\n import('ink'),\n import('react'),\n import('../tui/ChatApp'),\n ]);\n const instance = render(\n createElement(ChatApp, {\n backend,\n theme: ctx.theme,\n model: opts.model ?? `davecode/${config.routing.defaultRoute}`,\n version: VERSION,\n project: displayPath(projectRoot ?? ctx.cwd),\n ...(accountActions ? { account: accountActions } : {}),\n connect,\n }),\n {\n stdout: ctx.io.stdout as NodeJS.WriteStream,\n stdin: process.stdin,\n exitOnCtrlC: false,\n kittyKeyboard: { mode: 'auto', flags: ['disambiguateEscapeCodes'] },\n },\n );\n await instance.waitUntilExit();\n } finally {\n await runtime?.close();\n }\n return connectTo;\n}\n","import type {\n Account,\n AccountUsage,\n ChatCompletionChunk,\n ChatRequest,\n ModelInfo,\n Route,\n RunnerStatus,\n TaskGraph,\n} from '@davecode/core';\nimport type { GatewayClient, HealthResponse, StreamMeta } from '../client';\n\n/** What the TUI needs from a gateway. Backed by HTTP in production and by fakes in tests. */\nexport interface ChatBackend {\n /** Base URL, e.g. `http://127.0.0.1:4040`. */\n url: string;\n /** The gateway was started inside this process (no `davecode start` running). */\n inProcess: boolean;\n health(): Promise<HealthResponse | undefined>;\n stream(\n request: ChatRequest,\n signal: AbortSignal,\n ): Promise<{ meta: StreamMeta; chunks: AsyncIterable<ChatCompletionChunk> }>;\n models(): Promise<ModelInfo[]>;\n routes(): Promise<{ defaultRoute: string; routes: Route[] }>;\n accounts(): Promise<Account[]>;\n usage(): Promise<AccountUsage[]>;\n tasks(): Promise<{ project: { root: string; name: string } | null; graph: TaskGraph }>;\n runner(): Promise<RunnerStatus>;\n}\n\nexport function httpBackend(client: GatewayClient, inProcess: boolean): ChatBackend {\n return {\n url: client.baseUrl,\n inProcess,\n health: () => client.health(1500).catch(() => undefined),\n stream: (request, signal) => client.chatStream(request, signal),\n models: async () => (await client.get<{ data: ModelInfo[] }>('/v1/models')).data,\n routes: () => client.get('/api/routes'),\n accounts: async () => (await client.get<{ accounts: Account[] }>('/api/accounts')).accounts,\n usage: async () => (await client.get<{ usage: AccountUsage[] }>('/api/usage')).usage,\n tasks: () => client.get('/api/tasks'),\n runner: async () => (await client.get<{ status: RunnerStatus }>('/api/runner')).status,\n };\n}\n","import { execFile } from 'node:child_process';\nimport {\n CLI_LOGINS,\n loadConfig,\n type ProviderKind,\n type Route,\n VERSION,\n writeGlobalRouting,\n} from '@davecode/core';\nimport type { CliContext } from '../context';\nimport { type AccountsBackend, openAccounts } from '../lib/accounts-backend';\nimport { readPreferences, writePreferences } from '../lib/preferences';\nimport { which as whichOnPath } from '../lib/proc';\nimport { PromptCancelledError, type Prompter } from '../lib/prompter';\nimport { brandBanner } from '../ui/banner';\nimport { createTheme, type ThemeMode } from '../ui/theme';\nimport { type AccountDeps, accountsEnabled, currentAccount, describeUser, signIn } from './account';\nimport { accountsLogin } from './accounts';\n\n/** Agents DaveCode can drive today, in the order they are offered. */\nexport const AGENTS: ReadonlyArray<{\n provider: ProviderKind;\n name: string;\n models: Array<{ value: string; label: string; hint?: string }>;\n}> = [\n {\n provider: 'claude-cli',\n name: 'Claude Code',\n models: [\n { value: 'default', label: 'Default', hint: 'Claude Code decides' },\n { value: 'opus', label: 'Opus', hint: 'most capable' },\n { value: 'sonnet', label: 'Sonnet', hint: 'balanced' },\n { value: 'haiku', label: 'Haiku', hint: 'fastest' },\n ],\n },\n {\n provider: 'codex-cli',\n name: 'Codex',\n models: [{ value: 'default', label: 'Default', hint: 'Codex decides' }],\n },\n {\n provider: 'antigravity-cli',\n name: 'Antigravity',\n models: [{ value: 'default', label: 'Default', hint: 'Antigravity decides' }],\n },\n];\n\ntype ModelChoice = { value: string; label: string; hint?: string };\n\n/** Antigravity lists its models with `agy models` as `id<TAB>name` lines. */\nexport function parseAgyModels(stdout: string): ModelChoice[] {\n return stdout\n .split(/\\r?\\n/)\n .map((line) => line.split('\\t'))\n .filter(([id, name]) => id && name && /^[\\w.:-]+$/.test(id.trim()))\n .map(([id, name]) => ({ value: (id as string).trim(), label: (name as string).trim() }));\n}\n\nfunction agyModels(binary: string, env: NodeJS.ProcessEnv): Promise<ModelChoice[]> {\n return new Promise((resolve) => {\n execFile(binary, ['models'], { env, timeout: 30_000, windowsHide: true }, (error, stdout) =>\n resolve(error ? [] : parseAgyModels(String(stdout))),\n );\n });\n}\n\nconst OTHER_MODEL = '__other__';\n\nexport interface OnboardDeps extends AccountDeps {\n prompter?: Prompter;\n backend?: AccountsBackend;\n which?: (binary: string) => string | undefined;\n /** Models an agent offers beyond its built-in list (defaults to asking `agy models`). */\n models?: (provider: ProviderKind) => Promise<ModelChoice[]>;\n /** Runs the vendor's official login for an account (defaults to `accounts login`). */\n vendorLogin?: (ctx: CliContext, accountId: string) => Promise<number>;\n}\n\nasync function prompterFor(ctx: CliContext, deps: OnboardDeps): Promise<Prompter> {\n if (deps.prompter) return deps.prompter;\n const { createInkPrompter } = await import('../tui/prompts');\n return createInkPrompter({ stdout: ctx.io.stdout, stdin: ctx.io.stdin, theme: ctx.theme });\n}\n\nfunction welcome(ctx: CliContext): void {\n const { theme } = ctx;\n ctx.out();\n const text = [\n `${theme.bold('Welcome to DaveCode')} ${theme.dim(`v${VERSION}`)}`,\n theme.dim('Run Claude Code, Codex and Antigravity side by side.'),\n ];\n for (const line of brandBanner(theme, text, ctx.columns)) ctx.out(` ${line}`);\n ctx.out();\n}\n\nfunction withTheme(ctx: CliContext, mode: ThemeMode): CliContext {\n return {\n ...ctx,\n theme: createTheme({ color: ctx.theme.color, unicode: ctx.theme.unicode, mode }),\n errTheme: createTheme({ color: ctx.errTheme.color, unicode: ctx.errTheme.unicode, mode }),\n };\n}\n\nasync function chooseTheme(ctx: CliContext, prompter: Prompter): Promise<ThemeMode> {\n return prompter.select<ThemeMode>(\n 'Choose a look for the terminal',\n [\n { value: 'dark', label: 'Dark', hint: 'white text on black' },\n { value: 'light', label: 'Light', hint: 'black text on white' },\n { value: 'auto', label: 'Match my terminal' },\n ],\n readPreferences(ctx.home).theme ?? 'dark',\n );\n}\n\nasync function signInStep(ctx: CliContext, prompter: Prompter, deps: OnboardDeps): Promise<void> {\n const choice = await prompter.select('Sign in to continue', [\n { value: 'browser', label: 'Sign in with your browser', hint: 'Google, GitHub or email' },\n { value: 'quit', label: 'Quit' },\n ]);\n if (choice === 'quit') throw new PromptCancelledError();\n const session = await signIn(ctx, deps);\n ctx.out(\n `${ctx.theme.ok(ctx.theme.glyph.ok)} Signed in as ${ctx.theme.bold(describeUser(session))}`,\n );\n ctx.out();\n}\n\nasync function setDefaultRoute(ctx: CliContext, route: Route): Promise<void> {\n const global = loadConfig({ home: ctx.home, env: ctx.env });\n await writeGlobalRouting(ctx.home, {\n routes: [...global.routing.routes.filter((r) => r.name !== route.name), route],\n defaultRoute: route.name,\n });\n}\n\n/**\n * Connect one agent: reuse its account (or create one), run the official login when it is not\n * signed in, choose a model and make it the default route. Returns false when the agent is not\n * installed.\n */\nexport async function connectAgent(\n ctx: CliContext,\n provider: ProviderKind,\n prompter: Prompter,\n backend: AccountsBackend,\n deps: OnboardDeps = {},\n): Promise<boolean> {\n const { theme } = ctx;\n const which = deps.which ?? ((binary: string) => whichOnPath(binary, ctx.env));\n const agent = AGENTS.find((a) => a.provider === provider);\n const spec = CLI_LOGINS[provider];\n if (!agent || !spec) throw new Error(`${provider} is not an agent DaveCode can connect`);\n if (!which(spec.binary)) {\n ctx.out(theme.warn(` ${agent.name} is not installed.`));\n ctx.out(theme.dim(` ${spec.installHint}`));\n ctx.out();\n return false;\n }\n\n const account =\n (await backend.list()).find((a) => a.provider === provider) ??\n (await backend.create({ provider, label: agent.name }));\n const connection = await backend.connection(account.id).catch(() => undefined);\n if (connection?.status !== 'connected' || account.status === 'error') {\n const exit = await (deps.vendorLogin ?? ((c, id) => accountsLogin(c, id, [], {}, { backend })))(\n ctx,\n account.id,\n );\n if (exit !== 0) {\n ctx.out(theme.warn(` ${agent.name} did not finish signing in. You can retry later with:`));\n ctx.out(theme.dim(` davecode accounts login ${account.id}`));\n }\n } else {\n ctx.out(`${theme.ok(theme.glyph.ok)} ${agent.name} is already signed in.`);\n }\n\n const discovered =\n provider === 'antigravity-cli'\n ? await (deps.models ?? (() => agyModels(which(spec.binary) ?? spec.binary, ctx.env)))(\n provider,\n )\n : [];\n let model = await prompter.select(`Model for ${agent.name}`, [\n ...agent.models,\n ...discovered,\n { value: OTHER_MODEL, label: 'Another model…', hint: 'type its id' },\n ]);\n if (model === OTHER_MODEL) {\n model = await prompter.text('Model id', {\n validate: (v) =>\n /^[\\w.:[\\]-]+$/.test(v.trim()) ? undefined : 'Use letters, digits, . : - _',\n });\n model = model.trim();\n }\n await setDefaultRoute(ctx, {\n name: 'auto',\n description: `${agent.name} (${model})`,\n targets: [{ provider, model, accountId: account.id }],\n });\n ctx.out(`${theme.ok(theme.glyph.ok)} DaveCode will use ${theme.bold(agent.name)} · ${model}`);\n ctx.out();\n return true;\n}\n\n/** The agent picker shared by first-run setup and /connect. */\nexport function agentChoices(\n ctx: CliContext,\n deps: Pick<OnboardDeps, 'which'> = {},\n): Array<{ value: ProviderKind; label: string; hint: string }> {\n const which = deps.which ?? ((binary: string) => whichOnPath(binary, ctx.env));\n return AGENTS.map((agent) => ({\n value: agent.provider,\n label: agent.name,\n hint: which(CLI_LOGINS[agent.provider]?.binary ?? '') ? 'installed' : 'not installed',\n }));\n}\n\n/** First agent: pick one that is installed, log in with its official CLI, choose a model. */\nasync function connectAgentStep(\n ctx: CliContext,\n prompter: Prompter,\n backend: AccountsBackend,\n deps: OnboardDeps,\n): Promise<void> {\n for (;;) {\n const choice = await prompter.select<string>('Connect your first agent', [\n ...agentChoices(ctx, deps),\n { value: 'later', label: 'Later', hint: 'davecode accounts add' },\n ]);\n if (choice === 'later') return;\n if (await connectAgent(ctx, choice as ProviderKind, prompter, backend, deps)) return;\n }\n}\n\n/**\n * First-run setup, Claude Code style: logo, look, DaveCode account, first agent. Later runs\n * only ask to sign in again when the session is gone. Returns the context to continue with\n * (the chosen theme applied), or undefined when the user quits.\n */\nexport async function ensureReady(\n ctx: CliContext,\n deps: OnboardDeps = {},\n): Promise<CliContext | undefined> {\n const prefs = readPreferences(ctx.home);\n const needsAccount = accountsEnabled(ctx, deps) && !(await currentAccount(ctx, deps));\n if (prefs.onboarded && !needsAccount) return ctx;\n\n let current = ctx;\n const prompter = await prompterFor(ctx, deps);\n try {\n if (!prefs.onboarded) {\n welcome(ctx);\n const mode = await chooseTheme(ctx, prompter);\n writePreferences(ctx.home, { theme: mode });\n current = withTheme(ctx, mode);\n }\n if (needsAccount) await signInStep(current, prompter, deps);\n if (!prefs.onboarded) {\n const backend =\n deps.backend ?? (await openAccounts({ home: current.home, env: current.env, local: true }));\n try {\n const existing = await backend.list();\n if (existing.length === 0) await connectAgentStep(current, prompter, backend, deps);\n } finally {\n if (!deps.backend) backend.close();\n }\n writePreferences(current.home, { onboarded: true });\n }\n return current;\n } catch (err) {\n if (err instanceof PromptCancelledError) {\n ctx.err(ctx.errTheme.dim('Setup cancelled. Run davecode again to continue.'));\n return undefined;\n }\n throw err;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,SAAS,WAAW;;;AC8BtB,SAAS,YAAY,QAAuB,WAAiC;AAClF,SAAO;AAAA,IACL,KAAK,OAAO;AAAA,IACZ;AAAA,IACA,QAAQ,MAAM,OAAO,OAAO,IAAI,EAAE,MAAM,MAAM,MAAS;AAAA,IACvD,QAAQ,CAAC,SAAS,WAAW,OAAO,WAAW,SAAS,MAAM;AAAA,IAC9D,QAAQ,aAAa,MAAM,OAAO,IAA2B,YAAY,GAAG;AAAA,IAC5E,QAAQ,MAAM,OAAO,IAAI,aAAa;AAAA,IACtC,UAAU,aAAa,MAAM,OAAO,IAA6B,eAAe,GAAG;AAAA,IACnF,OAAO,aAAa,MAAM,OAAO,IAA+B,YAAY,GAAG;AAAA,IAC/E,OAAO,MAAM,OAAO,IAAI,YAAY;AAAA,IACpC,QAAQ,aAAa,MAAM,OAAO,IAA8B,aAAa,GAAG;AAAA,EAClF;AACF;;;AC5CA,SAAS,gBAAgB;AAoBlB,IAAM,SAIR;AAAA,EACH;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,EAAE,OAAO,WAAW,OAAO,WAAW,MAAM,sBAAsB;AAAA,MAClE,EAAE,OAAO,QAAQ,OAAO,QAAQ,MAAM,eAAe;AAAA,MACrD,EAAE,OAAO,UAAU,OAAO,UAAU,MAAM,WAAW;AAAA,MACrD,EAAE,OAAO,SAAS,OAAO,SAAS,MAAM,UAAU;AAAA,IACpD;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,OAAO,WAAW,OAAO,WAAW,MAAM,gBAAgB,CAAC;AAAA,EACxE;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,CAAC,EAAE,OAAO,WAAW,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAAA,EAC9E;AACF;AAKO,SAAS,eAAe,QAA+B;AAC5D,SAAO,OACJ,MAAM,OAAO,EACb,IAAI,CAAC,SAAS,KAAK,MAAM,GAAI,CAAC,EAC9B,OAAO,CAAC,CAAC,IAAI,IAAI,MAAM,MAAM,QAAQ,aAAa,KAAK,GAAG,KAAK,CAAC,CAAC,EACjE,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,EAAE,OAAQ,GAAc,KAAK,GAAG,OAAQ,KAAgB,KAAK,EAAE,EAAE;AAC3F;AAEA,SAAS,UAAU,QAAgB,KAAgD;AACjF,SAAO,IAAI,QAAQ,CAACA,aAAY;AAC9B;AAAA,MAAS;AAAA,MAAQ,CAAC,QAAQ;AAAA,MAAG,EAAE,KAAK,SAAS,KAAQ,aAAa,KAAK;AAAA,MAAG,CAAC,OAAO,WAChFA,SAAQ,QAAQ,CAAC,IAAI,eAAe,OAAO,MAAM,CAAC,CAAC;AAAA,IACrD;AAAA,EACF,CAAC;AACH;AAEA,IAAM,cAAc;AAYpB,eAAe,YAAY,KAAiB,MAAsC;AAChF,MAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,uBAAgB;AAC3D,SAAO,kBAAkB,EAAE,QAAQ,IAAI,GAAG,QAAQ,OAAO,IAAI,GAAG,OAAO,OAAO,IAAI,MAAM,CAAC;AAC3F;AAEA,SAAS,QAAQ,KAAuB;AACtC,QAAM,EAAE,MAAM,IAAI;AAClB,MAAI,IAAI;AACR,QAAM,OAAO;AAAA,IACX,GAAG,MAAM,KAAK,qBAAqB,CAAC,IAAI,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;AAAA,IAChE,MAAM,IAAI,sDAAsD;AAAA,EAClE;AACA,aAAW,QAAQ,YAAY,OAAO,MAAM,IAAI,OAAO,EAAG,KAAI,IAAI,IAAI,IAAI,EAAE;AAC5E,MAAI,IAAI;AACV;AAEA,SAAS,UAAU,KAAiB,MAA6B;AAC/D,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,YAAY,EAAE,OAAO,IAAI,MAAM,OAAO,SAAS,IAAI,MAAM,SAAS,KAAK,CAAC;AAAA,IAC/E,UAAU,YAAY,EAAE,OAAO,IAAI,SAAS,OAAO,SAAS,IAAI,SAAS,SAAS,KAAK,CAAC;AAAA,EAC1F;AACF;AAEA,eAAe,YAAY,KAAiB,UAAwC;AAClF,SAAO,SAAS;AAAA,IACd;AAAA,IACA;AAAA,MACE,EAAE,OAAO,QAAQ,OAAO,QAAQ,MAAM,sBAAsB;AAAA,MAC5D,EAAE,OAAO,SAAS,OAAO,SAAS,MAAM,sBAAsB;AAAA,MAC9D,EAAE,OAAO,QAAQ,OAAO,oBAAoB;AAAA,IAC9C;AAAA,IACA,gBAAgB,IAAI,IAAI,EAAE,SAAS;AAAA,EACrC;AACF;AAEA,eAAe,WAAW,KAAiB,UAAoB,MAAkC;AAC/F,QAAM,SAAS,MAAM,SAAS,OAAO,uBAAuB;AAAA,IAC1D,EAAE,OAAO,WAAW,OAAO,6BAA6B,MAAM,0BAA0B;AAAA,IACxF,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,EACjC,CAAC;AACD,MAAI,WAAW,OAAQ,OAAM,IAAI,qBAAqB;AACtD,QAAM,UAAU,MAAM,OAAO,KAAK,IAAI;AACtC,MAAI;AAAA,IACF,GAAG,IAAI,MAAM,GAAG,IAAI,MAAM,MAAM,EAAE,CAAC,iBAAiB,IAAI,MAAM,KAAK,aAAa,OAAO,CAAC,CAAC;AAAA,EAC3F;AACA,MAAI,IAAI;AACV;AAEA,eAAe,gBAAgB,KAAiB,OAA6B;AAC3E,QAAM,SAAS,WAAW,EAAE,MAAM,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC;AAC1D,QAAM,mBAAmB,IAAI,MAAM;AAAA,IACjC,QAAQ,CAAC,GAAG,OAAO,QAAQ,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI,GAAG,KAAK;AAAA,IAC7E,cAAc,MAAM;AAAA,EACtB,CAAC;AACH;AAOA,eAAsB,aACpB,KACA,UACA,UACA,SACA,OAAoB,CAAC,GACH;AAClB,QAAM,EAAE,MAAM,IAAI;AAClB,QAAMC,SAAQ,KAAK,UAAU,CAAC,WAAmB,MAAY,QAAQ,IAAI,GAAG;AAC5E,QAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AACxD,QAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,CAAC,SAAS,CAAC,KAAM,OAAM,IAAI,MAAM,GAAG,QAAQ,uCAAuC;AACvF,MAAI,CAACA,OAAM,KAAK,MAAM,GAAG;AACvB,QAAI,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,oBAAoB,CAAC;AACtD,QAAI,IAAI,MAAM,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;AACzC,QAAI,IAAI;AACR,WAAO;AAAA,EACT;AAEA,QAAM,WACH,MAAM,QAAQ,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ,KACzD,MAAM,QAAQ,OAAO,EAAE,UAAU,OAAO,MAAM,KAAK,CAAC;AACvD,QAAM,aAAa,MAAM,QAAQ,WAAW,QAAQ,EAAE,EAAE,MAAM,MAAM,MAAS;AAC7E,MAAI,YAAY,WAAW,eAAe,QAAQ,WAAW,SAAS;AACpE,UAAM,OAAO,OAAO,KAAK,gBAAgB,CAAC,GAAG,OAAO,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC;AAAA,MAC1F;AAAA,MACA,QAAQ;AAAA,IACV;AACA,QAAI,SAAS,GAAG;AACd,UAAI,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,uDAAuD,CAAC;AACzF,UAAI,IAAI,MAAM,IAAI,8BAA8B,QAAQ,EAAE,EAAE,CAAC;AAAA,IAC/D;AAAA,EACF,OAAO;AACL,QAAI,IAAI,GAAG,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,IAAI,MAAM,IAAI,wBAAwB;AAAA,EAC3E;AAEA,QAAM,aACJ,aAAa,oBACT,OAAO,KAAK,WAAW,MAAM,UAAUA,OAAM,KAAK,MAAM,KAAK,KAAK,QAAQ,IAAI,GAAG;AAAA,IAC/E;AAAA,EACF,IACA,CAAC;AACP,MAAI,QAAQ,MAAM,SAAS,OAAO,aAAa,MAAM,IAAI,IAAI;AAAA,IAC3D,GAAG,MAAM;AAAA,IACT,GAAG;AAAA,IACH,EAAE,OAAO,aAAa,OAAO,uBAAkB,MAAM,cAAc;AAAA,EACrE,CAAC;AACD,MAAI,UAAU,aAAa;AACzB,YAAQ,MAAM,SAAS,KAAK,YAAY;AAAA,MACtC,UAAU,CAAC,MACT,gBAAgB,KAAK,EAAE,KAAK,CAAC,IAAI,SAAY;AAAA,IACjD,CAAC;AACD,YAAQ,MAAM,KAAK;AAAA,EACrB;AACA,QAAM,gBAAgB,KAAK;AAAA,IACzB,MAAM;AAAA,IACN,aAAa,GAAG,MAAM,IAAI,KAAK,KAAK;AAAA,IACpC,SAAS,CAAC,EAAE,UAAU,OAAO,WAAW,QAAQ,GAAG,CAAC;AAAA,EACtD,CAAC;AACD,MAAI,IAAI,GAAG,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,sBAAsB,MAAM,KAAK,MAAM,IAAI,CAAC,SAAM,KAAK,EAAE;AAC5F,MAAI,IAAI;AACR,SAAO;AACT;AAGO,SAAS,aACd,KACA,OAAmC,CAAC,GACyB;AAC7D,QAAMA,SAAQ,KAAK,UAAU,CAAC,WAAmB,MAAY,QAAQ,IAAI,GAAG;AAC5E,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb,MAAMA,OAAM,WAAW,MAAM,QAAQ,GAAG,UAAU,EAAE,IAAI,cAAc;AAAA,EACxE,EAAE;AACJ;AAGA,eAAe,iBACb,KACA,UACA,SACA,MACe;AACf,aAAS;AACP,UAAM,SAAS,MAAM,SAAS,OAAe,4BAA4B;AAAA,MACvE,GAAG,aAAa,KAAK,IAAI;AAAA,MACzB,EAAE,OAAO,SAAS,OAAO,SAAS,MAAM,wBAAwB;AAAA,IAClE,CAAC;AACD,QAAI,WAAW,QAAS;AACxB,QAAI,MAAM,aAAa,KAAK,QAAwB,UAAU,SAAS,IAAI,EAAG;AAAA,EAChF;AACF;AAOA,eAAsB,YACpB,KACA,OAAoB,CAAC,GACY;AACjC,QAAM,QAAQ,gBAAgB,IAAI,IAAI;AACtC,QAAM,eAAe,gBAAgB,KAAK,IAAI,KAAK,CAAE,MAAM,eAAe,KAAK,IAAI;AACnF,MAAI,MAAM,aAAa,CAAC,aAAc,QAAO;AAE7C,MAAI,UAAU;AACd,QAAM,WAAW,MAAM,YAAY,KAAK,IAAI;AAC5C,MAAI;AACF,QAAI,CAAC,MAAM,WAAW;AACpB,cAAQ,GAAG;AACX,YAAM,OAAO,MAAM,YAAY,KAAK,QAAQ;AAC5C,uBAAiB,IAAI,MAAM,EAAE,OAAO,KAAK,CAAC;AAC1C,gBAAU,UAAU,KAAK,IAAI;AAAA,IAC/B;AACA,QAAI,aAAc,OAAM,WAAW,SAAS,UAAU,IAAI;AAC1D,QAAI,CAAC,MAAM,WAAW;AACpB,YAAM,UACJ,KAAK,WAAY,MAAM,aAAa,EAAE,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,OAAO,KAAK,CAAC;AAC3F,UAAI;AACF,cAAM,WAAW,MAAM,QAAQ,KAAK;AACpC,YAAI,SAAS,WAAW,EAAG,OAAM,iBAAiB,SAAS,UAAU,SAAS,IAAI;AAAA,MACpF,UAAE;AACA,YAAI,CAAC,KAAK,QAAS,SAAQ,MAAM;AAAA,MACnC;AACA,uBAAiB,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA,IACpD;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,eAAe,sBAAsB;AACvC,UAAI,IAAI,IAAI,SAAS,IAAI,kDAAkD,CAAC;AAC5E,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;;;AF9PA,SAAS,YAAY,MAAsB;AACzC,QAAM,OAAO,QAAQ;AACrB,SAAO,SAAS,QAAQ,KAAK,WAAW,OAAO,GAAG,IAAI,IAAI,KAAK,MAAM,KAAK,MAAM,CAAC,KAAK;AACxF;AAEA,eAAsB,YAAY,KAAiB,MAAoC;AACrF,MAAI,CAAC,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,GAAG,OAAO,OAAO;AAC/C,UAAM,IAAI,SAAS,8CAA8C;AAAA,MAC/D,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,QAAM,QAAQ,MAAM,YAAY,GAAG;AACnC,MAAI,CAAC,MAAO,QAAO,KAAK;AACxB,QAAM;AACN,QAAM,cAAc,KAAK,UACrB,QAAQ,IAAI,KAAK,KAAK,OAAO,IAC7B,MAAM,kBAAkB,IAAI,KAAK,IAAI,IAAI;AAE7C,aAAS;AACP,UAAM,WAAW,MAAM,QAAQ,KAAK,MAAM,WAAW;AACrD,QAAI,CAAC,SAAU,QAAO,KAAK;AAC3B,UAAM,gBAAgB,KAAK,QAAQ;AAAA,EACrC;AACF;AAEA,eAAe,gBAAgB,KAAiB,UAAuC;AACrF,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,uBAAgB;AAC3D,QAAM,WAAW,kBAAkB;AAAA,IACjC,QAAQ,IAAI,GAAG;AAAA,IACf,OAAO,IAAI,GAAG;AAAA,IACd,OAAO,IAAI;AAAA,EACb,CAAC;AACD,QAAM,UAAU,MAAM,aAAa,EAAE,MAAM,IAAI,MAAM,KAAK,IAAI,KAAK,OAAO,KAAK,CAAC;AAChF,MAAI;AACF,QAAI,IAAI;AACR,UAAM,aAAa,KAAK,UAAU,UAAU,OAAO;AAAA,EACrD,SAAS,KAAK;AACZ,QAAI,EAAE,eAAe,sBAAuB,OAAM;AAAA,EACpD,UAAE;AACA,YAAQ,MAAM;AAAA,EAChB;AACF;AAGA,eAAe,QACb,KACA,MACA,aACmC;AACnC,QAAM,SAAS,WAAW;AAAA,IACxB,MAAM,IAAI;AAAA,IACV,KAAK,IAAI;AAAA,IACT,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,EACvC,CAAC;AAED,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,MAAM,YAAY,MAAM;AACxC,MAAI,SAAS;AACX,cAAU,YAAY,QAAQ,QAAQ,KAAK;AAAA,EAC7C,OAAO;AACL,cAAU,MAAM,aAAa;AAAA,MAC3B,MAAM,IAAI;AAAA,MACV,KAAK,IAAI;AAAA,MACT,KAAK,IAAI;AAAA,MACT,MAAM;AAAA,MACN,MAAM;AAAA,MACN,WAAW;AAAA,MACX,GAAI,cAAc,EAAE,YAAY,YAAY,IAAI,EAAE,WAAW,KAAK;AAAA,IACpE,CAAC;AACD,UAAM,SAAS,OAAO,OAAO,YACzB,EAAE,SAAS,QAAQ,KAAK,OAAO,OAAO,OAAO,UAAU,IACvD,EAAE,SAAS,QAAQ,IAAI;AAC3B,cAAU,YAAY,IAAI,cAAc,MAAM,GAAG,IAAI;AAAA,EACvD;AAEA,QAAM,iBAAiB,mBAAmB,GAAG;AAC7C,MAAI;AACJ,QAAM,UAAU;AAAA,IACd,QAAQ,YAAY,aAAa,GAAG;AAAA,IACpC,QAAQ,CAAC,aAAqB;AAC5B,kBAAY;AAAA,IACd;AAAA,EACF;AACA,MAAI;AACF,UAAM,CAAC,EAAE,OAAO,GAAG,EAAE,cAAc,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,MACrE,OAAO,KAAK;AAAA,MACZ,OAAO,OAAO;AAAA,MACd,OAAO,uBAAgB;AAAA,IACzB,CAAC;AACD,UAAM,WAAW;AAAA,MACf,cAAc,SAAS;AAAA,QACrB;AAAA,QACA,OAAO,IAAI;AAAA,QACX,OAAO,KAAK,SAAS,YAAY,OAAO,QAAQ,YAAY;AAAA,QAC5D,SAAS;AAAA,QACT,SAAS,YAAY,eAAe,IAAI,GAAG;AAAA,QAC3C,GAAI,iBAAiB,EAAE,SAAS,eAAe,IAAI,CAAC;AAAA,QACpD;AAAA,MACF,CAAC;AAAA,MACD;AAAA,QACE,QAAQ,IAAI,GAAG;AAAA,QACf,OAAO,QAAQ;AAAA,QACf,aAAa;AAAA,QACb,eAAe,EAAE,MAAM,QAAQ,OAAO,CAAC,yBAAyB,EAAE;AAAA,MACpE;AAAA,IACF;AACA,UAAM,SAAS,cAAc;AAAA,EAC/B,UAAE;AACA,UAAM,SAAS,MAAM;AAAA,EACvB;AACA,SAAO;AACT;","names":["resolve","which"]}
1
+ {"version":3,"sources":["../src/commands/chat.ts","../src/tui/backend.ts","../src/commands/onboard.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { resolve, sep } from 'node:path';\nimport { loadConfig, type ProviderKind, VERSION } from '@davecode/core';\nimport { findGateway, GatewayClient } from '../client';\nimport type { CliContext } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { openAccounts } from '../lib/accounts-backend';\nimport { PromptCancelledError } from '../lib/prompter';\nimport { detectProjectRoot, type Runtime, startRuntime } from '../runtime';\nimport { type ChatBackend, httpBackend } from '../tui/backend';\nimport { chatAccountActions } from './account';\nimport { agentChoices, connectAgent, ensureReady } from './onboard';\n\nexport interface ChatOptions {\n model?: string;\n project?: string;\n}\n\n/**\n * Open the chat TUI against the running gateway, or start an in-process gateway on an ephemeral\n * loopback port when none is running (it stops when the TUI exits).\n */\n/** `C:\\Users\\me\\code\\app` → `~\\code\\app`, for the banner. */\nfunction displayPath(path: string): string {\n const home = homedir();\n return path === home || path.startsWith(home + sep) ? `~${path.slice(home.length)}` : path;\n}\n\nexport async function chatCommand(ctx: CliContext, opts: ChatOptions): Promise<number> {\n if (!ctx.io.stdin.isTTY || !ctx.io.stdout.isTTY) {\n throw new CliError('The chat TUI needs an interactive terminal', {\n hint: 'For scripts, call the OpenAI-compatible API: POST http://127.0.0.1:4040/v1/chat/completions',\n });\n }\n const ready = await ensureReady(ctx);\n if (!ready) return EXIT.failure;\n ctx = ready;\n const projectRoot = opts.project\n ? resolve(ctx.cwd, opts.project)\n : await detectProjectRoot(ctx.cwd, ctx.home);\n // /connect closes the chat so the vendor login can use the terminal, then opens it again.\n for (;;) {\n const provider = await runChat(ctx, opts, projectRoot);\n if (!provider) return EXIT.ok;\n await connectFromChat(ctx, provider);\n }\n}\n\nasync function connectFromChat(ctx: CliContext, provider: ProviderKind): Promise<void> {\n const { createInkPrompter } = await import('../tui/prompts');\n const prompter = createInkPrompter({\n stdout: ctx.io.stdout,\n stdin: ctx.io.stdin,\n theme: ctx.theme,\n });\n const backend = await openAccounts({ home: ctx.home, env: ctx.env, local: true });\n try {\n ctx.out();\n await connectAgent(ctx, provider, prompter, backend);\n } catch (err) {\n if (!(err instanceof PromptCancelledError)) throw err;\n } finally {\n backend.close();\n }\n}\n\n/** One chat session; resolves with the agent picked in /connect, if any. */\nasync function runChat(\n ctx: CliContext,\n opts: ChatOptions,\n projectRoot: string | undefined,\n): Promise<ProviderKind | undefined> {\n const config = loadConfig({\n home: ctx.home,\n env: ctx.env,\n ...(projectRoot ? { projectRoot } : {}),\n });\n\n let runtime: Runtime | undefined;\n let backend: ChatBackend;\n const running = await findGateway(config);\n if (running) {\n backend = httpBackend(running.client, false);\n } else {\n runtime = await startRuntime({\n home: ctx.home,\n env: ctx.env,\n cwd: ctx.cwd,\n host: '127.0.0.1',\n port: 0,\n dashboard: false,\n ...(projectRoot ? { projectDir: projectRoot } : { noProject: true }),\n });\n const target = config.server.authToken\n ? { baseUrl: runtime.url, token: config.server.authToken }\n : { baseUrl: runtime.url };\n backend = httpBackend(new GatewayClient(target), true);\n }\n\n const accountActions = chatAccountActions(ctx);\n let connectTo: ProviderKind | undefined;\n const connect = {\n agents: async () => agentChoices(ctx),\n choose: (provider: string) => {\n connectTo = provider as ProviderKind;\n },\n };\n try {\n const [{ render }, { createElement }, { ChatApp }] = await Promise.all([\n import('ink'),\n import('react'),\n import('../tui/ChatApp'),\n ]);\n const instance = render(\n createElement(ChatApp, {\n backend,\n theme: ctx.theme,\n model: opts.model ?? `davecode/${config.routing.defaultRoute}`,\n version: VERSION,\n project: displayPath(projectRoot ?? ctx.cwd),\n ...(accountActions ? { account: accountActions } : {}),\n connect,\n }),\n {\n stdout: ctx.io.stdout as NodeJS.WriteStream,\n stdin: process.stdin,\n exitOnCtrlC: false,\n kittyKeyboard: { mode: 'auto', flags: ['disambiguateEscapeCodes'] },\n },\n );\n await instance.waitUntilExit();\n } finally {\n await runtime?.close();\n }\n return connectTo;\n}\n","import type {\n Account,\n AccountUsage,\n ChatCompletionChunk,\n ChatRequest,\n ModelInfo,\n Route,\n RunnerStatus,\n TaskGraph,\n} from '@davecode/core';\nimport type { GatewayClient, HealthResponse, StreamMeta } from '../client';\n\n/** What the TUI needs from a gateway. Backed by HTTP in production and by fakes in tests. */\nexport interface ChatBackend {\n /** Base URL, e.g. `http://127.0.0.1:4040`. */\n url: string;\n /** The gateway was started inside this process (no `davecode start` running). */\n inProcess: boolean;\n health(): Promise<HealthResponse | undefined>;\n stream(\n request: ChatRequest,\n signal: AbortSignal,\n ): Promise<{ meta: StreamMeta; chunks: AsyncIterable<ChatCompletionChunk> }>;\n models(): Promise<ModelInfo[]>;\n routes(): Promise<{ defaultRoute: string; routes: Route[] }>;\n accounts(): Promise<Account[]>;\n usage(): Promise<AccountUsage[]>;\n tasks(): Promise<{ project: { root: string; name: string } | null; graph: TaskGraph }>;\n runner(): Promise<RunnerStatus>;\n}\n\nexport function httpBackend(client: GatewayClient, inProcess: boolean): ChatBackend {\n return {\n url: client.baseUrl,\n inProcess,\n health: () => client.health(1500).catch(() => undefined),\n stream: (request, signal) => client.chatStream(request, signal),\n models: async () => (await client.get<{ data: ModelInfo[] }>('/v1/models')).data,\n routes: () => client.get('/api/routes'),\n accounts: async () => (await client.get<{ accounts: Account[] }>('/api/accounts')).accounts,\n usage: async () => (await client.get<{ usage: AccountUsage[] }>('/api/usage')).usage,\n tasks: () => client.get('/api/tasks'),\n runner: async () => (await client.get<{ status: RunnerStatus }>('/api/runner')).status,\n };\n}\n","import { execFile } from 'node:child_process';\nimport {\n CLI_LOGINS,\n loadConfig,\n type ProviderKind,\n type Route,\n VERSION,\n writeGlobalRouting,\n} from '@davecode/core';\nimport type { CliContext } from '../context';\nimport { type AccountsBackend, openAccounts } from '../lib/accounts-backend';\nimport { readPreferences, writePreferences } from '../lib/preferences';\nimport { which as whichOnPath } from '../lib/proc';\nimport { PromptCancelledError, type Prompter } from '../lib/prompter';\nimport { brandBanner } from '../ui/banner';\nimport { createTheme, type ThemeMode } from '../ui/theme';\nimport { type AccountDeps, accountsEnabled, currentAccount, describeUser, signIn } from './account';\nimport { accountsLogin } from './accounts';\n\n/** Agents DaveCode can drive today, in the order they are offered. */\nexport const AGENTS: ReadonlyArray<{\n provider: ProviderKind;\n name: string;\n models: Array<{ value: string; label: string; hint?: string }>;\n}> = [\n {\n provider: 'claude-cli',\n name: 'Claude Code',\n models: [\n { value: 'default', label: 'Default', hint: 'Claude Code decides' },\n { value: 'opus', label: 'Opus', hint: 'most capable' },\n { value: 'sonnet', label: 'Sonnet', hint: 'balanced' },\n { value: 'haiku', label: 'Haiku', hint: 'fastest' },\n ],\n },\n {\n provider: 'codex-cli',\n name: 'Codex',\n models: [{ value: 'default', label: 'Default', hint: 'Codex decides' }],\n },\n {\n provider: 'antigravity-cli',\n name: 'Antigravity',\n models: [{ value: 'default', label: 'Default', hint: 'Antigravity decides' }],\n },\n];\n\ntype ModelChoice = { value: string; label: string; hint?: string };\n\n/** Antigravity lists its models with `agy models` as `id<TAB>name` lines. */\nexport function parseAgyModels(stdout: string): ModelChoice[] {\n return stdout\n .split(/\\r?\\n/)\n .map((line) => line.split('\\t'))\n .filter(([id, name]) => id && name && /^[\\w.:-]+$/.test(id.trim()))\n .map(([id, name]) => ({ value: (id as string).trim(), label: (name as string).trim() }));\n}\n\nfunction agyModels(binary: string, env: NodeJS.ProcessEnv): Promise<ModelChoice[]> {\n return new Promise((resolve) => {\n execFile(binary, ['models'], { env, timeout: 30_000, windowsHide: true }, (error, stdout) =>\n resolve(error ? [] : parseAgyModels(String(stdout))),\n );\n });\n}\n\nconst OTHER_MODEL = '__other__';\n\nexport interface OnboardDeps extends AccountDeps {\n prompter?: Prompter;\n backend?: AccountsBackend;\n which?: (binary: string) => string | undefined;\n /** Models an agent offers beyond its built-in list (defaults to asking `agy models`). */\n models?: (provider: ProviderKind) => Promise<ModelChoice[]>;\n /** Runs the vendor's official login for an account (defaults to `accounts login`). */\n vendorLogin?: (ctx: CliContext, accountId: string) => Promise<number>;\n}\n\nasync function prompterFor(ctx: CliContext, deps: OnboardDeps): Promise<Prompter> {\n if (deps.prompter) return deps.prompter;\n const { createInkPrompter } = await import('../tui/prompts');\n return createInkPrompter({ stdout: ctx.io.stdout, stdin: ctx.io.stdin, theme: ctx.theme });\n}\n\nfunction welcome(ctx: CliContext): void {\n const { theme } = ctx;\n ctx.out();\n const text = [\n `${theme.bold('Welcome to DaveCode')} ${theme.dim(`v${VERSION}`)}`,\n theme.dim('Run Claude Code, Codex and Antigravity side by side.'),\n ];\n for (const line of brandBanner(theme, text, ctx.columns)) ctx.out(` ${line}`);\n ctx.out();\n}\n\nfunction withTheme(ctx: CliContext, mode: ThemeMode): CliContext {\n return {\n ...ctx,\n theme: createTheme({ color: ctx.theme.color, unicode: ctx.theme.unicode, mode }),\n errTheme: createTheme({ color: ctx.errTheme.color, unicode: ctx.errTheme.unicode, mode }),\n };\n}\n\nasync function chooseTheme(ctx: CliContext, prompter: Prompter): Promise<ThemeMode> {\n return prompter.select<ThemeMode>(\n 'Choose a look for the terminal',\n [\n { value: 'dark', label: 'Dark', hint: 'white text on black' },\n { value: 'light', label: 'Light', hint: 'black text on white' },\n { value: 'auto', label: 'Match my terminal' },\n ],\n readPreferences(ctx.home).theme ?? 'dark',\n );\n}\n\nasync function signInStep(ctx: CliContext, prompter: Prompter, deps: OnboardDeps): Promise<void> {\n const choice = await prompter.select('Sign in to continue', [\n { value: 'browser', label: 'Sign in with your browser', hint: 'Google, GitHub or email' },\n { value: 'quit', label: 'Quit' },\n ]);\n if (choice === 'quit') throw new PromptCancelledError();\n const session = await signIn(ctx, deps);\n ctx.out(\n `${ctx.theme.ok(ctx.theme.glyph.ok)} Signed in as ${ctx.theme.bold(describeUser(session))}`,\n );\n ctx.out();\n}\n\nasync function setDefaultRoute(ctx: CliContext, route: Route): Promise<void> {\n const global = loadConfig({ home: ctx.home, env: ctx.env });\n await writeGlobalRouting(ctx.home, {\n routes: [...global.routing.routes.filter((r) => r.name !== route.name), route],\n defaultRoute: route.name,\n });\n}\n\n/**\n * Connect one agent: reuse its account (or create one), run the official login when it is not\n * signed in, choose a model and make it the default route. Returns false when the agent is not\n * installed.\n */\nexport async function connectAgent(\n ctx: CliContext,\n provider: ProviderKind,\n prompter: Prompter,\n backend: AccountsBackend,\n deps: OnboardDeps = {},\n): Promise<boolean> {\n const { theme } = ctx;\n const which = deps.which ?? ((binary: string) => whichOnPath(binary, ctx.env));\n const agent = AGENTS.find((a) => a.provider === provider);\n const spec = CLI_LOGINS[provider];\n if (!agent || !spec) throw new Error(`${provider} is not an agent DaveCode can connect`);\n if (!which(spec.binary)) {\n ctx.out(theme.warn(` ${agent.name} is not installed.`));\n ctx.out(theme.dim(` ${spec.installHint}`));\n ctx.out();\n return false;\n }\n\n const account =\n (await backend.list()).find((a) => a.provider === provider) ??\n (await backend.create({ provider, label: agent.name }));\n const connection = await backend.connection(account.id).catch(() => undefined);\n if (connection?.status !== 'connected' || account.status === 'error') {\n const exit = await (deps.vendorLogin ?? ((c, id) => accountsLogin(c, id, [], {}, { backend })))(\n ctx,\n account.id,\n );\n if (exit !== 0) {\n ctx.out(theme.warn(` ${agent.name} did not finish signing in. You can retry later with:`));\n ctx.out(theme.dim(` davecode accounts login ${account.id}`));\n }\n } else {\n ctx.out(`${theme.ok(theme.glyph.ok)} ${agent.name} is already signed in.`);\n }\n\n const discovered =\n provider === 'antigravity-cli'\n ? await (deps.models ?? (() => agyModels(which(spec.binary) ?? spec.binary, ctx.env)))(\n provider,\n )\n : [];\n let model = await prompter.select(`Model for ${agent.name}`, [\n ...agent.models,\n ...discovered,\n { value: OTHER_MODEL, label: 'Another model…', hint: 'type its id' },\n ]);\n if (model === OTHER_MODEL) {\n model = await prompter.text('Model id', {\n validate: (v) =>\n /^[\\w.:[\\]-]+$/.test(v.trim()) ? undefined : 'Use letters, digits, . : - _',\n });\n model = model.trim();\n }\n await setDefaultRoute(ctx, {\n name: 'auto',\n description: `${agent.name} (${model})`,\n targets: [{ provider, model, accountId: account.id }],\n });\n ctx.out(`${theme.ok(theme.glyph.ok)} DaveCode will use ${theme.bold(agent.name)} · ${model}`);\n ctx.out();\n return true;\n}\n\n/** The agent picker shared by first-run setup and /connect. */\nexport function agentChoices(\n ctx: CliContext,\n deps: Pick<OnboardDeps, 'which'> = {},\n): Array<{ value: ProviderKind; label: string; hint: string }> {\n const which = deps.which ?? ((binary: string) => whichOnPath(binary, ctx.env));\n return AGENTS.map((agent) => ({\n value: agent.provider,\n label: agent.name,\n hint: which(CLI_LOGINS[agent.provider]?.binary ?? '') ? 'installed' : 'not installed',\n }));\n}\n\n/** First agent: pick one that is installed, log in with its official CLI, choose a model. */\nasync function connectAgentStep(\n ctx: CliContext,\n prompter: Prompter,\n backend: AccountsBackend,\n deps: OnboardDeps,\n): Promise<void> {\n for (;;) {\n const choice = await prompter.select<string>('Connect your first agent', [\n ...agentChoices(ctx, deps),\n { value: 'later', label: 'Later', hint: 'davecode accounts add' },\n ]);\n if (choice === 'later') return;\n if (await connectAgent(ctx, choice as ProviderKind, prompter, backend, deps)) return;\n }\n}\n\n/**\n * First-run setup, Claude Code style: logo, look, DaveCode account, first agent. Later runs\n * only ask to sign in again when the session is gone. Returns the context to continue with\n * (the chosen theme applied), or undefined when the user quits.\n */\nexport async function ensureReady(\n ctx: CliContext,\n deps: OnboardDeps = {},\n): Promise<CliContext | undefined> {\n const prefs = readPreferences(ctx.home);\n const needsAccount = accountsEnabled(ctx, deps) && !(await currentAccount(ctx, deps));\n if (prefs.onboarded && !needsAccount) return ctx;\n\n let current = ctx;\n const prompter = await prompterFor(ctx, deps);\n try {\n if (!prefs.onboarded) {\n welcome(ctx);\n const mode = await chooseTheme(ctx, prompter);\n writePreferences(ctx.home, { theme: mode });\n current = withTheme(ctx, mode);\n }\n if (needsAccount) await signInStep(current, prompter, deps);\n if (!prefs.onboarded) {\n const backend =\n deps.backend ?? (await openAccounts({ home: current.home, env: current.env, local: true }));\n try {\n const existing = await backend.list();\n if (existing.length === 0) await connectAgentStep(current, prompter, backend, deps);\n } finally {\n if (!deps.backend) backend.close();\n }\n writePreferences(current.home, { onboarded: true });\n }\n return current;\n } catch (err) {\n if (err instanceof PromptCancelledError) {\n ctx.err(ctx.errTheme.dim('Setup cancelled. Run davecode again to continue.'));\n return undefined;\n }\n throw err;\n }\n}\n"],"mappings":"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","names":["resolve","which"]}
@@ -0,0 +1,23 @@
1
+ // src/lib/browser.ts
2
+ import { spawn } from "child_process";
3
+ var detached = (command, args) => new Promise((resolve) => {
4
+ const child = spawn(command, args, { detached: true, stdio: "ignore", windowsHide: true });
5
+ child.once("error", () => resolve(false));
6
+ child.once("spawn", () => {
7
+ child.unref();
8
+ resolve(true);
9
+ });
10
+ });
11
+ async function openBrowser(url, platform = process.platform, launch = detached) {
12
+ const parsed = new URL(url);
13
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
14
+ const target = parsed.toString();
15
+ if (platform === "win32") return launch("rundll32", ["url.dll,FileProtocolHandler", target]);
16
+ if (platform === "darwin") return launch("open", [target]);
17
+ return launch("xdg-open", [target]);
18
+ }
19
+
20
+ export {
21
+ openBrowser
22
+ };
23
+ //# sourceMappingURL=chunk-732GIHRQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/browser.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\n\nexport type Launcher = (command: string, args: string[]) => Promise<boolean>;\n\nconst detached: Launcher = (command, args) =>\n new Promise((resolve) => {\n const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true });\n child.once('error', () => resolve(false));\n child.once('spawn', () => {\n child.unref();\n resolve(true);\n });\n });\n\n/**\n * Open an http(s) URL in the default browser. Returns false when nothing could be launched, so\n * the caller can print the URL instead.\n */\nexport async function openBrowser(\n url: string,\n platform: NodeJS.Platform = process.platform,\n launch: Launcher = detached,\n): Promise<boolean> {\n const parsed = new URL(url);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false;\n const target = parsed.toString();\n // No shell is involved, so the URL is never interpreted as a command.\n if (platform === 'win32') return launch('rundll32', ['url.dll,FileProtocolHandler', target]);\n if (platform === 'darwin') return launch('open', [target]);\n return launch('xdg-open', [target]);\n}\n"],"mappings":";AAAA,SAAS,aAAa;AAItB,IAAM,WAAqB,CAAC,SAAS,SACnC,IAAI,QAAQ,CAAC,YAAY;AACvB,QAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,UAAU,MAAM,OAAO,UAAU,aAAa,KAAK,CAAC;AACzF,QAAM,KAAK,SAAS,MAAM,QAAQ,KAAK,CAAC;AACxC,QAAM,KAAK,SAAS,MAAM;AACxB,UAAM,MAAM;AACZ,YAAQ,IAAI;AAAA,EACd,CAAC;AACH,CAAC;AAMH,eAAsB,YACpB,KACA,WAA4B,QAAQ,UACpC,SAAmB,UACD;AAClB,QAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,MAAI,OAAO,aAAa,WAAW,OAAO,aAAa,SAAU,QAAO;AACxE,QAAM,SAAS,OAAO,SAAS;AAE/B,MAAI,aAAa,QAAS,QAAO,OAAO,YAAY,CAAC,+BAA+B,MAAM,CAAC;AAC3F,MAAI,aAAa,SAAU,QAAO,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzD,SAAO,OAAO,YAAY,CAAC,MAAM,CAAC;AACpC;","names":[]}
@@ -7,7 +7,7 @@ import {
7
7
  nextTask,
8
8
  summarize,
9
9
  topologicalOrder
10
- } from "./chunk-HENE4DA6.js";
10
+ } from "./chunk-QKY3OKVG.js";
11
11
 
12
12
  // src/lib/task-tree.ts
13
13
  function statusGlyph(theme, task, blocked) {
@@ -111,4 +111,4 @@ export {
111
111
  formatTaskTree,
112
112
  formatTaskDetail
113
113
  };
114
- //# sourceMappingURL=chunk-6SHOBL2X.js.map
114
+ //# sourceMappingURL=chunk-BJTXMET2.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ openBrowser
3
+ } from "./chunk-732GIHRQ.js";
1
4
  import {
2
5
  ConfigError,
3
6
  PROVIDER_KINDS,
@@ -11,6 +14,7 @@ import {
11
14
  WorkspaceLock,
12
15
  apiConfigIssues,
13
16
  connectionMethod,
17
+ createAccountControl,
14
18
  createEngine,
15
19
  createGoalService,
16
20
  createRunner,
@@ -21,7 +25,7 @@ import {
21
25
  newId2,
22
26
  routeSchema,
23
27
  taskIdSchema
24
- } from "./chunk-HENE4DA6.js";
28
+ } from "./chunk-QKY3OKVG.js";
25
29
 
26
30
  // src/runtime.ts
27
31
  import { existsSync as existsSync3 } from "fs";
@@ -37,13 +41,14 @@ import { resolve } from "path";
37
41
  import cors from "@fastify/cors";
38
42
  import fastifyStatic from "@fastify/static";
39
43
  import Fastify from "fastify";
40
- import { z as z4 } from "zod";
41
44
  import { z } from "zod";
45
+ import { z as z5 } from "zod";
42
46
  import { z as z2 } from "zod";
43
47
  import { z as z3 } from "zod";
44
- import { z as z5 } from "zod";
45
- import { Readable } from "stream";
48
+ import { z as z4 } from "zod";
46
49
  import { z as z6 } from "zod";
50
+ import { Readable } from "stream";
51
+ import { z as z7 } from "zod";
47
52
  function sendApiError(reply, status, code, message, extra = {}) {
48
53
  return reply.code(status).send({ error: { message, code, ...extra } });
49
54
  }
@@ -92,55 +97,81 @@ function describeRoutingError(err) {
92
97
  body: { message: "Internal gateway error", type: "api_error", code: "internal_error" }
93
98
  };
94
99
  }
95
- var providerKind = z.enum(PROVIDER_KINDS);
96
- var contentPart = z.discriminatedUnion("type", [
97
- z.object({ type: z.literal("text"), text: z.string() }),
98
- z.object({
99
- type: z.literal("image_url"),
100
- image_url: z.object({
101
- url: z.string(),
102
- detail: z.enum(["auto", "low", "high"]).optional()
100
+ var loginSchema = z.object({ provider: z.enum(["google", "github"]).optional() });
101
+ function registerAccountRoutes(app, account) {
102
+ if (!account) return;
103
+ app.get("/api/account", async (_request, reply) => {
104
+ reply.header("Cache-Control", "no-store");
105
+ return account.state();
106
+ });
107
+ app.post("/api/account/login", async (request, reply) => {
108
+ const parsed = loginSchema.safeParse(request.body ?? {});
109
+ if (!parsed.success)
110
+ return sendApiError(reply, 400, "invalid_body", "Unknown sign-in provider.");
111
+ try {
112
+ return reply.code(202).send(await account.login(parsed.data.provider));
113
+ } catch {
114
+ return sendApiError(reply, 502, "account_unavailable", "Could not start the sign-in.");
115
+ }
116
+ });
117
+ app.post("/api/account/cancel", async (_request, reply) => {
118
+ await account.cancel();
119
+ return reply.code(204).send();
120
+ });
121
+ app.post("/api/account/logout", async (_request, reply) => {
122
+ await account.logout();
123
+ return reply.code(204).send();
124
+ });
125
+ }
126
+ var providerKind = z2.enum(PROVIDER_KINDS);
127
+ var contentPart = z2.discriminatedUnion("type", [
128
+ z2.object({ type: z2.literal("text"), text: z2.string() }),
129
+ z2.object({
130
+ type: z2.literal("image_url"),
131
+ image_url: z2.object({
132
+ url: z2.string(),
133
+ detail: z2.enum(["auto", "low", "high"]).optional()
103
134
  })
104
135
  })
105
136
  ]);
106
- var toolCall = z.object({
107
- id: z.string(),
108
- type: z.literal("function"),
109
- function: z.object({ name: z.string(), arguments: z.string() })
137
+ var toolCall = z2.object({
138
+ id: z2.string(),
139
+ type: z2.literal("function"),
140
+ function: z2.object({ name: z2.string(), arguments: z2.string() })
110
141
  });
111
- var chatMessage = z.object({
112
- role: z.enum(["system", "user", "assistant", "tool"]),
113
- content: z.union([z.string(), z.array(contentPart), z.null()]),
114
- name: z.string().optional(),
115
- tool_call_id: z.string().optional(),
116
- tool_calls: z.array(toolCall).optional()
142
+ var chatMessage = z2.object({
143
+ role: z2.enum(["system", "user", "assistant", "tool"]),
144
+ content: z2.union([z2.string(), z2.array(contentPart), z2.null()]),
145
+ name: z2.string().optional(),
146
+ tool_call_id: z2.string().optional(),
147
+ tool_calls: z2.array(toolCall).optional()
117
148
  });
118
- var chatRequestSchema = z.object({
119
- model: z.string().min(1),
120
- messages: z.array(chatMessage).min(1),
121
- stream: z.boolean().optional(),
122
- temperature: z.number().min(0).max(2).optional(),
123
- top_p: z.number().min(0).max(1).optional(),
124
- max_tokens: z.number().int().positive().optional(),
125
- stop: z.union([z.string(), z.array(z.string())]).optional(),
126
- tools: z.array(
127
- z.object({
128
- type: z.literal("function"),
129
- function: z.object({
130
- name: z.string(),
131
- description: z.string().optional(),
132
- parameters: z.record(z.string(), z.unknown()).optional()
149
+ var chatRequestSchema = z2.object({
150
+ model: z2.string().min(1),
151
+ messages: z2.array(chatMessage).min(1),
152
+ stream: z2.boolean().optional(),
153
+ temperature: z2.number().min(0).max(2).optional(),
154
+ top_p: z2.number().min(0).max(1).optional(),
155
+ max_tokens: z2.number().int().positive().optional(),
156
+ stop: z2.union([z2.string(), z2.array(z2.string())]).optional(),
157
+ tools: z2.array(
158
+ z2.object({
159
+ type: z2.literal("function"),
160
+ function: z2.object({
161
+ name: z2.string(),
162
+ description: z2.string().optional(),
163
+ parameters: z2.record(z2.string(), z2.unknown()).optional()
133
164
  })
134
165
  })
135
166
  ).optional(),
136
- tool_choice: z.union([
137
- z.enum(["auto", "none", "required"]),
138
- z.object({ type: z.literal("function"), function: z.object({ name: z.string() }) })
167
+ tool_choice: z2.union([
168
+ z2.enum(["auto", "none", "required"]),
169
+ z2.object({ type: z2.literal("function"), function: z2.object({ name: z2.string() }) })
139
170
  ]).optional(),
140
- user: z.string().optional()
171
+ user: z2.string().optional()
141
172
  });
142
- var positiveInt = z.number().int().positive();
143
- var quotaLimits = z.object({
173
+ var positiveInt = z2.number().int().positive();
174
+ var quotaLimits = z2.object({
144
175
  tpm: positiveInt.optional(),
145
176
  rpm: positiveInt.optional(),
146
177
  tokens5h: positiveInt.optional(),
@@ -149,41 +180,41 @@ var quotaLimits = z.object({
149
180
  requestsDaily: positiveInt.optional()
150
181
  }).strict();
151
182
  var SECRET_KEY = /^(api[-_]?key|secret|client[-_]?secret|(access|refresh|session|auth|bearer)?[-_]?token|password|passwd|cookies?|authorization|credentials?)$/i;
152
- var accountConfig = z.record(z.string(), z.unknown()).refine((config) => !Object.keys(config).some((key) => SECRET_KEY.test(key)), {
183
+ var accountConfig = z2.record(z2.string(), z2.unknown()).refine((config) => !Object.keys(config).some((key) => SECRET_KEY.test(key)), {
153
184
  message: 'config must not contain credentials; send them in "secret" instead'
154
185
  });
155
- var subscriptionDate = z.string().trim().refine(
186
+ var subscriptionDate = z2.string().trim().refine(
156
187
  (value) => /^\d{4}-\d{2}-\d{2}(T[\d:.]+(Z|[+-]\d{2}:?\d{2})?)?$/.test(value) && !Number.isNaN(Date.parse(value)),
157
188
  { message: "expected a date (YYYY-MM-DD) or an ISO timestamp" }
158
189
  );
159
- var subscriptionSchema = z.object({
160
- plan: z.string().trim().min(1).max(120).optional(),
190
+ var subscriptionSchema = z2.object({
191
+ plan: z2.string().trim().min(1).max(120).optional(),
161
192
  paidAt: subscriptionDate.optional(),
162
193
  renewsAt: subscriptionDate.optional(),
163
- price: z.object({
164
- amount: z.number().min(0).max(1e6),
165
- currency: z.string().trim().regex(/^[A-Za-z]{3}$/, "expected a 3-letter currency code").transform((c) => c.toUpperCase()),
166
- period: z.enum(["month", "year"])
194
+ price: z2.object({
195
+ amount: z2.number().min(0).max(1e6),
196
+ currency: z2.string().trim().regex(/^[A-Za-z]{3}$/, "expected a 3-letter currency code").transform((c) => c.toUpperCase()),
197
+ period: z2.enum(["month", "year"])
167
198
  }).strict().optional(),
168
- notes: z.string().trim().max(1e3).optional()
199
+ notes: z2.string().trim().max(1e3).optional()
169
200
  }).strict();
170
201
  var accountFields = {
171
- label: z.string().trim().min(1).max(200),
172
- enabled: z.boolean().optional(),
173
- priority: z.number().int().min(0).max(1e6).optional(),
174
- weight: z.number().min(0).max(1e3).optional(),
202
+ label: z2.string().trim().min(1).max(200),
203
+ enabled: z2.boolean().optional(),
204
+ priority: z2.number().int().min(0).max(1e6).optional(),
205
+ weight: z2.number().min(0).max(1e3).optional(),
175
206
  limits: quotaLimits.optional(),
176
207
  config: accountConfig.optional(),
177
208
  /** API key / token. Encrypted at rest and never returned. */
178
- secret: z.string().min(1).max(1e5).optional()
209
+ secret: z2.string().min(1).max(1e5).optional()
179
210
  };
180
- var accountCreateSchema = z.object({ provider: providerKind, ...accountFields, subscription: subscriptionSchema.optional() }).strict();
181
- var accountPatchSchema = z.object({
211
+ var accountCreateSchema = z2.object({ provider: providerKind, ...accountFields, subscription: subscriptionSchema.optional() }).strict();
212
+ var accountPatchSchema = z2.object({
182
213
  ...accountFields,
183
214
  label: accountFields.label.optional(),
184
215
  subscription: subscriptionSchema.nullable().optional()
185
216
  }).strict();
186
- var routesUpdateSchema = z.object({ routes: z.array(routeSchema).max(200), defaultRoute: z.string().min(1).optional() }).strict().superRefine((body, ctx) => {
217
+ var routesUpdateSchema = z2.object({ routes: z2.array(routeSchema).max(200), defaultRoute: z2.string().min(1).optional() }).strict().superRefine((body, ctx) => {
187
218
  const seen = /* @__PURE__ */ new Set();
188
219
  body.routes.forEach((route, index) => {
189
220
  if (seen.has(route.name)) {
@@ -204,14 +235,14 @@ var routesUpdateSchema = z.object({ routes: z.array(routeSchema).max(200), defau
204
235
  }
205
236
  });
206
237
  var taskText = {
207
- title: z.string().trim().min(1).max(300),
208
- description: z.string().max(5e4),
209
- acceptance: z.array(z.string().trim().min(1).max(2e3)).max(100),
210
- notes: z.string().max(5e4),
211
- dependsOn: z.array(z.string().min(1)).max(500),
212
- priority: z.number().finite()
238
+ title: z2.string().trim().min(1).max(300),
239
+ description: z2.string().max(5e4),
240
+ acceptance: z2.array(z2.string().trim().min(1).max(2e3)).max(100),
241
+ notes: z2.string().max(5e4),
242
+ dependsOn: z2.array(z2.string().min(1)).max(500),
243
+ priority: z2.number().finite()
213
244
  };
214
- var taskCreateSchema = z.object({
245
+ var taskCreateSchema = z2.object({
215
246
  id: taskIdSchema,
216
247
  title: taskText.title,
217
248
  description: taskText.description.optional(),
@@ -219,30 +250,30 @@ var taskCreateSchema = z.object({
219
250
  priority: taskText.priority.optional(),
220
251
  acceptance: taskText.acceptance.optional()
221
252
  }).strict();
222
- var taskPatchSchema = z.object({
253
+ var taskPatchSchema = z2.object({
223
254
  title: taskText.title.optional(),
224
255
  description: taskText.description.nullable().optional(),
225
256
  dependsOn: taskText.dependsOn.optional(),
226
257
  priority: taskText.priority.nullable().optional(),
227
258
  acceptance: taskText.acceptance.nullable().optional(),
228
259
  notes: taskText.notes.nullable().optional(),
229
- status: z.enum(TASK_STATUSES).optional()
260
+ status: z2.enum(TASK_STATUSES).optional()
230
261
  }).strict().refine((patch) => Object.values(patch).some((v) => v !== void 0), {
231
262
  message: "send at least one field to change"
232
263
  });
233
- var runnerStartSchema = z.object({ taskId: z.string().min(1).optional() }).strict();
234
- var goalStartSchema = z.object({
235
- objective: z.string().trim().min(1).max(4e3),
236
- maxRounds: z.number().int().min(1).max(20).optional(),
237
- maxTasks: z.number().int().min(1).max(100).optional(),
238
- maxTokens: z.number().int().min(1e3).optional(),
239
- dryRun: z.boolean().optional()
264
+ var runnerStartSchema = z2.object({ taskId: z2.string().min(1).optional() }).strict();
265
+ var goalStartSchema = z2.object({
266
+ objective: z2.string().trim().min(1).max(4e3),
267
+ maxRounds: z2.number().int().min(1).max(20).optional(),
268
+ maxTasks: z2.number().int().min(1).max(100).optional(),
269
+ maxTokens: z2.number().int().min(1e3).optional(),
270
+ dryRun: z2.boolean().optional()
240
271
  }).strict();
241
- var timeseriesQuerySchema = z.object({
242
- minutes: z.coerce.number().int().min(1).max(7 * 24 * 60).default(60),
243
- bucketSec: z.coerce.number().int().min(1).max(86400).default(60)
272
+ var timeseriesQuerySchema = z2.object({
273
+ minutes: z2.coerce.number().int().min(1).max(7 * 24 * 60).default(60),
274
+ bucketSec: z2.coerce.number().int().min(1).max(86400).default(60)
244
275
  });
245
- var limitQuerySchema = (fallback, max = 1e3) => z.object({ limit: z.coerce.number().int().min(1).max(max).default(fallback) });
276
+ var limitQuerySchema = (fallback, max = 1e3) => z2.object({ limit: z2.coerce.number().int().min(1).max(max).default(fallback) });
246
277
  function registerGoalRoutes(app, engine, options) {
247
278
  const { audit } = engine;
248
279
  const unavailable = (reply) => sendApiError(
@@ -284,7 +315,7 @@ function registerGoalRoutes(app, engine, options) {
284
315
  app.post("/api/goals", async (request, reply) => {
285
316
  const parsed = goalStartSchema.safeParse(request.body);
286
317
  if (!parsed.success) {
287
- return sendApiError(reply, 400, "invalid_body", z2.prettifyError(parsed.error));
318
+ return sendApiError(reply, 400, "invalid_body", z3.prettifyError(parsed.error));
288
319
  }
289
320
  const goals = control(reply);
290
321
  if (!goals) return reply;
@@ -388,7 +419,7 @@ function registerTaskRoutes(app, engine, options) {
388
419
  app.post("/api/tasks", async (request, reply) => {
389
420
  const parsed = taskCreateSchema.safeParse(request.body);
390
421
  if (!parsed.success) {
391
- return sendApiError(reply, 400, "invalid_body", z3.prettifyError(parsed.error));
422
+ return sendApiError(reply, 400, "invalid_body", z4.prettifyError(parsed.error));
392
423
  }
393
424
  const brain = writable(reply);
394
425
  if (isReply(brain)) return brain;
@@ -403,7 +434,7 @@ function registerTaskRoutes(app, engine, options) {
403
434
  app.patch("/api/tasks/:id", async (request, reply) => {
404
435
  const parsed = taskPatchSchema.safeParse(request.body);
405
436
  if (!parsed.success) {
406
- return sendApiError(reply, 400, "invalid_body", z3.prettifyError(parsed.error));
437
+ return sendApiError(reply, 400, "invalid_body", z4.prettifyError(parsed.error));
407
438
  }
408
439
  const brain = writable(reply);
409
440
  if (isReply(brain)) return brain;
@@ -446,7 +477,7 @@ function isReply(value) {
446
477
  var EMPTY_GRAPH = { version: 1, tasks: [] };
447
478
  var IDLE = { state: "idle" };
448
479
  function invalid(reply, error) {
449
- return sendApiError(reply, 400, "invalid_body", z4.prettifyError(error));
480
+ return sendApiError(reply, 400, "invalid_body", z5.prettifyError(error));
450
481
  }
451
482
  function registerApiRoutes(app, engine, options) {
452
483
  const { accounts, events, audit, quota, keyring } = engine;
@@ -748,7 +779,7 @@ function registerAuthRoutes(app, login) {
748
779
  reply.header("Cache-Control", "no-store").header("Referrer-Policy", "no-referrer");
749
780
  if (!login)
750
781
  return sendApiError(reply, 503, "login_not_configured", "Sign-in is not configured.");
751
- const parsed = z5.object({ state: z5.string().min(1).max(200), code: z5.string().min(1).max(4096) }).safeParse(request.query);
782
+ const parsed = z6.object({ state: z6.string().min(1).max(200), code: z6.string().min(1).max(4096) }).safeParse(request.query);
752
783
  const binding = authCookie(request, "dc_login");
753
784
  if (!parsed.success || !binding)
754
785
  return reply.redirect(`${login.options.origin}/#/login?error=signin_failed`);
@@ -848,7 +879,7 @@ function registerV1Routes(app, engine) {
848
879
  const parsed = chatRequestSchema.safeParse(request.body);
849
880
  if (!parsed.success) {
850
881
  return sendOpenAIError(reply, 400, {
851
- message: z6.prettifyError(parsed.error),
882
+ message: z7.prettifyError(parsed.error),
852
883
  type: "invalid_request_error",
853
884
  code: "invalid_body"
854
885
  });
@@ -1095,6 +1126,7 @@ async function buildGateway(engine, options = {}) {
1095
1126
  });
1096
1127
  registerV1Routes(app, engine);
1097
1128
  registerAuthRoutes(app, login);
1129
+ registerAccountRoutes(app, options.account);
1098
1130
  registerApiRoutes(app, engine, options);
1099
1131
  registerEventRoutes(app, engine, options.heartbeatMs ?? 15e3);
1100
1132
  return app;
@@ -1633,18 +1665,25 @@ async function startRuntime(options) {
1633
1665
  ...projectRoot ? { projectRoot } : {},
1634
1666
  ...options.config ? { config: options.config } : {}
1635
1667
  });
1668
+ let account;
1636
1669
  try {
1637
1670
  const project = projectRoot ? wireProject(engine, projectRoot, { env: options.env }) : void 0;
1638
1671
  const wantDashboard = options.dashboard !== false && engine.config.server.dashboard;
1639
1672
  const dashboardDir = wantDashboard ? resolveDashboardDir(options.env) : void 0;
1640
1673
  const bindHost = options.host ?? engine.config.server.host;
1641
1674
  const loginLauncher = LOOPBACK_HOSTS.has(bindHost) ? {
1642
- launch: async (account) => ({
1643
- launched: await launchLoginTerminal(account.id, { env: options.env })
1675
+ launch: async (account2) => ({
1676
+ launched: await launchLoginTerminal(account2.id, { env: options.env })
1644
1677
  })
1645
1678
  } : void 0;
1679
+ account = LOOPBACK_HOSTS.has(bindHost) ? createAccountControl({
1680
+ home: options.home,
1681
+ env: options.env,
1682
+ open: (url) => openBrowser(url)
1683
+ }) : void 0;
1646
1684
  const app = await startGateway(engine, {
1647
1685
  logger: options.logger ?? false,
1686
+ ...account ? { account } : {},
1648
1687
  ...options.loginLauncher ?? loginLauncher ? { loginLauncher: options.loginLauncher ?? loginLauncher } : {},
1649
1688
  ...dashboardDir ? { dashboardDir } : {},
1650
1689
  ...project ? { brain: project.source, runner: project.runner, goals: project.goals } : {},
@@ -1674,6 +1713,7 @@ async function startRuntime(options) {
1674
1713
  const active = goals?.activeGoalId;
1675
1714
  if (active) await goals?.cancel(active);
1676
1715
  await runner?.stop();
1716
+ await account?.close();
1677
1717
  await app.close();
1678
1718
  } finally {
1679
1719
  engine.close();
@@ -1681,6 +1721,7 @@ async function startRuntime(options) {
1681
1721
  }
1682
1722
  };
1683
1723
  } catch (err) {
1724
+ await account?.close();
1684
1725
  engine.close();
1685
1726
  throw err;
1686
1727
  }
@@ -1703,4 +1744,4 @@ export {
1703
1744
  resolveProjectRoot,
1704
1745
  startRuntime
1705
1746
  };
1706
- //# sourceMappingURL=chunk-JFWMLYV7.js.map
1747
+ //# sourceMappingURL=chunk-KUY3QHGS.js.map