@dmrlzz/davecode 0.0.0-stage → 0.4.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.
- package/LICENSE +21 -0
- package/README.md +34 -2
- package/bin/davecode.js +2 -0
- package/dist/ChatApp-BO7QMBWD.js +1045 -0
- package/dist/ChatApp-BO7QMBWD.js.map +1 -0
- package/dist/RunnerView-A7JSPUW5.js +18 -0
- package/dist/RunnerView-A7JSPUW5.js.map +1 -0
- package/dist/account-UGNLJPQ4.js +23 -0
- package/dist/account-UGNLJPQ4.js.map +1 -0
- package/dist/accounts-NM7IWP53.js +37 -0
- package/dist/accounts-NM7IWP53.js.map +1 -0
- package/dist/budget-INAW6FXD.js +115 -0
- package/dist/budget-INAW6FXD.js.map +1 -0
- package/dist/chat-U4QJQ7M6.js +359 -0
- package/dist/chat-U4QJQ7M6.js.map +1 -0
- package/dist/chunk-3Q2KCXLN.js +9608 -0
- package/dist/chunk-3Q2KCXLN.js.map +1 -0
- package/dist/chunk-5LKOJBUL.js +20 -0
- package/dist/chunk-5LKOJBUL.js.map +1 -0
- package/dist/chunk-7XVXEO7U.js +23 -0
- package/dist/chunk-7XVXEO7U.js.map +1 -0
- package/dist/chunk-CWBVNS3L.js +731 -0
- package/dist/chunk-CWBVNS3L.js.map +1 -0
- package/dist/chunk-DZ2FDABF.js +1614 -0
- package/dist/chunk-DZ2FDABF.js.map +1 -0
- package/dist/chunk-FRFSJMN4.js +79 -0
- package/dist/chunk-FRFSJMN4.js.map +1 -0
- package/dist/chunk-GE22VBCY.js +200 -0
- package/dist/chunk-GE22VBCY.js.map +1 -0
- package/dist/chunk-GHDOUII5.js +144 -0
- package/dist/chunk-GHDOUII5.js.map +1 -0
- package/dist/chunk-GZJ6JSO7.js +213 -0
- package/dist/chunk-GZJ6JSO7.js.map +1 -0
- package/dist/chunk-IC3TDONV.js +12 -0
- package/dist/chunk-IC3TDONV.js.map +1 -0
- package/dist/chunk-MC7RIPL7.js +116 -0
- package/dist/chunk-MC7RIPL7.js.map +1 -0
- package/dist/chunk-MOK5D3XJ.js +114 -0
- package/dist/chunk-MOK5D3XJ.js.map +1 -0
- package/dist/chunk-PGPXE6LF.js +20 -0
- package/dist/chunk-PGPXE6LF.js.map +1 -0
- package/dist/chunk-QA3O6RA2.js +81 -0
- package/dist/chunk-QA3O6RA2.js.map +1 -0
- package/dist/chunk-ROIUTNQV.js +20 -0
- package/dist/chunk-ROIUTNQV.js.map +1 -0
- package/dist/chunk-SF5ZEAP4.js +105 -0
- package/dist/chunk-SF5ZEAP4.js.map +1 -0
- package/dist/chunk-Z6NQDJEN.js +180 -0
- package/dist/chunk-Z6NQDJEN.js.map +1 -0
- package/dist/config-YEAPBMBX.js +158 -0
- package/dist/config-YEAPBMBX.js.map +1 -0
- package/dist/dashboard/assets/Accounts-CeVyX4a3.js +1 -0
- package/dist/dashboard/assets/Badge-CGY6DgGx.js +55 -0
- package/dist/dashboard/assets/Callout-DauCk_cW.js +1 -0
- package/dist/dashboard/assets/Field-OXMnT9Tb.js +1 -0
- package/dist/dashboard/assets/Goals-DH-KDh8M.js +1 -0
- package/dist/dashboard/assets/Library-C15szLjZ.js +1 -0
- package/dist/dashboard/assets/Markdown-Xhao3sgL.js +69 -0
- package/dist/dashboard/assets/Panel-Du40pM7n.js +1 -0
- package/dist/dashboard/assets/RoutesPage-Cj1OelGO.js +4 -0
- package/dist/dashboard/assets/Runner-BK1-ZF7h.js +1 -0
- package/dist/dashboard/assets/Settings-C1nzQQhe.js +1 -0
- package/dist/dashboard/assets/Switch-bwtIJ00H.js +1 -0
- package/dist/dashboard/assets/Tasks-x4s8IiPG.js +2 -0
- package/dist/dashboard/assets/Traffic-CV7TaTid.js +1 -0
- package/dist/dashboard/assets/Workspace-DAL-MBc0.js +1 -0
- package/dist/dashboard/assets/arrow-up-goM9r-I9.js +1 -0
- package/dist/dashboard/assets/forms-DtpfI65-.js +1 -0
- package/dist/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/dashboard/assets/index-BCi-Vuvh.js +104 -0
- package/dist/dashboard/assets/index-CcJFhc98.css +2 -0
- package/dist/dashboard/assets/task-errors-B2pgjUGN.js +1 -0
- package/dist/dashboard/assets/trash-BVojETtF.js +1 -0
- package/dist/dashboard/favicon.svg +1 -0
- package/dist/dashboard/github.svg +1 -0
- package/dist/dashboard/index.html +31 -0
- package/dist/dist-RRQGUBL3.js +379 -0
- package/dist/dist-RRQGUBL3.js.map +1 -0
- package/dist/doctor-MQYK44S2.js +279 -0
- package/dist/doctor-MQYK44S2.js.map +1 -0
- package/dist/goal-6NUX4AIT.js +302 -0
- package/dist/goal-6NUX4AIT.js.map +1 -0
- package/dist/index.js +399 -0
- package/dist/index.js.map +1 -0
- package/dist/init-AXYIWAB3.js +194 -0
- package/dist/init-AXYIWAB3.js.map +1 -0
- package/dist/prompts-TZ7UC42M.js +219 -0
- package/dist/prompts-TZ7UC42M.js.map +1 -0
- package/dist/run-QRTLHFB3.js +376 -0
- package/dist/run-QRTLHFB3.js.map +1 -0
- package/dist/runner-state-QFU3LJVJ.js +17 -0
- package/dist/runner-state-QFU3LJVJ.js.map +1 -0
- package/dist/runtime.js +18 -0
- package/dist/runtime.js.map +1 -0
- package/dist/start-O5I5TSO2.js +285 -0
- package/dist/start-O5I5TSO2.js.map +1 -0
- package/dist/status-LEA3RVUX.js +25 -0
- package/dist/status-LEA3RVUX.js.map +1 -0
- package/dist/tasks-XOTVICVZ.js +184 -0
- package/dist/tasks-XOTVICVZ.js.map +1 -0
- package/package.json +73 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts","../../server/src/errors.ts","../../server/src/gateway.ts","../../server/src/routes/api.ts","../../server/src/schemas.ts","../../server/src/routes/goals.ts","../../server/src/routes/tasks.ts","../../server/src/routes/auth.ts","../../server/src/routes/events.ts","../../server/src/routes/v1.ts","../src/sse.ts","../src/client.ts","../src/lib/terminal.ts","../src/lib/proc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { access } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport type { AddressInfo } from 'node:net';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type Account,\n type AutonomousRunner,\n createEngine,\n createGoalService,\n createRunner,\n type DaveConfigInput,\n type Engine,\n type GoalService,\n ProjectBrain,\n ProjectBrainSource,\n WorkspaceLock,\n} from '@davecode/core';\nimport { type GatewayOptions, type ProjectInfo, startGateway } from '@davecode/server';\nimport { connectHost } from './client';\nimport { launchLoginTerminal } from './lib/terminal';\n\nexport { launchLoginTerminal } from './lib/terminal';\n\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);\n\n/**\n * Process wiring shared by `davecode start`, `chat` (in-process gateway) and `run`:\n * engine + project brain + autonomous runner + gateway.\n */\n\n// ---------------------------------------------------------------------------\n// Project discovery\n// ---------------------------------------------------------------------------\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Walk up from `startDir` to the first directory with `.davecode/` or `.git`. Unlike\n * `findProjectRoot` from core, a `.davecode` directory that is the DaveCode home itself\n * (`~/.davecode`) never marks a project, so running in `~/code/foo` cannot adopt `~`.\n */\nexport async function detectProjectRoot(\n startDir: string,\n home: string,\n): Promise<string | undefined> {\n const homeDir = resolve(home);\n let dir = resolve(startDir);\n for (;;) {\n const brainDir = join(dir, '.davecode');\n if (await exists(join(dir, '.git'))) return dir;\n if (resolve(brainDir) !== homeDir && (await exists(brainDir))) return dir;\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Project brain + runner\n// ---------------------------------------------------------------------------\n\nexport interface ProjectWiring {\n brain: ProjectBrain;\n /** Read-only view for `/api/tasks` and `/api/brain` (missing files read as empty). */\n source: ProjectBrainSource;\n /** The Phase 4 autonomous runner for this repository; satisfies `RunnerControl`. */\n runner: AutonomousRunner;\n /** Goal mode for this repository; satisfies `GoalControl`. */\n goals: GoalService;\n /** Held by the runner (`runner`) or goal mode (`goal`) while they touch the working tree. */\n lock: WorkspaceLock;\n}\n\n/**\n * Open the project brain of `root` on the engine's event bus and build its autonomous runner\n * and goal service, which share one workspace lock. The runner is created even before\n * `davecode init`: `start()`/`runOnce()` then reject with a `RunnerError` (`no_brain`,\n * `not_a_repo`...) that the CLI and the dashboard explain.\n */\nexport function wireProject(\n engine: Engine,\n root: string,\n options: { env?: NodeJS.ProcessEnv; name?: string } = {},\n): ProjectWiring {\n const brain = new ProjectBrain(root, { events: engine.events });\n const lock = new WorkspaceLock();\n const budgetContext = () => engine.budget.context();\n return {\n brain,\n source: new ProjectBrainSource(brain, options.name),\n runner: createRunner(engine, {\n brain,\n lock: { lock, owner: 'runner' },\n contextExtras: budgetContext,\n ...(options.env ? { env: options.env } : {}),\n }),\n goals: createGoalService(engine, { brain, lock, budgetContext }),\n lock,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dashboard\n// ---------------------------------------------------------------------------\n\nfunction isDashboard(dir: string | undefined): dir is string {\n return dir !== undefined && existsSync(join(dir, 'index.html'));\n}\n\n/**\n * Locate the built dashboard (`index.html` inside), in order: `DAVECODE_DASHBOARD_DIR`, a\n * `dashboard/` folder shipped next to this bundle, the monorepo layout (`packages/ui/dist`, which\n * resolves the same from `packages/cli/dist/*.js` and `packages/cli/src/runtime.ts`) and finally\n * an installed `@davecode/ui` package's `dist/` (`lookupPackage: false` skips that step).\n */\nexport function resolveDashboardDir(\n env: NodeJS.ProcessEnv = process.env,\n moduleUrl: string = import.meta.url,\n lookupPackage = true,\n): string | undefined {\n const candidates: Array<() => string | undefined> = [\n () => (env.DAVECODE_DASHBOARD_DIR ? resolve(env.DAVECODE_DASHBOARD_DIR) : undefined),\n () => fileURLToPath(new URL('./dashboard', moduleUrl)),\n () => fileURLToPath(new URL('../../ui/dist', moduleUrl)),\n () => {\n if (!lookupPackage) return undefined;\n try {\n const pkg = createRequire(moduleUrl).resolve('@davecode/ui/package.json');\n return join(dirname(pkg), 'dist');\n } catch {\n return undefined;\n }\n },\n ];\n for (const candidate of candidates) {\n const dir = candidate();\n if (isDashboard(dir)) return dir;\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Runtime\n// ---------------------------------------------------------------------------\n\nexport interface RuntimeOptions {\n home: string;\n env: NodeJS.ProcessEnv;\n cwd: string;\n /** Repository to serve the project brain for (default: detected from `cwd`). */\n projectDir?: string;\n /** Skip project detection entirely (no brain, no runner). */\n noProject?: boolean;\n host?: string;\n /** `0` picks a free port. */\n port?: number;\n /** Serve the dashboard when it is built (default true, and `server.dashboard` must be on). */\n dashboard?: boolean;\n logger?: GatewayOptions['logger'];\n /** Desktop hosts can launch official sign-in using their bundled CLI and Node runtime. */\n loginLauncher?: GatewayOptions['loginLauncher'];\n /** Programmatic config overrides. */\n config?: DaveConfigInput;\n /** Inject a pre-built engine (tests). It is closed with the runtime. */\n engine?: Engine;\n}\n\nexport interface Runtime {\n engine: Engine;\n brain: ProjectBrain | undefined;\n project: ProjectInfo | undefined;\n runner: AutonomousRunner | undefined;\n goals: GoalService | undefined;\n /** Address to connect to, e.g. `http://127.0.0.1:4040`. */\n url: string;\n host: string;\n port: number;\n /** Absolute dashboard directory when it is being served. */\n dashboardDir: string | undefined;\n /** Stop the runner (safely parking any task), the gateway and the engine. */\n close(): Promise<void>;\n}\n\n/** Resolve the project root for `projectDir` (relative to `cwd`) or detect it from `cwd`. */\nexport async function resolveProjectRoot(\n options: Pick<RuntimeOptions, 'cwd' | 'home' | 'projectDir' | 'noProject'>,\n): Promise<string | undefined> {\n if (options.noProject) return undefined;\n return options.projectDir\n ? resolve(options.cwd, options.projectDir)\n : detectProjectRoot(options.cwd, options.home);\n}\n\n/** Create the engine, open the project brain and runner, and start the gateway. */\nexport async function startRuntime(options: RuntimeOptions): Promise<Runtime> {\n const projectRoot = await resolveProjectRoot(options);\n const engine =\n options.engine ??\n createEngine({\n home: options.home,\n env: options.env,\n ...(projectRoot ? { projectRoot } : {}),\n ...(options.config ? { config: options.config } : {}),\n });\n\n try {\n const project = projectRoot\n ? wireProject(engine, projectRoot, { env: options.env })\n : undefined;\n const wantDashboard = options.dashboard !== false && engine.config.server.dashboard;\n const dashboardDir = wantDashboard ? resolveDashboardDir(options.env) : undefined;\n const bindHost = options.host ?? engine.config.server.host;\n // Only a gateway on loopback shares a screen with its user; elsewhere a login window\n // would open on a machine nobody is looking at.\n const loginLauncher = LOOPBACK_HOSTS.has(bindHost)\n ? {\n launch: async (account: Account) => ({\n launched: await launchLoginTerminal(account.id, { env: options.env }),\n }),\n }\n : undefined;\n\n const app = await startGateway(engine, {\n logger: options.logger ?? false,\n ...((options.loginLauncher ?? loginLauncher)\n ? { loginLauncher: options.loginLauncher ?? loginLauncher }\n : {}),\n ...(dashboardDir ? { dashboardDir } : {}),\n ...(project ? { brain: project.source, runner: project.runner, goals: project.goals } : {}),\n ...(options.host !== undefined ? { host: options.host } : {}),\n ...(options.port !== undefined ? { port: options.port } : {}),\n });\n\n const address = app.server.address() as AddressInfo | null;\n const host = options.host ?? engine.config.server.host;\n const port = address?.port ?? options.port ?? engine.config.server.port;\n\n let closed = false;\n const runner = project?.runner;\n const goals = project?.goals;\n return {\n engine,\n brain: project?.brain,\n project: project?.source.project(),\n runner,\n goals,\n url: `http://${connectHost(host)}:${port}`,\n host,\n port,\n dashboardDir,\n async close() {\n if (closed) return;\n closed = true;\n try {\n const active = goals?.activeGoalId;\n if (active) await goals?.cancel(active);\n await runner?.stop();\n await app.close();\n } finally {\n engine.close();\n }\n },\n };\n } catch (err) {\n engine.close();\n throw err;\n }\n}\n","import { ProviderError, RouterError } from '@davecode/core';\nimport type { FastifyReply } from 'fastify';\n\n/** `/api/*` error envelope. */\nexport function sendApiError(\n reply: FastifyReply,\n status: number,\n code: string,\n message: string,\n extra: Record<string, unknown> = {},\n): FastifyReply {\n return reply.code(status).send({ error: { message, code, ...extra } });\n}\n\n/** OpenAI-compatible error body used on `/v1/*` (also inside SSE error frames). */\nexport interface OpenAIErrorBody {\n message: string;\n type: string;\n code: string | null;\n}\n\nexport function sendOpenAIError(\n reply: FastifyReply,\n status: number,\n body: OpenAIErrorBody,\n): FastifyReply {\n return reply.code(status).send({ error: body });\n}\n\nfunction openAIType(status: number): string {\n if (status === 401) return 'authentication_error';\n if (status === 404 || status === 400 || status === 422) return 'invalid_request_error';\n if (status === 429) return 'rate_limit_error';\n return 'api_error';\n}\n\n/** Map a routing failure to an HTTP status and OpenAI error body. */\nexport function describeRoutingError(err: unknown): { status: number; body: OpenAIErrorBody } {\n if (err instanceof RouterError) {\n return {\n status: err.status,\n body: { message: err.message, type: openAIType(err.status), code: err.code },\n };\n }\n if (err instanceof ProviderError) {\n let status: number;\n let code: string = err.kind;\n switch (err.kind) {\n case 'bad_request':\n status = 400;\n break;\n case 'context_length':\n status = 400;\n code = 'context_length_exceeded';\n break;\n case 'rate_limit':\n case 'quota_exhausted':\n status = 429;\n break;\n case 'auth':\n // The client's request is fine; DaveCode's upstream credentials are not.\n status = 502;\n code = 'upstream_auth_error';\n break;\n default:\n status = 502;\n }\n return { status, body: { message: err.message, type: openAIType(status), code } };\n }\n return {\n status: 500,\n body: { message: 'Internal gateway error', type: 'api_error', code: 'internal_error' },\n };\n}\n","import { timingSafeEqual } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { type Engine, loginFromEnv } from '@davecode/core';\nimport cors from '@fastify/cors';\nimport fastifyStatic from '@fastify/static';\nimport Fastify, { type FastifyInstance, type FastifyRequest } from 'fastify';\nimport { sendApiError, sendOpenAIError } from './errors';\nimport type { GatewayOptions, StartGatewayOptions } from './options';\nimport { registerApiRoutes } from './routes/api';\nimport { authCookie, registerAuthRoutes } from './routes/auth';\nimport { registerEventRoutes } from './routes/events';\nimport { registerV1Routes } from './routes/v1';\n\n/** Origins allowed to call the gateway from a browser (the Vite dev server). */\nexport const DEFAULT_CORS_ORIGINS = ['http://localhost:5173', 'http://127.0.0.1:5173'];\n\nconst EXPOSED_HEADERS = [\n 'x-davecode-request-id',\n 'x-davecode-account',\n 'x-davecode-provider',\n 'x-davecode-failovers',\n];\n\ntype Surface = 'v1' | 'api' | 'other';\n\nfunction pathOf(url: string): string {\n const q = url.indexOf('?');\n return q === -1 ? url : url.slice(0, q);\n}\n\nfunction surfaceOf(url: string): Surface {\n const path = pathOf(url);\n if (path === '/v1' || path.startsWith('/v1/')) return 'v1';\n if (path === '/api' || path.startsWith('/api/')) return 'api';\n return 'other';\n}\n\n/** Hide `?token=` values from logs. */\nexport function redactUrl(url: string): string {\n return url.replace(/([?&](?:token|code|state|error_description)=)[^&]*/gi, '$1[redacted]');\n}\n\nfunction safeEqual(a: string, b: string): boolean {\n const left = Buffer.from(a);\n const right = Buffer.from(b);\n return left.length === right.length && timingSafeEqual(left, right);\n}\n\nfunction bearer(request: FastifyRequest): string | undefined {\n const header = request.headers.authorization;\n if (!header) return undefined;\n const match = /^Bearer\\s+(.+)$/i.exec(header.trim());\n return match?.[1];\n}\n\nfunction loggerOption(logger: GatewayOptions['logger']) {\n if (!logger) return false;\n return {\n ...(typeof logger === 'object' ? logger : {}),\n serializers: {\n req(req: FastifyRequest) {\n return { method: req.method, url: redactUrl(req.url), remoteAddress: req.ip };\n },\n },\n };\n}\n\n/**\n * Build the DaveCode gateway (OpenAI-compatible `/v1`, dashboard `/api`, SSE events and the\n * optional dashboard). The instance is not listening; use `app.inject()` or `startGateway()`.\n */\nexport async function buildGateway(\n engine: Engine,\n options: GatewayOptions = {},\n): Promise<FastifyInstance> {\n const app = Fastify({ logger: loggerOption(options.logger), bodyLimit: 10 * 1024 * 1024 });\n const login = options.login === false ? undefined : (options.login ?? loginFromEnv(process.env));\n if (login) app.addHook('onClose', async () => login.clear());\n\n await app.register(cors, {\n origin: [...DEFAULT_CORS_ORIGINS, ...(options.corsOrigins ?? [])],\n methods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['content-type', 'authorization'],\n exposedHeaders: EXPOSED_HEADERS,\n });\n\n const token = engine.config.server.authToken;\n if (token || login) {\n app.addHook('onRequest', async (request, reply) => {\n if (request.method === 'OPTIONS') return;\n const surface = surfaceOf(request.url);\n if (surface === 'other') return;\n if (pathOf(request.url).startsWith('/api/auth/')) return;\n let provided = bearer(request);\n if (!provided && pathOf(request.url) === '/api/events') {\n const query = request.query as Record<string, unknown> | undefined;\n if (typeof query?.token === 'string') provided = query.token;\n }\n if (provided && token && safeEqual(provided, token)) return;\n if (surface === 'api' && login?.user(authCookie(request, 'dc_session'))) {\n if (\n !['GET', 'HEAD'].includes(request.method) &&\n request.headers.origin !== login.options.origin\n ) {\n return sendApiError(\n reply,\n 403,\n 'invalid_origin',\n 'Open DaveCode from its configured origin.',\n );\n }\n return;\n }\n const message = 'Missing or invalid bearer token';\n if (surface === 'v1') {\n return sendOpenAIError(reply, 401, {\n message,\n type: 'authentication_error',\n code: 'invalid_api_key',\n });\n }\n return sendApiError(reply, 401, 'unauthorized', message);\n });\n }\n\n // Accept `Content-Type: application/json` with an empty body (clients that always send the\n // header, e.g. `POST /api/runner/start` or `DELETE`): the body is simply undefined.\n app.addContentTypeParser('application/json', { parseAs: 'string' }, (_request, body, done) => {\n const text = typeof body === 'string' ? body : body.toString('utf8');\n if (text.trim() === '') return done(null, undefined);\n try {\n done(null, JSON.parse(text));\n } catch (err) {\n const error = Object.assign(err instanceof Error ? err : new Error('Invalid JSON'), {\n statusCode: 400,\n });\n done(error, undefined);\n }\n });\n\n app.setErrorHandler((error, request, reply) => {\n const status =\n typeof (error as { statusCode?: unknown }).statusCode === 'number'\n ? (error as { statusCode: number }).statusCode\n : 500;\n const message =\n status >= 500 ? 'Internal gateway error' : error instanceof Error ? error.message : 'Error';\n if (status >= 500) request.log.error({ err: error }, 'request failed');\n if (surfaceOf(request.url) === 'v1') {\n return sendOpenAIError(reply, status, {\n message,\n type: status >= 500 ? 'api_error' : 'invalid_request_error',\n code: status >= 500 ? 'internal_error' : 'bad_request',\n });\n }\n return sendApiError(reply, status, status >= 500 ? 'internal_error' : 'bad_request', message);\n });\n\n const dashboardDir =\n engine.config.server.dashboard && options.dashboardDir && existsSync(options.dashboardDir)\n ? resolve(options.dashboardDir)\n : undefined;\n if (dashboardDir) {\n await app.register(fastifyStatic, { root: dashboardDir });\n }\n\n app.setNotFoundHandler((request, reply) => {\n const surface = surfaceOf(request.url);\n const message = `Unknown endpoint ${request.method} ${pathOf(request.url)}`;\n if (surface === 'v1') {\n return sendOpenAIError(reply, 404, {\n message,\n type: 'invalid_request_error',\n code: 'not_found',\n });\n }\n if (surface === 'api') return sendApiError(reply, 404, 'not_found', message);\n if (dashboardDir && (request.method === 'GET' || request.method === 'HEAD')) {\n // SPA fallback: client-side routes are served by index.html.\n return reply.type('text/html; charset=utf-8').sendFile('index.html');\n }\n return reply.code(404).send({ error: { message, code: 'not_found' } });\n });\n\n registerV1Routes(app, engine);\n registerAuthRoutes(app, login);\n registerApiRoutes(app, engine, options);\n registerEventRoutes(app, engine, options.heartbeatMs ?? 15_000);\n\n return app;\n}\n\nconst LOOPBACK = new Set(['127.0.0.1', '::1', 'localhost']);\n\n/** Build and listen on `server.host:server.port` (or the overrides). */\nexport async function startGateway(\n engine: Engine,\n options: StartGatewayOptions = {},\n): Promise<FastifyInstance> {\n const app = await buildGateway(engine, options);\n const host = options.host ?? engine.config.server.host;\n const port = options.port ?? engine.config.server.port;\n if (!engine.config.server.authToken && !LOOPBACK.has(host)) {\n app.log.warn(\n `Listening on ${host} without server.authToken: anyone on the network can use your accounts.`,\n );\n }\n await app.listen({ host, port });\n return app;\n}\n","import {\n type Account,\n type AccountUpdateInput,\n ConfigError,\n type ConnectResult,\n connectionMethod,\n type Engine,\n loginCommandFor,\n type RunnerStatus,\n type TaskGraph,\n VERSION,\n} from '@davecode/core';\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';\nimport { z } from 'zod';\nimport { sendApiError } from '../errors';\nimport type { GatewayOptions, RunnerControl } from '../options';\nimport {\n accountCreateSchema,\n accountPatchSchema,\n limitQuerySchema,\n routesUpdateSchema,\n runnerStartSchema,\n timeseriesQuerySchema,\n} from '../schemas';\nimport { registerGoalRoutes } from './goals';\nimport { registerTaskRoutes } from './tasks';\n\nconst EMPTY_GRAPH: TaskGraph = { version: 1, tasks: [] };\nconst IDLE: RunnerStatus = { state: 'idle' };\n\nfunction invalid(reply: FastifyReply, error: z.ZodError): FastifyReply {\n return sendApiError(reply, 400, 'invalid_body', z.prettifyError(error));\n}\n\n/** Dashboard REST API (`/api/*`, except the SSE stream). */\nexport function registerApiRoutes(\n app: FastifyInstance,\n engine: Engine,\n options: GatewayOptions,\n): void {\n const { accounts, events, audit, quota, keyring } = engine;\n const now = () => quota.clock();\n\n const emitAccount = (account: Account) => events.emit({ type: 'account.updated', account });\n\n app.get('/api/health', async () => ({\n status: 'ok',\n version: VERSION,\n uptimeSec: Math.max(0, Math.floor((now() - engine.startedAt) / 1000)),\n experimental: { ...engine.config.experimental },\n }));\n\n // --- accounts --------------------------------------------------------------\n\n app.get('/api/accounts', async () => ({ accounts: accounts.list() }));\n\n app.post('/api/accounts', async (request, reply) => {\n const parsed = accountCreateSchema.safeParse(request.body);\n if (!parsed.success) return invalid(reply, parsed.error);\n const { secret, ...input } = parsed.data;\n if (input.provider === 'gemini-web' && !engine.config.experimental.geminiWeb) {\n return sendApiError(\n reply,\n 400,\n 'experimental_disabled',\n 'gemini-web is experimental and may violate Google’s Terms of Service. Set experimental.geminiWeb to true to opt in.',\n );\n }\n const account = engine.db.transaction(() => {\n const created = accounts.create(input);\n if (secret !== undefined) keyring.set(created.id, secret);\n audit.record({\n actor: 'api',\n action: 'account.create',\n target: created.id,\n details: { provider: created.provider, label: created.label, hasSecret: !!secret },\n });\n // Re-read so the response reflects the stored secret (hasSecret).\n return accounts.get(created.id) ?? created;\n })();\n emitAccount(account);\n return reply.code(201).send({ account });\n });\n\n app.patch<{ Params: { id: string } }>('/api/accounts/:id', async (request, reply) => {\n const parsed = accountPatchSchema.safeParse(request.body);\n if (!parsed.success) return invalid(reply, parsed.error);\n const existing = accounts.get(request.params.id);\n if (!existing) return sendApiError(reply, 404, 'not_found', 'Account not found');\n\n const { secret, ...fields } = parsed.data;\n const patch: AccountUpdateInput = { ...fields };\n const enabled = fields.enabled ?? existing.enabled;\n if (!enabled) {\n patch.status = 'disabled';\n } else if (fields.enabled === true || secret !== undefined) {\n // Re-enabling or rotating credentials is a manual reset of error/cooldown state.\n patch.status = 'active';\n patch.cooldownUntil = null;\n patch.lastError = null;\n engine.router.breaker.reset(existing.id);\n engine.reported.forget(existing.id);\n }\n const account = engine.db.transaction(() => {\n // Store the secret first so the re-read account reports hasSecret correctly.\n if (secret !== undefined) keyring.set(existing.id, secret);\n const updated = accounts.update(existing.id, patch)!;\n audit.record({\n actor: 'api',\n action: 'account.update',\n target: existing.id,\n details: { fields: Object.keys(fields), secretChanged: secret !== undefined },\n });\n return updated;\n })();\n emitAccount(account);\n return { account };\n });\n\n app.delete<{ Params: { id: string } }>('/api/accounts/:id', async (request, reply) => {\n const { id } = request.params;\n if (!accounts.delete(id)) return sendApiError(reply, 404, 'not_found', 'Account not found');\n for (const cleanup of [() => engine.sandboxes.remove(id), () => engine.chromium.remove(id)]) {\n try {\n cleanup();\n } catch (err) {\n request.log.warn({ err, accountId: id }, 'failed to remove account directory');\n }\n }\n quota.forget(id);\n engine.reported.forget(id);\n engine.router.breaker.reset(id);\n audit.record({ actor: 'api', action: 'account.delete', target: id });\n events.emit({ type: 'account.removed', accountId: id });\n return reply.code(204).send();\n });\n\n // --- connect (official vendor login) ---------------------------------------\n\n app.get<{ Params: { id: string } }>('/api/accounts/:id/connection', async (request, reply) => {\n const account = accounts.get(request.params.id);\n if (!account) return sendApiError(reply, 404, 'not_found', 'Account not found');\n return { connection: await engine.connections.check(account) };\n });\n\n app.post<{ Params: { id: string } }>('/api/accounts/:id/connect', async (request, reply) => {\n // A JSON content type cannot be sent cross-site without a CORS preflight, so other web\n // pages cannot make the gateway open a login window.\n if (!request.headers['content-type']?.toLowerCase().startsWith('application/json')) {\n return sendApiError(reply, 415, 'json_required', 'Send Content-Type: application/json');\n }\n const account = accounts.get(request.params.id);\n if (!account) return sendApiError(reply, 404, 'not_found', 'Account not found');\n if (connectionMethod(account.provider) !== 'cli-login') {\n return sendApiError(\n reply,\n 400,\n 'not_cli_login',\n account.provider === 'gemini-web'\n ? 'gemini-web signs in through its own browser profile, not a CLI login'\n : 'This account connects with an API key: set it in the account settings',\n );\n }\n const command = loginCommandFor(account.id);\n const { launched } = options.loginLauncher\n ? await options.loginLauncher.launch(account)\n : { launched: false };\n audit.record({\n actor: 'api',\n action: 'account.connect',\n target: account.id,\n details: { launched },\n });\n const result: ConnectResult = {\n connection: await engine.connections.check(account),\n launched,\n command,\n message: launched\n ? 'A terminal opened on the gateway machine with the official sign-in. Finish it there; this page updates when the login is detected.'\n : `Run this in a terminal on the machine running the gateway: ${command}`,\n };\n return { connect: result };\n });\n\n // --- usage -----------------------------------------------------------------\n\n app.get('/api/usage', async () => ({\n usage: accounts.list().map((account) => quota.usage(account)),\n }));\n\n app.get('/api/budget', async () => ({ budget: engine.budget.snapshot() }));\n\n app.get('/api/usage/timeseries', async (request, reply) => {\n const parsed = timeseriesQuerySchema.safeParse(request.query);\n if (!parsed.success) return invalid(reply, parsed.error);\n const { minutes, bucketSec } = parsed.data;\n const until = now() + 1;\n const buckets = engine.usage.timeseries({\n since: until - minutes * 60_000,\n until,\n bucketMs: bucketSec * 1000,\n });\n return { buckets };\n });\n\n app.get('/api/requests', async (request, reply) => {\n const parsed = limitQuerySchema(100).safeParse(request.query);\n if (!parsed.success) return invalid(reply, parsed.error);\n return { requests: engine.usage.recent(parsed.data.limit) };\n });\n\n app.get('/api/logs', async (request, reply) => {\n const parsed = limitQuerySchema(200).safeParse(request.query);\n if (!parsed.success) return invalid(reply, parsed.error);\n return { events: events.recent(parsed.data.limit) };\n });\n\n // --- routes ----------------------------------------------------------------\n\n app.get('/api/routes', async () => ({\n defaultRoute: engine.config.routing.defaultRoute,\n routes: engine.config.routing.routes,\n }));\n\n app.put('/api/routes', async (request, reply) => {\n const parsed = routesUpdateSchema.safeParse(request.body);\n if (!parsed.success) return invalid(reply, parsed.error);\n const { routes, defaultRoute } = parsed.data;\n for (const route of routes) {\n for (const target of route.targets) {\n if (target.accountId !== undefined && !accounts.get(target.accountId)) {\n return sendApiError(\n reply,\n 400,\n 'unknown_account',\n `route \"${route.name}\" pins unknown account \"${target.accountId}\"`,\n );\n }\n }\n }\n let result: Awaited<ReturnType<Engine['updateRouting']>>;\n try {\n result = await engine.updateRouting({\n routes,\n ...(defaultRoute !== undefined ? { defaultRoute } : {}),\n });\n } catch (error) {\n // The stored config.json is unreadable or invalid: it is never overwritten.\n if (error instanceof ConfigError) {\n return sendApiError(reply, 409, 'config_invalid', error.message);\n }\n throw error;\n }\n audit.record({\n actor: 'api',\n action: 'routes.update',\n details: {\n routes: routes.map((r) => r.name),\n defaultRoute: engine.config.routing.defaultRoute,\n },\n });\n return {\n defaultRoute: engine.config.routing.defaultRoute,\n routes: engine.config.routing.routes,\n shadowedByProject: result.shadowedByProject,\n };\n });\n\n // --- project brain (Phase 3) -------------------------------------------------\n\n app.get('/api/tasks', async () => {\n const { brain } = options;\n if (!brain) return { project: null, graph: EMPTY_GRAPH };\n return { project: brain.project(), graph: await brain.graph() };\n });\n\n registerTaskRoutes(app, engine, options);\n registerGoalRoutes(app, engine, options);\n\n app.get('/api/brain', async () => {\n const { brain } = options;\n if (!brain) return { state: '', architecture: '' };\n const [state, architecture] = await Promise.all([brain.state(), brain.architecture()]);\n return { state, architecture };\n });\n\n // --- runner (Phase 4) --------------------------------------------------------\n\n app.get('/api/runner', async () => ({ status: options.runner?.status() ?? IDLE }));\n\n const control = (action: keyof Omit<RunnerControl, 'status'>) =>\n async function handler(request: FastifyRequest, reply: FastifyReply) {\n const { runner } = options;\n if (!runner) {\n return sendApiError(\n reply,\n 501,\n 'runner_unavailable',\n 'The autonomous runner is not available',\n );\n }\n let taskId: string | undefined;\n if (action === 'start' && request.body !== undefined && request.body !== null) {\n const parsed = runnerStartSchema.safeParse(request.body);\n if (!parsed.success) return invalid(reply, parsed.error);\n taskId = parsed.data.taskId;\n }\n try {\n if (action === 'start') await runner.start(taskId === undefined ? {} : { taskId });\n else await runner[action]();\n } catch (error) {\n // Runner refusals (RunnerError: dirty_worktree, no_brain, busy…) carry an HTTP status\n // and a machine-readable code; forward both so clients don't have to parse messages.\n const { statusCode, code } = error as { statusCode?: unknown; code?: unknown };\n if (typeof statusCode === 'number' && statusCode < 500 && typeof code === 'string') {\n return sendApiError(reply, statusCode, code, (error as Error).message);\n }\n throw error;\n }\n audit.record({\n actor: 'api',\n action: `runner.${action}`,\n ...(taskId !== undefined ? { details: { taskId } } : {}),\n });\n return { status: runner.status() };\n };\n app.post('/api/runner/start', control('start'));\n app.post('/api/runner/pause', control('pause'));\n app.post('/api/runner/stop', control('stop'));\n}\n","import {\n PROVIDER_KINDS,\n type ProviderKind,\n routeSchema,\n TASK_STATUSES,\n taskIdSchema,\n} from '@davecode/core';\nimport { z } from 'zod';\n\nconst providerKind = z.enum(PROVIDER_KINDS as [ProviderKind, ...ProviderKind[]]);\n\nconst contentPart = z.discriminatedUnion('type', [\n z.object({ type: z.literal('text'), text: z.string() }),\n z.object({\n type: z.literal('image_url'),\n image_url: z.object({\n url: z.string(),\n detail: z.enum(['auto', 'low', 'high']).optional(),\n }),\n }),\n]);\n\nconst toolCall = z.object({\n id: z.string(),\n type: z.literal('function'),\n function: z.object({ name: z.string(), arguments: z.string() }),\n});\n\nconst chatMessage = z.object({\n role: z.enum(['system', 'user', 'assistant', 'tool']),\n content: z.union([z.string(), z.array(contentPart), z.null()]),\n name: z.string().optional(),\n tool_call_id: z.string().optional(),\n tool_calls: z.array(toolCall).optional(),\n});\n\n/** `POST /v1/chat/completions` body. Unknown OpenAI parameters are accepted and dropped. */\nexport const chatRequestSchema = z.object({\n model: z.string().min(1),\n messages: z.array(chatMessage).min(1),\n stream: z.boolean().optional(),\n temperature: z.number().min(0).max(2).optional(),\n top_p: z.number().min(0).max(1).optional(),\n max_tokens: z.number().int().positive().optional(),\n stop: z.union([z.string(), z.array(z.string())]).optional(),\n tools: z\n .array(\n z.object({\n type: z.literal('function'),\n function: z.object({\n name: z.string(),\n description: z.string().optional(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n }),\n }),\n )\n .optional(),\n tool_choice: z\n .union([\n z.enum(['auto', 'none', 'required']),\n z.object({ type: z.literal('function'), function: z.object({ name: z.string() }) }),\n ])\n .optional(),\n user: z.string().optional(),\n});\n\nconst positiveInt = z.number().int().positive();\n\nconst quotaLimits = z\n .object({\n tpm: positiveInt.optional(),\n rpm: positiveInt.optional(),\n tokens5h: positiveInt.optional(),\n requests5h: positiveInt.optional(),\n tokensDaily: positiveInt.optional(),\n requestsDaily: positiveInt.optional(),\n })\n .strict();\n\n/** Keys that look like credentials must go in `secret`, never in plain `config`. */\nconst SECRET_KEY =\n /^(api[-_]?key|secret|client[-_]?secret|(access|refresh|session|auth|bearer)?[-_]?token|password|passwd|cookies?|authorization|credentials?)$/i;\n\nconst accountConfig = z\n .record(z.string(), z.unknown())\n .refine((config) => !Object.keys(config).some((key) => SECRET_KEY.test(key)), {\n message: 'config must not contain credentials; send them in \"secret\" instead',\n });\n\n/** `YYYY-MM-DD` or a full ISO 8601 timestamp. */\nconst subscriptionDate = z\n .string()\n .trim()\n .refine(\n (value) =>\n /^\\d{4}-\\d{2}-\\d{2}(T[\\d:.]+(Z|[+-]\\d{2}:?\\d{2})?)?$/.test(value) &&\n !Number.isNaN(Date.parse(value)),\n { message: 'expected a date (YYYY-MM-DD) or an ISO timestamp' },\n );\n\n/** Plan, payment and renewal details for an account (`SubscriptionInfo`). */\nexport const subscriptionSchema = z\n .object({\n plan: z.string().trim().min(1).max(120).optional(),\n paidAt: subscriptionDate.optional(),\n renewsAt: subscriptionDate.optional(),\n price: z\n .object({\n amount: z.number().min(0).max(1_000_000),\n currency: z\n .string()\n .trim()\n .regex(/^[A-Za-z]{3}$/, 'expected a 3-letter currency code')\n .transform((c) => c.toUpperCase()),\n period: z.enum(['month', 'year']),\n })\n .strict()\n .optional(),\n notes: z.string().trim().max(1000).optional(),\n })\n .strict();\n\nconst accountFields = {\n label: z.string().trim().min(1).max(200),\n enabled: z.boolean().optional(),\n priority: z.number().int().min(0).max(1_000_000).optional(),\n weight: z.number().min(0).max(1_000).optional(),\n limits: quotaLimits.optional(),\n config: accountConfig.optional(),\n /** API key / token. Encrypted at rest and never returned. */\n secret: z.string().min(1).max(100_000).optional(),\n};\n\n/** `POST /api/accounts` body (`AccountCreate`). */\nexport const accountCreateSchema = z\n .object({ provider: providerKind, ...accountFields, subscription: subscriptionSchema.optional() })\n .strict();\n\n/** `PATCH /api/accounts/:id` body (`AccountPatch`); `subscription: null` clears it. */\nexport const accountPatchSchema = z\n .object({\n ...accountFields,\n label: accountFields.label.optional(),\n subscription: subscriptionSchema.nullable().optional(),\n })\n .strict();\n\n/**\n * `PUT /api/routes` body: the complete route list (validated with the config schema's route\n * shape) and optionally the default route, which must be one of those routes.\n */\nexport const routesUpdateSchema = z\n .object({ routes: z.array(routeSchema).max(200), defaultRoute: z.string().min(1).optional() })\n .strict()\n .superRefine((body, ctx) => {\n const seen = new Set<string>();\n body.routes.forEach((route, index) => {\n if (seen.has(route.name)) {\n ctx.addIssue({\n code: 'custom',\n path: ['routes', index, 'name'],\n message: `duplicate route name \"${route.name}\"`,\n });\n }\n seen.add(route.name);\n });\n if (body.defaultRoute !== undefined && !seen.has(body.defaultRoute)) {\n ctx.addIssue({\n code: 'custom',\n path: ['defaultRoute'],\n message: `defaultRoute \"${body.defaultRoute}\" is not one of the routes`,\n });\n }\n });\n\nconst taskText = {\n title: z.string().trim().min(1).max(300),\n description: z.string().max(50_000),\n acceptance: z.array(z.string().trim().min(1).max(2_000)).max(100),\n notes: z.string().max(50_000),\n dependsOn: z.array(z.string().min(1)).max(500),\n priority: z.number().finite(),\n};\n\n/** `POST /api/tasks` body. The task always starts PENDING. */\nexport const taskCreateSchema = z\n .object({\n id: taskIdSchema,\n title: taskText.title,\n description: taskText.description.optional(),\n dependsOn: taskText.dependsOn.optional(),\n priority: taskText.priority.optional(),\n acceptance: taskText.acceptance.optional(),\n })\n .strict();\n\n/**\n * `PATCH /api/tasks/:id` body. `null` clears an optional field (`description`, `priority`,\n * `acceptance`, `notes`).\n */\nexport const taskPatchSchema = z\n .object({\n title: taskText.title.optional(),\n description: taskText.description.nullable().optional(),\n dependsOn: taskText.dependsOn.optional(),\n priority: taskText.priority.nullable().optional(),\n acceptance: taskText.acceptance.nullable().optional(),\n notes: taskText.notes.nullable().optional(),\n status: z.enum(TASK_STATUSES).optional(),\n })\n .strict()\n .refine((patch) => Object.values(patch).some((v) => v !== undefined), {\n message: 'send at least one field to change',\n });\n\n/** `POST /api/runner/start` body: optional, `{}` or empty means \"pick the next task\". */\nexport const runnerStartSchema = z.object({ taskId: z.string().min(1).optional() }).strict();\n\n/** `POST /api/goals` body. Limits are further clamped to the `goal.*` config. */\nexport const goalStartSchema = z\n .object({\n objective: z.string().trim().min(1).max(4_000),\n maxRounds: z.number().int().min(1).max(20).optional(),\n maxTasks: z.number().int().min(1).max(100).optional(),\n maxTokens: z.number().int().min(1_000).optional(),\n dryRun: z.boolean().optional(),\n })\n .strict();\n\nexport const timeseriesQuerySchema = z.object({\n minutes: z.coerce\n .number()\n .int()\n .min(1)\n .max(7 * 24 * 60)\n .default(60),\n bucketSec: z.coerce.number().int().min(1).max(86_400).default(60),\n});\n\nexport const limitQuerySchema = (fallback: number, max = 1000) =>\n z.object({ limit: z.coerce.number().int().min(1).max(max).default(fallback) });\n","import type { Engine } from '@davecode/core';\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport { z } from 'zod';\nimport { sendApiError } from '../errors';\nimport type { GatewayOptions, GoalControl, GoalStartInput } from '../options';\nimport { goalStartSchema } from '../schemas';\n\n/** Goal mode: `GET/POST /api/goals`, `GET /api/goals/:id`, `POST /api/goals/:id/cancel`. */\nexport function registerGoalRoutes(\n app: FastifyInstance,\n engine: Engine,\n options: GatewayOptions,\n): void {\n const { audit } = engine;\n\n const unavailable = (reply: FastifyReply) =>\n sendApiError(\n reply,\n 501,\n 'goals_unavailable',\n 'Goal mode is not available; start the gateway inside a project (davecode init)',\n );\n\n /** Forwards refusals that carry an HTTP status and a code (GoalError: busy, not_found…). */\n const guarded = async <T>(reply: FastifyReply, run: () => Promise<T>) => {\n try {\n return { ok: true as const, value: await run() };\n } catch (error) {\n const { statusCode, code } = error as { statusCode?: unknown; code?: unknown };\n if (typeof statusCode === 'number' && statusCode < 500 && typeof code === 'string') {\n return {\n ok: false as const,\n reply: sendApiError(reply, statusCode, code, (error as Error).message),\n };\n }\n throw error;\n }\n };\n\n const control = (reply: FastifyReply): GoalControl | undefined => {\n if (!options.goals) unavailable(reply);\n return options.goals;\n };\n\n app.get('/api/goals', async (_request, reply) => {\n const goals = control(reply);\n if (!goals) return reply;\n return { goals: await goals.list() };\n });\n\n app.get<{ Params: { id: string } }>('/api/goals/:id', async (request, reply) => {\n const goals = control(reply);\n if (!goals) return reply;\n const goal = await goals.get(request.params.id);\n if (!goal) return sendApiError(reply, 404, 'not_found', `unknown goal \"${request.params.id}\"`);\n return { goal };\n });\n\n app.post('/api/goals', async (request, reply) => {\n const parsed = goalStartSchema.safeParse(request.body);\n if (!parsed.success) {\n return sendApiError(reply, 400, 'invalid_body', z.prettifyError(parsed.error));\n }\n const goals = control(reply);\n if (!goals) return reply;\n const input: GoalStartInput = parsed.data;\n const result = await guarded(reply, () => goals.start(input));\n if (!result.ok) return result.reply;\n audit.record({\n actor: 'api',\n action: 'goal.start',\n target: result.value.id,\n details: { dryRun: input.dryRun === true },\n });\n return reply.code(202).send({ goal: result.value });\n });\n\n app.post<{ Params: { id: string } }>('/api/goals/:id/execute', async (request, reply) => {\n const goals = control(reply);\n if (!goals) return reply;\n if (!goals.execute) return unavailable(reply);\n const execute = goals.execute.bind(goals);\n const result = await guarded(reply, () => execute(request.params.id));\n if (!result.ok) return result.reply;\n audit.record({ actor: 'api', action: 'goal.execute', target: request.params.id });\n return reply.code(202).send({ goal: result.value });\n });\n\n app.get<{ Params: { id: string; taskId: string } }>(\n '/api/goals/:id/artifacts/:taskId',\n async (request, reply) => {\n const goals = control(reply);\n if (!goals) return reply;\n if (!goals.readArtifact) return unavailable(reply);\n const readArtifact = goals.readArtifact.bind(goals);\n const result = await guarded(reply, () =>\n readArtifact(request.params.id, request.params.taskId),\n );\n if (!result.ok) return result.reply;\n return { artifact: result.value };\n },\n );\n\n app.post<{ Params: { id: string } }>('/api/goals/:id/cancel', async (request, reply) => {\n const goals = control(reply);\n if (!goals) return reply;\n const result = await guarded(reply, () => goals.cancel(request.params.id));\n if (!result.ok) return result.reply;\n audit.record({ actor: 'api', action: 'goal.cancel', target: request.params.id });\n return { goal: result.value };\n });\n}\n","import {\n type ClearableTaskField,\n type Engine,\n type NewTask,\n TaskGraphError,\n type TaskGraphIssue,\n type TaskPatch,\n} from '@davecode/core';\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport { z } from 'zod';\nimport { sendApiError } from '../errors';\nimport type { BrainSource, GatewayOptions } from '../options';\nimport { taskCreateSchema, taskPatchSchema } from '../schemas';\n\n/** How a task graph issue is reported: HTTP status plus the extra envelope fields. */\nfunction describeIssue(issue: TaskGraphIssue): {\n status: number;\n code: string;\n extra: Record<string, unknown>;\n} {\n switch (issue.code) {\n case 'unknown_task':\n return { status: 404, code: 'not_found', extra: {} };\n case 'duplicate_id':\n case 'invalid_transition':\n case 'task_in_progress':\n return { status: 409, code: issue.code, extra: {} };\n case 'has_dependents':\n return { status: 409, code: 'has_dependents', extra: { dependents: issue.dependents ?? [] } };\n case 'cycle':\n return { status: 400, code: 'cycle', extra: { cycle: issue.cycle ?? [] } };\n case 'unknown_dependency':\n case 'self_dependency':\n return { status: 400, code: issue.code, extra: {} };\n default:\n // The stored TASK_GRAPH.json itself is unreadable or invalid.\n return { status: 409, code: 'invalid_graph', extra: {} };\n }\n}\n\nfunction sendGraphError(reply: FastifyReply, error: TaskGraphError): FastifyReply {\n // Report the most specific issue: a cycle beats generic dependency problems.\n const issue = error.issues.find((i) => i.code === 'cycle') ?? error.issues[0];\n if (!issue) return sendApiError(reply, 400, 'invalid_graph', error.message);\n const { status, code, extra } = describeIssue(issue);\n return sendApiError(reply, status, code, issue.message, extra);\n}\n\n/** Task graph writes: `POST /api/tasks`, `PATCH /api/tasks/:id`, `DELETE /api/tasks/:id`. */\nexport function registerTaskRoutes(\n app: FastifyInstance,\n engine: Engine,\n options: GatewayOptions,\n): void {\n const { audit } = engine;\n\n /** The brain, or the error reply to send (no project, or a read-only source). */\n const writable = (reply: FastifyReply): BrainSource | FastifyReply => {\n const { brain } = options;\n if (!brain) {\n return sendApiError(\n reply,\n 409,\n 'no_brain',\n 'No project brain is loaded; start the gateway inside a project (davecode init)',\n );\n }\n return brain;\n };\n\n const guarded = async <T>(\n reply: FastifyReply,\n run: () => Promise<T>,\n ): Promise<T | FastifyReply> => {\n try {\n return await run();\n } catch (error) {\n if (error instanceof TaskGraphError) return sendGraphError(reply, error);\n // Brain refusals such as MissingBrainError carry an HTTP status and a machine code.\n const { statusCode, code } = error as { statusCode?: unknown; code?: unknown };\n if (typeof statusCode === 'number' && statusCode < 500 && typeof code === 'string') {\n return sendApiError(reply, statusCode, code, (error as Error).message);\n }\n throw error;\n }\n };\n\n const readOnly = (reply: FastifyReply) =>\n sendApiError(reply, 501, 'brain_read_only', 'The project brain of this gateway is read-only');\n\n app.post('/api/tasks', async (request, reply) => {\n const parsed = taskCreateSchema.safeParse(request.body);\n if (!parsed.success) {\n return sendApiError(reply, 400, 'invalid_body', z.prettifyError(parsed.error));\n }\n const brain = writable(reply);\n if (isReply(brain)) return brain;\n if (!brain.createTask) return readOnly(reply);\n const input: NewTask = parsed.data;\n const create = brain.createTask.bind(brain);\n const result = await guarded(reply, () => create(input));\n if (isReply(result)) return result;\n audit.record({ actor: 'api', action: 'task.create', target: input.id });\n return reply.code(201).send({ task: result });\n });\n\n app.patch<{ Params: { id: string } }>('/api/tasks/:id', async (request, reply) => {\n const parsed = taskPatchSchema.safeParse(request.body);\n if (!parsed.success) {\n return sendApiError(reply, 400, 'invalid_body', z.prettifyError(parsed.error));\n }\n const brain = writable(reply);\n if (isReply(brain)) return brain;\n if (!brain.updateTask) return readOnly(reply);\n\n const { description, priority, acceptance, notes, ...rest } = parsed.data;\n const patch: TaskPatch = { ...rest };\n const clear: ClearableTaskField[] = [];\n const optional = { description, priority, acceptance, notes };\n for (const [key, value] of Object.entries(optional) as Array<[ClearableTaskField, unknown]>) {\n if (value === null) clear.push(key);\n else if (value !== undefined) (patch as Record<string, unknown>)[key] = value;\n }\n const { id } = request.params;\n const update = brain.updateTask.bind(brain);\n const result = await guarded(reply, () => update(id, patch, { clear }));\n if (isReply(result)) return result;\n audit.record({\n actor: 'api',\n action: 'task.update',\n target: id,\n details: { fields: Object.keys(parsed.data) },\n });\n return { task: result };\n });\n\n app.delete<{ Params: { id: string } }>('/api/tasks/:id', async (request, reply) => {\n const brain = writable(reply);\n if (isReply(brain)) return brain;\n if (!brain.removeTask) return readOnly(reply);\n const { id } = request.params;\n const remove = brain.removeTask.bind(brain);\n const result = await guarded(reply, () => remove(id));\n if (isReply(result)) return result;\n audit.record({ actor: 'api', action: 'task.delete', target: id });\n return reply.code(204).send();\n });\n}\n\nfunction isReply(value: unknown): value is FastifyReply {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'sent' in value &&\n typeof (value as { code?: unknown }).code === 'function'\n );\n}\n","import type { LoginProvider, LoginService } from '@davecode/core';\nimport type { FastifyInstance, FastifyRequest } from 'fastify';\nimport { z } from 'zod';\nimport { sendApiError } from '../errors';\n\nexport function authCookie(request: FastifyRequest, name: string): string | undefined {\n return request.headers.cookie\n ?.split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith(`${name}=`))\n ?.slice(name.length + 1);\n}\n\nfunction cookie(name: string, value: string, seconds: number, secure: boolean): string {\n return `${name}=${value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${seconds}${secure ? '; Secure' : ''}`;\n}\n\nexport function registerAuthRoutes(app: FastifyInstance, login?: LoginService): void {\n const secure = login?.options.origin.startsWith('https:') ?? false;\n app.get('/api/auth/session', async (request, reply) => {\n reply.header('Cache-Control', 'no-store');\n return (\n login?.status(authCookie(request, 'dc_session')) ?? {\n required: false,\n providers: [],\n user: null,\n }\n );\n });\n app.post('/api/auth/logout', async (request, reply) => {\n if (login && request.headers.origin !== login.options.origin)\n return sendApiError(\n reply,\n 403,\n 'invalid_origin',\n 'Open DaveCode from its configured origin.',\n );\n login?.logout(authCookie(request, 'dc_session'));\n return reply\n .header('Cache-Control', 'no-store')\n .header('Set-Cookie', cookie('dc_session', '', 0, secure))\n .code(204)\n .send();\n });\n for (const provider of ['google', 'github'] as const satisfies readonly LoginProvider[]) {\n app.get(`/api/auth/${provider}/start`, async (_request, reply) => {\n if (!login?.options.providers[provider])\n return sendApiError(\n reply,\n 503,\n 'login_not_configured',\n 'This sign-in provider is not configured.',\n );\n try {\n const flow = login.begin(provider);\n return reply\n .header('Cache-Control', 'no-store')\n .header('Referrer-Policy', 'no-referrer')\n .header('Set-Cookie', cookie('dc_login', flow.binding, 600, secure))\n .redirect(flow.url);\n } catch {\n return sendApiError(reply, 429, 'login_busy', 'Sign-in is busy. Try again shortly.');\n }\n });\n app.get(`/api/auth/${provider}/callback`, async (request, reply) => {\n reply.header('Cache-Control', 'no-store').header('Referrer-Policy', 'no-referrer');\n if (!login)\n return sendApiError(reply, 503, 'login_not_configured', 'Sign-in is not configured.');\n const parsed = z\n .object({ state: z.string().min(1).max(200), code: z.string().min(1).max(4096) })\n .safeParse(request.query);\n const binding = authCookie(request, 'dc_login');\n if (!parsed.success || !binding)\n return reply.redirect(`${login.options.origin}/#/login?error=signin_failed`);\n try {\n const session = await login.finish(provider, parsed.data.state, parsed.data.code, binding);\n login.logout(authCookie(request, 'dc_session'));\n return reply\n .header('Set-Cookie', [\n cookie('dc_session', session, 8 * 60 * 60, secure),\n cookie('dc_login', '', 0, secure),\n ])\n .redirect(`${login.options.origin}/`);\n } catch {\n return reply\n .header('Set-Cookie', cookie('dc_login', '', 0, secure))\n .redirect(`${login.options.origin}/#/login?error=signin_failed`);\n }\n });\n }\n}\n","import type { OutgoingHttpHeaders, ServerResponse } from 'node:http';\nimport type { DaveEvent, Engine } from '@davecode/core';\nimport type { FastifyInstance } from 'fastify';\n\nfunction frame(event: DaveEvent, seq: number): string {\n return `id: ${seq}\\nevent: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`;\n}\n\n/** Resume cursor from the `Last-Event-ID` header (sent by EventSource on reconnect) or query. */\nfunction resumeCursor(headers: Record<string, unknown>, query: unknown): number {\n const fromQuery =\n typeof query === 'object' && query !== null && 'lastEventId' in query\n ? (query as { lastEventId: unknown }).lastEventId\n : undefined;\n const raw = headers['last-event-id'] ?? fromQuery;\n const value = Number(Array.isArray(raw) ? raw[0] : raw);\n return Number.isSafeInteger(value) && value >= 0 ? value : 0;\n}\n\n/**\n * `GET /api/events`: replays the buffered events after the client's `Last-Event-ID` (all of\n * them on a first connect), then streams live events with an `id:` per event and a `: ping`\n * heartbeat. Subscriptions and timers are released when the client disconnects.\n */\nexport function registerEventRoutes(\n app: FastifyInstance,\n engine: Engine,\n heartbeatMs: number,\n): void {\n const clients = new Set<ServerResponse>();\n\n // End open streams before the server stops, otherwise close() would wait on them.\n app.addHook('preClose', async () => {\n for (const res of clients) res.end();\n clients.clear();\n });\n\n app.get('/api/events', (request, reply) => {\n const cursor = resumeCursor(request.headers, request.query);\n reply.hijack();\n const res = reply.raw;\n // Keep headers set by earlier hooks (CORS).\n const inherited: OutgoingHttpHeaders = {};\n for (const [name, value] of Object.entries(reply.getHeaders())) {\n if (value !== undefined) inherited[name] = value;\n }\n res.writeHead(200, {\n ...inherited,\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n 'x-accel-buffering': 'no',\n });\n res.write('retry: 3000\\n\\n');\n clients.add(res);\n\n for (const { seq, event } of engine.events.since(cursor)) res.write(frame(event, seq));\n const unsubscribe = engine.events.subscribe((event, seq) => {\n res.write(frame(event, seq));\n });\n const heartbeat = setInterval(() => res.write(': ping\\n\\n'), heartbeatMs);\n heartbeat.unref();\n\n let closed = false;\n const cleanup = () => {\n if (closed) return;\n closed = true;\n clearInterval(heartbeat);\n unsubscribe();\n clients.delete(res);\n };\n // Not request.raw 'close': Node emits that once the (empty) body is consumed.\n res.on('close', cleanup);\n res.on('error', cleanup);\n });\n}\n","import { Readable } from 'node:stream';\nimport {\n type ChatCompletionChunk,\n type ChatRequest,\n type Engine,\n newId,\n type RoutingMeta,\n type StreamResult,\n} from '@davecode/core';\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport { z } from 'zod';\nimport { describeRoutingError, sendOpenAIError } from '../errors';\nimport { chatRequestSchema } from '../schemas';\n\nfunction setRoutingHeaders(reply: FastifyReply, meta: RoutingMeta): void {\n reply\n .header('x-davecode-request-id', meta.requestId)\n .header('x-davecode-account', meta.accountId)\n .header('x-davecode-provider', meta.provider)\n .header('x-davecode-failovers', String(meta.failovers));\n}\n\nfunction sendRoutingError(reply: FastifyReply, err: unknown): FastifyReply {\n const { status, body } = describeRoutingError(err);\n if (status === 500) reply.log.error({ err }, 'chat completion failed');\n if (err instanceof Error && 'failovers' in err && typeof err.failovers === 'number') {\n reply.header('x-davecode-failovers', String(err.failovers));\n }\n return sendOpenAIError(reply, status, body);\n}\n\n/** Frame chunks as SSE, forwarding a mid-stream failure as a final error frame. */\nasync function* sseFrames(chunks: AsyncIterable<ChatCompletionChunk>): AsyncGenerator<string> {\n try {\n for await (const chunk of chunks) yield `data: ${JSON.stringify(chunk)}\\n\\n`;\n } catch (err) {\n yield `data: ${JSON.stringify({ error: describeRoutingError(err).body })}\\n\\n`;\n }\n yield 'data: [DONE]\\n\\n';\n}\n\n/** OpenAI-compatible surface: `GET /v1/models`, `POST /v1/chat/completions`. */\nexport function registerV1Routes(app: FastifyInstance, engine: Engine): void {\n app.get('/v1/models', async () => ({ object: 'list', data: await engine.router.listModels() }));\n\n app.post('/v1/chat/completions', async (request, reply) => {\n const parsed = chatRequestSchema.safeParse(request.body);\n if (!parsed.success) {\n return sendOpenAIError(reply, 400, {\n message: z.prettifyError(parsed.error),\n type: 'invalid_request_error',\n code: 'invalid_body',\n });\n }\n const body = parsed.data as ChatRequest;\n const requestId = newId('req');\n reply.header('x-davecode-request-id', requestId);\n\n // Abort upstream work if the client goes away before we finish.\n const abort = new AbortController();\n reply.raw.on('close', () => {\n if (!reply.raw.writableFinished) abort.abort();\n });\n const routeOptions = { requestId, signal: abort.signal };\n\n if (!body.stream) {\n try {\n const { completion, meta } = await engine.router.complete(body, routeOptions);\n setRoutingHeaders(reply, meta);\n return completion;\n } catch (err) {\n return sendRoutingError(reply, err);\n }\n }\n\n let result: StreamResult;\n try {\n // Resolves after the first upstream chunk, so failover never leaks partial output.\n result = await engine.router.stream(body, routeOptions);\n } catch (err) {\n return sendRoutingError(reply, err);\n }\n setRoutingHeaders(reply, result.meta);\n reply\n .header('content-type', 'text/event-stream; charset=utf-8')\n .header('cache-control', 'no-cache, no-transform')\n .header('x-accel-buffering', 'no');\n return reply.send(Readable.from(sseFrames(result.chunks)));\n });\n}\n","import type { ChatCompletionChunk, DaveEvent } from '@davecode/core';\n\n/**\n * Server-Sent Events client parsing per the WHATWG spec: `\\n`, `\\r\\n` and `\\r` line endings\n * (even when split across chunks), comments, multi-line `data`, `event`, `id` and `retry`.\n */\n\nexport interface SSEMessage {\n /** `message` when the server sent no `event:` field. */\n event: string;\n data: string;\n /** Last event id seen on the stream, if any. */\n id?: string;\n retry?: number;\n}\n\nexport class SSEParser {\n private buffer = '';\n private pendingCR = false;\n private data: string[] = [];\n private event = '';\n private lastId: string | undefined;\n private retry: number | undefined;\n\n /** Feed decoded text; returns the messages completed by it. */\n push(chunk: string): SSEMessage[] {\n const out: SSEMessage[] = [];\n let text = chunk;\n if (this.pendingCR && text.startsWith('\\n')) text = text.slice(1);\n this.pendingCR = false;\n this.buffer += text;\n\n let start = 0;\n for (let i = 0; i < this.buffer.length; i++) {\n const ch = this.buffer[i];\n if (ch !== '\\n' && ch !== '\\r') continue;\n const line = this.buffer.slice(start, i);\n if (ch === '\\r') {\n if (i + 1 < this.buffer.length) {\n if (this.buffer[i + 1] === '\\n') i++;\n } else {\n // A CR at the end of the chunk may be the first half of CRLF.\n this.pendingCR = true;\n }\n }\n this.processLine(line, out);\n start = i + 1;\n }\n this.buffer = this.buffer.slice(start);\n return out;\n }\n\n /** End of stream. An unterminated final event is discarded, as the spec requires. */\n end(): SSEMessage[] {\n this.buffer = '';\n this.data = [];\n this.event = '';\n return [];\n }\n\n private processLine(line: string, out: SSEMessage[]): void {\n if (line === '') {\n this.dispatch(out);\n return;\n }\n if (line.startsWith(':')) return;\n const colon = line.indexOf(':');\n const field = colon === -1 ? line : line.slice(0, colon);\n let value = colon === -1 ? '' : line.slice(colon + 1);\n if (value.startsWith(' ')) value = value.slice(1);\n switch (field) {\n case 'data':\n this.data.push(value);\n break;\n case 'event':\n this.event = value;\n break;\n case 'id':\n if (!value.includes('\\u0000')) this.lastId = value;\n break;\n case 'retry':\n if (/^\\d+$/.test(value)) this.retry = Number(value);\n break;\n default:\n break;\n }\n }\n\n private dispatch(out: SSEMessage[]): void {\n if (this.data.length === 0) {\n this.event = '';\n return;\n }\n const message: SSEMessage = { event: this.event || 'message', data: this.data.join('\\n') };\n if (this.lastId !== undefined) message.id = this.lastId;\n if (this.retry !== undefined) message.retry = this.retry;\n out.push(message);\n this.data = [];\n this.event = '';\n }\n}\n\n/** Decode a byte stream (e.g. `fetch().body`) into SSE messages. */\nexport async function* readSSE(\n body: AsyncIterable<Uint8Array | string>,\n): AsyncGenerator<SSEMessage> {\n const parser = new SSEParser();\n const decoder = new TextDecoder();\n for await (const chunk of body) {\n const text = typeof chunk === 'string' ? chunk : decoder.decode(chunk, { stream: true });\n yield* parser.push(text);\n }\n const tail = decoder.decode();\n if (tail) yield* parser.push(tail);\n yield* parser.end();\n}\n\n/** An error frame (`data: {\"error\": …}`) sent by the gateway after a stream started. */\nexport class ChatStreamError extends Error {\n readonly code: string | undefined;\n\n constructor(message: string, code?: string | null) {\n super(message);\n this.name = 'ChatStreamError';\n this.code = code ?? undefined;\n }\n}\n\n/** OpenAI-style completion chunks from SSE messages, until `data: [DONE]`. */\nexport async function* chatChunks(\n messages: AsyncIterable<SSEMessage>,\n): AsyncGenerator<ChatCompletionChunk> {\n for await (const message of messages) {\n if (message.data === '[DONE]') return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(message.data);\n } catch {\n continue;\n }\n if (!parsed || typeof parsed !== 'object') continue;\n const record = parsed as {\n error?: { message?: string; code?: string | null };\n choices?: unknown;\n };\n if (record.error && !record.choices) {\n throw new ChatStreamError(record.error.message ?? 'Upstream error', record.error.code);\n }\n if (Array.isArray(record.choices)) yield parsed as ChatCompletionChunk;\n }\n}\n\n/** Typed DaveCode events from `/api/events`. Malformed frames are skipped. */\nexport async function* daveEvents(messages: AsyncIterable<SSEMessage>): AsyncGenerator<DaveEvent> {\n for await (const message of messages) {\n try {\n const event = JSON.parse(message.data) as DaveEvent;\n if (event && typeof event.type === 'string') yield event;\n } catch {\n // ignore\n }\n }\n}\n","import type { ChatCompletionChunk, ChatRequest, DaveConfig, DaveEvent } from '@davecode/core';\nimport { chatChunks, daveEvents, readSSE } from './sse';\n\n/**\n * Thin HTTP client for a running DaveCode gateway (`/api/*`, `/v1/*`). Used by `status`,\n * `accounts`, the chat TUI and the runner view. Never logs request bodies (they may carry\n * account secrets).\n */\n\nexport interface GatewayTarget {\n /** e.g. `http://127.0.0.1:4040` (no trailing slash). */\n baseUrl: string;\n token?: string;\n}\n\nexport interface HealthResponse {\n status: string;\n version: string;\n uptimeSec: number;\n experimental: { geminiWeb: boolean; multiAccountRotation: boolean };\n}\n\nexport type FetchLike = typeof fetch;\n\n/** An `/api` or `/v1` error response, or a transport failure (`status` 0). */\nexport class GatewayError extends Error {\n readonly status: number;\n readonly code: string;\n\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'GatewayError';\n this.status = status;\n this.code = code;\n }\n}\n\nconst WILDCARD_HOSTS = new Set(['0.0.0.0', '::', '[::]', '']);\n\n/** Host to connect to for a configured bind address (wildcards resolve to loopback). */\nexport function connectHost(host: string): string {\n if (WILDCARD_HOSTS.has(host)) return '127.0.0.1';\n if (host.includes(':') && !host.startsWith('[')) return `[${host}]`;\n return host;\n}\n\nexport function targetFor(config: DaveConfig, port = config.server.port): GatewayTarget {\n const target: GatewayTarget = { baseUrl: `http://${connectHost(config.server.host)}:${port}` };\n if (config.server.authToken) target.token = config.server.authToken;\n return target;\n}\n\nasync function errorFrom(res: Response): Promise<GatewayError> {\n let code = `http_${res.status}`;\n let message = `${res.status} ${res.statusText}`.trim();\n try {\n const body = (await res.json()) as { error?: { message?: string; code?: string | null } };\n if (body.error?.message) message = body.error.message;\n if (body.error?.code) code = body.error.code;\n } catch {\n // Not JSON; keep the status line.\n }\n return new GatewayError(res.status, code, message);\n}\n\nexport interface RequestOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport class GatewayClient {\n readonly baseUrl: string;\n private readonly token: string | undefined;\n private readonly fetchImpl: FetchLike;\n\n constructor(target: GatewayTarget, fetchImpl: FetchLike = fetch) {\n this.baseUrl = target.baseUrl.replace(/\\/+$/, '');\n this.token = target.token;\n this.fetchImpl = fetchImpl;\n }\n\n headers(extra: Record<string, string> = {}): Record<string, string> {\n return this.token ? { authorization: `Bearer ${this.token}`, ...extra } : extra;\n }\n\n /** Low-level fetch with auth, an optional timeout and transport errors as `GatewayError`. */\n async fetch(path: string, init: RequestInit = {}, options: RequestOptions = {}) {\n const signals: AbortSignal[] = [];\n if (options.signal) signals.push(options.signal);\n if (options.timeoutMs !== undefined) signals.push(AbortSignal.timeout(options.timeoutMs));\n const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];\n try {\n return await this.fetchImpl(`${this.baseUrl}${path}`, {\n ...init,\n headers: this.headers((init.headers as Record<string, string> | undefined) ?? {}),\n ...(signal ? { signal } : {}),\n });\n } catch (err) {\n if (options.signal?.aborted) throw err;\n const reason = err instanceof Error ? err.message : String(err);\n throw new GatewayError(\n 0,\n 'unreachable',\n `Cannot reach the gateway at ${this.baseUrl} (${reason})`,\n );\n }\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const init: RequestInit = { method };\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n init.headers = { 'content-type': 'application/json' };\n }\n const res = await this.fetch(path, init, { timeoutMs: 10_000, ...options });\n if (!res.ok) throw await errorFrom(res);\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n get<T>(path: string, options?: RequestOptions): Promise<T> {\n return this.request<T>('GET', path, undefined, options);\n }\n\n post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>('POST', path, body, options);\n }\n\n patch<T>(path: string, body: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>('PATCH', path, body, options);\n }\n\n delete(path: string, options?: RequestOptions): Promise<void> {\n return this.request<void>('DELETE', path, undefined, options);\n }\n\n /**\n * `GET /api/health`, or `undefined` when nothing (or something that is not DaveCode) answers.\n * Throws `GatewayError` 401 when a gateway is running but rejects our bearer token.\n */\n async health(timeoutMs = 800): Promise<HealthResponse | undefined> {\n let res: Response;\n try {\n res = await this.fetch('/api/health', {}, { timeoutMs });\n } catch {\n return undefined;\n }\n if (res.status === 401) {\n throw new GatewayError(\n 401,\n 'unauthorized',\n `The gateway at ${this.baseUrl} requires a bearer token`,\n );\n }\n if (!res.ok) return undefined;\n try {\n const body = (await res.json()) as Partial<HealthResponse>;\n return body.status === 'ok' && typeof body.version === 'string'\n ? (body as HealthResponse)\n : undefined;\n } catch {\n return undefined;\n }\n }\n /**\n * `POST /v1/chat/completions` with `stream: true`. Resolves once the gateway answered (after\n * routing and any failover), so `meta` already names the serving account.\n */\n async chatStream(\n request: ChatRequest,\n signal?: AbortSignal,\n ): Promise<{ meta: StreamMeta; chunks: AsyncIterable<ChatCompletionChunk> }> {\n const res = await this.fetch(\n '/v1/chat/completions',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'text/event-stream' },\n body: JSON.stringify({ ...request, stream: true }),\n },\n signal ? { signal } : {},\n );\n if (!res.ok) throw await errorFrom(res);\n return { meta: streamMeta(res.headers), chunks: chatChunks(readSSE(bodyOf(res))) };\n }\n\n /** Live `/api/events` (the server replays its recent buffer first). */\n async *events(signal?: AbortSignal): AsyncGenerator<DaveEvent> {\n const res = await this.fetch(\n '/api/events',\n { headers: { accept: 'text/event-stream' } },\n signal ? { signal } : {},\n );\n if (!res.ok) throw await errorFrom(res);\n yield* daveEvents(readSSE(bodyOf(res)));\n }\n}\n\n/** Routing details the gateway reports in `x-davecode-*` response headers. */\nexport interface StreamMeta {\n requestId?: string;\n accountId?: string;\n provider?: string;\n failovers: number;\n}\n\nexport function streamMeta(headers: Headers): StreamMeta {\n const meta: StreamMeta = { failovers: Number(headers.get('x-davecode-failovers') ?? 0) || 0 };\n const requestId = headers.get('x-davecode-request-id');\n const accountId = headers.get('x-davecode-account');\n const provider = headers.get('x-davecode-provider');\n if (requestId) meta.requestId = requestId;\n if (accountId) meta.accountId = accountId;\n if (provider) meta.provider = provider;\n return meta;\n}\n\nfunction bodyOf(res: Response): AsyncIterable<Uint8Array> {\n if (!res.body) throw new GatewayError(res.status, 'empty_body', 'The gateway sent no body');\n return res.body as unknown as AsyncIterable<Uint8Array>;\n}\n\n/** A client for the configured gateway if one is answering `/api/health`, else `undefined`. */\nexport async function findGateway(\n config: DaveConfig,\n options: { port?: number; fetch?: FetchLike; timeoutMs?: number } = {},\n): Promise<{ client: GatewayClient; health: HealthResponse } | undefined> {\n const client = new GatewayClient(targetFor(config, options.port), options.fetch);\n const health = await client.health(options.timeoutMs);\n return health ? { client, health } : undefined;\n}\n","import { spawn as nodeSpawn, type SpawnOptions } from 'node:child_process';\nimport { which } from './proc';\n\nexport type DetachedSpawn = (\n command: string,\n args: readonly string[],\n options: SpawnOptions,\n) => {\n once(event: 'spawn', listener: () => void): unknown;\n once(event: 'error', listener: (err: Error) => void): unknown;\n unref(): void;\n};\n\nexport interface TerminalOptions {\n /** Override the CLI entry point when hosted by a bundled desktop runtime. */\n argv?: readonly string[];\n platform?: NodeJS.Platform;\n env?: NodeJS.ProcessEnv;\n spawn?: DetachedSpawn;\n}\n\n/** A program and its arguments, run in a new terminal window. */\nexport interface TerminalCommand {\n command: string;\n args: string[];\n}\n\nconst SAFE_ACCOUNT_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;\n\n/** `davecode accounts login <id> --hold`, using this same Node binary and CLI entry point. */\nexport function loginInvocation(\n accountId: string,\n argv: readonly string[] = process.argv,\n): string[] {\n if (!SAFE_ACCOUNT_ID.test(accountId)) throw new Error('Invalid account id');\n const entry = argv[1];\n if (!entry) throw new Error('Cannot locate the davecode entry point');\n return [process.execPath, entry, 'accounts', 'login', accountId, '--hold'];\n}\n\nfunction posixQuote(arg: string): string {\n return `'${arg.replace(/'/g, `'\\\\''`)}'`;\n}\n\n/** Linux terminal emulators and how each takes a command to run. */\nconst LINUX_TERMINALS: Array<{ bin: string; flag: string[] }> = [\n { bin: 'x-terminal-emulator', flag: ['-e'] },\n { bin: 'gnome-terminal', flag: ['--'] },\n { bin: 'konsole', flag: ['-e'] },\n { bin: 'xfce4-terminal', flag: ['-x'] },\n { bin: 'xterm', flag: ['-e'] },\n];\n\n/**\n * How to open `argv` in a new terminal window on this platform, or `undefined` when no\n * terminal can be opened (headless Linux, unsafe characters for cmd.exe).\n */\nexport function terminalCommand(\n argv: readonly string[],\n options: Pick<TerminalOptions, 'platform' | 'env'> = {},\n): TerminalCommand | undefined {\n const platform = options.platform ?? process.platform;\n const env = options.env ?? process.env;\n if (platform === 'win32') {\n // cmd.exe expands %VAR% even inside quotes and quotes cannot be escaped, so refuse both.\n if (argv.some((a) => /[\"%\\r\\n]/.test(a))) return undefined;\n const line = argv.map((a) => `\"${a}\"`).join(' ');\n return {\n command: env.ComSpec ?? env.COMSPEC ?? 'cmd.exe',\n args: ['/d', '/c', `start \"DaveCode login\" ${line}`],\n };\n }\n if (platform === 'darwin') {\n const script = argv.map(posixQuote).join(' ').replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return {\n command: 'osascript',\n args: [\n '-e',\n `tell application \"Terminal\" to do script \"${script}\"`,\n '-e',\n 'tell application \"Terminal\" to activate',\n ],\n };\n }\n if (!env.DISPLAY && !env.WAYLAND_DISPLAY) return undefined;\n for (const terminal of LINUX_TERMINALS) {\n if (which(terminal.bin, env, platform)) {\n return { command: terminal.bin, args: [...terminal.flag, ...argv] };\n }\n }\n return undefined;\n}\n\n/**\n * Open a terminal window on this machine running the official vendor login for an account\n * (via `davecode accounts login`). Resolves `false` when no window could be opened.\n */\nexport function launchLoginTerminal(\n accountId: string,\n options: TerminalOptions = {},\n): Promise<boolean> {\n const platform = options.platform ?? process.platform;\n const target = terminalCommand(loginInvocation(accountId, options.argv), options);\n if (!target) return Promise.resolve(false);\n const spawn = options.spawn ?? (nodeSpawn as unknown as DetachedSpawn);\n return new Promise((resolve) => {\n try {\n const child = spawn(target.command, target.args, {\n detached: true,\n stdio: 'ignore',\n env: options.env ?? process.env,\n ...(platform === 'win32' ? { windowsVerbatimArguments: true } : {}),\n });\n child.once('spawn', () => {\n child.unref();\n resolve(true);\n });\n child.once('error', () => resolve(false));\n } catch {\n resolve(false);\n }\n });\n}\n","import { spawn as nodeSpawn, type SpawnOptions } from 'node:child_process';\nimport { existsSync, statSync } from 'node:fs';\nimport path from 'node:path';\n\n/** Split PATH, keeping Windows drive-letter prefixes when the delimiter is `:`. */\nexport function splitPathEnv(pathEnv: string, delimiter: string): string[] {\n if (delimiter === ';') return pathEnv.split(';').filter(Boolean);\n const dirs: string[] = [];\n let pendingDrive: string | undefined;\n for (const piece of pathEnv.split(':')) {\n if (pendingDrive !== undefined) {\n dirs.push(`${pendingDrive}:${piece}`);\n pendingDrive = undefined;\n continue;\n }\n if (/^[A-Za-z]$/.test(piece)) {\n pendingDrive = piece;\n continue;\n }\n if (piece) dirs.push(piece);\n }\n if (pendingDrive !== undefined) dirs.push(pendingDrive);\n return dirs;\n}\n\n/**\n * Locate `command` on PATH (honouring PATHEXT on Windows, preferring `.exe` over `.cmd` shims).\n * Paths containing a separator are returned as-is when they exist.\n */\nexport function which(\n command: string,\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): string | undefined {\n const isFile = (p: string) => {\n try {\n return existsSync(p) && statSync(p).isFile();\n } catch {\n return false;\n }\n };\n if (command.includes('/') || command.includes('\\\\') || path.isAbsolute(command)) {\n return isFile(command) ? command : undefined;\n }\n const win = platform === 'win32';\n const delimiter = win ? ';' : ':';\n // When tests simulate linux/darwin on a Windows host, PATH still has drive letters\n // (`C:\\tmp\\bin`). Splitting only on `:` would yield `C` + `\\tmp\\bin`.\n const dirs = splitPathEnv(env.PATH ?? env.Path ?? '', delimiter);\n const exts = win\n ? [\n '.exe',\n '.com',\n '',\n ...(env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD')\n .split(';')\n .map((e) => e.toLowerCase())\n .filter((e) => e && e !== '.exe' && e !== '.com'),\n ]\n : [''];\n for (const dir of dirs) {\n for (const ext of exts) {\n const candidate = path.join(dir, command + ext);\n if (isFile(candidate)) return candidate;\n }\n }\n return undefined;\n}\n\n/** Characters cmd.exe never interprets, so such arguments need no escaping. */\nconst SHELL_SAFE = /^[\\w.:=@+\\-/\\\\[\\]]+$/;\n\nexport type SpawnFn = (\n command: string,\n args: readonly string[],\n options: SpawnOptions,\n) => {\n on(\n event: 'exit',\n listener: (code: number | null, signal: NodeJS.Signals | null) => void,\n ): unknown;\n on(event: 'error', listener: (err: Error) => void): unknown;\n};\n\nexport interface InteractiveOptions {\n env: NodeJS.ProcessEnv;\n cwd?: string;\n spawn?: SpawnFn;\n platform?: NodeJS.Platform;\n}\n\n/**\n * Run a command attached to this terminal (`stdio: 'inherit'`) and resolve with its exit code.\n * Windows `.cmd`/`.bat` shims (npm-installed CLIs) need a shell; their arguments must then be\n * shell-safe, which is checked rather than escaped.\n */\nexport function spawnInteractive(\n command: string,\n args: string[],\n options: InteractiveOptions,\n): Promise<number> {\n const spawn = options.spawn ?? (nodeSpawn as unknown as SpawnFn);\n const platform = options.platform ?? process.platform;\n const shim = platform === 'win32' && /\\.(cmd|bat)$/i.test(command);\n if (shim) {\n const unsafe = args.find((a) => !SHELL_SAFE.test(a));\n if (unsafe !== undefined) {\n return Promise.reject(\n new Error(`Argument ${JSON.stringify(unsafe)} cannot be passed safely to ${command}`),\n );\n }\n }\n return new Promise((resolve, reject) => {\n const child = shim\n ? spawn(`\"${command}\"`, args, {\n stdio: 'inherit',\n env: options.env,\n shell: true,\n ...(options.cwd ? { cwd: options.cwd } : {}),\n })\n : spawn(command, args, {\n stdio: 'inherit',\n env: options.env,\n ...(options.cwd ? { cwd: options.cwd } : {}),\n });\n child.on('error', reject);\n child.on('exit', (code, signal) => resolve(code ?? (signal ? 130 : 1)));\n });\n}\n"],"mappings":"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","names":["existsSync","resolve","z","z","z","z","path","path","nodeSpawn","existsSync","resolve","nodeSpawn","resolve","path","resolve","existsSync"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/tui/runner-state.ts
|
|
2
|
+
var LOG_LIMIT = 200;
|
|
3
|
+
function initialRunnerState(status = { state: "idle" }, tasks = []) {
|
|
4
|
+
return {
|
|
5
|
+
status,
|
|
6
|
+
tasks: Object.fromEntries(
|
|
7
|
+
tasks.map((t) => [
|
|
8
|
+
t.id,
|
|
9
|
+
{
|
|
10
|
+
id: t.id,
|
|
11
|
+
title: t.title,
|
|
12
|
+
status: t.status,
|
|
13
|
+
...t.attempts ? { attempts: t.attempts } : {}
|
|
14
|
+
}
|
|
15
|
+
])
|
|
16
|
+
),
|
|
17
|
+
logs: [],
|
|
18
|
+
done: { success: 0, failed: 0 }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function runnerReducer(state, event) {
|
|
22
|
+
switch (event.type) {
|
|
23
|
+
case "runner.status":
|
|
24
|
+
return { ...state, status: event.status };
|
|
25
|
+
case "runner.log": {
|
|
26
|
+
const line = { ts: event.ts, level: event.level, message: event.message };
|
|
27
|
+
if (event.taskId) line.taskId = event.taskId;
|
|
28
|
+
return { ...state, logs: [...state.logs, line].slice(-LOG_LIMIT) };
|
|
29
|
+
}
|
|
30
|
+
case "task.updated": {
|
|
31
|
+
const { task } = event;
|
|
32
|
+
const previous = state.tasks[task.id];
|
|
33
|
+
const done = { ...state.done };
|
|
34
|
+
if (previous?.status !== task.status) {
|
|
35
|
+
if (task.status === "SUCCESS") done.success++;
|
|
36
|
+
if (task.status === "FAILED") done.failed++;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...state,
|
|
40
|
+
done,
|
|
41
|
+
tasks: {
|
|
42
|
+
...state.tasks,
|
|
43
|
+
[task.id]: {
|
|
44
|
+
id: task.id,
|
|
45
|
+
title: task.title,
|
|
46
|
+
status: task.status,
|
|
47
|
+
...task.attempts ? { attempts: task.attempts } : {}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var PIPELINE = [
|
|
57
|
+
"selecting",
|
|
58
|
+
"preparing",
|
|
59
|
+
"implementing",
|
|
60
|
+
"validating",
|
|
61
|
+
"merging"
|
|
62
|
+
];
|
|
63
|
+
function pipelineIndex(state) {
|
|
64
|
+
if (state === "repairing") return PIPELINE.indexOf("validating");
|
|
65
|
+
return PIPELINE.indexOf(state);
|
|
66
|
+
}
|
|
67
|
+
function isTerminal(state) {
|
|
68
|
+
return state === "stopped" || state === "error";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
LOG_LIMIT,
|
|
73
|
+
initialRunnerState,
|
|
74
|
+
runnerReducer,
|
|
75
|
+
PIPELINE,
|
|
76
|
+
pipelineIndex,
|
|
77
|
+
isTerminal
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=chunk-FRFSJMN4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/runner-state.ts"],"sourcesContent":["import type { DaveEvent, LogLevel, RunnerState, RunnerStatus, TaskNode } from '@davecode/core';\n\n/** Pure state behind the runner view, folded from `runner.*` and `task.updated` events. */\n\nexport interface LogLine {\n ts: number;\n level: LogLevel;\n message: string;\n taskId?: string;\n}\n\nexport interface RunnerViewState {\n status: RunnerStatus;\n /** Latest known version of each task (from `task.updated` or the initial graph). */\n tasks: Record<string, Pick<TaskNode, 'id' | 'title' | 'status' | 'attempts'>>;\n logs: LogLine[];\n /** Tasks finished in this session. */\n done: { success: number; failed: number };\n}\n\nexport const LOG_LIMIT = 200;\n\nexport function initialRunnerState(\n status: RunnerStatus = { state: 'idle' },\n tasks: TaskNode[] = [],\n): RunnerViewState {\n return {\n status,\n tasks: Object.fromEntries(\n tasks.map((t) => [\n t.id,\n {\n id: t.id,\n title: t.title,\n status: t.status,\n ...(t.attempts ? { attempts: t.attempts } : {}),\n },\n ]),\n ),\n logs: [],\n done: { success: 0, failed: 0 },\n };\n}\n\nexport function runnerReducer(state: RunnerViewState, event: DaveEvent): RunnerViewState {\n switch (event.type) {\n case 'runner.status':\n return { ...state, status: event.status };\n case 'runner.log': {\n const line: LogLine = { ts: event.ts, level: event.level, message: event.message };\n if (event.taskId) line.taskId = event.taskId;\n return { ...state, logs: [...state.logs, line].slice(-LOG_LIMIT) };\n }\n case 'task.updated': {\n const { task } = event;\n const previous = state.tasks[task.id];\n const done = { ...state.done };\n if (previous?.status !== task.status) {\n if (task.status === 'SUCCESS') done.success++;\n if (task.status === 'FAILED') done.failed++;\n }\n return {\n ...state,\n done,\n tasks: {\n ...state.tasks,\n [task.id]: {\n id: task.id,\n title: task.title,\n status: task.status,\n ...(task.attempts ? { attempts: task.attempts } : {}),\n },\n },\n };\n }\n default:\n return state;\n }\n}\n\n/** The happy path of the state machine, as drawn in the view. */\nexport const PIPELINE: readonly RunnerState[] = [\n 'selecting',\n 'preparing',\n 'implementing',\n 'validating',\n 'merging',\n];\n\n/** Index of `state` on the pipeline (`repairing` sits on `validating`), or -1 off-pipeline. */\nexport function pipelineIndex(state: RunnerState): number {\n if (state === 'repairing') return PIPELINE.indexOf('validating');\n return PIPELINE.indexOf(state);\n}\n\n/** True once the runner has come to rest after a stop or a fatal error. */\nexport function isTerminal(state: RunnerState): boolean {\n return state === 'stopped' || state === 'error';\n}\n"],"mappings":";AAoBO,IAAM,YAAY;AAElB,SAAS,mBACd,SAAuB,EAAE,OAAO,OAAO,GACvC,QAAoB,CAAC,GACJ;AACjB,SAAO;AAAA,IACL;AAAA,IACA,OAAO,OAAO;AAAA,MACZ,MAAM,IAAI,CAAC,MAAM;AAAA,QACf,EAAE;AAAA,QACF;AAAA,UACE,IAAI,EAAE;AAAA,UACN,OAAO,EAAE;AAAA,UACT,QAAQ,EAAE;AAAA,UACV,GAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,IAAI,CAAC;AAAA,QAC/C;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,MAAM,CAAC;AAAA,IACP,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE;AAAA,EAChC;AACF;AAEO,SAAS,cAAc,OAAwB,OAAmC;AACvF,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,MAAM,OAAO;AAAA,IAC1C,KAAK,cAAc;AACjB,YAAM,OAAgB,EAAE,IAAI,MAAM,IAAI,OAAO,MAAM,OAAO,SAAS,MAAM,QAAQ;AACjF,UAAI,MAAM,OAAQ,MAAK,SAAS,MAAM;AACtC,aAAO,EAAE,GAAG,OAAO,MAAM,CAAC,GAAG,MAAM,MAAM,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE;AAAA,IACnE;AAAA,IACA,KAAK,gBAAgB;AACnB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,WAAW,MAAM,MAAM,KAAK,EAAE;AACpC,YAAM,OAAO,EAAE,GAAG,MAAM,KAAK;AAC7B,UAAI,UAAU,WAAW,KAAK,QAAQ;AACpC,YAAI,KAAK,WAAW,UAAW,MAAK;AACpC,YAAI,KAAK,WAAW,SAAU,MAAK;AAAA,MACrC;AACA,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,QACA,OAAO;AAAA,UACL,GAAG,MAAM;AAAA,UACT,CAAC,KAAK,EAAE,GAAG;AAAA,YACT,IAAI,KAAK;AAAA,YACT,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK;AAAA,YACb,GAAI,KAAK,WAAW,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,UACrD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAGO,IAAM,WAAmC;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,cAAc,OAA4B;AACxD,MAAI,UAAU,YAAa,QAAO,SAAS,QAAQ,YAAY;AAC/D,SAAO,SAAS,QAAQ,KAAK;AAC/B;AAGO,SAAS,WAAW,OAA6B;AACtD,SAAO,UAAU,aAAa,UAAU;AAC1C;","names":[]}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PIPELINE,
|
|
3
|
+
isTerminal,
|
|
4
|
+
pipelineIndex,
|
|
5
|
+
runnerReducer
|
|
6
|
+
} from "./chunk-FRFSJMN4.js";
|
|
7
|
+
import {
|
|
8
|
+
Spinner
|
|
9
|
+
} from "./chunk-5LKOJBUL.js";
|
|
10
|
+
import {
|
|
11
|
+
ThemeProvider,
|
|
12
|
+
useTheme
|
|
13
|
+
} from "./chunk-PGPXE6LF.js";
|
|
14
|
+
import {
|
|
15
|
+
bar,
|
|
16
|
+
truncate
|
|
17
|
+
} from "./chunk-SF5ZEAP4.js";
|
|
18
|
+
|
|
19
|
+
// src/tui/RunnerView.tsx
|
|
20
|
+
import { Box, Text, useApp, useInput, useWindowSize } from "ink";
|
|
21
|
+
import { useEffect, useReducer, useRef, useState } from "react";
|
|
22
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
23
|
+
var IDLE_STATES = ["idle", "paused", "stopped", "error"];
|
|
24
|
+
function pipelineText(theme, state, repairCycle) {
|
|
25
|
+
const current = pipelineIndex(state);
|
|
26
|
+
const arrow = theme.dim(` ${theme.glyph.arrow} `);
|
|
27
|
+
return PIPELINE.map((step, i) => {
|
|
28
|
+
let label = step;
|
|
29
|
+
if (step === "validating" && state === "repairing") {
|
|
30
|
+
label = `repairing${repairCycle ? ` #${repairCycle}` : ""}`;
|
|
31
|
+
}
|
|
32
|
+
if (current === -1) return theme.dim(label);
|
|
33
|
+
if (i < current) return theme.ok(label);
|
|
34
|
+
if (i === current) return theme.bold(theme.accent(label));
|
|
35
|
+
return theme.dim(label);
|
|
36
|
+
}).join(arrow);
|
|
37
|
+
}
|
|
38
|
+
function logColor(theme, line) {
|
|
39
|
+
if (line.level === "error") return theme.error;
|
|
40
|
+
if (line.level === "warn") return theme.warn;
|
|
41
|
+
if (line.level === "debug") return theme.dim;
|
|
42
|
+
return (t) => t;
|
|
43
|
+
}
|
|
44
|
+
function logText(theme, line, width) {
|
|
45
|
+
const time = new Date(line.ts).toTimeString().slice(0, 8);
|
|
46
|
+
const prefix = `${time} `;
|
|
47
|
+
return `${theme.dim(prefix)}${logColor(theme, line)(truncate(line.message.replace(/\s*\n\s*/g, " "), Math.max(10, width - prefix.length)))}`;
|
|
48
|
+
}
|
|
49
|
+
function RunnerView({
|
|
50
|
+
state,
|
|
51
|
+
maxRepairCycles,
|
|
52
|
+
projectName,
|
|
53
|
+
width,
|
|
54
|
+
logLines,
|
|
55
|
+
notice
|
|
56
|
+
}) {
|
|
57
|
+
const theme = useTheme();
|
|
58
|
+
const { status } = state;
|
|
59
|
+
const task = status.taskId ? state.tasks[status.taskId] : void 0;
|
|
60
|
+
const busy = !IDLE_STATES.includes(status.state);
|
|
61
|
+
const cycle = status.repairCycle ?? 0;
|
|
62
|
+
const stateColor = status.state === "error" ? theme.ink.error : status.state === "paused" || status.state === "stopped" ? theme.ink.warn : theme.ink.accent;
|
|
63
|
+
const rule = theme.dim(theme.glyph.rule.repeat(Math.max(10, width)));
|
|
64
|
+
const tail = state.logs.slice(-logLines);
|
|
65
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, children: [
|
|
66
|
+
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
67
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
68
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.ink.accent, children: "DaveCode runner" }),
|
|
69
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
70
|
+
" \xB7 ",
|
|
71
|
+
projectName
|
|
72
|
+
] })
|
|
73
|
+
] }),
|
|
74
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
75
|
+
state.done.success,
|
|
76
|
+
" done \xB7 ",
|
|
77
|
+
state.done.failed,
|
|
78
|
+
" failed"
|
|
79
|
+
] })
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
82
|
+
busy ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsx(Text, { color: stateColor, children: theme.glyph.dot }),
|
|
83
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color: stateColor, children: [
|
|
84
|
+
" ",
|
|
85
|
+
status.state
|
|
86
|
+
] }),
|
|
87
|
+
status.startedAt ? /* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
88
|
+
" since ",
|
|
89
|
+
status.startedAt.slice(11, 19)
|
|
90
|
+
] }) : null
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsx(Text, { wrap: "truncate-end", children: pipelineText(theme, status.state, status.repairCycle) }),
|
|
93
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
94
|
+
/* @__PURE__ */ jsxs(Text, { wrap: "truncate-end", children: [
|
|
95
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: "task " }),
|
|
96
|
+
task ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
97
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: task.id }),
|
|
98
|
+
" ",
|
|
99
|
+
task.title,
|
|
100
|
+
task.attempts ? /* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
101
|
+
" \xB7 attempt ",
|
|
102
|
+
task.attempts
|
|
103
|
+
] }) : null
|
|
104
|
+
] }) : status.taskId ? /* @__PURE__ */ jsx(Text, { bold: true, children: status.taskId }) : /* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: "none" })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
107
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: "repair " }),
|
|
108
|
+
bar(
|
|
109
|
+
theme,
|
|
110
|
+
maxRepairCycles > 0 ? cycle / maxRepairCycles : 0,
|
|
111
|
+
Math.max(1, maxRepairCycles * 2)
|
|
112
|
+
),
|
|
113
|
+
" ",
|
|
114
|
+
cycle,
|
|
115
|
+
"/",
|
|
116
|
+
maxRepairCycles
|
|
117
|
+
] }),
|
|
118
|
+
status.lastError ? /* @__PURE__ */ jsxs(Text, { color: theme.ink.error, wrap: "truncate-end", children: [
|
|
119
|
+
theme.glyph.fail,
|
|
120
|
+
" ",
|
|
121
|
+
status.lastError
|
|
122
|
+
] }) : null
|
|
123
|
+
] }),
|
|
124
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(Text, { children: rule }),
|
|
126
|
+
tail.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: "waiting for runner output\u2026" }) : null,
|
|
127
|
+
tail.map((line, i) => (
|
|
128
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: log lines are append-only
|
|
129
|
+
/* @__PURE__ */ jsx(Text, { wrap: "truncate-end", children: logText(theme, line, width) }, `${line.ts}-${i}`)
|
|
130
|
+
)),
|
|
131
|
+
/* @__PURE__ */ jsx(Text, { children: rule })
|
|
132
|
+
] }),
|
|
133
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: notice ?? "q or Ctrl+C stop safely \xB7 p pause/resume" })
|
|
134
|
+
] });
|
|
135
|
+
}
|
|
136
|
+
function RunnerScreen(props) {
|
|
137
|
+
const { exit } = useApp();
|
|
138
|
+
const { columns, rows } = useWindowSize();
|
|
139
|
+
const [state, dispatch] = useReducer(runnerReducer, props.initial);
|
|
140
|
+
const [notice, setNotice] = useState();
|
|
141
|
+
const stopping = useRef(false);
|
|
142
|
+
const sawActive = useRef(!isTerminal(props.initial.status.state));
|
|
143
|
+
useEffect(() => props.subscribe(dispatch), [props.subscribe]);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!isTerminal(state.status.state)) {
|
|
146
|
+
sawActive.current = true;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (sawActive.current || stopping.current) {
|
|
150
|
+
const timer = setTimeout(() => exit(), 50);
|
|
151
|
+
return () => clearTimeout(timer);
|
|
152
|
+
}
|
|
153
|
+
}, [state.status.state, exit]);
|
|
154
|
+
const requestStop = () => {
|
|
155
|
+
if (stopping.current) {
|
|
156
|
+
exit();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
stopping.current = true;
|
|
160
|
+
setNotice("Stopping safely (the task goes back to PENDING)\u2026 press again to quit now");
|
|
161
|
+
props.stop().then(
|
|
162
|
+
() => exit(),
|
|
163
|
+
(err) => {
|
|
164
|
+
setNotice(`stop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
165
|
+
setTimeout(() => exit(), 500);
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
useInput((input, key) => {
|
|
170
|
+
if (key.ctrl && input === "c" || input === "q") return requestStop();
|
|
171
|
+
if (input === "p") {
|
|
172
|
+
if (state.status.state === "paused") void props.resume?.();
|
|
173
|
+
else void props.pause?.();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const width = Math.max(30, columns);
|
|
177
|
+
const logLines = Math.max(3, Math.min(30, rows - 14));
|
|
178
|
+
return /* @__PURE__ */ jsx(
|
|
179
|
+
RunnerView,
|
|
180
|
+
{
|
|
181
|
+
state,
|
|
182
|
+
maxRepairCycles: props.maxRepairCycles,
|
|
183
|
+
projectName: props.projectName,
|
|
184
|
+
width,
|
|
185
|
+
logLines,
|
|
186
|
+
...notice ? { notice } : {}
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
function RunnerApp(props) {
|
|
191
|
+
return /* @__PURE__ */ jsx(ThemeProvider, { theme: props.theme, children: /* @__PURE__ */ jsx(RunnerScreen, { ...props }) });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export {
|
|
195
|
+
pipelineText,
|
|
196
|
+
logText,
|
|
197
|
+
RunnerView,
|
|
198
|
+
RunnerApp
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=chunk-GE22VBCY.js.map
|