@covro/covro 0.1.1-dev.2 → 0.1.1-dev.3

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/codex/transport.ts"],
4
- "sourcesContent": ["import { EventEmitter } from 'node:events';\nimport { AppError } from '../shared/errors.js';\nimport type { Readable, Writable } from 'node:stream';\n\nexport type RpcId = string | number;\nexport type RpcNotification = { method: string; params?: unknown };\nexport type RpcServerRequest = RpcNotification & { id: RpcId };\nexport const DEFAULT_MAX_LINE_BYTES = 64 * 1024 * 1024;\nexport class CodexError extends AppError {\n constructor(\n code: string,\n message: string,\n public readonly uncertain = false,\n public readonly rpcCode?: number,\n diagnostics?: Record<string, unknown>,\n ) {\n super(code, message, 503, { uncertain, rpcCode, ...diagnostics });\n this.name = 'CodexError';\n }\n}\n\n/** Buffers at most one bounded line. Decoding the complete byte sequence preserves split UTF-8. */\nexport class JsonLineReader {\n private parts: Buffer[] = [];\n private size = 0;\n private readonly decoder = new TextDecoder('utf-8', { fatal: true });\n constructor(\n private readonly consume: (value: unknown) => void,\n private readonly maxBytes = DEFAULT_MAX_LINE_BYTES,\n ) {}\n push(chunk: Buffer): void {\n let start = 0;\n while (start < chunk.length) {\n const newline = chunk.indexOf(10, start);\n const end = newline < 0 ? chunk.length : newline;\n const part = chunk.subarray(start, end);\n this.size += part.length;\n if (this.size > this.maxBytes) {\n const receivedBytes = this.size;\n this.parts = [];\n this.size = 0;\n throw new CodexError(\n 'PROTOCOL_ERROR',\n `Codex JSONL message exceeds the configured limit (received at least ${receivedBytes} bytes; limit ${this.maxBytes} bytes)`,\n true,\n undefined,\n { receivedBytes, maxLineBytes: this.maxBytes },\n );\n }\n if (part.length) this.parts.push(part);\n if (newline < 0) break;\n this.flush();\n start = newline + 1;\n }\n }\n finish(): void {\n if (this.size) this.flush();\n }\n private flush(): void {\n const bytes = Buffer.concat(this.parts, this.size);\n this.parts = [];\n this.size = 0;\n try {\n const line = this.decoder.decode(bytes).trim();\n if (line) this.consume(JSON.parse(line) as unknown);\n } catch (error) {\n if (error instanceof CodexError) throw error;\n throw new CodexError('PROTOCOL_ERROR', 'Invalid UTF-8 or JSON from Codex', true);\n }\n }\n}\n\ntype Pending = {\n method: string;\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n};\nexport type TransportOptions = {\n timeoutMs?: number;\n maxLineBytes?: number;\n maxPending?: number;\n maxWriteBytes?: number;\n};\n\n/** RPC receipt deadlines do not cancel upstream turns. A timeout always reports an uncertain outcome. */\nexport class RpcTransport extends EventEmitter {\n private readonly pending = new Map<RpcId, Pending>();\n private nextId = 0;\n private failure?: CodexError;\n private readonly reader: JsonLineReader;\n constructor(\n private readonly input: Writable,\n output: Readable,\n private readonly options: TransportOptions = {},\n ) {\n super();\n this.reader = new JsonLineReader((value) => this.receive(value), options.maxLineBytes);\n output.on('data', (chunk: Buffer) => {\n if (this.failure) return;\n try {\n this.reader.push(chunk);\n } catch (error) {\n this.fail(asCodexError(error));\n }\n });\n output.on('end', () => {\n if (this.failure) return;\n try {\n this.reader.finish();\n } catch (error) {\n this.fail(asCodexError(error));\n }\n this.fail(new CodexError('CODEX_UNAVAILABLE', 'Codex stdout reached EOF', true));\n });\n output.on('close', () =>\n this.fail(new CodexError('CODEX_UNAVAILABLE', 'Codex stdout closed', true)),\n );\n output.on('error', (error) =>\n this.fail(new CodexError('CODEX_UNAVAILABLE', error.message, true)),\n );\n input.on('error', (error) =>\n this.fail(new CodexError('CODEX_UNAVAILABLE', error.message, true)),\n );\n }\n request<T>(method: string, params: unknown, id: RpcId = this.nextId++): Promise<T> {\n if (this.failure) return Promise.reject(this.failure);\n if (this.pending.has(id))\n return Promise.reject(new CodexError('PROTOCOL_ERROR', 'Duplicate outgoing RPC id'));\n if (this.pending.size >= (this.options.maxPending ?? 256))\n return Promise.reject(new CodexError('CODEX_BUSY', 'Too many pending Codex requests'));\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new CodexError(\n 'RESULT_UNKNOWN',\n `Codex ${method} acknowledgement timed out; it may have executed`,\n true,\n ),\n );\n }, this.options.timeoutMs ?? 30_000);\n this.pending.set(id, { method, resolve: (result) => resolve(result as T), reject, timer });\n void this.write({ id, method, params }).catch((error: unknown) => {\n const pending = this.pending.get(id);\n if (!pending) return;\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(asCodexError(error));\n });\n });\n }\n notify(method: string, params?: unknown): Promise<void> {\n return this.write({ method, ...(params === undefined ? {} : { params }) });\n }\n respond(id: RpcId, result: unknown): Promise<void> {\n return this.write({ id, result });\n }\n reject(id: RpcId, code: number, message: string): Promise<void> {\n return this.write({ id, error: { code, message } });\n }\n fail(error: CodexError): void {\n if (this.failure) return;\n error = new CodexError(error.code, error.message, error.uncertain, error.rpcCode, {\n ...(isRecord(error.details) ? error.details : {}),\n pendingRequests: [...this.pending].map(([id, pending]) => ({ id, method: pending.method })),\n });\n this.failure = error;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pending.clear();\n this.emit('failure', error);\n }\n private async write(message: unknown): Promise<void> {\n if (this.failure) throw this.failure;\n const data = JSON.stringify(message) + '\\n';\n if (this.input.destroyed || !this.input.writable)\n throw new CodexError('CODEX_UNAVAILABLE', 'Codex stdin is closed', true);\n if (\n this.input.writableLength + Buffer.byteLength(data) >\n (this.options.maxWriteBytes ?? 4 * 1024 * 1024)\n ) {\n throw new CodexError('CODEX_BUSY', 'Codex stdin buffer limit reached');\n }\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n const error = new CodexError('RESULT_UNKNOWN', 'Codex stdin write timed out', true);\n this.fail(error);\n reject(error);\n }, this.options.timeoutMs ?? 30_000);\n this.input.write(data, (error) => {\n clearTimeout(timer);\n if (error) reject(new CodexError('CODEX_UNAVAILABLE', error.message, true));\n else resolve();\n });\n });\n }\n private receive(value: unknown): void {\n if (!isRecord(value))\n throw new CodexError('PROTOCOL_ERROR', 'Codex sent a non-object RPC message', true);\n const hasId = Object.hasOwn(value, 'id');\n if (hasId && !isRpcId(value.id))\n throw new CodexError('PROTOCOL_ERROR', 'Codex sent an invalid RPC id', true);\n if (typeof value.method === 'string') {\n if (Object.hasOwn(value, 'result') || Object.hasOwn(value, 'error'))\n throw new CodexError('PROTOCOL_ERROR', 'Codex mixed a method and RPC result', true);\n if (hasId) this.emit('request', value as RpcServerRequest);\n else this.emit('notification', value as RpcNotification);\n return;\n }\n if (!hasId || Object.hasOwn(value, 'result') === Object.hasOwn(value, 'error'))\n throw new CodexError('PROTOCOL_ERROR', 'Unrecognized Codex RPC message', true);\n const pending = this.pending.get(value.id as RpcId);\n if (!pending) return; // Late receipts after a timeout must never cause a prompt to be resent.\n this.pending.delete(value.id as RpcId);\n clearTimeout(pending.timer);\n if (Object.hasOwn(value, 'error')) {\n if (\n !isRecord(value.error) ||\n typeof value.error.message !== 'string' ||\n typeof value.error.code !== 'number'\n ) {\n const error = new CodexError('PROTOCOL_ERROR', 'Malformed Codex RPC error', true);\n pending.reject(error);\n throw error;\n }\n pending.reject(\n new CodexError(\n value.error.code === -32601 ? 'PROTOCOL_INCOMPATIBLE' : 'CODEX_REQUEST_FAILED',\n value.error.message,\n false,\n value.error.code,\n ),\n );\n } else pending.resolve(value.result);\n }\n}\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\nexport function isRpcId(value: unknown): value is RpcId {\n return typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value));\n}\nexport function asCodexError(error: unknown): CodexError {\n return error instanceof CodexError\n ? error\n : new CodexError(\n 'CODEX_UNAVAILABLE',\n error instanceof Error ? error.message : String(error),\n true,\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EventEmitter } from 'node:events';\nimport { AppError } from '../shared/errors.js';\nimport type { Readable, Writable } from 'node:stream';\n\nexport type RpcId = string | number;\nexport type RpcNotification = { method: string; params?: unknown };\nexport type RpcServerRequest = RpcNotification & { id: RpcId };\nexport const DEFAULT_MAX_LINE_BYTES = 64 * 1024 * 1024;\nexport class CodexError extends AppError {\n constructor(\n code: string,\n message: string,\n public readonly uncertain = false,\n public readonly rpcCode?: number,\n diagnostics?: Record<string, unknown>,\n ) {\n super(code, message, 503, { uncertain, rpcCode, ...diagnostics });\n this.name = 'CodexError';\n }\n}\n\n/** Buffers at most one bounded line. Decoding the complete byte sequence preserves split UTF-8. */\nexport class JsonLineReader {\n private parts: Buffer[] = [];\n private size = 0;\n private readonly decoder = new TextDecoder('utf-8', { fatal: true });\n constructor(\n private readonly consume: (value: unknown) => void,\n private readonly maxBytes = DEFAULT_MAX_LINE_BYTES,\n ) {}\n push(chunk: Buffer): void {\n let start = 0;\n while (start < chunk.length) {\n const newline = chunk.indexOf(10, start);\n const end = newline < 0 ? chunk.length : newline;\n const part = chunk.subarray(start, end);\n this.size += part.length;\n if (this.size > this.maxBytes) {\n const receivedBytes = this.size;\n this.parts = [];\n this.size = 0;\n throw new CodexError(\n 'PROTOCOL_ERROR',\n `Codex JSONL message exceeds the configured limit (received at least ${receivedBytes} bytes; limit ${this.maxBytes} bytes)`,\n true,\n undefined,\n { receivedBytes, maxLineBytes: this.maxBytes },\n );\n }\n if (part.length) this.parts.push(part);\n if (newline < 0) break;\n this.flush();\n start = newline + 1;\n }\n }\n finish(): void {\n if (this.size) this.flush();\n }\n private flush(): void {\n const bytes = Buffer.concat(this.parts, this.size);\n this.parts = [];\n this.size = 0;\n try {\n const line = this.decoder.decode(bytes).trim();\n if (line) this.consume(JSON.parse(line) as unknown);\n } catch (error) {\n if (error instanceof CodexError) throw error;\n throw new CodexError('PROTOCOL_ERROR', 'Invalid UTF-8 or JSON from Codex', true);\n }\n }\n}\n\ntype Pending = {\n method: string;\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n};\nexport type TransportOptions = {\n timeoutMs?: number;\n maxLineBytes?: number;\n maxPending?: number;\n maxWriteBytes?: number;\n};\n\n/** RPC receipt deadlines do not cancel upstream turns. A timeout always reports an uncertain outcome. */\nexport class RpcTransport extends EventEmitter {\n private readonly pending = new Map<RpcId, Pending>();\n private nextId = 0;\n private failure?: CodexError;\n private readonly reader: JsonLineReader;\n constructor(\n private readonly input: Writable,\n output: Readable,\n private readonly options: TransportOptions = {},\n ) {\n super();\n this.reader = new JsonLineReader((value) => this.receive(value), options.maxLineBytes);\n output.on('data', (chunk: Buffer) => {\n if (this.failure) return;\n try {\n this.reader.push(chunk);\n } catch (error) {\n this.fail(asCodexError(error));\n }\n });\n output.on('end', () => {\n if (this.failure) return;\n try {\n this.reader.finish();\n } catch (error) {\n this.fail(asCodexError(error));\n }\n this.fail(new CodexError('CODEX_UNAVAILABLE', 'Codex stdout reached EOF', true));\n });\n output.on('close', () =>\n this.fail(new CodexError('CODEX_UNAVAILABLE', 'Codex stdout closed', true)),\n );\n output.on('error', (error) =>\n this.fail(new CodexError('CODEX_UNAVAILABLE', error.message, true)),\n );\n input.on('error', (error) =>\n this.fail(new CodexError('CODEX_UNAVAILABLE', error.message, true)),\n );\n }\n request<T>(\n method: string,\n params: unknown,\n id: RpcId = this.nextId++,\n timeoutMs = this.options.timeoutMs ?? 30_000,\n ): Promise<T> {\n if (this.failure) return Promise.reject(this.failure);\n if (this.pending.has(id))\n return Promise.reject(new CodexError('PROTOCOL_ERROR', 'Duplicate outgoing RPC id'));\n if (this.pending.size >= (this.options.maxPending ?? 256))\n return Promise.reject(new CodexError('CODEX_BUSY', 'Too many pending Codex requests'));\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new CodexError(\n 'RESULT_UNKNOWN',\n `Codex ${method} acknowledgement timed out; it may have executed`,\n true,\n ),\n );\n }, timeoutMs);\n this.pending.set(id, { method, resolve: (result) => resolve(result as T), reject, timer });\n void this.write({ id, method, params }).catch((error: unknown) => {\n const pending = this.pending.get(id);\n if (!pending) return;\n clearTimeout(pending.timer);\n this.pending.delete(id);\n pending.reject(asCodexError(error));\n });\n });\n }\n notify(method: string, params?: unknown): Promise<void> {\n return this.write({ method, ...(params === undefined ? {} : { params }) });\n }\n respond(id: RpcId, result: unknown): Promise<void> {\n return this.write({ id, result });\n }\n reject(id: RpcId, code: number, message: string): Promise<void> {\n return this.write({ id, error: { code, message } });\n }\n fail(error: CodexError): void {\n if (this.failure) return;\n error = new CodexError(error.code, error.message, error.uncertain, error.rpcCode, {\n ...(isRecord(error.details) ? error.details : {}),\n pendingRequests: [...this.pending].map(([id, pending]) => ({ id, method: pending.method })),\n });\n this.failure = error;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pending.clear();\n this.emit('failure', error);\n }\n private async write(message: unknown): Promise<void> {\n if (this.failure) throw this.failure;\n const data = JSON.stringify(message) + '\\n';\n if (this.input.destroyed || !this.input.writable)\n throw new CodexError('CODEX_UNAVAILABLE', 'Codex stdin is closed', true);\n if (\n this.input.writableLength + Buffer.byteLength(data) >\n (this.options.maxWriteBytes ?? 4 * 1024 * 1024)\n ) {\n throw new CodexError('CODEX_BUSY', 'Codex stdin buffer limit reached');\n }\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n const error = new CodexError('RESULT_UNKNOWN', 'Codex stdin write timed out', true);\n this.fail(error);\n reject(error);\n }, this.options.timeoutMs ?? 30_000);\n this.input.write(data, (error) => {\n clearTimeout(timer);\n if (error) reject(new CodexError('CODEX_UNAVAILABLE', error.message, true));\n else resolve();\n });\n });\n }\n private receive(value: unknown): void {\n if (!isRecord(value))\n throw new CodexError('PROTOCOL_ERROR', 'Codex sent a non-object RPC message', true);\n const hasId = Object.hasOwn(value, 'id');\n if (hasId && !isRpcId(value.id))\n throw new CodexError('PROTOCOL_ERROR', 'Codex sent an invalid RPC id', true);\n if (typeof value.method === 'string') {\n if (Object.hasOwn(value, 'result') || Object.hasOwn(value, 'error'))\n throw new CodexError('PROTOCOL_ERROR', 'Codex mixed a method and RPC result', true);\n if (hasId) this.emit('request', value as RpcServerRequest);\n else this.emit('notification', value as RpcNotification);\n return;\n }\n if (!hasId || Object.hasOwn(value, 'result') === Object.hasOwn(value, 'error'))\n throw new CodexError('PROTOCOL_ERROR', 'Unrecognized Codex RPC message', true);\n const pending = this.pending.get(value.id as RpcId);\n if (!pending) return; // Late receipts after a timeout must never cause a prompt to be resent.\n this.pending.delete(value.id as RpcId);\n clearTimeout(pending.timer);\n if (Object.hasOwn(value, 'error')) {\n if (\n !isRecord(value.error) ||\n typeof value.error.message !== 'string' ||\n typeof value.error.code !== 'number'\n ) {\n const error = new CodexError('PROTOCOL_ERROR', 'Malformed Codex RPC error', true);\n pending.reject(error);\n throw error;\n }\n pending.reject(\n new CodexError(\n value.error.code === -32601 ? 'PROTOCOL_INCOMPATIBLE' : 'CODEX_REQUEST_FAILED',\n value.error.message,\n false,\n value.error.code,\n ),\n );\n } else pending.resolve(value.result);\n }\n}\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\nexport function isRpcId(value: unknown): value is RpcId {\n return typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value));\n}\nexport function asCodexError(error: unknown): CodexError {\n return error instanceof CodexError\n ? error\n : new CodexError(\n 'CODEX_UNAVAILABLE',\n error instanceof Error ? error.message : String(error),\n true,\n );\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -12,6 +12,7 @@ import { socketHandlers } from "./websocket.js";
12
12
  import { z } from "zod";
13
13
  import { releaseChannels } from "../shared/updates.js";
14
14
  import { settingsSchema, updateRequestSchema } from "../updates/manager.js";
15
+ import { startShellSchema } from "../shared/shell.js";
15
16
  function createApp({
16
17
  config,
17
18
  service,
@@ -83,6 +84,29 @@ function createApp({
83
84
  return c.json(await configuration.export());
84
85
  });
85
86
  app.get("/api/system", (c) => c.json(service.getSystem()));
87
+ app.get(
88
+ "/api/shell",
89
+ (c) => c.json(
90
+ service.shell.snapshot(
91
+ c.req.query("epoch"),
92
+ z.coerce.number().int().min(0).max(Number.MAX_SAFE_INTEGER).default(0).parse(c.req.query("after"))
93
+ )
94
+ )
95
+ );
96
+ app.post("/api/projects/:projectId/shell", async (c) => {
97
+ const op = await service.startShell(
98
+ id(c.req.param("projectId")),
99
+ await parse(c, startShellSchema)
100
+ );
101
+ return c.json(op, status(op));
102
+ });
103
+ app.post("/api/shell/:commandId/interrupt", async (c) => {
104
+ const op = await service.interruptShell(
105
+ id(c.req.param("commandId")),
106
+ await parse(c, schemas.operationSchema)
107
+ );
108
+ return c.json(op, status(op));
109
+ });
86
110
  app.post("/api/system/compatibility/confirm", async (c) => {
87
111
  const op = await service.confirmCompatibility(
88
112
  await parse(c, schemas.confirmCompatibilitySchema)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/server/http.ts"],
4
- "sourcesContent": ["import { Hono } from 'hono';\nimport { bodyLimit } from 'hono/body-limit';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport { upgradeWebSocket, type HttpBindings } from '@hono/node-server';\nimport type { ZodType } from 'zod';\nimport { ZodError } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport { IMAGE_REQUEST_BYTES } from '../shared/images.js';\nimport * as schemas from '../shared/validation.js';\nimport type { Operation } from '../shared/types.js';\nimport type { AppConfig } from './config.js';\nimport { ConfigStore } from './config-store.js';\nimport { createAuthenticator, type Authenticator, type Identity } from './auth.js';\nimport type { GatewayService } from './service.js';\nimport { socketHandlers } from './websocket.js';\nimport { z } from 'zod';\nimport { releaseChannels, type UpdateController } from '../shared/updates.js';\nimport { settingsSchema, updateRequestSchema } from '../updates/manager.js';\nexport interface HttpDependencies {\n config: AppConfig;\n service: GatewayService;\n staticDir: string;\n authenticate?: Authenticator;\n updates?: UpdateController;\n}\nexport function createApp({\n config,\n service,\n staticDir,\n authenticate = createAuthenticator(config),\n updates,\n}: HttpDependencies) {\n const app = new Hono<{\n Bindings: HttpBindings;\n Variables: { identity: Identity; operationId?: string };\n }>();\n app.onError((error, c) => {\n const failure =\n error instanceof AppError\n ? error\n : error instanceof ZodError || error instanceof SyntaxError\n ? new AppError('VALIDATION_FAILED', 'Invalid request data', 400)\n : new AppError('INTERNAL_ERROR', 'An internal error occurred', 500);\n return c.json({ error: failure.toJSON(c.get('operationId')) }, failure.status as 400);\n });\n app.get('/api/health', (c) => c.json({ ok: true }));\n app.use('*', async (c, next) => {\n c.set('identity', await authenticate(c.req.raw, c.req.path === '/ws'));\n await next();\n });\n app.use('/api/*', (c, next) =>\n bodyLimit({\n maxSize: /^\\/api\\/threads\\/[^/]+\\/(turns|steer)$/.test(c.req.path)\n ? IMAGE_REQUEST_BYTES\n : 512 * 1024,\n onError: () => {\n throw new AppError('BODY_TOO_LARGE', 'Request body exceeds limit', 413);\n },\n })(c, next),\n );\n app.use('/api/*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n if (\n service.isInMaintenance() &&\n !['GET', 'HEAD', 'OPTIONS'].includes(c.req.method) &&\n !c.req.path.startsWith('/api/updates')\n )\n throw new AppError('UPDATE_MAINTENANCE', '\u6B63\u5728\u5347\u7EA7\uFF0C\u8BF7\u7B49\u5F85\u670D\u52A1\u91CD\u65B0\u8FDE\u63A5', 503);\n await next();\n });\n if (updates) {\n app.get('/api/updates', async (c) => c.json(await updates.status()));\n app.post('/api/updates/check', async (c) => {\n const input = z\n .object({ channel: z.enum(releaseChannels).optional() })\n .strict()\n .parse(await c.req.json());\n return c.json(await updates.check(input.channel));\n });\n app.post('/api/updates/settings', async (c) =>\n c.json(await updates.configure(settingsSchema.parse(await c.req.json()))),\n );\n app.post('/api/updates/run', async (c) =>\n c.json(await updates.start(updateRequestSchema.parse(await c.req.json())), 202),\n );\n }\n const id = (value: string) => schemas.idSchema.parse(value);\n const parse = async <T>(\n c: { req: { json: () => Promise<unknown> }; set: (key: 'operationId', value: string) => void },\n schema: ZodType<T>,\n ): Promise<T> => {\n const value: unknown = await c.req.json();\n if (\n value &&\n typeof value === 'object' &&\n 'clientOperationId' in value &&\n typeof value.clientOperationId === 'string'\n )\n c.set('operationId', value.clientOperationId);\n return schema.parse(value);\n };\n const status = (op: Operation) =>\n op.status === 'dispatching' || op.status === 'accepted' ? (202 as const) : (200 as const);\n const configuration = new ConfigStore(config);\n app.use('/api/config*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n await next();\n });\n app.get('/api/config', async (c) => c.json(await configuration.snapshot()));\n app.post('/api/config', async (c) => c.json(await configuration.save(await c.req.json())));\n app.post('/api/config/import', async (c) =>\n c.json(await configuration.previewImport(await c.req.json())),\n );\n app.get('/api/config/export', async (c) => {\n c.header('Content-Disposition', 'attachment; filename=\"codex-web-config.json\"');\n return c.json(await configuration.export());\n });\n app.get('/api/system', (c) => c.json(service.getSystem()));\n app.post('/api/system/compatibility/confirm', async (c) => {\n const op = await service.confirmCompatibility(\n await parse(c, schemas.confirmCompatibilitySchema),\n );\n return c.json(op, status(op));\n });\n app.get('/api/projects', async (c) => c.json({ data: await service.listProjects() }));\n app.get('/api/projects/:projectId/runtime-settings', async (c) =>\n c.json(await service.readRuntimeSettings(id(c.req.param('projectId')))),\n );\n app.post('/api/projects/:projectId/model-defaults', async (c) => {\n const op = await service.saveModelDefaults(\n id(c.req.param('projectId')),\n await parse(c, schemas.saveModelDefaultsSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/projects', async (c) =>\n c.json(service.addProject(await parse(c, schemas.createProjectSchema))),\n );\n app.post('/api/projects/:projectId/rename', async (c) =>\n c.json(\n service.renameProject(\n id(c.req.param('projectId')),\n await parse(c, schemas.renameProjectSchema),\n ),\n ),\n );\n app.post('/api/projects/:projectId/remove', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.removeProject(id(c.req.param('projectId'))));\n });\n app.get('/api/projects/:projectId/threads', async (c) =>\n c.json(\n await service.listThreadSummaries(\n id(c.req.param('projectId')),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.get('/api/threads', async (c) =>\n c.json(\n await service.listThreads(\n id(c.req.query('projectId') ?? ''),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.post('/api/threads/:threadId/archive', async (c) => {\n return c.json(\n await service.setThreadArchived(\n id(c.req.param('threadId')),\n await parse(c, schemas.archiveThreadSchema),\n ),\n );\n });\n app.get('/api/threads/:threadId', async (c) =>\n c.json(await service.getThread(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/metadata', async (c) =>\n c.json(await service.getThreadMetadata(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/export', async (c) => {\n const exported = await service.exportThread(id(c.req.param('threadId')));\n c.header('Content-Type', 'text/markdown; charset=utf-8');\n c.header('Content-Disposition', `attachment; filename=\"${exported.filename}\"`);\n c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('X-Export-Completeness', exported.partial ? 'partial' : 'saved-history');\n return c.body(exported.markdown);\n });\n app.get('/api/threads/:threadId/images', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n c.header('Content-Security-Policy', \"default-src 'none'; sandbox\");\n const image = await service.getThreadImage(id(c.req.param('threadId')), c.req.query('path'));\n return c.body(image.bytes, 200, {\n 'Content-Type': image.contentType,\n 'Content-Length': String(image.bytes.length),\n });\n });\n app.get('/api/threads/:threadId/markdown', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(\n await service.getThreadMarkdown(id(c.req.param('threadId')), c.req.query('path')),\n );\n });\n app.get('/api/threads/:threadId/json', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(await service.getThreadJson(id(c.req.param('threadId')), c.req.query('path')));\n });\n app.get('/api/operations/:operationId', (c) =>\n c.json(service.getOperation(id(c.req.param('operationId')))),\n );\n app.post('/api/projects/:projectId/threads', async (c) => {\n const op = await service.createThread(\n id(c.req.param('projectId')),\n await parse(c, schemas.createThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/resume', async (c) => {\n const op = await service.resumeThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.resumeThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/fork', async (c) => {\n const op = await service.forkThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.forkThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/rename', async (c) => {\n const op = await service.renameThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.renameThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/turns', async (c) => {\n const op = await service.startTurn(\n id(c.req.param('threadId')),\n await parse(c, schemas.startTurnSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/review', async (c) => {\n const op = await service.startReview(\n id(c.req.param('threadId')),\n await parse(c, schemas.startReviewSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/steer', async (c) => {\n const op = await service.steer(\n id(c.req.param('threadId')),\n await parse(c, schemas.steerSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/compact', async (c) => {\n const op = await service.compactThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/interrupt', async (c) => {\n const op = await service.interrupt(\n id(c.req.param('threadId')),\n await parse(c, schemas.interruptSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/actions/:actionId/resolve', async (c) => {\n const op = await service.resolveAction(\n id(c.req.param('actionId')),\n await parse(c, schemas.resolveActionSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/operations/:operationId/reconcile', async (c) => {\n const op = await service.reconcileOperation(\n id(c.req.param('operationId')),\n await parse(c, schemas.reconcileSchema),\n );\n return c.json(op, status(op));\n });\n app.get(\n '/ws',\n upgradeWebSocket((c) =>\n socketHandlers(service, config, c.get('identity'), c.req.query('overview') === '1'),\n ),\n );\n app.all('/api/*', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/api', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/ws/*', () => {\n throw new AppError('NOT_FOUND', 'WebSocket route not found', 404);\n });\n app.use('*', async (c, next) => {\n if (!c.req.path.startsWith('/assets/')) c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'same-origin');\n c.header(\n 'Content-Security-Policy',\n // Remote Markdown images load directly in the browser, including HTTPS redirects.\n // Keep scripts, connections and all other resource policies restricted as before.\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'\",\n );\n await next();\n });\n app.get('*', serveStatic({ root: staticDir }));\n app.get('*', async (c, next) => {\n if (!c.req.header('accept')?.includes('text/html') || /\\.[^/]+$/.test(c.req.path))\n return c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404);\n return serveStatic({ path: `${staticDir}/index.html` })(c, next);\n });\n app.notFound((c) => c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404));\n return app;\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,wBAA2C;AAEpD,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,2BAA2B;AACpC,YAAY,aAAa;AAGzB,SAAS,mBAAmB;AAC5B,SAAS,2BAA8D;AAEvE,SAAS,sBAAsB;AAC/B,SAAS,SAAS;AAClB,SAAS,uBAA8C;AACvD,SAAS,gBAAgB,2BAA2B;AAQ7C,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,oBAAoB,MAAM;AAAA,EACzC;AACF,GAAqB;AACnB,QAAM,MAAM,IAAI,KAGb;AACH,MAAI,QAAQ,CAAC,OAAO,MAAM;AACxB,UAAM,UACJ,iBAAiB,WACb,QACA,iBAAiB,YAAY,iBAAiB,cAC5C,IAAI,SAAS,qBAAqB,wBAAwB,GAAG,IAC7D,IAAI,SAAS,kBAAkB,8BAA8B,GAAG;AACxE,WAAO,EAAE,KAAK,EAAE,OAAO,QAAQ,OAAO,EAAE,IAAI,aAAa,CAAC,EAAE,GAAG,QAAQ,MAAa;AAAA,EACtF,CAAC;AACD,MAAI,IAAI,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,CAAC;AAClD,MAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AAC9B,MAAE,IAAI,YAAY,MAAM,aAAa,EAAE,IAAI,KAAK,EAAE,IAAI,SAAS,KAAK,CAAC;AACrE,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAAU,CAAC,GAAG,SACpB,UAAU;AAAA,MACR,SAAS,yCAAyC,KAAK,EAAE,IAAI,IAAI,IAC7D,sBACA,MAAM;AAAA,MACV,SAAS,MAAM;AACb,cAAM,IAAI,SAAS,kBAAkB,8BAA8B,GAAG;AAAA,MACxE;AAAA,IACF,CAAC,EAAE,GAAG,IAAI;AAAA,EACZ;AACA,MAAI,IAAI,UAAU,OAAO,GAAG,SAAS;AACnC,MAAE,OAAO,iBAAiB,UAAU;AACpC,QACE,QAAQ,gBAAgB,KACxB,CAAC,CAAC,OAAO,QAAQ,SAAS,EAAE,SAAS,EAAE,IAAI,MAAM,KACjD,CAAC,EAAE,IAAI,KAAK,WAAW,cAAc;AAErC,YAAM,IAAI,SAAS,sBAAsB,wFAAkB,GAAG;AAChE,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI,SAAS;AACX,QAAI,IAAI,gBAAgB,OAAO,MAAM,EAAE,KAAK,MAAM,QAAQ,OAAO,CAAC,CAAC;AACnE,QAAI,KAAK,sBAAsB,OAAO,MAAM;AAC1C,YAAM,QAAQ,EACX,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,SAAS,EAAE,CAAC,EACtD,OAAO,EACP,MAAM,MAAM,EAAE,IAAI,KAAK,CAAC;AAC3B,aAAO,EAAE,KAAK,MAAM,QAAQ,MAAM,MAAM,OAAO,CAAC;AAAA,IAClD,CAAC;AACD,QAAI;AAAA,MAAK;AAAA,MAAyB,OAAO,MACvC,EAAE,KAAK,MAAM,QAAQ,UAAU,eAAe,MAAM,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC;AAAA,IAC1E;AACA,QAAI;AAAA,MAAK;AAAA,MAAoB,OAAO,MAClC,EAAE,KAAK,MAAM,QAAQ,MAAM,oBAAoB,MAAM,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,GAAG,GAAG;AAAA,IAChF;AAAA,EACF;AACA,QAAM,KAAK,CAAC,UAAkB,QAAQ,SAAS,MAAM,KAAK;AAC1D,QAAM,QAAQ,OACZ,GACA,WACe;AACf,UAAM,QAAiB,MAAM,EAAE,IAAI,KAAK;AACxC,QACE,SACA,OAAO,UAAU,YACjB,uBAAuB,SACvB,OAAO,MAAM,sBAAsB;AAEnC,QAAE,IAAI,eAAe,MAAM,iBAAiB;AAC9C,WAAO,OAAO,MAAM,KAAK;AAAA,EAC3B;AACA,QAAM,SAAS,CAAC,OACd,GAAG,WAAW,iBAAiB,GAAG,WAAW,aAAc,MAAiB;AAC9E,QAAM,gBAAgB,IAAI,YAAY,MAAM;AAC5C,MAAI,IAAI,gBAAgB,OAAO,GAAG,SAAS;AACzC,MAAE,OAAO,iBAAiB,UAAU;AACpC,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI,IAAI,eAAe,OAAO,MAAM,EAAE,KAAK,MAAM,cAAc,SAAS,CAAC,CAAC;AAC1E,MAAI,KAAK,eAAe,OAAO,MAAM,EAAE,KAAK,MAAM,cAAc,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC;AACzF,MAAI;AAAA,IAAK;AAAA,IAAsB,OAAO,MACpC,EAAE,KAAK,MAAM,cAAc,cAAc,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC;AAAA,EAC9D;AACA,MAAI,IAAI,sBAAsB,OAAO,MAAM;AACzC,MAAE,OAAO,uBAAuB,8CAA8C;AAC9E,WAAO,EAAE,KAAK,MAAM,cAAc,OAAO,CAAC;AAAA,EAC5C,CAAC;AACD,MAAI,IAAI,eAAe,CAAC,MAAM,EAAE,KAAK,QAAQ,UAAU,CAAC,CAAC;AACzD,MAAI,KAAK,qCAAqC,OAAO,MAAM;AACzD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,MAAM,MAAM,GAAG,QAAQ,0BAA0B;AAAA,IACnD;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,IAAI,iBAAiB,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,MAAM,QAAQ,aAAa,EAAE,CAAC,CAAC;AACpF,MAAI;AAAA,IAAI;AAAA,IAA6C,OAAO,MAC1D,EAAE,KAAK,MAAM,QAAQ,oBAAoB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC;AAAA,EACxE;AACA,MAAI,KAAK,2CAA2C,OAAO,MAAM;AAC/D,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,MAC3B,MAAM,MAAM,GAAG,QAAQ,uBAAuB;AAAA,IAChD;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI;AAAA,IAAK;AAAA,IAAiB,OAAO,MAC/B,EAAE,KAAK,QAAQ,WAAW,MAAM,MAAM,GAAG,QAAQ,mBAAmB,CAAC,CAAC;AAAA,EACxE;AACA,MAAI;AAAA,IAAK;AAAA,IAAmC,OAAO,MACjD,EAAE;AAAA,MACA,QAAQ;AAAA,QACN,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,QAC3B,MAAM,MAAM,GAAG,QAAQ,mBAAmB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,mCAAmC,OAAO,MAAM;AACvD,UAAM,MAAM,GAAG,QAAQ,mBAAmB;AAC1C,WAAO,EAAE,KAAK,QAAQ,cAAc,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC;AAAA,EACnE,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAAoC,OAAO,MACjD,EAAE;AAAA,MACA,MAAM,QAAQ;AAAA,QACZ,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,QAC3B,EAAE,IAAI,MAAM,QAAQ,MAAM,SACtB,SACA,QAAQ,aAAa,MAAM,EAAE,IAAI,MAAM,QAAQ,CAAC;AAAA,QACpD,QAAQ,oBAAoB,MAAM,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACA,MAAI;AAAA,IAAI;AAAA,IAAgB,OAAO,MAC7B,EAAE;AAAA,MACA,MAAM,QAAQ;AAAA,QACZ,GAAG,EAAE,IAAI,MAAM,WAAW,KAAK,EAAE;AAAA,QACjC,EAAE,IAAI,MAAM,QAAQ,MAAM,SACtB,SACA,QAAQ,aAAa,MAAM,EAAE,IAAI,MAAM,QAAQ,CAAC;AAAA,QACpD,QAAQ,oBAAoB,MAAM,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,WAAO,EAAE;AAAA,MACP,MAAM,QAAQ;AAAA,QACZ,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,QAC1B,MAAM,MAAM,GAAG,QAAQ,mBAAmB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAA0B,OAAO,MACvC,EAAE,KAAK,MAAM,QAAQ,UAAU,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EAC7D;AACA,MAAI;AAAA,IAAI;AAAA,IAAmC,OAAO,MAChD,EAAE,KAAK,MAAM,QAAQ,kBAAkB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EACrE;AACA,MAAI,IAAI,iCAAiC,OAAO,MAAM;AACpD,UAAM,WAAW,MAAM,QAAQ,aAAa,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC;AACvE,MAAE,OAAO,gBAAgB,8BAA8B;AACvD,MAAE,OAAO,uBAAuB,yBAAyB,SAAS,QAAQ,GAAG;AAC7E,MAAE,OAAO,iBAAiB,UAAU;AACpC,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,yBAAyB,SAAS,UAAU,YAAY,eAAe;AAChF,WAAO,EAAE,KAAK,SAAS,QAAQ;AAAA,EACjC,CAAC;AACD,MAAI,IAAI,iCAAiC,OAAO,MAAM;AACpD,MAAE,OAAO,iBAAiB,mBAAmB;AAC7C,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,gCAAgC,aAAa;AACtD,MAAE,OAAO,2BAA2B,6BAA6B;AACjE,UAAM,QAAQ,MAAM,QAAQ,eAAe,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC;AAC3F,WAAO,EAAE,KAAK,MAAM,OAAO,KAAK;AAAA,MAC9B,gBAAgB,MAAM;AAAA,MACtB,kBAAkB,OAAO,MAAM,MAAM,MAAM;AAAA,IAC7C,CAAC;AAAA,EACH,CAAC;AACD,MAAI,IAAI,mCAAmC,OAAO,MAAM;AACtD,MAAE,OAAO,iBAAiB,mBAAmB;AAC7C,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,gCAAgC,aAAa;AACtD,WAAO,EAAE;AAAA,MACP,MAAM,QAAQ,kBAAkB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC;AAAA,IAClF;AAAA,EACF,CAAC;AACD,MAAI,IAAI,+BAA+B,OAAO,MAAM;AAClD,MAAE,OAAO,iBAAiB,mBAAmB;AAC7C,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,gCAAgC,aAAa;AACtD,WAAO,EAAE,KAAK,MAAM,QAAQ,cAAc,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,EAC7F,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAAgC,CAAC,MACvC,EAAE,KAAK,QAAQ,aAAa,GAAG,EAAE,IAAI,MAAM,aAAa,CAAC,CAAC,CAAC;AAAA,EAC7D;AACA,MAAI,KAAK,oCAAoC,OAAO,MAAM;AACxD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,MAC3B,MAAM,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IAC3C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,iCAAiC,OAAO,MAAM;AACrD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IAC3C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,+BAA+B,OAAO,MAAM;AACnD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,gBAAgB;AAAA,IACzC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,iCAAiC,OAAO,MAAM;AACrD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IAC3C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,gCAAgC,OAAO,MAAM;AACpD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,iCAAiC,OAAO,MAAM;AACrD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,iBAAiB;AAAA,IAC1C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,gCAAgC,OAAO,MAAM;AACpD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,WAAW;AAAA,IACpC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,oCAAoC,OAAO,MAAM;AACxD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,mBAAmB;AAAA,IAC5C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,0CAA0C,OAAO,MAAM;AAC9D,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,aAAa,CAAC;AAAA,MAC7B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI;AAAA,IACF;AAAA,IACA;AAAA,MAAiB,CAAC,MAChB,eAAe,SAAS,QAAQ,EAAE,IAAI,UAAU,GAAG,EAAE,IAAI,MAAM,UAAU,MAAM,GAAG;AAAA,IACpF;AAAA,EACF;AACA,MAAI,IAAI,UAAU,MAAM;AACtB,UAAM,IAAI,SAAS,aAAa,uBAAuB,GAAG;AAAA,EAC5D,CAAC;AACD,MAAI,IAAI,QAAQ,MAAM;AACpB,UAAM,IAAI,SAAS,aAAa,uBAAuB,GAAG;AAAA,EAC5D,CAAC;AACD,MAAI,IAAI,SAAS,MAAM;AACrB,UAAM,IAAI,SAAS,aAAa,6BAA6B,GAAG;AAAA,EAClE,CAAC;AACD,MAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AAC9B,QAAI,CAAC,EAAE,IAAI,KAAK,WAAW,UAAU,EAAG,GAAE,OAAO,iBAAiB,UAAU;AAC5E,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,mBAAmB,aAAa;AACzC,MAAE;AAAA,MACA;AAAA;AAAA;AAAA,MAGA;AAAA,IACF;AACA,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI,IAAI,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC,CAAC;AAC7C,MAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AAC9B,QAAI,CAAC,EAAE,IAAI,OAAO,QAAQ,GAAG,SAAS,WAAW,KAAK,WAAW,KAAK,EAAE,IAAI,IAAI;AAC9E,aAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,SAAS,qBAAqB,EAAE,GAAG,GAAG;AACpF,WAAO,YAAY,EAAE,MAAM,GAAG,SAAS,cAAc,CAAC,EAAE,GAAG,IAAI;AAAA,EACjE,CAAC;AACD,MAAI,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,SAAS,qBAAqB,EAAE,GAAG,GAAG,CAAC;AAChG,SAAO;AACT;",
4
+ "sourcesContent": ["import { Hono } from 'hono';\nimport { bodyLimit } from 'hono/body-limit';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport { upgradeWebSocket, type HttpBindings } from '@hono/node-server';\nimport type { ZodType } from 'zod';\nimport { ZodError } from 'zod';\nimport { AppError } from '../shared/errors.js';\nimport { IMAGE_REQUEST_BYTES } from '../shared/images.js';\nimport * as schemas from '../shared/validation.js';\nimport type { Operation } from '../shared/types.js';\nimport type { AppConfig } from './config.js';\nimport { ConfigStore } from './config-store.js';\nimport { createAuthenticator, type Authenticator, type Identity } from './auth.js';\nimport type { GatewayService } from './service.js';\nimport { socketHandlers } from './websocket.js';\nimport { z } from 'zod';\nimport { releaseChannels, type UpdateController } from '../shared/updates.js';\nimport { settingsSchema, updateRequestSchema } from '../updates/manager.js';\nimport { startShellSchema } from '../shared/shell.js';\nexport interface HttpDependencies {\n config: AppConfig;\n service: GatewayService;\n staticDir: string;\n authenticate?: Authenticator;\n updates?: UpdateController;\n}\nexport function createApp({\n config,\n service,\n staticDir,\n authenticate = createAuthenticator(config),\n updates,\n}: HttpDependencies) {\n const app = new Hono<{\n Bindings: HttpBindings;\n Variables: { identity: Identity; operationId?: string };\n }>();\n app.onError((error, c) => {\n const failure =\n error instanceof AppError\n ? error\n : error instanceof ZodError || error instanceof SyntaxError\n ? new AppError('VALIDATION_FAILED', 'Invalid request data', 400)\n : new AppError('INTERNAL_ERROR', 'An internal error occurred', 500);\n return c.json({ error: failure.toJSON(c.get('operationId')) }, failure.status as 400);\n });\n app.get('/api/health', (c) => c.json({ ok: true }));\n app.use('*', async (c, next) => {\n c.set('identity', await authenticate(c.req.raw, c.req.path === '/ws'));\n await next();\n });\n app.use('/api/*', (c, next) =>\n bodyLimit({\n maxSize: /^\\/api\\/threads\\/[^/]+\\/(turns|steer)$/.test(c.req.path)\n ? IMAGE_REQUEST_BYTES\n : 512 * 1024,\n onError: () => {\n throw new AppError('BODY_TOO_LARGE', 'Request body exceeds limit', 413);\n },\n })(c, next),\n );\n app.use('/api/*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n if (\n service.isInMaintenance() &&\n !['GET', 'HEAD', 'OPTIONS'].includes(c.req.method) &&\n !c.req.path.startsWith('/api/updates')\n )\n throw new AppError('UPDATE_MAINTENANCE', '\u6B63\u5728\u5347\u7EA7\uFF0C\u8BF7\u7B49\u5F85\u670D\u52A1\u91CD\u65B0\u8FDE\u63A5', 503);\n await next();\n });\n if (updates) {\n app.get('/api/updates', async (c) => c.json(await updates.status()));\n app.post('/api/updates/check', async (c) => {\n const input = z\n .object({ channel: z.enum(releaseChannels).optional() })\n .strict()\n .parse(await c.req.json());\n return c.json(await updates.check(input.channel));\n });\n app.post('/api/updates/settings', async (c) =>\n c.json(await updates.configure(settingsSchema.parse(await c.req.json()))),\n );\n app.post('/api/updates/run', async (c) =>\n c.json(await updates.start(updateRequestSchema.parse(await c.req.json())), 202),\n );\n }\n const id = (value: string) => schemas.idSchema.parse(value);\n const parse = async <T>(\n c: { req: { json: () => Promise<unknown> }; set: (key: 'operationId', value: string) => void },\n schema: ZodType<T>,\n ): Promise<T> => {\n const value: unknown = await c.req.json();\n if (\n value &&\n typeof value === 'object' &&\n 'clientOperationId' in value &&\n typeof value.clientOperationId === 'string'\n )\n c.set('operationId', value.clientOperationId);\n return schema.parse(value);\n };\n const status = (op: Operation) =>\n op.status === 'dispatching' || op.status === 'accepted' ? (202 as const) : (200 as const);\n const configuration = new ConfigStore(config);\n app.use('/api/config*', async (c, next) => {\n c.header('Cache-Control', 'no-store');\n await next();\n });\n app.get('/api/config', async (c) => c.json(await configuration.snapshot()));\n app.post('/api/config', async (c) => c.json(await configuration.save(await c.req.json())));\n app.post('/api/config/import', async (c) =>\n c.json(await configuration.previewImport(await c.req.json())),\n );\n app.get('/api/config/export', async (c) => {\n c.header('Content-Disposition', 'attachment; filename=\"codex-web-config.json\"');\n return c.json(await configuration.export());\n });\n app.get('/api/system', (c) => c.json(service.getSystem()));\n app.get('/api/shell', (c) =>\n c.json(\n service.shell.snapshot(\n c.req.query('epoch'),\n z.coerce\n .number()\n .int()\n .min(0)\n .max(Number.MAX_SAFE_INTEGER)\n .default(0)\n .parse(c.req.query('after')),\n ),\n ),\n );\n app.post('/api/projects/:projectId/shell', async (c) => {\n const op = await service.startShell(\n id(c.req.param('projectId')),\n await parse(c, startShellSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/shell/:commandId/interrupt', async (c) => {\n const op = await service.interruptShell(\n id(c.req.param('commandId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/system/compatibility/confirm', async (c) => {\n const op = await service.confirmCompatibility(\n await parse(c, schemas.confirmCompatibilitySchema),\n );\n return c.json(op, status(op));\n });\n app.get('/api/projects', async (c) => c.json({ data: await service.listProjects() }));\n app.get('/api/projects/:projectId/runtime-settings', async (c) =>\n c.json(await service.readRuntimeSettings(id(c.req.param('projectId')))),\n );\n app.post('/api/projects/:projectId/model-defaults', async (c) => {\n const op = await service.saveModelDefaults(\n id(c.req.param('projectId')),\n await parse(c, schemas.saveModelDefaultsSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/projects', async (c) =>\n c.json(service.addProject(await parse(c, schemas.createProjectSchema))),\n );\n app.post('/api/projects/:projectId/rename', async (c) =>\n c.json(\n service.renameProject(\n id(c.req.param('projectId')),\n await parse(c, schemas.renameProjectSchema),\n ),\n ),\n );\n app.post('/api/projects/:projectId/remove', async (c) => {\n await parse(c, schemas.removeProjectSchema);\n return c.json(service.removeProject(id(c.req.param('projectId'))));\n });\n app.get('/api/projects/:projectId/threads', async (c) =>\n c.json(\n await service.listThreadSummaries(\n id(c.req.param('projectId')),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.get('/api/threads', async (c) =>\n c.json(\n await service.listThreads(\n id(c.req.query('projectId') ?? ''),\n c.req.query('cursor') === undefined\n ? undefined\n : schemas.cursorSchema.parse(c.req.query('cursor')),\n schemas.archivedQuerySchema.parse(c.req.query('archived')),\n ),\n ),\n );\n app.post('/api/threads/:threadId/archive', async (c) => {\n return c.json(\n await service.setThreadArchived(\n id(c.req.param('threadId')),\n await parse(c, schemas.archiveThreadSchema),\n ),\n );\n });\n app.get('/api/threads/:threadId', async (c) =>\n c.json(await service.getThread(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/metadata', async (c) =>\n c.json(await service.getThreadMetadata(id(c.req.param('threadId')))),\n );\n app.get('/api/threads/:threadId/export', async (c) => {\n const exported = await service.exportThread(id(c.req.param('threadId')));\n c.header('Content-Type', 'text/markdown; charset=utf-8');\n c.header('Content-Disposition', `attachment; filename=\"${exported.filename}\"`);\n c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('X-Export-Completeness', exported.partial ? 'partial' : 'saved-history');\n return c.body(exported.markdown);\n });\n app.get('/api/threads/:threadId/images', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n c.header('Content-Security-Policy', \"default-src 'none'; sandbox\");\n const image = await service.getThreadImage(id(c.req.param('threadId')), c.req.query('path'));\n return c.body(image.bytes, 200, {\n 'Content-Type': image.contentType,\n 'Content-Length': String(image.bytes.length),\n });\n });\n app.get('/api/threads/:threadId/markdown', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(\n await service.getThreadMarkdown(id(c.req.param('threadId')), c.req.query('path')),\n );\n });\n app.get('/api/threads/:threadId/json', async (c) => {\n c.header('Cache-Control', 'private, no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Cross-Origin-Resource-Policy', 'same-origin');\n return c.json(await service.getThreadJson(id(c.req.param('threadId')), c.req.query('path')));\n });\n app.get('/api/operations/:operationId', (c) =>\n c.json(service.getOperation(id(c.req.param('operationId')))),\n );\n app.post('/api/projects/:projectId/threads', async (c) => {\n const op = await service.createThread(\n id(c.req.param('projectId')),\n await parse(c, schemas.createThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/resume', async (c) => {\n const op = await service.resumeThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.resumeThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/fork', async (c) => {\n const op = await service.forkThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.forkThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/rename', async (c) => {\n const op = await service.renameThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.renameThreadSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/turns', async (c) => {\n const op = await service.startTurn(\n id(c.req.param('threadId')),\n await parse(c, schemas.startTurnSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/review', async (c) => {\n const op = await service.startReview(\n id(c.req.param('threadId')),\n await parse(c, schemas.startReviewSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/steer', async (c) => {\n const op = await service.steer(\n id(c.req.param('threadId')),\n await parse(c, schemas.steerSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/compact', async (c) => {\n const op = await service.compactThread(\n id(c.req.param('threadId')),\n await parse(c, schemas.operationSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/threads/:threadId/interrupt', async (c) => {\n const op = await service.interrupt(\n id(c.req.param('threadId')),\n await parse(c, schemas.interruptSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/actions/:actionId/resolve', async (c) => {\n const op = await service.resolveAction(\n id(c.req.param('actionId')),\n await parse(c, schemas.resolveActionSchema),\n );\n return c.json(op, status(op));\n });\n app.post('/api/operations/:operationId/reconcile', async (c) => {\n const op = await service.reconcileOperation(\n id(c.req.param('operationId')),\n await parse(c, schemas.reconcileSchema),\n );\n return c.json(op, status(op));\n });\n app.get(\n '/ws',\n upgradeWebSocket((c) =>\n socketHandlers(service, config, c.get('identity'), c.req.query('overview') === '1'),\n ),\n );\n app.all('/api/*', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/api', () => {\n throw new AppError('NOT_FOUND', 'API route not found', 404);\n });\n app.all('/ws/*', () => {\n throw new AppError('NOT_FOUND', 'WebSocket route not found', 404);\n });\n app.use('*', async (c, next) => {\n if (!c.req.path.startsWith('/assets/')) c.header('Cache-Control', 'no-store');\n c.header('X-Content-Type-Options', 'nosniff');\n c.header('Referrer-Policy', 'same-origin');\n c.header(\n 'Content-Security-Policy',\n // Remote Markdown images load directly in the browser, including HTTPS redirects.\n // Keep scripts, connections and all other resource policies restricted as before.\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'\",\n );\n await next();\n });\n app.get('*', serveStatic({ root: staticDir }));\n app.get('*', async (c, next) => {\n if (!c.req.header('accept')?.includes('text/html') || /\\.[^/]+$/.test(c.req.path))\n return c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404);\n return serveStatic({ path: `${staticDir}/index.html` })(c, next);\n });\n app.notFound((c) => c.json({ error: { code: 'NOT_FOUND', message: 'Resource not found' } }, 404));\n return app;\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,wBAA2C;AAEpD,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,2BAA2B;AACpC,YAAY,aAAa;AAGzB,SAAS,mBAAmB;AAC5B,SAAS,2BAA8D;AAEvE,SAAS,sBAAsB;AAC/B,SAAS,SAAS;AAClB,SAAS,uBAA8C;AACvD,SAAS,gBAAgB,2BAA2B;AACpD,SAAS,wBAAwB;AAQ1B,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,oBAAoB,MAAM;AAAA,EACzC;AACF,GAAqB;AACnB,QAAM,MAAM,IAAI,KAGb;AACH,MAAI,QAAQ,CAAC,OAAO,MAAM;AACxB,UAAM,UACJ,iBAAiB,WACb,QACA,iBAAiB,YAAY,iBAAiB,cAC5C,IAAI,SAAS,qBAAqB,wBAAwB,GAAG,IAC7D,IAAI,SAAS,kBAAkB,8BAA8B,GAAG;AACxE,WAAO,EAAE,KAAK,EAAE,OAAO,QAAQ,OAAO,EAAE,IAAI,aAAa,CAAC,EAAE,GAAG,QAAQ,MAAa;AAAA,EACtF,CAAC;AACD,MAAI,IAAI,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,CAAC;AAClD,MAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AAC9B,MAAE,IAAI,YAAY,MAAM,aAAa,EAAE,IAAI,KAAK,EAAE,IAAI,SAAS,KAAK,CAAC;AACrE,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAAU,CAAC,GAAG,SACpB,UAAU;AAAA,MACR,SAAS,yCAAyC,KAAK,EAAE,IAAI,IAAI,IAC7D,sBACA,MAAM;AAAA,MACV,SAAS,MAAM;AACb,cAAM,IAAI,SAAS,kBAAkB,8BAA8B,GAAG;AAAA,MACxE;AAAA,IACF,CAAC,EAAE,GAAG,IAAI;AAAA,EACZ;AACA,MAAI,IAAI,UAAU,OAAO,GAAG,SAAS;AACnC,MAAE,OAAO,iBAAiB,UAAU;AACpC,QACE,QAAQ,gBAAgB,KACxB,CAAC,CAAC,OAAO,QAAQ,SAAS,EAAE,SAAS,EAAE,IAAI,MAAM,KACjD,CAAC,EAAE,IAAI,KAAK,WAAW,cAAc;AAErC,YAAM,IAAI,SAAS,sBAAsB,wFAAkB,GAAG;AAChE,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI,SAAS;AACX,QAAI,IAAI,gBAAgB,OAAO,MAAM,EAAE,KAAK,MAAM,QAAQ,OAAO,CAAC,CAAC;AACnE,QAAI,KAAK,sBAAsB,OAAO,MAAM;AAC1C,YAAM,QAAQ,EACX,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,SAAS,EAAE,CAAC,EACtD,OAAO,EACP,MAAM,MAAM,EAAE,IAAI,KAAK,CAAC;AAC3B,aAAO,EAAE,KAAK,MAAM,QAAQ,MAAM,MAAM,OAAO,CAAC;AAAA,IAClD,CAAC;AACD,QAAI;AAAA,MAAK;AAAA,MAAyB,OAAO,MACvC,EAAE,KAAK,MAAM,QAAQ,UAAU,eAAe,MAAM,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC;AAAA,IAC1E;AACA,QAAI;AAAA,MAAK;AAAA,MAAoB,OAAO,MAClC,EAAE,KAAK,MAAM,QAAQ,MAAM,oBAAoB,MAAM,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,GAAG,GAAG;AAAA,IAChF;AAAA,EACF;AACA,QAAM,KAAK,CAAC,UAAkB,QAAQ,SAAS,MAAM,KAAK;AAC1D,QAAM,QAAQ,OACZ,GACA,WACe;AACf,UAAM,QAAiB,MAAM,EAAE,IAAI,KAAK;AACxC,QACE,SACA,OAAO,UAAU,YACjB,uBAAuB,SACvB,OAAO,MAAM,sBAAsB;AAEnC,QAAE,IAAI,eAAe,MAAM,iBAAiB;AAC9C,WAAO,OAAO,MAAM,KAAK;AAAA,EAC3B;AACA,QAAM,SAAS,CAAC,OACd,GAAG,WAAW,iBAAiB,GAAG,WAAW,aAAc,MAAiB;AAC9E,QAAM,gBAAgB,IAAI,YAAY,MAAM;AAC5C,MAAI,IAAI,gBAAgB,OAAO,GAAG,SAAS;AACzC,MAAE,OAAO,iBAAiB,UAAU;AACpC,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI,IAAI,eAAe,OAAO,MAAM,EAAE,KAAK,MAAM,cAAc,SAAS,CAAC,CAAC;AAC1E,MAAI,KAAK,eAAe,OAAO,MAAM,EAAE,KAAK,MAAM,cAAc,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC;AACzF,MAAI;AAAA,IAAK;AAAA,IAAsB,OAAO,MACpC,EAAE,KAAK,MAAM,cAAc,cAAc,MAAM,EAAE,IAAI,KAAK,CAAC,CAAC;AAAA,EAC9D;AACA,MAAI,IAAI,sBAAsB,OAAO,MAAM;AACzC,MAAE,OAAO,uBAAuB,8CAA8C;AAC9E,WAAO,EAAE,KAAK,MAAM,cAAc,OAAO,CAAC;AAAA,EAC5C,CAAC;AACD,MAAI,IAAI,eAAe,CAAC,MAAM,EAAE,KAAK,QAAQ,UAAU,CAAC,CAAC;AACzD,MAAI;AAAA,IAAI;AAAA,IAAc,CAAC,MACrB,EAAE;AAAA,MACA,QAAQ,MAAM;AAAA,QACZ,EAAE,IAAI,MAAM,OAAO;AAAA,QACnB,EAAE,OACC,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,OAAO,gBAAgB,EAC3B,QAAQ,CAAC,EACT,MAAM,EAAE,IAAI,MAAM,OAAO,CAAC;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,MAC3B,MAAM,MAAM,GAAG,gBAAgB;AAAA,IACjC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,mCAAmC,OAAO,MAAM;AACvD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,MAC3B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,qCAAqC,OAAO,MAAM;AACzD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,MAAM,MAAM,GAAG,QAAQ,0BAA0B;AAAA,IACnD;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,IAAI,iBAAiB,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,MAAM,QAAQ,aAAa,EAAE,CAAC,CAAC;AACpF,MAAI;AAAA,IAAI;AAAA,IAA6C,OAAO,MAC1D,EAAE,KAAK,MAAM,QAAQ,oBAAoB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC;AAAA,EACxE;AACA,MAAI,KAAK,2CAA2C,OAAO,MAAM;AAC/D,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,MAC3B,MAAM,MAAM,GAAG,QAAQ,uBAAuB;AAAA,IAChD;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI;AAAA,IAAK;AAAA,IAAiB,OAAO,MAC/B,EAAE,KAAK,QAAQ,WAAW,MAAM,MAAM,GAAG,QAAQ,mBAAmB,CAAC,CAAC;AAAA,EACxE;AACA,MAAI;AAAA,IAAK;AAAA,IAAmC,OAAO,MACjD,EAAE;AAAA,MACA,QAAQ;AAAA,QACN,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,QAC3B,MAAM,MAAM,GAAG,QAAQ,mBAAmB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,mCAAmC,OAAO,MAAM;AACvD,UAAM,MAAM,GAAG,QAAQ,mBAAmB;AAC1C,WAAO,EAAE,KAAK,QAAQ,cAAc,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC;AAAA,EACnE,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAAoC,OAAO,MACjD,EAAE;AAAA,MACA,MAAM,QAAQ;AAAA,QACZ,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,QAC3B,EAAE,IAAI,MAAM,QAAQ,MAAM,SACtB,SACA,QAAQ,aAAa,MAAM,EAAE,IAAI,MAAM,QAAQ,CAAC;AAAA,QACpD,QAAQ,oBAAoB,MAAM,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACA,MAAI;AAAA,IAAI;AAAA,IAAgB,OAAO,MAC7B,EAAE;AAAA,MACA,MAAM,QAAQ;AAAA,QACZ,GAAG,EAAE,IAAI,MAAM,WAAW,KAAK,EAAE;AAAA,QACjC,EAAE,IAAI,MAAM,QAAQ,MAAM,SACtB,SACA,QAAQ,aAAa,MAAM,EAAE,IAAI,MAAM,QAAQ,CAAC;AAAA,QACpD,QAAQ,oBAAoB,MAAM,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,WAAO,EAAE;AAAA,MACP,MAAM,QAAQ;AAAA,QACZ,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,QAC1B,MAAM,MAAM,GAAG,QAAQ,mBAAmB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAA0B,OAAO,MACvC,EAAE,KAAK,MAAM,QAAQ,UAAU,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EAC7D;AACA,MAAI;AAAA,IAAI;AAAA,IAAmC,OAAO,MAChD,EAAE,KAAK,MAAM,QAAQ,kBAAkB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC;AAAA,EACrE;AACA,MAAI,IAAI,iCAAiC,OAAO,MAAM;AACpD,UAAM,WAAW,MAAM,QAAQ,aAAa,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC;AACvE,MAAE,OAAO,gBAAgB,8BAA8B;AACvD,MAAE,OAAO,uBAAuB,yBAAyB,SAAS,QAAQ,GAAG;AAC7E,MAAE,OAAO,iBAAiB,UAAU;AACpC,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,yBAAyB,SAAS,UAAU,YAAY,eAAe;AAChF,WAAO,EAAE,KAAK,SAAS,QAAQ;AAAA,EACjC,CAAC;AACD,MAAI,IAAI,iCAAiC,OAAO,MAAM;AACpD,MAAE,OAAO,iBAAiB,mBAAmB;AAC7C,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,gCAAgC,aAAa;AACtD,MAAE,OAAO,2BAA2B,6BAA6B;AACjE,UAAM,QAAQ,MAAM,QAAQ,eAAe,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC;AAC3F,WAAO,EAAE,KAAK,MAAM,OAAO,KAAK;AAAA,MAC9B,gBAAgB,MAAM;AAAA,MACtB,kBAAkB,OAAO,MAAM,MAAM,MAAM;AAAA,IAC7C,CAAC;AAAA,EACH,CAAC;AACD,MAAI,IAAI,mCAAmC,OAAO,MAAM;AACtD,MAAE,OAAO,iBAAiB,mBAAmB;AAC7C,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,gCAAgC,aAAa;AACtD,WAAO,EAAE;AAAA,MACP,MAAM,QAAQ,kBAAkB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC;AAAA,IAClF;AAAA,EACF,CAAC;AACD,MAAI,IAAI,+BAA+B,OAAO,MAAM;AAClD,MAAE,OAAO,iBAAiB,mBAAmB;AAC7C,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,gCAAgC,aAAa;AACtD,WAAO,EAAE,KAAK,MAAM,QAAQ,cAAc,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,EAC7F,CAAC;AACD,MAAI;AAAA,IAAI;AAAA,IAAgC,CAAC,MACvC,EAAE,KAAK,QAAQ,aAAa,GAAG,EAAE,IAAI,MAAM,aAAa,CAAC,CAAC,CAAC;AAAA,EAC7D;AACA,MAAI,KAAK,oCAAoC,OAAO,MAAM;AACxD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,WAAW,CAAC;AAAA,MAC3B,MAAM,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IAC3C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,iCAAiC,OAAO,MAAM;AACrD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IAC3C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,+BAA+B,OAAO,MAAM;AACnD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,gBAAgB;AAAA,IACzC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,iCAAiC,OAAO,MAAM;AACrD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,kBAAkB;AAAA,IAC3C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,gCAAgC,OAAO,MAAM;AACpD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,iCAAiC,OAAO,MAAM;AACrD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,iBAAiB;AAAA,IAC1C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,gCAAgC,OAAO,MAAM;AACpD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,WAAW;AAAA,IACpC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,oCAAoC,OAAO,MAAM;AACxD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,kCAAkC,OAAO,MAAM;AACtD,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,MAC1B,MAAM,MAAM,GAAG,QAAQ,mBAAmB;AAAA,IAC5C;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI,KAAK,0CAA0C,OAAO,MAAM;AAC9D,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,GAAG,EAAE,IAAI,MAAM,aAAa,CAAC;AAAA,MAC7B,MAAM,MAAM,GAAG,QAAQ,eAAe;AAAA,IACxC;AACA,WAAO,EAAE,KAAK,IAAI,OAAO,EAAE,CAAC;AAAA,EAC9B,CAAC;AACD,MAAI;AAAA,IACF;AAAA,IACA;AAAA,MAAiB,CAAC,MAChB,eAAe,SAAS,QAAQ,EAAE,IAAI,UAAU,GAAG,EAAE,IAAI,MAAM,UAAU,MAAM,GAAG;AAAA,IACpF;AAAA,EACF;AACA,MAAI,IAAI,UAAU,MAAM;AACtB,UAAM,IAAI,SAAS,aAAa,uBAAuB,GAAG;AAAA,EAC5D,CAAC;AACD,MAAI,IAAI,QAAQ,MAAM;AACpB,UAAM,IAAI,SAAS,aAAa,uBAAuB,GAAG;AAAA,EAC5D,CAAC;AACD,MAAI,IAAI,SAAS,MAAM;AACrB,UAAM,IAAI,SAAS,aAAa,6BAA6B,GAAG;AAAA,EAClE,CAAC;AACD,MAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AAC9B,QAAI,CAAC,EAAE,IAAI,KAAK,WAAW,UAAU,EAAG,GAAE,OAAO,iBAAiB,UAAU;AAC5E,MAAE,OAAO,0BAA0B,SAAS;AAC5C,MAAE,OAAO,mBAAmB,aAAa;AACzC,MAAE;AAAA,MACA;AAAA;AAAA;AAAA,MAGA;AAAA,IACF;AACA,UAAM,KAAK;AAAA,EACb,CAAC;AACD,MAAI,IAAI,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC,CAAC;AAC7C,MAAI,IAAI,KAAK,OAAO,GAAG,SAAS;AAC9B,QAAI,CAAC,EAAE,IAAI,OAAO,QAAQ,GAAG,SAAS,WAAW,KAAK,WAAW,KAAK,EAAE,IAAI,IAAI;AAC9E,aAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,SAAS,qBAAqB,EAAE,GAAG,GAAG;AACpF,WAAO,YAAY,EAAE,MAAM,GAAG,SAAS,cAAc,CAAC,EAAE,GAAG,IAAI;AAAA,EACjE,CAAC;AACD,MAAI,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,SAAS,qBAAqB,EAAE,GAAG,GAAG,CAAC;AAChG,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { ShellService } from "./shell.js";
1
2
  import { realpathSync } from "node:fs";
2
3
  import { AppError } from "../shared/errors.js";
3
4
  import { threadNameError } from "../shared/thread-name.js";
@@ -24,6 +25,7 @@ class GatewayService {
24
25
  this.turnUsage = new TurnUsageStore(this.store.database);
25
26
  this.projects = options.projects;
26
27
  this.codex = options.codex;
28
+ this.shell = new ShellService(this.codex);
27
29
  this.events = new EventHub(options.limits?.eventBytes);
28
30
  this.projection = new Projection(options.limits?.itemBytes);
29
31
  this.currentEpoch = this.codex.epoch;
@@ -31,6 +33,7 @@ class GatewayService {
31
33
  this.removeListener = this.codex.onEvent((event) => this.consume(event));
32
34
  }
33
35
  options;
36
+ shell;
34
37
  events;
35
38
  projection;
36
39
  store;
@@ -61,6 +64,21 @@ class GatewayService {
61
64
  listProjects() {
62
65
  return this.projects.list();
63
66
  }
67
+ startShell(projectId, input) {
68
+ return this.perform("shell.start", projectId, input, async () => {
69
+ this.ensureCompatibilityConfirmed();
70
+ const project = this.projects.get(projectId);
71
+ this.store.dispatch(input.clientOperationId);
72
+ return { result: { shellCommand: this.shell.start(project, input) } };
73
+ });
74
+ }
75
+ interruptShell(id, input) {
76
+ return this.perform("shell.interrupt", id, input, async () => {
77
+ this.store.dispatch(input.clientOperationId);
78
+ await this.shell.interrupt(id);
79
+ return { result: { interrupted: true } };
80
+ });
81
+ }
64
82
  isInMaintenance() {
65
83
  return this.maintenance;
66
84
  }
@@ -68,6 +86,7 @@ class GatewayService {
68
86
  this.maintenance = value;
69
87
  }
70
88
  prepareUpdate() {
89
+ if (this.shell.active()) return { ready: false, reason: "\u7B49\u5F85 Shell \u547D\u4EE4\u7ED3\u675F" };
71
90
  if (this.inFlightOperations.size || this.resumes.size || this.pendingThreadCreations.size)
72
91
  return { ready: false, reason: "\u7B49\u5F85\u6B63\u5728\u63D0\u4EA4\u6216\u6062\u590D\u7684\u64CD\u4F5C\u5B8C\u6210" };
73
92
  if (this.store.activeMarkers().length || this.store.unresolvedOperations().length)
@@ -99,7 +118,7 @@ class GatewayService {
99
118
  const active = this.projection.values().find(
100
119
  (thread) => thread.projectId === projectId && (isActive(thread.status) || thread.actions.some((action) => ["pending", "responding"].includes(action.status)))
101
120
  );
102
- if (pending || marker || active)
121
+ if (pending || marker || active || this.shell.active(projectId))
103
122
  throw new AppError(
104
123
  "PROJECT_BUSY",
105
124
  "\u9879\u76EE\u4ECD\u6709\u6D3B\u52A8\u4EFB\u52A1\u6216\u7ED3\u679C\u5F85\u6838\u5BF9\u7684\u64CD\u4F5C\uFF1B\u8BF7\u5148\u7ED3\u675F\u4EFB\u52A1\u5E76\u6838\u5BF9\u65E7\u64CD\u4F5C",