chorus-codes 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/cache/.previewinfo +1 -1
  4. package/.next/cache/.rscinfo +1 -1
  5. package/.next/cache/.tsbuildinfo +1 -1
  6. package/.next/fallback-build-manifest.json +3 -3
  7. package/.next/prerender-manifest.json +3 -3
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  20. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  21. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  23. package/.next/server/app/new.html +1 -1
  24. package/.next/server/app/new.rsc +1 -1
  25. package/.next/server/app/new.segments/_full.segment.rsc +1 -1
  26. package/.next/server/app/new.segments/_head.segment.rsc +1 -1
  27. package/.next/server/app/new.segments/_index.segment.rsc +1 -1
  28. package/.next/server/app/new.segments/_tree.segment.rsc +1 -1
  29. package/.next/server/app/new.segments/new/__PAGE__.segment.rsc +1 -1
  30. package/.next/server/app/new.segments/new.segment.rsc +1 -1
  31. package/.next/server/app/onboarding.html +1 -1
  32. package/.next/server/app/onboarding.rsc +1 -1
  33. package/.next/server/app/onboarding.segments/_full.segment.rsc +1 -1
  34. package/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
  35. package/.next/server/app/onboarding.segments/_index.segment.rsc +1 -1
  36. package/.next/server/app/onboarding.segments/_tree.segment.rsc +1 -1
  37. package/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
  38. package/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
  39. package/.next/server/app/personas.html +1 -1
  40. package/.next/server/app/personas.rsc +1 -1
  41. package/.next/server/app/personas.segments/_full.segment.rsc +1 -1
  42. package/.next/server/app/personas.segments/_head.segment.rsc +1 -1
  43. package/.next/server/app/personas.segments/_index.segment.rsc +1 -1
  44. package/.next/server/app/personas.segments/_tree.segment.rsc +1 -1
  45. package/.next/server/app/personas.segments/personas/__PAGE__.segment.rsc +1 -1
  46. package/.next/server/app/personas.segments/personas.segment.rsc +1 -1
  47. package/.next/server/app/settings.html +1 -1
  48. package/.next/server/app/settings.rsc +1 -1
  49. package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  50. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  51. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  52. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  53. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
  54. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  55. package/.next/server/app/templates.html +1 -1
  56. package/.next/server/app/templates.rsc +1 -1
  57. package/.next/server/app/templates.segments/_full.segment.rsc +1 -1
  58. package/.next/server/app/templates.segments/_head.segment.rsc +1 -1
  59. package/.next/server/app/templates.segments/_index.segment.rsc +1 -1
  60. package/.next/server/app/templates.segments/_tree.segment.rsc +1 -1
  61. package/.next/server/app/templates.segments/templates/__PAGE__.segment.rsc +1 -1
  62. package/.next/server/app/templates.segments/templates.segment.rsc +1 -1
  63. package/.next/server/middleware-build-manifest.js +3 -3
  64. package/.next/server/pages/404.html +1 -1
  65. package/.next/server/pages/500.html +1 -1
  66. package/.next/server/server-reference-manifest.js +1 -1
  67. package/.next/server/server-reference-manifest.json +1 -1
  68. package/.next/trace +1 -1
  69. package/.next/trace-build +1 -1
  70. package/README.md +38 -15
  71. package/dist/cli/commands/doctor.js +116 -0
  72. package/dist/cli/commands/doctor.js.map +1 -0
  73. package/dist/cli/commands/init.js +211 -0
  74. package/dist/cli/commands/init.js.map +1 -0
  75. package/dist/cli/commands/start.js +298 -0
  76. package/dist/cli/commands/start.js.map +1 -0
  77. package/dist/cli/commands/status.js +54 -0
  78. package/dist/cli/commands/status.js.map +1 -0
  79. package/dist/cli/commands/stop.js +97 -0
  80. package/dist/cli/commands/stop.js.map +1 -0
  81. package/dist/cli/connect.js +108 -0
  82. package/dist/cli/connect.js.map +1 -0
  83. package/dist/cli/index.js +99 -0
  84. package/dist/cli/index.js.map +1 -0
  85. package/dist/cli/port-utils.js +260 -0
  86. package/dist/cli/port-utils.js.map +1 -0
  87. package/dist/cli/runtime-env.js +60 -0
  88. package/dist/cli/runtime-env.js.map +1 -0
  89. package/dist/cli/shared.js +54 -0
  90. package/dist/cli/shared.js.map +1 -0
  91. package/dist/cli/ui.js +60 -0
  92. package/dist/cli/ui.js.map +1 -0
  93. package/dist/daemon/agents/claude.js +98 -0
  94. package/dist/daemon/agents/claude.js.map +1 -0
  95. package/dist/daemon/agents/codex.js +160 -0
  96. package/dist/daemon/agents/codex.js.map +1 -0
  97. package/dist/daemon/agents/gemini.js +111 -0
  98. package/dist/daemon/agents/gemini.js.map +1 -0
  99. package/dist/daemon/agents/index.js +59 -0
  100. package/dist/daemon/agents/index.js.map +1 -0
  101. package/dist/daemon/agents/kimi.js +206 -0
  102. package/dist/daemon/agents/kimi.js.map +1 -0
  103. package/dist/daemon/agents/opencode.js +228 -0
  104. package/dist/daemon/agents/opencode.js.map +1 -0
  105. package/dist/daemon/agents/openrouter.js +274 -0
  106. package/dist/daemon/agents/openrouter.js.map +1 -0
  107. package/dist/daemon/agents/parsers/claude.js +63 -0
  108. package/dist/daemon/agents/parsers/claude.js.map +1 -0
  109. package/dist/daemon/agents/parsers/codex.js +51 -0
  110. package/dist/daemon/agents/parsers/codex.js.map +1 -0
  111. package/dist/daemon/agents/parsers/gemini.js +144 -0
  112. package/dist/daemon/agents/parsers/gemini.js.map +1 -0
  113. package/dist/daemon/agents/parsers/index.js +31 -0
  114. package/dist/daemon/agents/parsers/index.js.map +1 -0
  115. package/dist/daemon/agents/parsers/kimi.js +8 -0
  116. package/dist/daemon/agents/parsers/kimi.js.map +1 -0
  117. package/dist/daemon/agents/parsers/opencode.js +105 -0
  118. package/dist/daemon/agents/parsers/opencode.js.map +1 -0
  119. package/dist/daemon/agents/parsers/openrouter.js +69 -0
  120. package/dist/daemon/agents/parsers/openrouter.js.map +1 -0
  121. package/dist/daemon/agents/parsers/shared.js +17 -0
  122. package/dist/daemon/agents/parsers/shared.js.map +1 -0
  123. package/dist/daemon/agents/preflight.js +83 -0
  124. package/dist/daemon/agents/preflight.js.map +1 -0
  125. package/dist/daemon/agents/quote.js +45 -0
  126. package/dist/daemon/agents/quote.js.map +1 -0
  127. package/dist/daemon/agents/sandbox-guard.js +69 -0
  128. package/dist/daemon/agents/sandbox-guard.js.map +1 -0
  129. package/dist/daemon/agents/types.js +6 -0
  130. package/dist/daemon/agents/types.js.map +1 -0
  131. package/dist/daemon/api-response.js +65 -0
  132. package/dist/daemon/api-response.js.map +1 -0
  133. package/dist/daemon/error-detector.js +329 -0
  134. package/dist/daemon/error-detector.js.map +1 -0
  135. package/dist/daemon/headless.js +533 -0
  136. package/dist/daemon/headless.js.map +1 -0
  137. package/dist/daemon/index.js +333 -0
  138. package/dist/daemon/index.js.map +1 -0
  139. package/dist/daemon/openrouter.js +192 -0
  140. package/dist/daemon/openrouter.js.map +1 -0
  141. package/dist/daemon/orchestrators/claude.js +163 -0
  142. package/dist/daemon/orchestrators/claude.js.map +1 -0
  143. package/dist/daemon/orchestrators/codex.js +101 -0
  144. package/dist/daemon/orchestrators/codex.js.map +1 -0
  145. package/dist/daemon/orchestrators/cursor-windsurf.js +118 -0
  146. package/dist/daemon/orchestrators/cursor-windsurf.js.map +1 -0
  147. package/dist/daemon/orchestrators/gemini.js +108 -0
  148. package/dist/daemon/orchestrators/gemini.js.map +1 -0
  149. package/dist/daemon/orchestrators/index.js +90 -0
  150. package/dist/daemon/orchestrators/index.js.map +1 -0
  151. package/dist/daemon/orchestrators/kimi.js +108 -0
  152. package/dist/daemon/orchestrators/kimi.js.map +1 -0
  153. package/dist/daemon/orchestrators/opencode.js +152 -0
  154. package/dist/daemon/orchestrators/opencode.js.map +1 -0
  155. package/dist/daemon/orchestrators/shared.js +60 -0
  156. package/dist/daemon/orchestrators/shared.js.map +1 -0
  157. package/dist/daemon/output-watcher.js +131 -0
  158. package/dist/daemon/output-watcher.js.map +1 -0
  159. package/dist/daemon/participant-aborts.js +123 -0
  160. package/dist/daemon/participant-aborts.js.map +1 -0
  161. package/dist/daemon/reaper.js +46 -0
  162. package/dist/daemon/reaper.js.map +1 -0
  163. package/dist/daemon/routes/chats-events.js +62 -0
  164. package/dist/daemon/routes/chats-events.js.map +1 -0
  165. package/dist/daemon/routes/chats-stream.js +241 -0
  166. package/dist/daemon/routes/chats-stream.js.map +1 -0
  167. package/dist/daemon/routes/chats-validation.js +13 -0
  168. package/dist/daemon/routes/chats-validation.js.map +1 -0
  169. package/dist/daemon/routes/chats.js +545 -0
  170. package/dist/daemon/routes/chats.js.map +1 -0
  171. package/dist/daemon/routes/openrouter.js +103 -0
  172. package/dist/daemon/routes/openrouter.js.map +1 -0
  173. package/dist/daemon/routes/settings.js +199 -0
  174. package/dist/daemon/routes/settings.js.map +1 -0
  175. package/dist/daemon/routes/stats.js +155 -0
  176. package/dist/daemon/routes/stats.js.map +1 -0
  177. package/dist/daemon/routes/system.js +208 -0
  178. package/dist/daemon/routes/system.js.map +1 -0
  179. package/dist/daemon/routes/templates-personas.js +254 -0
  180. package/dist/daemon/routes/templates-personas.js.map +1 -0
  181. package/dist/daemon/routes/voices.js +139 -0
  182. package/dist/daemon/routes/voices.js.map +1 -0
  183. package/dist/daemon/runner/doer-driver.js +346 -0
  184. package/dist/daemon/runner/doer-driver.js.map +1 -0
  185. package/dist/daemon/runner/doer.js +336 -0
  186. package/dist/daemon/runner/doer.js.map +1 -0
  187. package/dist/daemon/runner/prior-round.js +140 -0
  188. package/dist/daemon/runner/prior-round.js.map +1 -0
  189. package/dist/daemon/runner/prompt-builder.js +292 -0
  190. package/dist/daemon/runner/prompt-builder.js.map +1 -0
  191. package/dist/daemon/runner/review-only-phase.js +103 -0
  192. package/dist/daemon/runner/review-only-phase.js.map +1 -0
  193. package/dist/daemon/runner/reviewer-driver.js +410 -0
  194. package/dist/daemon/runner/reviewer-driver.js.map +1 -0
  195. package/dist/daemon/runner/reviewer.js +384 -0
  196. package/dist/daemon/runner/reviewer.js.map +1 -0
  197. package/dist/daemon/runner/run-with-fallback.js +56 -0
  198. package/dist/daemon/runner/run-with-fallback.js.map +1 -0
  199. package/dist/daemon/runner/sanitize-name.js +8 -0
  200. package/dist/daemon/runner/sanitize-name.js.map +1 -0
  201. package/dist/daemon/runner/stream-file-writer.js +116 -0
  202. package/dist/daemon/runner/stream-file-writer.js.map +1 -0
  203. package/dist/daemon/runner/swap-sidecar.js +102 -0
  204. package/dist/daemon/runner/swap-sidecar.js.map +1 -0
  205. package/dist/daemon/runner/template-fallback.js +119 -0
  206. package/dist/daemon/runner/template-fallback.js.map +1 -0
  207. package/dist/daemon/runner/types.js +3 -0
  208. package/dist/daemon/runner/types.js.map +1 -0
  209. package/dist/daemon/runner/verdict.js +51 -0
  210. package/dist/daemon/runner/verdict.js.map +1 -0
  211. package/dist/daemon/runner-multiplex.js +364 -0
  212. package/dist/daemon/runner-multiplex.js.map +1 -0
  213. package/dist/daemon/runner.js +427 -0
  214. package/dist/daemon/runner.js.map +1 -0
  215. package/dist/daemon/ship.js +340 -0
  216. package/dist/daemon/ship.js.map +1 -0
  217. package/dist/daemon/template-cache.js +37 -0
  218. package/dist/daemon/template-cache.js.map +1 -0
  219. package/dist/daemon/tmux-types.js +9 -0
  220. package/dist/daemon/tmux-types.js.map +1 -0
  221. package/dist/daemon/tmux.js +341 -0
  222. package/dist/daemon/tmux.js.map +1 -0
  223. package/dist/lib/atomic-write.js +55 -0
  224. package/dist/lib/atomic-write.js.map +1 -0
  225. package/dist/lib/chat-events-bus.js +27 -0
  226. package/dist/lib/chat-events-bus.js.map +1 -0
  227. package/dist/lib/chat-slug.js +105 -0
  228. package/dist/lib/chat-slug.js.map +1 -0
  229. package/dist/lib/cli-detect.js +388 -0
  230. package/dist/lib/cli-detect.js.map +1 -0
  231. package/dist/lib/cli-health.js +156 -0
  232. package/dist/lib/cli-health.js.map +1 -0
  233. package/dist/lib/cli-paths.js +113 -0
  234. package/dist/lib/cli-paths.js.map +1 -0
  235. package/dist/lib/cli-precheck.js +141 -0
  236. package/dist/lib/cli-precheck.js.map +1 -0
  237. package/dist/lib/db/chats.js +244 -0
  238. package/dist/lib/db/chats.js.map +1 -0
  239. package/dist/lib/db/connection.js +254 -0
  240. package/dist/lib/db/connection.js.map +1 -0
  241. package/dist/lib/db/index.js +34 -0
  242. package/dist/lib/db/index.js.map +1 -0
  243. package/dist/lib/db/personas.js +65 -0
  244. package/dist/lib/db/personas.js.map +1 -0
  245. package/dist/lib/db/phase-events.js +172 -0
  246. package/dist/lib/db/phase-events.js.map +1 -0
  247. package/dist/lib/db/secrets.js +53 -0
  248. package/dist/lib/db/secrets.js.map +1 -0
  249. package/dist/lib/db/settings.js +47 -0
  250. package/dist/lib/db/settings.js.map +1 -0
  251. package/dist/lib/db/templates.js +75 -0
  252. package/dist/lib/db/templates.js.map +1 -0
  253. package/dist/lib/db/voices.js +184 -0
  254. package/dist/lib/db/voices.js.map +1 -0
  255. package/dist/lib/lineage-maps.js +200 -0
  256. package/dist/lib/lineage-maps.js.map +1 -0
  257. package/dist/lib/logger.js +186 -0
  258. package/dist/lib/logger.js.map +1 -0
  259. package/dist/lib/personas.js +117 -0
  260. package/dist/lib/personas.js.map +1 -0
  261. package/dist/lib/runtime-path.js +222 -0
  262. package/dist/lib/runtime-path.js.map +1 -0
  263. package/dist/lib/settings/billing.js +58 -0
  264. package/dist/lib/settings/billing.js.map +1 -0
  265. package/dist/lib/settings/permissions.js +81 -0
  266. package/dist/lib/settings/permissions.js.map +1 -0
  267. package/dist/lib/settings/transport.js +113 -0
  268. package/dist/lib/settings/transport.js.map +1 -0
  269. package/dist/lib/telemetry.js +290 -0
  270. package/dist/lib/telemetry.js.map +1 -0
  271. package/dist/lib/template-schema.js +319 -0
  272. package/dist/lib/template-schema.js.map +1 -0
  273. package/dist/lib/template-validation.js +82 -0
  274. package/dist/lib/template-validation.js.map +1 -0
  275. package/dist/lib/voices.js +533 -0
  276. package/dist/lib/voices.js.map +1 -0
  277. package/dist/mcp/client.js +138 -0
  278. package/dist/mcp/client.js.map +1 -0
  279. package/dist/mcp/index.js +178 -0
  280. package/dist/mcp/index.js.map +1 -0
  281. package/dist/mcp/tools.js +355 -0
  282. package/dist/mcp/tools.js.map +1 -0
  283. package/package.json +2 -1
  284. package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js +0 -6077
  285. package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js.map +0 -69
  286. package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js +0 -6318
  287. package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js.map +0 -71
  288. package/.next/dev/types/cache-life.d.ts +0 -145
  289. package/.next/dev/types/routes.d.ts +0 -84
  290. package/.next/dev/types/validator.ts +0 -178
  291. /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 9cD3yIOGe_Aqr17uJHTQS}/_buildManifest.js +0 -0
  292. /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 9cD3yIOGe_Aqr17uJHTQS}/_clientMiddlewareManifest.js +0 -0
  293. /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 9cD3yIOGe_Aqr17uJHTQS}/_ssgManifest.js +0 -0
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ /**
3
+ * Chorus daemon — Fastify HTTP server.
4
+ *
5
+ * Boots the DB, seeds builtin personas/voices/templates, registers route
6
+ * groups, and starts the reaper. Routes live in `routes/*.ts`; the
7
+ * runChat multi-subscriber wrapper lives in `runner-multiplex.ts`.
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.successResponse = exports.errorResponse = exports.isValidChatId = exports.getParsedTemplate = void 0;
14
+ exports.getTmuxManager = getTmuxManager;
15
+ const cors_1 = __importDefault(require("@fastify/cors"));
16
+ const fastify_1 = __importDefault(require("fastify"));
17
+ const fs_1 = __importDefault(require("fs"));
18
+ const path_1 = __importDefault(require("path"));
19
+ const index_js_1 = require("../lib/db/index.js");
20
+ const logger_js_1 = require("../lib/logger.js");
21
+ const error_detector_js_1 = require("./error-detector.js");
22
+ const reaper_js_1 = require("./reaper.js");
23
+ const runner_multiplex_js_1 = require("./runner-multiplex.js");
24
+ const chats_js_1 = require("./routes/chats.js");
25
+ const chats_events_js_1 = require("./routes/chats-events.js");
26
+ const openrouter_js_1 = require("./routes/openrouter.js");
27
+ const templates_personas_js_1 = require("./routes/templates-personas.js");
28
+ const settings_js_1 = require("./routes/settings.js");
29
+ const stats_js_1 = require("./routes/stats.js");
30
+ const system_js_1 = require("./routes/system.js");
31
+ const voices_js_1 = require("./routes/voices.js");
32
+ const tmux_js_1 = require("./tmux.js");
33
+ const api_response_js_1 = require("./api-response.js");
34
+ Object.defineProperty(exports, "errorResponse", { enumerable: true, get: function () { return api_response_js_1.errorResponse; } });
35
+ Object.defineProperty(exports, "successResponse", { enumerable: true, get: function () { return api_response_js_1.successResponse; } });
36
+ var template_cache_js_1 = require("./template-cache.js");
37
+ Object.defineProperty(exports, "getParsedTemplate", { enumerable: true, get: function () { return template_cache_js_1.getParsedTemplate; } });
38
+ var chats_js_2 = require("./routes/chats.js");
39
+ Object.defineProperty(exports, "isValidChatId", { enumerable: true, get: function () { return chats_js_2.isValidChatId; } });
40
+ /**
41
+ * Resolve daemon port from env, with hard validation. parseInt('chorus', 10)
42
+ * silently returns NaN, which Fastify accepts as "let the OS pick a port" —
43
+ * the daemon would start, bind to a random port, and the cockpit would
44
+ * never find it. Catch this at boot with a useful error message instead.
45
+ */
46
+ function resolveDaemonPort() {
47
+ const raw = process.env.CHORUS_DAEMON_PORT;
48
+ if (!raw)
49
+ return 7707;
50
+ const parsed = parseInt(raw, 10);
51
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
52
+ throw new Error(`CHORUS_DAEMON_PORT must be an integer between 1 and 65535. Got: ${JSON.stringify(raw)}`);
53
+ }
54
+ return parsed;
55
+ }
56
+ const PORT = resolveDaemonPort();
57
+ const HOST = '127.0.0.1';
58
+ const VERSION = '0.7.2';
59
+ const startTime = Date.now();
60
+ // Absolute path to bin/chorus.mjs — used by /orchestrators/:name/connect
61
+ // when the cockpit triggers a one-click wire-up. Both src/daemon/index.ts
62
+ // (tsx) and dist/daemon/index.js (PM2/built) resolve to <pkg-root>/bin/
63
+ // chorus.mjs.
64
+ const CHORUS_BIN_PATH = path_1.default.resolve(__dirname, '..', '..', 'bin', 'chorus.mjs');
65
+ // Singletons shared across the daemon lifetime.
66
+ let tmuxMgr;
67
+ let stopReaper = null;
68
+ const errorDetector = new error_detector_js_1.ErrorDetector();
69
+ async function main() {
70
+ // Eager DB probe: trying to open the sqlite file at startup catches
71
+ // permission errors, schema-migration crashes, and missing-init
72
+ // issues *before* the first HTTP request fails opaquely.
73
+ try {
74
+ await index_js_1.chats.list({ limit: 1 });
75
+ }
76
+ catch (err) {
77
+ const msg = err instanceof Error ? err.message : String(err);
78
+ console.error(`\n[chorus] Could not open database. Run \`chorus init\` first, ` +
79
+ `or check permissions on ~/.chorus/chorus.db.\n detail: ${msg}\n`);
80
+ process.exit(1);
81
+ }
82
+ const fastify = (0, fastify_1.default)({ logger: false });
83
+ await fastify.register(cors_1.default, {
84
+ origin: ['http://127.0.0.1:5050'],
85
+ credentials: true,
86
+ });
87
+ // Seed built-in personas from prompts/personas/*.md. Idempotent:
88
+ // builtin rows refresh from the file source of truth on every
89
+ // startup; user-created rows (builtin=0) are not touched.
90
+ try {
91
+ const { seedBuiltinPersonas } = await import('../lib/personas.js');
92
+ const count = await seedBuiltinPersonas();
93
+ console.log(`[daemon] seeded ${count} built-in personas`);
94
+ }
95
+ catch (err) {
96
+ // Non-fatal: daemon still works without personas.
97
+ console.warn('[daemon] persona seed failed:', err instanceof Error ? err.message : err);
98
+ }
99
+ // Prime the merged spawn PATH BEFORE voice seed. seedCliVoices runs
100
+ // detectAllClis(), which honours saved manual paths via the cli-paths
101
+ // cache; without priming, the cache is empty and a custom-location
102
+ // CLI shows up as undetected on the first boot after the user pasted
103
+ // its path into onboarding.
104
+ try {
105
+ const { buildRuntimePath } = await import('../lib/runtime-path.js');
106
+ const { cliPaths } = await import('../lib/cli-paths.js');
107
+ const { setSpawnPath } = await import('./headless.js');
108
+ await cliPaths.refreshCache();
109
+ const merged = await buildRuntimePath({
110
+ additionalDirs: cliPaths.cachedDirs(),
111
+ });
112
+ setSpawnPath(merged);
113
+ console.log(`[daemon] runtime PATH primed (${merged.split(':').length} dirs)`);
114
+ }
115
+ catch (err) {
116
+ console.warn('[daemon] runtime PATH prime failed (falling back to process.env.PATH):', err instanceof Error ? err.message : err);
117
+ }
118
+ // Voices Phase 1 — synchronous, pre-listen seed of single-model CLIs
119
+ // + first-boot migration from <lineage>.enabled_models. Fast (no
120
+ // shell-outs); blocks listen on intent (we want voices ready before
121
+ // routes serve).
122
+ try {
123
+ const { seedCliVoices } = await import('../lib/voices.js');
124
+ const result = await seedCliVoices();
125
+ console.log(`[daemon] voices Phase 1: +${result.added} added, ${result.updated} updated, ${result.disabled} auto-disabled`);
126
+ }
127
+ catch (err) {
128
+ console.warn('[daemon] voices Phase 1 seed failed:', err instanceof Error ? err.message : err);
129
+ }
130
+ // ─── Routes ─────────────────────────────────────────────────────────
131
+ //
132
+ // Every public REST + SSE route mounts under /api/v1. Pre-launch
133
+ // shape-freeze (v0.7) — adding /api/v2 later is non-breaking.
134
+ //
135
+ // Bare paths (`/health`, `/chats`, ...) are kept as transitional
136
+ // aliases for one minor (v0.7) so that globally-installed MCP servers
137
+ // shipping older chorus-codes versions don't break the moment a user
138
+ // upgrades the daemon. Callers should migrate to /api/v1; the bare
139
+ // paths are dropped in v0.8.
140
+ // Initialize tmux manager BEFORE registering chat routes — the chat
141
+ // route handlers capture it for the duration of the daemon.
142
+ tmuxMgr = new tmux_js_1.TmuxManagerImpl();
143
+ const registerAll = (api) => {
144
+ api.get('/health', async () => {
145
+ // The redundant inner `ok: true` from earlier shipped versions
146
+ // was dropped here — the envelope's outer `ok: true` is the
147
+ // canonical liveness signal. Consumers that want a flat
148
+ // monitor-friendly probe still read `data.version` /
149
+ // `data.uptime`.
150
+ return (0, api_response_js_1.successResponse)({
151
+ version: VERSION,
152
+ uptime: Date.now() - startTime,
153
+ });
154
+ });
155
+ (0, chats_js_1.registerChatRoutes)(api, { tmuxMgr: tmuxMgr, errorDetector });
156
+ (0, chats_events_js_1.registerChatEventsRoute)(api);
157
+ (0, templates_personas_js_1.registerTemplateRoutes)(api);
158
+ (0, templates_personas_js_1.registerPersonaRoutes)(api);
159
+ (0, settings_js_1.registerSettingsRoutes)(api);
160
+ (0, settings_js_1.registerSecretRoutes)(api);
161
+ (0, system_js_1.registerSystemRoutes)(api, { chorusBinPath: CHORUS_BIN_PATH });
162
+ (0, voices_js_1.registerVoiceRoutes)(api);
163
+ (0, openrouter_js_1.registerOpenRouterRoutes)(api);
164
+ (0, stats_js_1.registerStatsRoutes)(api);
165
+ };
166
+ await fastify.register(async (api) => registerAll(api), { prefix: '/api/v1' });
167
+ // v0.7 transitional aliases — drop in v0.8.
168
+ await fastify.register(async (api) => registerAll(api));
169
+ await seedBuiltinTemplates();
170
+ // Reap orphan headless subprocesses from any prior daemon crash.
171
+ // Without this, a hung CLI from a previous run keeps burning
172
+ // subscription quota until manually killed.
173
+ try {
174
+ const { reapOrphanProcesses } = await import('./headless.js');
175
+ const result = reapOrphanProcesses();
176
+ if (result.reaped > 0 || result.cleared > 0) {
177
+ console.log(`[chorus] reaper: killed ${result.reaped} orphan headless processes, cleared ${result.cleared} stale records`);
178
+ }
179
+ }
180
+ catch (err) {
181
+ // Non-fatal — orphan cleanup is best-effort.
182
+ console.warn('[chorus] reaper: failed to scan PID dir', err);
183
+ }
184
+ stopReaper = (0, reaper_js_1.startReaper)(tmuxMgr, async () => {
185
+ // getActiveChats: chatId → status for active chats only (drafting
186
+ // and reviewing). Terminal states are reaped.
187
+ const allChats = await index_js_1.chats.list({ limit: 1000, offset: 0 });
188
+ const activeMap = new Map();
189
+ const activeStatuses = new Set(['drafting', 'reviewing']);
190
+ for (const chat of allChats) {
191
+ if (activeStatuses.has(chat.status)) {
192
+ activeMap.set(chat.id, chat.status);
193
+ }
194
+ }
195
+ return activeMap;
196
+ }, {
197
+ intervalMs: 5 * 60 * 1000,
198
+ idleDestroyMinutes: 30,
199
+ });
200
+ // ─── Graceful shutdown ──────────────────────────────────────────────
201
+ const shutdown = async (signal) => {
202
+ if ((0, runner_multiplex_js_1.activeRunsCount)() > 0) {
203
+ const runs = (0, runner_multiplex_js_1.activeRunsSnapshot)();
204
+ for (const entry of runs) {
205
+ entry.abortController.abort();
206
+ }
207
+ try {
208
+ await Promise.race([
209
+ Promise.allSettled(runs.map((e) => e.promise)),
210
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout waiting for active runs')), 10000)),
211
+ ]);
212
+ console.log(`[chorus] aborted ${runs.length} active runs (${signal})`);
213
+ }
214
+ catch {
215
+ console.warn('[chorus] timeout or error waiting for active runs to abort');
216
+ }
217
+ }
218
+ if (stopReaper)
219
+ stopReaper();
220
+ await fastify.close();
221
+ process.exit(0);
222
+ };
223
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
224
+ process.on('SIGINT', () => void shutdown('SIGINT'));
225
+ await fastify.listen({ port: PORT, host: HOST });
226
+ logger_js_1.logger.info({ port: PORT, host: HOST, version: VERSION }, 'daemon listening');
227
+ // Keep the human-readable startup line — the install script +
228
+ // onboarding grep for it. Structured line above is what `chorus logs`
229
+ // consumes.
230
+ console.log(`Chorus daemon listening on http://${HOST}:${PORT}`);
231
+ // Anonymous opt-out telemetry. First send is delayed 5s so the
232
+ // listener is definitely up; subsequent sends every 24h. All three
233
+ // opt-out paths (env, touch-file, settings) are honoured per send.
234
+ const { startTelemetryHeartbeat } = await import('../lib/telemetry.js');
235
+ const telemetryHandle = startTelemetryHeartbeat({
236
+ version: VERSION,
237
+ daemonStartedAt: startTime,
238
+ });
239
+ process.on('SIGTERM', () => telemetryHandle.stop());
240
+ process.on('SIGINT', () => telemetryHandle.stop());
241
+ // Voices Phase 2 — background warmup. `opencode models` shells out
242
+ // and can take up to 10s; running it post-listen avoids that boot-
243
+ // latency hit. Errors are logged but don't crash the daemon.
244
+ void (async () => {
245
+ try {
246
+ const { seedOpencodeVoicesAsync } = await import('../lib/voices.js');
247
+ const result = await seedOpencodeVoicesAsync();
248
+ if (result) {
249
+ console.log(`[daemon] voices Phase 2 (opencode): +${result.added} added, ${result.updated} updated, ${result.disabled} auto-disabled`);
250
+ }
251
+ else {
252
+ console.log('[daemon] voices Phase 2 (opencode): skipped (CLI not detected or shell-out failed)');
253
+ }
254
+ }
255
+ catch (err) {
256
+ console.warn('[daemon] voices Phase 2 failed:', err instanceof Error ? err.message : err);
257
+ }
258
+ })();
259
+ }
260
+ async function seedBuiltinTemplates() {
261
+ const templatesDir = path_1.default.join(__dirname, '..', '..', 'templates');
262
+ if (!fs_1.default.existsSync(templatesDir)) {
263
+ console.log('No templates directory found, skipping seed');
264
+ return;
265
+ }
266
+ const files = fs_1.default.readdirSync(templatesDir).filter((f) => f.endsWith('.yaml'));
267
+ const onDiskIds = new Set();
268
+ for (const file of files) {
269
+ const id = file.replace('.yaml', '');
270
+ onDiskIds.add(id);
271
+ const yamlPath = path_1.default.join(templatesDir, file);
272
+ const yamlContent = fs_1.default.readFileSync(yamlPath, 'utf-8');
273
+ const existing = await index_js_1.templates.getById(id);
274
+ if (!existing) {
275
+ await index_js_1.templates.create(id, yamlContent, 'builtin');
276
+ console.log(`[daemon] seeded template: ${id}`);
277
+ continue;
278
+ }
279
+ // Re-sync builtin rows from disk on every boot. User-cloned rows
280
+ // (source='user') are NEVER overwritten — those belong to the user
281
+ // and will be edited via /templates POST. Keeps YAML source-of-
282
+ // truth aligned with the DB after a chorus upgrade.
283
+ if (existing.source === 'builtin' && existing.yaml !== yamlContent) {
284
+ await index_js_1.templates.create(id, yamlContent, 'builtin');
285
+ console.log(`[daemon] refreshed builtin template from disk: ${id}`);
286
+ }
287
+ }
288
+ // Flag any builtin templates that are no longer present on disk.
289
+ // templates.delete is exposed by the DB layer but the loop only logs
290
+ // intent today — refresh-on-disk-change keeps content fresh, and
291
+ // stale rows are inert (just don't appear in templatesDir). Tracked
292
+ // outside the libsql migration scope.
293
+ try {
294
+ const allTemplates = await index_js_1.templates.list();
295
+ let staleCount = 0;
296
+ for (const tmpl of allTemplates) {
297
+ if (tmpl.source === 'builtin' && !onDiskIds.has(tmpl.id)) {
298
+ console.log(`[daemon] would delete stale builtin template (no delete method): ${tmpl.id}`);
299
+ staleCount++;
300
+ }
301
+ }
302
+ if (staleCount > 0) {
303
+ console.log(`[daemon] flagged ${staleCount} stale builtin templates for cleanup`);
304
+ }
305
+ }
306
+ catch (err) {
307
+ // Non-fatal: if templates.list() fails, skip cleanup.
308
+ console.warn('[daemon] failed to scan stale builtin templates:', err);
309
+ }
310
+ }
311
+ // Auto-run main() only when this file is the process entry point. When
312
+ // the daemon module is imported from a test (e.g.
313
+ // tests/template-cache.test.ts importing the exported getParsedTemplate),
314
+ // we don't want a side-effecty fastify boot or DB probe firing on module
315
+ // load.
316
+ const isEntryPoint = typeof require !== 'undefined' && require.main === module;
317
+ if (isEntryPoint) {
318
+ main().catch((error) => {
319
+ console.error('Failed to start daemon:', error);
320
+ process.exit(1);
321
+ });
322
+ }
323
+ /**
324
+ * @internal — for tests / cross-module daemon code. Throws if called
325
+ * before main() has wired the singleton.
326
+ */
327
+ function getTmuxManager() {
328
+ if (!tmuxMgr) {
329
+ throw new Error('TmuxManager not initialized. Daemon may not have started yet.');
330
+ }
331
+ return tmuxMgr;
332
+ }
333
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ /**
3
+ * OpenRouter inline integration — validate API key, list available models,
4
+ * add models as voices.
5
+ *
6
+ * Why an inline flow: the v0.7 voices table can already store
7
+ * `source='api'` voices, but until now there was no UX path to populate
8
+ * them — users could only enable CLI-detected voices. OpenRouter is the
9
+ * widest practical API gateway (Anthropic, OpenAI, Google, Meta, Kimi,
10
+ * DeepSeek, Mistral, Grok, …) so a single key unlocks dozens of models
11
+ * across every lineage we score for diversity.
12
+ *
13
+ * Architecture lifted from 9router's `/api/providers/validate` route
14
+ * (which we studied before writing this) — same Bearer-auth contract,
15
+ * same `GET /api/v1/models` validation. Differences:
16
+ * - We persist the key in chorus's `secrets` table, not a JSON blob.
17
+ * - We classify each model into a chorus lineage via the existing
18
+ * `classifyOpencodeModel` helper (the "anthropic/claude-..." prefix
19
+ * pattern matches OpenRouter's id format directly).
20
+ * - We do NOT proxy chat completions through this module yet — that
21
+ * comes in a follow-up HTTP-shim PR. This PR ships the validate +
22
+ * model-catalog + voices-insert flow only; selecting an OpenRouter
23
+ * voice in a template won't dispatch successfully until the shim
24
+ * lands. The voices appear in the picker as preview-only.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.validateKey = validateKey;
28
+ exports.listModels = listModels;
29
+ exports.saveKey = saveKey;
30
+ exports.addModelsAsVoices = addModelsAsVoices;
31
+ const index_js_1 = require("../lib/db/index.js");
32
+ const voices_js_1 = require("../lib/voices.js");
33
+ const OPENROUTER_BASE = 'https://openrouter.ai/api/v1';
34
+ const VALIDATE_TIMEOUT_MS = 8_000;
35
+ const MODELS_TIMEOUT_MS = 15_000;
36
+ /**
37
+ * Live-probe an API key by hitting `/api/v1/models`. OpenRouter rejects
38
+ * bad keys with HTTP 401, so a 200 means the key is real. Anything else
39
+ * (network errors, 5xx, 403) we surface verbatim — the cockpit then
40
+ * shows the user what to fix.
41
+ */
42
+ async function validateKey(apiKey) {
43
+ if (!apiKey || apiKey.trim().length === 0) {
44
+ return { valid: false, error: 'API key is empty' };
45
+ }
46
+ try {
47
+ // Hit /auth/key — the only catalog-side endpoint that actually
48
+ // requires the bearer to be valid. /models is public and returns
49
+ // 200 for ANY auth header (including junk), so the previous
50
+ // implementation rubber-stamped invalid keys as valid. Verified
51
+ // behaviour: 401 on invalid bearer, 200 with key metadata on valid.
52
+ const res = await fetch(`${OPENROUTER_BASE}/auth/key`, {
53
+ headers: { Authorization: `Bearer ${apiKey}` },
54
+ signal: AbortSignal.timeout(VALIDATE_TIMEOUT_MS),
55
+ });
56
+ if (res.ok)
57
+ return { valid: true };
58
+ if (res.status === 401 || res.status === 403) {
59
+ return { valid: false, error: 'Invalid API key' };
60
+ }
61
+ return { valid: false, error: `OpenRouter returned ${res.status}` };
62
+ }
63
+ catch (err) {
64
+ const message = err instanceof Error ? err.message : String(err);
65
+ return { valid: false, error: `Network error: ${message}` };
66
+ }
67
+ }
68
+ /**
69
+ * Fetch the full model catalog. Uses the stored OpenRouter key from
70
+ * the secrets table — callers must save the key first via
71
+ * `saveKey` (or pass an explicit key for one-off lookups).
72
+ *
73
+ * We trim the response to the four fields the cockpit picker actually
74
+ * needs, dropping the long descriptions, capability flags, and per-modality
75
+ * pricing. Keeps the wire payload manageable (200+ models * ~2 KB each
76
+ * crosses 400 KB unfiltered).
77
+ *
78
+ * Defensive pagination: OpenRouter currently returns the full catalog
79
+ * in a single response with no cursor. If they ever add pagination
80
+ * (`next_cursor` or `cursor` field), this loop follows it up to
81
+ * MAX_PAGES guard. Today's behaviour is identical to a single fetch.
82
+ */
83
+ const MAX_PAGES = 20;
84
+ function mapModel(m) {
85
+ // OpenRouter pricing is reported as USD-per-token (e.g. "0.000003"
86
+ // for $3/Mtok). Convert to per-Mtok USD to match chorus's voices
87
+ // table convention. Strings parse to NaN if missing — guard that.
88
+ const promptCost = m.pricing?.prompt ? parseFloat(m.pricing.prompt) : NaN;
89
+ const completionCost = m.pricing?.completion ? parseFloat(m.pricing.completion) : NaN;
90
+ const result = { id: m.id, name: m.name ?? m.id };
91
+ if (typeof m.context_length === 'number') {
92
+ result.contextLength = m.context_length;
93
+ }
94
+ if (Number.isFinite(promptCost)) {
95
+ result.inputCostPerMtok = promptCost * 1_000_000;
96
+ }
97
+ if (Number.isFinite(completionCost)) {
98
+ result.outputCostPerMtok = completionCost * 1_000_000;
99
+ }
100
+ return result;
101
+ }
102
+ async function listModels(apiKey) {
103
+ const key = apiKey ?? (await index_js_1.secrets.get('openrouter'))?.value;
104
+ if (!key) {
105
+ throw new Error('No OpenRouter API key saved. POST /openrouter/save-key with {apiKey} first.');
106
+ }
107
+ const all = [];
108
+ let cursor;
109
+ let pages = 0;
110
+ do {
111
+ const url = cursor
112
+ ? `${OPENROUTER_BASE}/models?cursor=${encodeURIComponent(cursor)}`
113
+ : `${OPENROUTER_BASE}/models`;
114
+ const res = await fetch(url, {
115
+ headers: { Authorization: `Bearer ${key}` },
116
+ signal: AbortSignal.timeout(MODELS_TIMEOUT_MS),
117
+ });
118
+ if (!res.ok) {
119
+ throw new Error(`OpenRouter /models returned ${res.status}`);
120
+ }
121
+ const body = (await res.json());
122
+ if (Array.isArray(body.data)) {
123
+ all.push(...body.data.map(mapModel));
124
+ }
125
+ // Future-compat: OpenRouter may add `next_cursor` (most common naming)
126
+ // or `cursor`. We stop when neither is a non-empty string. Safety cap
127
+ // prevents an infinite loop if a buggy server echoes the same cursor.
128
+ const next = body.next_cursor ?? body.cursor;
129
+ cursor = typeof next === 'string' && next.length > 0 ? next : undefined;
130
+ pages += 1;
131
+ } while (cursor && pages < MAX_PAGES);
132
+ return all;
133
+ }
134
+ /**
135
+ * Validate, then persist the key under provider='openrouter' in secrets.
136
+ * Returns the validation result; on failure the key is NOT saved.
137
+ */
138
+ async function saveKey(apiKey) {
139
+ const result = await validateKey(apiKey);
140
+ if (!result.valid)
141
+ return result;
142
+ await index_js_1.secrets.set('openrouter', 'api_key', apiKey);
143
+ return result;
144
+ }
145
+ /**
146
+ * Add the chosen models as voices. Idempotent — voices.upsert dedupes
147
+ * by id. Returns the list of voice ids actually inserted/updated.
148
+ *
149
+ * Voice id format mirrors the kimi/opencode-go convention:
150
+ * `openrouter:<openrouter-model-id>`
151
+ * so the picker can show "openrouter:anthropic/claude-3.5-sonnet" and
152
+ * a future template can reference it unambiguously.
153
+ */
154
+ async function addModelsAsVoices(modelIds, apiKey) {
155
+ if (modelIds.length === 0)
156
+ return { added: [], skipped: [] };
157
+ // Pull the catalog ONCE so we can fill labels + pricing for each id
158
+ // without re-hitting OpenRouter per-model. Optional `apiKey` plumbs
159
+ // through to listModels so callers that already hold the key don't
160
+ // race a concurrent save-key write to the secrets table.
161
+ const catalog = await listModels(apiKey);
162
+ const byId = new Map(catalog.map((m) => [m.id, m]));
163
+ const added = [];
164
+ const skipped = [];
165
+ for (const modelId of modelIds) {
166
+ const meta = byId.get(modelId);
167
+ if (!meta) {
168
+ skipped.push(modelId);
169
+ continue;
170
+ }
171
+ const { lineage, vendor_family } = (0, voices_js_1.classifyOpencodeModel)(modelId);
172
+ const voiceId = `openrouter:${modelId}`;
173
+ // Enabled by default — the HTTP dispatch shim now exists at
174
+ // src/daemon/agents/openrouter.ts, so a template referencing this
175
+ // voice will dispatch successfully via /api/v1/chat/completions.
176
+ await index_js_1.voices.upsert({
177
+ id: voiceId,
178
+ label: meta.name,
179
+ source: 'api',
180
+ provider: 'openrouter',
181
+ model_id: modelId,
182
+ lineage,
183
+ vendor_family: vendor_family ?? null,
184
+ input_cost_per_mtok: meta.inputCostPerMtok ?? null,
185
+ output_cost_per_mtok: meta.outputCostPerMtok ?? null,
186
+ enabled: true,
187
+ });
188
+ added.push(voiceId);
189
+ }
190
+ return { added, skipped };
191
+ }
192
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/daemon/openrouter.ts"],"names":[],"mappings":"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"}