c0de-agent 0.0.0 → 1.0.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.
Files changed (330) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -0
  3. package/dist/cli/commands/acp.d.ts +19 -0
  4. package/dist/cli/commands/acp.js +50 -0
  5. package/dist/cli/commands/chat.d.ts +13 -0
  6. package/dist/cli/commands/chat.js +26 -0
  7. package/dist/cli/commands/config.d.ts +17 -0
  8. package/dist/cli/commands/config.js +77 -0
  9. package/dist/cli/commands/init.d.ts +9 -0
  10. package/dist/cli/commands/init.js +14 -0
  11. package/dist/cli/commands/plugin.d.ts +11 -0
  12. package/dist/cli/commands/plugin.js +31 -0
  13. package/dist/cli/commands/serve.d.ts +18 -0
  14. package/dist/cli/commands/serve.js +35 -0
  15. package/dist/cli/commands/update.d.ts +24 -0
  16. package/dist/cli/commands/update.js +40 -0
  17. package/dist/cli/deps.d.ts +19 -0
  18. package/dist/cli/deps.js +57 -0
  19. package/dist/cli/index.d.ts +8 -0
  20. package/dist/cli/index.js +149 -0
  21. package/dist/cli/modes/acp.d.ts +18 -0
  22. package/dist/cli/modes/acp.js +57 -0
  23. package/dist/cli/modes/print.d.ts +16 -0
  24. package/dist/cli/modes/print.js +30 -0
  25. package/dist/cli/parser.d.ts +19 -0
  26. package/dist/cli/parser.js +28 -0
  27. package/dist/cli/utils/output.d.ts +11 -0
  28. package/dist/cli/utils/output.js +23 -0
  29. package/dist/cli/utils/prompt.d.ts +12 -0
  30. package/dist/cli/utils/prompt.js +18 -0
  31. package/dist/core/agent.d.ts +12 -0
  32. package/dist/core/agent.js +95 -0
  33. package/dist/core/agents/builtin.d.ts +6 -0
  34. package/dist/core/agents/builtin.js +76 -0
  35. package/dist/core/agents/discovery.d.ts +6 -0
  36. package/dist/core/agents/discovery.js +96 -0
  37. package/dist/core/agents/index.d.ts +6 -0
  38. package/dist/core/agents/index.js +4 -0
  39. package/dist/core/agents/parallel.d.ts +13 -0
  40. package/dist/core/agents/parallel.js +37 -0
  41. package/dist/core/agents/registry.d.ts +4 -0
  42. package/dist/core/agents/registry.js +23 -0
  43. package/dist/core/agents/types.d.ts +37 -0
  44. package/dist/core/agents/types.js +1 -0
  45. package/dist/core/compact.d.ts +11 -0
  46. package/dist/core/compact.js +31 -0
  47. package/dist/core/config.d.ts +7 -0
  48. package/dist/core/config.js +84 -0
  49. package/dist/core/context.d.ts +23 -0
  50. package/dist/core/context.js +93 -0
  51. package/dist/core/index.d.ts +13 -0
  52. package/dist/core/index.js +11 -0
  53. package/dist/core/loop.d.ts +25 -0
  54. package/dist/core/loop.js +651 -0
  55. package/dist/core/metrics.d.ts +27 -0
  56. package/dist/core/metrics.js +94 -0
  57. package/dist/core/prompt-registry.d.ts +21 -0
  58. package/dist/core/prompt-registry.js +200 -0
  59. package/dist/core/prompt.d.ts +11 -0
  60. package/dist/core/prompt.js +13 -0
  61. package/dist/core/secret.d.ts +12 -0
  62. package/dist/core/secret.js +51 -0
  63. package/dist/core/slash.d.ts +15 -0
  64. package/dist/core/slash.js +114 -0
  65. package/dist/core/steering.d.ts +8 -0
  66. package/dist/core/steering.js +15 -0
  67. package/dist/core/title.d.ts +42 -0
  68. package/dist/core/title.js +115 -0
  69. package/dist/core/tool-exec.d.ts +20 -0
  70. package/dist/core/tool-exec.js +64 -0
  71. package/dist/core/types.d.ts +95 -0
  72. package/dist/core/types.js +1 -0
  73. package/dist/core/worktree.d.ts +24 -0
  74. package/dist/core/worktree.js +68 -0
  75. package/dist/dap/index.d.ts +6 -0
  76. package/dist/dap/index.js +4 -0
  77. package/dist/dap/protocol.d.ts +40 -0
  78. package/dist/dap/protocol.js +132 -0
  79. package/dist/dap/session.d.ts +23 -0
  80. package/dist/dap/session.js +130 -0
  81. package/dist/dap/transport.d.ts +8 -0
  82. package/dist/dap/transport.js +30 -0
  83. package/dist/dap/types.d.ts +50 -0
  84. package/dist/dap/types.js +2 -0
  85. package/dist/db/client.d.ts +22 -0
  86. package/dist/db/client.js +39 -0
  87. package/dist/db/index.d.ts +6 -0
  88. package/dist/db/index.js +4 -0
  89. package/dist/db/migrate.d.ts +9 -0
  90. package/dist/db/migrate.js +15 -0
  91. package/dist/db/schema.d.ts +965 -0
  92. package/dist/db/schema.js +112 -0
  93. package/dist/db/types.d.ts +11 -0
  94. package/dist/db/types.js +1 -0
  95. package/dist/llm/index.d.ts +13 -0
  96. package/dist/llm/index.js +8 -0
  97. package/dist/llm/protocols/openai-compat.d.ts +139 -0
  98. package/dist/llm/protocols/openai-compat.js +214 -0
  99. package/dist/llm/protocols/utils/index.d.ts +2 -0
  100. package/dist/llm/protocols/utils/index.js +2 -0
  101. package/dist/llm/protocols/utils/lifecycle.d.ts +23 -0
  102. package/dist/llm/protocols/utils/lifecycle.js +46 -0
  103. package/dist/llm/protocols/utils/tool-stream.d.ts +46 -0
  104. package/dist/llm/protocols/utils/tool-stream.js +94 -0
  105. package/dist/llm/provider-error.d.ts +5 -0
  106. package/dist/llm/provider-error.js +33 -0
  107. package/dist/llm/provider.d.ts +32 -0
  108. package/dist/llm/provider.js +191 -0
  109. package/dist/llm/registry.d.ts +44 -0
  110. package/dist/llm/registry.js +107 -0
  111. package/dist/llm/retry.d.ts +37 -0
  112. package/dist/llm/retry.js +82 -0
  113. package/dist/llm/routing.d.ts +36 -0
  114. package/dist/llm/routing.js +53 -0
  115. package/dist/llm/schema/errors.d.ts +88 -0
  116. package/dist/llm/schema/errors.js +33 -0
  117. package/dist/llm/schema/events.d.ts +135 -0
  118. package/dist/llm/schema/events.js +35 -0
  119. package/dist/llm/schema/ids.d.ts +22 -0
  120. package/dist/llm/schema/ids.js +3 -0
  121. package/dist/llm/schema/index.d.ts +5 -0
  122. package/dist/llm/schema/index.js +5 -0
  123. package/dist/llm/schema/messages.d.ts +97 -0
  124. package/dist/llm/schema/messages.js +18 -0
  125. package/dist/llm/schema/options.d.ts +54 -0
  126. package/dist/llm/schema/options.js +54 -0
  127. package/dist/llm/token.d.ts +2 -0
  128. package/dist/llm/token.js +13 -0
  129. package/dist/llm/transport.d.ts +31 -0
  130. package/dist/llm/transport.js +165 -0
  131. package/dist/mcp/index.d.ts +1 -0
  132. package/dist/mcp/index.js +3 -0
  133. package/dist/plugins/builtin.d.ts +6 -0
  134. package/dist/plugins/builtin.js +54 -0
  135. package/dist/plugins/hooks.d.ts +3 -0
  136. package/dist/plugins/hooks.js +79 -0
  137. package/dist/plugins/index.d.ts +9 -0
  138. package/dist/plugins/index.js +8 -0
  139. package/dist/plugins/init.d.ts +23 -0
  140. package/dist/plugins/init.js +30 -0
  141. package/dist/plugins/lifecycle.d.ts +6 -0
  142. package/dist/plugins/lifecycle.js +74 -0
  143. package/dist/plugins/loader.d.ts +16 -0
  144. package/dist/plugins/loader.js +68 -0
  145. package/dist/plugins/logger.d.ts +4 -0
  146. package/dist/plugins/logger.js +31 -0
  147. package/dist/plugins/registry.d.ts +7 -0
  148. package/dist/plugins/registry.js +24 -0
  149. package/dist/plugins/types.d.ts +113 -0
  150. package/dist/plugins/types.js +1 -0
  151. package/dist/project/detect.d.ts +4 -0
  152. package/dist/project/detect.js +131 -0
  153. package/dist/project/index.d.ts +4 -0
  154. package/dist/project/index.js +2 -0
  155. package/dist/project/project.d.ts +23 -0
  156. package/dist/project/project.js +87 -0
  157. package/dist/project/resolve.d.ts +8 -0
  158. package/dist/project/resolve.js +81 -0
  159. package/dist/server/agent-manager.d.ts +32 -0
  160. package/dist/server/agent-manager.js +54 -0
  161. package/dist/server/app.d.ts +5 -0
  162. package/dist/server/app.js +75 -0
  163. package/dist/server/context.d.ts +20 -0
  164. package/dist/server/context.js +47 -0
  165. package/dist/server/dev.d.ts +11 -0
  166. package/dist/server/dev.js +83 -0
  167. package/dist/server/index.d.ts +11 -0
  168. package/dist/server/index.js +7 -0
  169. package/dist/server/middleware/auth.d.ts +15 -0
  170. package/dist/server/middleware/auth.js +29 -0
  171. package/dist/server/middleware/cors.d.ts +16 -0
  172. package/dist/server/middleware/cors.js +44 -0
  173. package/dist/server/middleware/error.d.ts +15 -0
  174. package/dist/server/middleware/error.js +11 -0
  175. package/dist/server/permission/interactive.d.ts +24 -0
  176. package/dist/server/permission/interactive.js +56 -0
  177. package/dist/server/permission/store.d.ts +30 -0
  178. package/dist/server/permission/store.js +47 -0
  179. package/dist/server/routes/agent.d.ts +4 -0
  180. package/dist/server/routes/agent.js +19 -0
  181. package/dist/server/routes/catalog.d.ts +42 -0
  182. package/dist/server/routes/catalog.js +194 -0
  183. package/dist/server/routes/chat.d.ts +8 -0
  184. package/dist/server/routes/chat.js +282 -0
  185. package/dist/server/routes/commands.d.ts +5 -0
  186. package/dist/server/routes/commands.js +17 -0
  187. package/dist/server/routes/config.d.ts +4 -0
  188. package/dist/server/routes/config.js +27 -0
  189. package/dist/server/routes/files.d.ts +4 -0
  190. package/dist/server/routes/files.js +183 -0
  191. package/dist/server/routes/filesystem.d.ts +29 -0
  192. package/dist/server/routes/filesystem.js +108 -0
  193. package/dist/server/routes/health.d.ts +3 -0
  194. package/dist/server/routes/health.js +13 -0
  195. package/dist/server/routes/permissions.d.ts +5 -0
  196. package/dist/server/routes/permissions.js +23 -0
  197. package/dist/server/routes/project.d.ts +4 -0
  198. package/dist/server/routes/project.js +51 -0
  199. package/dist/server/routes/provider.d.ts +16 -0
  200. package/dist/server/routes/provider.js +66 -0
  201. package/dist/server/routes/session.d.ts +4 -0
  202. package/dist/server/routes/session.js +130 -0
  203. package/dist/server/routes/tool.d.ts +4 -0
  204. package/dist/server/routes/tool.js +28 -0
  205. package/dist/server/routes/update.d.ts +14 -0
  206. package/dist/server/routes/update.js +49 -0
  207. package/dist/server/server.d.ts +43 -0
  208. package/dist/server/server.js +197 -0
  209. package/dist/server/types.d.ts +73 -0
  210. package/dist/server/types.js +2 -0
  211. package/dist/server/util/safe-path.d.ts +7 -0
  212. package/dist/server/util/safe-path.js +7 -0
  213. package/dist/session/archive.d.ts +15 -0
  214. package/dist/session/archive.js +129 -0
  215. package/dist/session/branch.d.ts +9 -0
  216. package/dist/session/branch.js +63 -0
  217. package/dist/session/compaction.d.ts +27 -0
  218. package/dist/session/compaction.js +192 -0
  219. package/dist/session/context.d.ts +16 -0
  220. package/dist/session/context.js +173 -0
  221. package/dist/session/index.d.ts +10 -0
  222. package/dist/session/index.js +10 -0
  223. package/dist/session/message.d.ts +22 -0
  224. package/dist/session/message.js +145 -0
  225. package/dist/session/session.d.ts +37 -0
  226. package/dist/session/session.js +144 -0
  227. package/dist/session/snapshot.d.ts +11 -0
  228. package/dist/session/snapshot.js +60 -0
  229. package/dist/session/squash.d.ts +9 -0
  230. package/dist/session/squash.js +88 -0
  231. package/dist/session/token.d.ts +10 -0
  232. package/dist/session/token.js +33 -0
  233. package/dist/session/types.d.ts +122 -0
  234. package/dist/session/types.js +1 -0
  235. package/dist/shared/index.d.ts +2 -0
  236. package/dist/shared/index.js +1 -0
  237. package/dist/shared/types/agent.d.ts +202 -0
  238. package/dist/shared/types/agent.js +1 -0
  239. package/dist/shared/types/base.d.ts +32 -0
  240. package/dist/shared/types/base.js +1 -0
  241. package/dist/shared/types/config.d.ts +104 -0
  242. package/dist/shared/types/config.js +1 -0
  243. package/dist/shared/types/index.d.ts +6 -0
  244. package/dist/shared/types/index.js +1 -0
  245. package/dist/shared/types/llm.d.ts +122 -0
  246. package/dist/shared/types/llm.js +1 -0
  247. package/dist/shared/types/message.d.ts +71 -0
  248. package/dist/shared/types/message.js +1 -0
  249. package/dist/shared/types/tool.d.ts +134 -0
  250. package/dist/shared/types/tool.js +1 -0
  251. package/dist/shared/utils/path.d.ts +15 -0
  252. package/dist/shared/utils/path.js +23 -0
  253. package/dist/shared/utils.d.ts +5 -0
  254. package/dist/shared/utils.js +10 -0
  255. package/dist/tools/builtin/bash.d.ts +12 -0
  256. package/dist/tools/builtin/bash.js +121 -0
  257. package/dist/tools/builtin/dap.d.ts +23 -0
  258. package/dist/tools/builtin/dap.js +231 -0
  259. package/dist/tools/builtin/edit.d.ts +12 -0
  260. package/dist/tools/builtin/edit.js +151 -0
  261. package/dist/tools/builtin/glob.d.ts +11 -0
  262. package/dist/tools/builtin/glob.js +120 -0
  263. package/dist/tools/builtin/grep.d.ts +6 -0
  264. package/dist/tools/builtin/grep.js +141 -0
  265. package/dist/tools/builtin/read.d.ts +6 -0
  266. package/dist/tools/builtin/read.js +73 -0
  267. package/dist/tools/builtin/resolvers.d.ts +12 -0
  268. package/dist/tools/builtin/resolvers.js +79 -0
  269. package/dist/tools/builtin/task.d.ts +9 -0
  270. package/dist/tools/builtin/task.js +100 -0
  271. package/dist/tools/builtin/write.d.ts +6 -0
  272. package/dist/tools/builtin/write.js +36 -0
  273. package/dist/tools/builtin/yield.d.ts +9 -0
  274. package/dist/tools/builtin/yield.js +40 -0
  275. package/dist/tools/executor.d.ts +9 -0
  276. package/dist/tools/executor.js +54 -0
  277. package/dist/tools/hashline/index.d.ts +2 -0
  278. package/dist/tools/hashline/index.js +3 -0
  279. package/dist/tools/hashline/patch.d.ts +48 -0
  280. package/dist/tools/hashline/patch.js +201 -0
  281. package/dist/tools/index.d.ts +30 -0
  282. package/dist/tools/index.js +55 -0
  283. package/dist/tools/permission.d.ts +13 -0
  284. package/dist/tools/permission.js +39 -0
  285. package/dist/tools/registry.d.ts +15 -0
  286. package/dist/tools/registry.js +54 -0
  287. package/dist/tools/resolver.d.ts +15 -0
  288. package/dist/tools/resolver.js +47 -0
  289. package/dist/tools/truncate.d.ts +8 -0
  290. package/dist/tools/truncate.js +50 -0
  291. package/dist/tools/types.d.ts +101 -0
  292. package/dist/tools/types.js +1 -0
  293. package/dist/tools/validate.d.ts +8 -0
  294. package/dist/tools/validate.js +130 -0
  295. package/dist/tools/websearch/fetch.d.ts +11 -0
  296. package/dist/tools/websearch/fetch.js +21 -0
  297. package/dist/tools/websearch/index.d.ts +19 -0
  298. package/dist/tools/websearch/index.js +78 -0
  299. package/dist/tools/websearch/providers/brave.d.ts +5 -0
  300. package/dist/tools/websearch/providers/brave.js +53 -0
  301. package/dist/tools/websearch/providers/duckduckgo.d.ts +3 -0
  302. package/dist/tools/websearch/providers/duckduckgo.js +75 -0
  303. package/dist/tools/websearch/providers/tavily.d.ts +11 -0
  304. package/dist/tools/websearch/providers/tavily.js +89 -0
  305. package/dist/tools/websearch/types.d.ts +51 -0
  306. package/dist/tools/websearch/types.js +18 -0
  307. package/dist/tools/websearch/websearch.d.ts +10 -0
  308. package/dist/tools/websearch/websearch.js +48 -0
  309. package/dist/update/index.d.ts +10 -0
  310. package/dist/update/index.js +6 -0
  311. package/dist/update/ipc.d.ts +15 -0
  312. package/dist/update/ipc.js +41 -0
  313. package/dist/update/scheduler.d.ts +34 -0
  314. package/dist/update/scheduler.js +57 -0
  315. package/dist/update/snapshot.d.ts +39 -0
  316. package/dist/update/snapshot.js +99 -0
  317. package/dist/update/updater.d.ts +48 -0
  318. package/dist/update/updater.js +83 -0
  319. package/dist/update/version.d.ts +18 -0
  320. package/dist/update/version.js +49 -0
  321. package/drizzle/0000_magical_nightshade.sql +55 -0
  322. package/drizzle/0001_chemical_doctor_faustus.sql +13 -0
  323. package/drizzle/0002_amusing_micromacro.sql +12 -0
  324. package/drizzle/0003_regular_purifiers.sql +2 -0
  325. package/drizzle/meta/0000_snapshot.json +448 -0
  326. package/drizzle/meta/0001_snapshot.json +539 -0
  327. package/drizzle/meta/0002_snapshot.json +633 -0
  328. package/drizzle/meta/0003_snapshot.json +645 -0
  329. package/drizzle/meta/_journal.json +34 -0
  330. package/package.json +100 -8
@@ -0,0 +1,47 @@
1
+ // src/server/context.ts
2
+ import { BUILTIN_AGENTS, createAgentRegistry } from '../core/agents/index.js';
3
+ import { DEFAULT_CONFIG, mergeConfig } from '../core/config.js';
4
+ import { createHookRunner, createPluginRegistry } from '../plugins/index.js';
5
+ import { createDefaultRegistry, createDefaultURLRegistry } from '../tools/index.js';
6
+ import { createUpdateScheduler } from '../update/index.js';
7
+ import { createAgentManager } from './agent-manager.js';
8
+ import { createPermissionStore } from './permission/store.js';
9
+ function createServerContext(opts) {
10
+ // 测试/dev 工厂:补足 urlRegistry + hookRunner + pluginRegistry(空壳,不激活插件),
11
+ // 生产启动走 bootstrapServerContext → initPlugins(激活 builtin + 发现外部插件)。
12
+ // 用 mergeConfig 合并 DEFAULT_CONFIG,兼容测试传入 Partial<Config>(历史 cast 用法)。
13
+ const config = mergeConfig(opts.config ?? DEFAULT_CONFIG);
14
+ const hookRunner = createHookRunner();
15
+ // Agent 注册表:测试可注入;默认含 4 个内置 agent(general/coder/researcher/reviewer)。
16
+ const agentRegistry = opts.agentRegistry ??
17
+ (() => {
18
+ const reg = createAgentRegistry();
19
+ for (const def of BUILTIN_AGENTS)
20
+ reg.register(def);
21
+ return reg;
22
+ })();
23
+ return {
24
+ db: opts.db,
25
+ config,
26
+ toolRegistry: opts.toolRegistry ?? createDefaultRegistry(config),
27
+ llmRegistry: opts.llmRegistry,
28
+ urlRegistry: createDefaultURLRegistry(),
29
+ hookRunner,
30
+ pluginRegistry: createPluginRegistry(hookRunner),
31
+ agentManager: createAgentManager(),
32
+ permissionStore: createPermissionStore(),
33
+ permissionMode: config.permission.defaultMode,
34
+ agentRegistry,
35
+ // 测试上下文:默认 scheduler 不启动(enabled=false 由调用方控制)。
36
+ updateScheduler: createUpdateScheduler({
37
+ checkFn: async () => ({
38
+ hasUpdate: false,
39
+ currentVersion: '0.0.0',
40
+ latestVersion: '0.0.0',
41
+ }),
42
+ }),
43
+ cwd: opts.cwd ?? process.cwd(),
44
+ ...(opts.chatStream ? { chatStream: opts.chatStream } : {}),
45
+ };
46
+ }
47
+ export { createServerContext, DEFAULT_CONFIG };
@@ -0,0 +1,11 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { Hono } from 'hono';
3
+ declare function getDevApp(): Promise<Hono>;
4
+ /** vite dev server 关闭时调用,确保 PGLite WASM 正常 close 并刷写 WAL。 */
5
+ declare function closeDevApp(): Promise<void>;
6
+ /**
7
+ * 把 Node 请求桥接到 Hono app 并流式回写响应(支持 SSE)。
8
+ * 用 Readable.fromWeb 管道转发 Response body,原生处理背压与分块。
9
+ */
10
+ declare function handleApiRequest(app: Hono, req: IncomingMessage, res: ServerResponse): Promise<void>;
11
+ export { closeDevApp, getDevApp, handleApiRequest };
@@ -0,0 +1,83 @@
1
+ // src/server/dev.ts
2
+ import { Readable } from 'node:stream';
3
+ import { createApp } from './app.js';
4
+ import { bootstrapServerContext } from './server.js';
5
+ /**
6
+ * 开发环境入口:初始化并返回 Hono app。
7
+ * 供 vite dev server 中间件复用,使前后端共享同一端口。
8
+ *
9
+ * 用 globalThis 缓存而非模块级变量:Vite ssrLoadModule 在 server 端代码变更时会
10
+ * 重新执行本模块,模块级 cachedApp 会重置;这会导致运行中的 AgentManager(含
11
+ * 权限确认 pending)丢失,前端 POST /api/tools/confirm 拿不到 pending 而 404。
12
+ * globalThis 跨模块重载保持单例,避免权限流程中途断裂。
13
+ */
14
+ const DEV_APP_KEY = '__c0de_dev_app__';
15
+ const DEV_CLOSE_KEY = '__c0de_dev_close__';
16
+ async function getDevApp() {
17
+ const g = globalThis;
18
+ if (!g[DEV_APP_KEY]) {
19
+ const { ctx, close } = await bootstrapServerContext({ cwd: process.cwd() });
20
+ g[DEV_APP_KEY] = createApp(ctx);
21
+ g[DEV_CLOSE_KEY] = close;
22
+ }
23
+ return g[DEV_APP_KEY];
24
+ }
25
+ /** vite dev server 关闭时调用,确保 PGLite WASM 正常 close 并刷写 WAL。 */
26
+ async function closeDevApp() {
27
+ const g = globalThis;
28
+ const close = g[DEV_CLOSE_KEY];
29
+ if (close) {
30
+ await close();
31
+ delete g[DEV_APP_KEY];
32
+ delete g[DEV_CLOSE_KEY];
33
+ }
34
+ }
35
+ /**
36
+ * 把 Node 请求桥接到 Hono app 并流式回写响应(支持 SSE)。
37
+ * 用 Readable.fromWeb 管道转发 Response body,原生处理背压与分块。
38
+ */
39
+ async function handleApiRequest(app, req, res) {
40
+ const host = req.headers.host ?? 'localhost';
41
+ const url = new URL(req.url ?? '/', `http://${host}`);
42
+ let body;
43
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
44
+ const chunks = [];
45
+ for await (const chunk of req) {
46
+ chunks.push(chunk);
47
+ }
48
+ body = Buffer.concat(chunks);
49
+ }
50
+ const headers = new Headers();
51
+ for (const [key, value] of Object.entries(req.headers)) {
52
+ if (value != null) {
53
+ headers.set(key, Array.isArray(value) ? value.join(', ') : value);
54
+ }
55
+ }
56
+ const init = {
57
+ method: req.method,
58
+ headers,
59
+ body: body ?? undefined,
60
+ };
61
+ if (body !== undefined) {
62
+ init.duplex = 'half';
63
+ }
64
+ const request = new Request(url.toString(), init);
65
+ const response = await app.fetch(request);
66
+ res.statusCode = response.status;
67
+ response.headers.forEach((value, key) => {
68
+ res.setHeader(key, value);
69
+ });
70
+ const responseBody = response.body;
71
+ if (responseBody) {
72
+ res.flushHeaders();
73
+ await new Promise((resolve, reject) => {
74
+ const nodeStream = Readable.fromWeb(responseBody);
75
+ nodeStream.on('error', reject);
76
+ nodeStream.pipe(res).on('finish', resolve);
77
+ });
78
+ }
79
+ else {
80
+ res.end();
81
+ }
82
+ }
83
+ export { closeDevApp, getDevApp, handleApiRequest };
@@ -0,0 +1,11 @@
1
+ export type { ActiveRun, AgentManager } from './agent-manager.js';
2
+ export { createAgentManager } from './agent-manager.js';
3
+ export { createApp } from './app.js';
4
+ export type { CreateServerContextOptions } from './context.js';
5
+ export { createServerContext } from './context.js';
6
+ export { apiError, errorHandler } from './middleware/error.js';
7
+ export type { InteractivePermissionChecker, InteractivePermissionCheckerOptions, PermissionRequest, } from './permission/interactive.js';
8
+ export { createInteractivePermissionChecker } from './permission/interactive.js';
9
+ export type { RunningServer, StartServerOptions } from './server.js';
10
+ export { startServer } from './server.js';
11
+ export type { APIErrorBody, ChatRequest, ConfirmRequest, ControlRequest, ServerContext, SteerRequest, } from './types.js';
@@ -0,0 +1,7 @@
1
+ // src/server/index.ts
2
+ export { createAgentManager } from './agent-manager.js';
3
+ export { createApp } from './app.js';
4
+ export { createServerContext } from './context.js';
5
+ export { apiError, errorHandler } from './middleware/error.js';
6
+ export { createInteractivePermissionChecker } from './permission/interactive.js';
7
+ export { startServer } from './server.js';
@@ -0,0 +1,15 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ export type AuthOptions = {
3
+ /** 不需认证的路径(默认放行 /api/health 探活)。 */
4
+ publicPaths?: string[];
5
+ };
6
+ /**
7
+ * Bearer token 认证中间件(spec §24.2「认证」)。
8
+ *
9
+ * - 未配置 token(undefined/空)→ 放行,适配本地开发场景。
10
+ * - 配置了 token → 校验 `Authorization: Bearer <token>`,不匹配返回 401。
11
+ * - publicPaths 中的路径始终放行(如健康检查)。
12
+ *
13
+ * 生产或远程访问场景应在 config.security.token 配置随机 token。
14
+ */
15
+ export declare function createAuthMiddleware(token: string | undefined, opts?: AuthOptions): MiddlewareHandler;
@@ -0,0 +1,29 @@
1
+ import { apiError } from './error.js';
2
+ /**
3
+ * Bearer token 认证中间件(spec §24.2「认证」)。
4
+ *
5
+ * - 未配置 token(undefined/空)→ 放行,适配本地开发场景。
6
+ * - 配置了 token → 校验 `Authorization: Bearer <token>`,不匹配返回 401。
7
+ * - publicPaths 中的路径始终放行(如健康检查)。
8
+ *
9
+ * 生产或远程访问场景应在 config.security.token 配置随机 token。
10
+ */
11
+ export function createAuthMiddleware(token, opts = {}) {
12
+ const expected = token && token.length > 0 ? `Bearer ${token}` : '';
13
+ const publicPaths = new Set(opts.publicPaths ?? ['/api/health']);
14
+ return async (c, next) => {
15
+ if (!expected) {
16
+ await next();
17
+ return;
18
+ }
19
+ if (publicPaths.has(c.req.path)) {
20
+ await next();
21
+ return;
22
+ }
23
+ const auth = c.req.header('Authorization') ?? '';
24
+ if (auth !== expected) {
25
+ return apiError(c, 401, 'UNAUTHORIZED', 'Missing or invalid bearer token');
26
+ }
27
+ await next();
28
+ };
29
+ }
@@ -0,0 +1,16 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ /**
3
+ * 判断 origin 是否为本地/可信来源(spec §24.2「CORS 限制本地 origin」)。
4
+ * 允许:本机回环任意端口、file://(PWA 安装后)、浏览器扩展、'null'
5
+ * (file 协议/隐私沙箱发出的 Origin 头)。
6
+ */
7
+ export declare function isLocalOrigin(origin: string): boolean;
8
+ export type CORSOptions = {
9
+ /** 额外允许的 origin(如远程访问场景配置的域名)。 */
10
+ allowedOrigins?: string[];
11
+ };
12
+ /**
13
+ * CORS 中间件:仅对本地/可信 origin 回显 Access-Control-Allow-Origin,
14
+ * 拒绝外部网页跨域读取本地 server(spec §24.2)。处理 OPTIONS 预检。
15
+ */
16
+ export declare function createCORSMiddleware(opts?: CORSOptions): MiddlewareHandler;
@@ -0,0 +1,44 @@
1
+ /** 本地回环 origin 模式:http(s)://(localhost|127.0.0.1|[::1])(:port)。 */
2
+ const LOCAL_ORIGIN = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/;
3
+ /**
4
+ * 判断 origin 是否为本地/可信来源(spec §24.2「CORS 限制本地 origin」)。
5
+ * 允许:本机回环任意端口、file://(PWA 安装后)、浏览器扩展、'null'
6
+ * (file 协议/隐私沙箱发出的 Origin 头)。
7
+ */
8
+ export function isLocalOrigin(origin) {
9
+ if (!origin)
10
+ return false;
11
+ if (LOCAL_ORIGIN.test(origin))
12
+ return true;
13
+ if (origin === 'null')
14
+ return true;
15
+ if (origin.startsWith('file://'))
16
+ return true;
17
+ if (origin.startsWith('chrome-extension://'))
18
+ return true;
19
+ if (origin.startsWith('moz-extension://'))
20
+ return true;
21
+ return false;
22
+ }
23
+ /**
24
+ * CORS 中间件:仅对本地/可信 origin 回显 Access-Control-Allow-Origin,
25
+ * 拒绝外部网页跨域读取本地 server(spec §24.2)。处理 OPTIONS 预检。
26
+ */
27
+ export function createCORSMiddleware(opts = {}) {
28
+ const extra = new Set(opts.allowedOrigins ?? []);
29
+ return async (c, next) => {
30
+ const origin = c.req.header('Origin') ?? '';
31
+ const allowed = isLocalOrigin(origin) || extra.has(origin);
32
+ if (origin.length > 0 && allowed) {
33
+ c.header('Access-Control-Allow-Origin', origin);
34
+ c.header('Vary', 'Origin');
35
+ }
36
+ if (c.req.method === 'OPTIONS') {
37
+ c.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
38
+ c.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
39
+ c.header('Access-Control-Max-Age', '86400');
40
+ return c.body(null, 204);
41
+ }
42
+ await next();
43
+ };
44
+ }
@@ -0,0 +1,15 @@
1
+ import type { Context } from 'hono';
2
+ import type { ContentfulStatusCode } from 'hono/utils/http-status';
3
+ /** 构建标准 JSON 错误响应。 */
4
+ declare function apiError(c: Context, status: ContentfulStatusCode, code: string, message: string, details?: Record<string, unknown>): Response & import("hono").TypedResponse<{
5
+ error: {
6
+ code: string;
7
+ message: string;
8
+ details?: {
9
+ [x: string]: import("hono/utils/types").JSONValue;
10
+ } | undefined;
11
+ };
12
+ }, ContentfulStatusCode, "json">;
13
+ /** Hono onError 处理器:捕获未处理异常,返回 500。 */
14
+ declare function errorHandler(err: Error, c: Context): Response;
15
+ export { apiError, errorHandler };
@@ -0,0 +1,11 @@
1
+ // src/server/middleware/error.ts
2
+ /** 构建标准 JSON 错误响应。 */
3
+ function apiError(c, status, code, message, details) {
4
+ return c.json({ error: { code, message, ...(details ? { details } : {}) } }, status);
5
+ }
6
+ /** Hono onError 处理器:捕获未处理异常,返回 500。 */
7
+ function errorHandler(err, c) {
8
+ const message = err instanceof Error ? err.message : 'Internal server error';
9
+ return c.json({ error: { code: 'INTERNAL', message } }, 500);
10
+ }
11
+ export { apiError, errorHandler };
@@ -0,0 +1,24 @@
1
+ import type { PermissionChecker } from '../../tools/types.js';
2
+ import type { PermissionRequest, PermissionStore } from './store.js';
3
+ export type { PermissionRequest };
4
+ type PermissionMode = 'default' | 'auto';
5
+ type InteractivePermissionCheckerOptions = {
6
+ /** 始终允许的工具名(覆盖 permission 字段)。 */
7
+ alwaysAllow?: string[];
8
+ /** 始终拒绝的工具名(覆盖 permission 字段)。 */
9
+ alwaysDeny?: string[];
10
+ /** 读取当前授权模式:'auto' 时跳过 ask 交互确认(YOLO 自动授权)。 */
11
+ getMode?: () => PermissionMode;
12
+ /** 遇到 ask 权限时调用(用于通知前端)。 */
13
+ onPermissionRequired?: (request: PermissionRequest) => void | Promise<void>;
14
+ };
15
+ /** 阻塞式权限检查器:ask 权限会阻塞等待用户确认。
16
+ * pending 存在全局 PermissionStore 中,独立于 agent run 生命周期。 */
17
+ type InteractivePermissionChecker = PermissionChecker & {
18
+ confirm(toolCallId: string, approved: boolean): boolean;
19
+ hasPending(toolCallId: string): boolean;
20
+ pendingCount(): number;
21
+ };
22
+ declare function createInteractivePermissionChecker(store: PermissionStore, opts?: InteractivePermissionCheckerOptions): InteractivePermissionChecker;
23
+ export type { InteractivePermissionChecker, InteractivePermissionCheckerOptions };
24
+ export { createInteractivePermissionChecker };
@@ -0,0 +1,56 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ function createInteractivePermissionChecker(store, opts = {}) {
3
+ const allowSet = new Set(opts.alwaysAllow ?? []);
4
+ const denySet = new Set(opts.alwaysDeny ?? []);
5
+ return {
6
+ check: async (tool, input, ctx) => {
7
+ if (denySet.has(tool.name)) {
8
+ return { _tag: 'deny', reason: `Tool "${tool.name}" is denied by configuration` };
9
+ }
10
+ if (allowSet.has(tool.name) || tool.permission === 'auto') {
11
+ return { _tag: 'allow' };
12
+ }
13
+ if (tool.permission === 'deny') {
14
+ return { _tag: 'deny', reason: `Tool "${tool.name}" is disabled` };
15
+ }
16
+ // tool.permission === 'ask'
17
+ // YOLO 自动授权:mode==='auto' 时跳过交互确认直接放行。
18
+ // denySet 与 permission==='deny' 已在上游拦截,故此处只剩 ask 工具。
19
+ if (opts.getMode?.() === 'auto') {
20
+ return { _tag: 'allow' };
21
+ }
22
+ // 交互式确认
23
+ const toolCallId = randomUUID();
24
+ const request = { toolCallId, tool: tool.name, input };
25
+ const promise = new Promise((resolve) => {
26
+ // pending 注册到全局 store,confirm 端点按 toolCallId 直接寻址,
27
+ // 不依赖当前 agent run 是否仍在 agentManager 中注册。
28
+ // store 内置超时:到期自动 deny 并清理条目(防永久挂起 + Map 泄漏)。
29
+ store.register(toolCallId, { request, resolve });
30
+ });
31
+ await opts.onPermissionRequired?.(request);
32
+ // agent abort 联动:abort 时主动 deny 并清理 store 条目,
33
+ // 避免 agent 已中止后 promise 仍悬空等待超时。store.resolve 是幂等终结,
34
+ // 与超时/confirm 互斥——谁先到谁生效。
35
+ if (ctx.abort.aborted) {
36
+ store.resolve(toolCallId, false);
37
+ }
38
+ else {
39
+ ctx.abort.addEventListener('abort', () => {
40
+ store.resolve(toolCallId, false);
41
+ }, { once: true });
42
+ }
43
+ return promise;
44
+ },
45
+ confirm(toolCallId, approved) {
46
+ return store.resolve(toolCallId, approved);
47
+ },
48
+ hasPending(toolCallId) {
49
+ return store.has(toolCallId);
50
+ },
51
+ pendingCount() {
52
+ return store.size();
53
+ },
54
+ };
55
+ }
56
+ export { createInteractivePermissionChecker };
@@ -0,0 +1,30 @@
1
+ import type { PermissionResult } from '../../tools/types.js';
2
+ /** 默认 pending 超时时长:5 分钟(300_000ms)。
3
+ * 超过后自动 resolve 为 deny 并清理 store 条目,避免 promise 永久挂起与 pending Map 泄漏。 */
4
+ declare const DEFAULT_PERMISSION_TIMEOUT_MS: number;
5
+ /** 权限请求(需要用户确认)。 */
6
+ type PermissionRequest = {
7
+ toolCallId: string;
8
+ tool: string;
9
+ input: unknown;
10
+ };
11
+ /** 待处理的权限确认。 */
12
+ type PendingPermission = {
13
+ request: PermissionRequest;
14
+ resolve: (result: PermissionResult) => void;
15
+ };
16
+ /** createPermissionStore 的配置。 */
17
+ type PermissionStoreOptions = {
18
+ /** pending 超时毫秒数;省略时取 DEFAULT_PERMISSION_TIMEOUT_MS(5 分钟)。 */
19
+ timeoutMs?: number;
20
+ };
21
+ /** 全局权限确认 store:注册/解析 pending permission。 */
22
+ type PermissionStore = {
23
+ register(toolCallId: string, pending: PendingPermission): void;
24
+ resolve(toolCallId: string, approved: boolean): boolean;
25
+ has(toolCallId: string): boolean;
26
+ size(): number;
27
+ };
28
+ declare function createPermissionStore(opts?: PermissionStoreOptions): PermissionStore;
29
+ export type { PendingPermission, PermissionRequest, PermissionStore, PermissionStoreOptions };
30
+ export { createPermissionStore, DEFAULT_PERMISSION_TIMEOUT_MS };
@@ -0,0 +1,47 @@
1
+ // 全局权限确认 store —— 进程级单例,pending 独立于 agent run 生命周期。
2
+ //
3
+ // 设计参考 opencode 的 PermissionV2.Service:pending 挂在全局/Service 层,
4
+ // reply 按 toolCallId 直接寻址,不依赖 session/agent run 的注册状态。
5
+ // 这样即使 agent run 被覆盖、注销或 abort,pending 仍在 store 里,
6
+ // confirm 仍能找到并 resolve。
7
+ /** 默认 pending 超时时长:5 分钟(300_000ms)。
8
+ * 超过后自动 resolve 为 deny 并清理 store 条目,避免 promise 永久挂起与 pending Map 泄漏。 */
9
+ const DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 60 * 1000;
10
+ function createPermissionStore(opts = {}) {
11
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
12
+ const pending = new Map();
13
+ // 统一终结路径:clearTimeout → 删除条目 → resolve。
14
+ // 三处调用(confirm、超时回调、abort 联动)都汇聚于此:
15
+ // 保证 promise 只 settle 一次(resolve 幂等)、定时器必被清理、Map 条目必被释放。
16
+ const settle = (toolCallId, result) => {
17
+ const p = pending.get(toolCallId);
18
+ if (!p)
19
+ return false;
20
+ clearTimeout(p.timer);
21
+ pending.delete(toolCallId);
22
+ p.resolve(result);
23
+ return true;
24
+ };
25
+ return {
26
+ register(toolCallId, entry) {
27
+ // 重复注册同 id 时先清理旧定时器,避免旧 timer 泄漏。
28
+ const existing = pending.get(toolCallId);
29
+ if (existing)
30
+ clearTimeout(existing.timer);
31
+ const timer = setTimeout(() => {
32
+ settle(toolCallId, { _tag: 'deny', reason: 'Permission request timed out' });
33
+ }, timeoutMs);
34
+ pending.set(toolCallId, { ...entry, timer });
35
+ },
36
+ resolve(toolCallId, approved) {
37
+ return settle(toolCallId, approved ? { _tag: 'allow' } : { _tag: 'deny', reason: 'User denied permission' });
38
+ },
39
+ has(toolCallId) {
40
+ return pending.has(toolCallId);
41
+ },
42
+ size() {
43
+ return pending.size;
44
+ },
45
+ };
46
+ }
47
+ export { createPermissionStore, DEFAULT_PERMISSION_TIMEOUT_MS };
@@ -0,0 +1,4 @@
1
+ import { Hono } from 'hono';
2
+ import type { ServerContext } from '../types.js';
3
+ declare function createAgentRoute(ctx: ServerContext): Hono;
4
+ export { createAgentRoute };
@@ -0,0 +1,19 @@
1
+ import { Hono } from 'hono';
2
+ function createAgentRoute(ctx) {
3
+ const app = new Hono();
4
+ // GET / — 返回所有 agent(前端按 mode 过滤展示)
5
+ app.get('/', (c) => {
6
+ const all = ctx.agentRegistry.list();
7
+ return c.json({
8
+ agents: all.map((d) => ({
9
+ name: d.name,
10
+ description: d.description,
11
+ mode: d.mode,
12
+ source: d.source,
13
+ hasTools: Boolean(d.tools),
14
+ })),
15
+ });
16
+ });
17
+ return app;
18
+ }
19
+ export { createAgentRoute };
@@ -0,0 +1,42 @@
1
+ import { Hono } from 'hono';
2
+ import type { ServerContext } from '../types.js';
3
+ /** 完整目录数据。 */
4
+ type CatalogData = Record<string, CatalogProviderInternal>;
5
+ type CatalogProviderInternal = {
6
+ id: string;
7
+ name: string;
8
+ npm?: string;
9
+ api?: string;
10
+ env: string[];
11
+ doc?: string;
12
+ models: Record<string, CatalogModelInternal>;
13
+ };
14
+ type CatalogModelInternal = {
15
+ id: string;
16
+ name: string;
17
+ family?: string;
18
+ reasoning: boolean;
19
+ tool_call: boolean;
20
+ attachment: boolean;
21
+ temperature: boolean;
22
+ limit: {
23
+ context: number;
24
+ output: number;
25
+ };
26
+ cost?: {
27
+ input?: number;
28
+ output?: number;
29
+ };
30
+ };
31
+ /** 清除内存 + 磁盘缓存(主要用于测试)。 */
32
+ declare function clearCatalogCache(): void;
33
+ /**
34
+ * 获取 models.dev 目录数据(带缓存)。
35
+ * 优先用内存缓存,其次磁盘缓存,最后发起网络请求。
36
+ */
37
+ declare function fetchCatalog(force?: boolean): Promise<{
38
+ data: CatalogData;
39
+ fetchedAt: number;
40
+ }>;
41
+ declare function createCatalogRoute(ctx: ServerContext): Hono;
42
+ export { clearCatalogCache, createCatalogRoute, fetchCatalog };