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,113 @@
1
+ "use strict";
2
+ /**
3
+ * Persistence for user-supplied "I know where the CLI lives" paths.
4
+ *
5
+ * Pre-fix the onboarding `/onboard/validate-cli-path` endpoint validated
6
+ * a custom path and returned `{ found: true, path: ... }` to the cockpit
7
+ * — which only updated React state. The path was never persisted, so:
8
+ *
9
+ * - Next daemon boot: cli-detect ran the bare PATH search and lost the
10
+ * hint entirely. CLIs in non-standard locations stayed undetected.
11
+ * - Reviewer spawns: never saw the path. Same "command not found" we
12
+ * hit in the launch smoke tests.
13
+ *
14
+ * This module closes the loop. The cockpit calls `/onboard/save-cli-path`
15
+ * after validation succeeds; cli-detect.ts honours saved paths first
16
+ * (with `source: 'manual'`); the headless spawn merges each saved path's
17
+ * dirname into the runtime PATH.
18
+ *
19
+ * Storage shape: one settings row per CLI, keyed `cli_paths.<id>`. Value
20
+ * is the absolute path string. We deliberately don't use a dedicated
21
+ * table — the data is sparse, write-rarely, single-string, and lives
22
+ * fine alongside other settings.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.KNOWN_CLI_IDS = exports.cliPaths = void 0;
26
+ const settings_js_1 = require("./db/settings.js");
27
+ const ALL_CLI_IDS = [
28
+ 'claude-code',
29
+ 'codex-cli',
30
+ 'gemini-cli',
31
+ 'opencode-cli',
32
+ 'kimi-cli',
33
+ ];
34
+ const keyFor = (id) => `cli_paths.${id}`;
35
+ /**
36
+ * Sync read-through cache. cli-detect.ts is synchronous (it's called
37
+ * from many places that pre-date the async refactor) but the source of
38
+ * truth lives in the async `settings` table. Boot calls
39
+ * `refreshManualPathCache()` once to populate; sync callers read via
40
+ * `getCached`. Tests / save endpoint call `refresh` again to invalidate.
41
+ *
42
+ * Empty string in DB = explicit clear; we treat as absent in the cache.
43
+ */
44
+ const cache = new Map();
45
+ let cacheLoaded = false;
46
+ exports.cliPaths = {
47
+ /** Save (or overwrite) a manual path for a CLI. Caller must validate. */
48
+ async set(id, absolutePath) {
49
+ await settings_js_1.settings.set(keyFor(id), absolutePath);
50
+ cache.set(id, absolutePath);
51
+ cacheLoaded = true;
52
+ },
53
+ async get(id) {
54
+ const raw = await settings_js_1.settings.get(keyFor(id));
55
+ return typeof raw === 'string' && raw.length > 0 ? raw : null;
56
+ },
57
+ async clear(id) {
58
+ // settings has no delete; null-store via empty string + treat empty
59
+ // as absent in `get`. Keeps the table cleanable without a schema add.
60
+ await settings_js_1.settings.set(keyFor(id), '');
61
+ cache.delete(id);
62
+ },
63
+ /** Load every saved manual path. Used by buildRuntimeEnv() and chorus doctor. */
64
+ async listAll() {
65
+ const out = {};
66
+ for (const id of ALL_CLI_IDS) {
67
+ out[id] = await exports.cliPaths.get(id);
68
+ }
69
+ return out;
70
+ },
71
+ /**
72
+ * Sync lookup against the in-memory cache. Returns null when the cache
73
+ * hasn't been hydrated yet — caller should treat that as "no manual
74
+ * path" (the worst case is a bare PATH search, same as before this
75
+ * feature shipped).
76
+ */
77
+ getCached(id) {
78
+ if (!cacheLoaded)
79
+ return null;
80
+ return cache.get(id) ?? null;
81
+ },
82
+ /**
83
+ * List every cached manual path's directory. Drives PATH prepend in
84
+ * runtime-path.ts so even a custom-location binary can be invoked by
85
+ * bare name from a daemon-spawned subprocess.
86
+ */
87
+ cachedDirs() {
88
+ if (!cacheLoaded)
89
+ return [];
90
+ return Array.from(cache.values()).map((abs) => abs.replace(/\/[^/]+$/, ''));
91
+ },
92
+ /**
93
+ * Hydrate the sync cache from settings. Called once at daemon boot;
94
+ * also called by the save/clear endpoints to ensure fresh reads. Safe
95
+ * to call multiple times — wipes and repopulates.
96
+ */
97
+ async refreshCache() {
98
+ cache.clear();
99
+ for (const id of ALL_CLI_IDS) {
100
+ const raw = await settings_js_1.settings.get(keyFor(id));
101
+ if (typeof raw === 'string' && raw.length > 0)
102
+ cache.set(id, raw);
103
+ }
104
+ cacheLoaded = true;
105
+ },
106
+ /** @internal — for tests that rebuild the DB across cases. */
107
+ _resetCacheForTests() {
108
+ cache.clear();
109
+ cacheLoaded = false;
110
+ },
111
+ };
112
+ exports.KNOWN_CLI_IDS = ALL_CLI_IDS;
113
+ //# sourceMappingURL=cli-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-paths.js","sourceRoot":"","sources":["../../src/lib/cli-paths.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAEH,kDAA4C;AAS5C,MAAM,WAAW,GAAqB;IACpC,aAAa;IACb,WAAW;IACX,YAAY;IACZ,cAAc;IACd,UAAU;CACF,CAAC;AAEX,MAAM,MAAM,GAAG,CAAC,EAAS,EAAU,EAAE,CAAC,aAAa,EAAE,EAAE,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAiB,CAAC;AACvC,IAAI,WAAW,GAAG,KAAK,CAAC;AAEX,QAAA,QAAQ,GAAG;IACtB,yEAAyE;IACzE,KAAK,CAAC,GAAG,CAAC,EAAS,EAAE,YAAoB;QACvC,MAAM,sBAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;QAC7C,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QAC5B,WAAW,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAS;QACjB,MAAM,GAAG,GAAG,MAAM,sBAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAS;QACnB,oEAAoE;QACpE,sEAAsE;QACtE,MAAM,sBAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACnC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IAED,iFAAiF;IACjF,KAAK,CAAC,OAAO;QACX,MAAM,GAAG,GAAG,EAAkC,CAAC;QAC/C,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;YAC7B,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,gBAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,EAAS;QACjB,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAC9B,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,UAAU;QACR,IAAI,CAAC,WAAW;YAAE,OAAO,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,MAAM,sBAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACpE,CAAC;QACD,WAAW,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,8DAA8D;IAC9D,mBAAmB;QACjB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,WAAW,GAAG,KAAK,CAAC;IACtB,CAAC;CACF,CAAC;AAEW,QAAA,aAAa,GAAG,WAAW,CAAC"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * Pre-spawn CLI health check.
4
+ *
5
+ * Runs before each CLI subprocess spawn (doer or reviewer) to short-circuit
6
+ * three classes of doomed runs:
7
+ * 1. Quota already exhausted (cli-health.ts has a recent resetAt in the
8
+ * future). Spawning would just hit the same wall in 5–10 seconds and
9
+ * burn UI time.
10
+ * 2. CLI not logged in / cred file missing. Spawning would emit either a
11
+ * cold_start_timeout or token_refresh_lost from the pane scraper after
12
+ * we already paid the spawn tax (~3-5s for tmux/headless boot).
13
+ * 3. CLI binary not present (handled at boot by `cli-detect`; we just
14
+ * mention it here for completeness — not re-checked per spawn).
15
+ *
16
+ * Deliberately cheap: no network calls. We check filesystem state + the
17
+ * existing health record. A real network probe (ping `/v1/models` with the
18
+ * stored bearer to confirm token validity) is the natural next layer; for
19
+ * v0.7 the file-existence check catches "user logged out 3 days ago" which
20
+ * is the highest-frequency failure mode.
21
+ *
22
+ * Returns:
23
+ * - { ok: true } → proceed with spawn
24
+ * - { ok: false, reason, cta } → skip spawn, runner emits cli_warning
25
+ */
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.precheckLineage = precheckLineage;
31
+ const node_fs_1 = __importDefault(require("node:fs"));
32
+ const node_path_1 = __importDefault(require("node:path"));
33
+ const node_os_1 = __importDefault(require("node:os"));
34
+ const cli_health_1 = require("./cli-health");
35
+ /**
36
+ * Per-lineage credential file we treat as "user is logged in." Each CLI
37
+ * stores its OAuth bearer somewhere different; if the file doesn't exist
38
+ * the user almost certainly hasn't run the CLI's login command. False
39
+ * positives are possible (some users move credential dirs via env), so we
40
+ * only fail-closed when the file is *missing or unreadable* — never on
41
+ * shape mismatches inside the file.
42
+ */
43
+ const CRED_PATHS = {
44
+ anthropic: () => [
45
+ node_path_1.default.join(node_os_1.default.homedir(), '.claude', '.credentials.json'),
46
+ node_path_1.default.join(node_os_1.default.homedir(), '.config', 'anthropic', 'claude.json'),
47
+ ],
48
+ openai: () => [
49
+ node_path_1.default.join(node_os_1.default.homedir(), '.codex', 'auth.json'),
50
+ ],
51
+ google: () => [
52
+ node_path_1.default.join(node_os_1.default.homedir(), '.gemini', 'oauth_creds.json'),
53
+ node_path_1.default.join(node_os_1.default.homedir(), '.config', 'gemini', 'oauth_creds.json'),
54
+ ],
55
+ opencode: () => [
56
+ node_path_1.default.join(node_os_1.default.homedir(), '.opencode', 'auth.json'),
57
+ node_path_1.default.join(node_os_1.default.homedir(), '.local', 'share', 'opencode', 'auth.json'),
58
+ ],
59
+ moonshot: () => [
60
+ node_path_1.default.join(node_os_1.default.homedir(), '.kimi', 'auth.json'),
61
+ // OpenCode stores its auth in two places depending on install path. The
62
+ // kimi shim delegates to `opencode --model opencode-go/kimi-k2.6` when
63
+ // the requested model carries the opencode-go/ prefix, so a moonshot
64
+ // voice routed via opencode is actually authed by opencode's creds —
65
+ // not the kimi-cli ones. Both opencode candidates accepted here.
66
+ node_path_1.default.join(node_os_1.default.homedir(), '.opencode', 'auth.json'),
67
+ node_path_1.default.join(node_os_1.default.homedir(), '.local', 'share', 'opencode', 'auth.json'),
68
+ ],
69
+ // OpenRouter has no on-disk credential file — its API key lives in
70
+ // the secrets table. The shim itself returns auth_missing when the
71
+ // key is unset, which surfaces the same UX without a file probe.
72
+ openrouter: () => [],
73
+ };
74
+ const LOGIN_HINT = {
75
+ anthropic: 'Run `claude login` in a terminal.',
76
+ openai: 'Run `codex login` in a terminal.',
77
+ google: 'Run `gemini` once interactively to complete OAuth.',
78
+ opencode: 'Run `opencode auth login` in a terminal.',
79
+ moonshot: 'Run `kimi` once interactively, or set up opencode if you use the kimi-via-opencode transport.',
80
+ openrouter: 'Save an OpenRouter API key on the Connect page.',
81
+ };
82
+ /**
83
+ * Check whether at least one of the candidate credential paths for `lineage`
84
+ * exists and is readable. Existence-only — we don't parse contents (each CLI
85
+ * has its own JSON shape and bearer-refresh lifecycle, neither of which we
86
+ * want to couple to). Readable-but-empty counts as missing.
87
+ */
88
+ function hasCredFile(lineage) {
89
+ const candidates = CRED_PATHS[lineage]();
90
+ for (const p of candidates) {
91
+ try {
92
+ const stat = node_fs_1.default.statSync(p);
93
+ if (stat.isFile() && stat.size > 0) {
94
+ return { exists: true, tried: candidates };
95
+ }
96
+ }
97
+ catch {
98
+ // ENOENT or perm-denied — try next candidate
99
+ }
100
+ }
101
+ return { exists: false, tried: candidates };
102
+ }
103
+ async function precheckLineage(lineage) {
104
+ // Layer 1: quota state from cli-health (populated reactively when the
105
+ // error-detector observes a quota_exhausted pane). If a previous run
106
+ // tripped the limit and the reset hasn't elapsed, skip the spawn.
107
+ const health = await (0, cli_health_1.getHealth)(lineage);
108
+ if (health.status === 'quota_exhausted') {
109
+ const now = Date.now();
110
+ if (typeof health.resetAt === 'number' && health.resetAt > now) {
111
+ const minsLeft = Math.ceil((health.resetAt - now) / 60_000);
112
+ return {
113
+ ok: false,
114
+ reason: 'quota_exhausted',
115
+ message: `${lineage} quota still exhausted (resets in ~${minsLeft} min).`,
116
+ cta: 'Wait for reset, switch account, or disable this voice.',
117
+ resetAt: health.resetAt,
118
+ };
119
+ }
120
+ // resetAt missing or already past — fall through and let the spawn try.
121
+ // Stale health markers self-clear when a successful run records 'healthy'.
122
+ }
123
+ // OpenRouter has no on-disk creds — the shim itself errors with
124
+ // auth_missing when the secrets-table key is absent. Skip the file probe.
125
+ if (lineage === 'openrouter') {
126
+ return { ok: true };
127
+ }
128
+ // Layer 2: credential file presence. Cheap, catches "logged out" without
129
+ // paying the spawn tax. See CRED_PATHS for the per-CLI lookups.
130
+ const cred = hasCredFile(lineage);
131
+ if (!cred.exists) {
132
+ return {
133
+ ok: false,
134
+ reason: 'auth_missing',
135
+ message: `${lineage} CLI is not logged in (no credential file found).`,
136
+ cta: LOGIN_HINT[lineage],
137
+ };
138
+ }
139
+ return { ok: true };
140
+ }
141
+ //# sourceMappingURL=cli-precheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-precheck.js","sourceRoot":"","sources":["../../src/lib/cli-precheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chats = void 0;
4
+ const zod_1 = require("zod");
5
+ const chat_events_bus_js_1 = require("../chat-events-bus.js");
6
+ const connection_js_1 = require("./connection.js");
7
+ const ChatRowSchema = zod_1.z.object({
8
+ id: zod_1.z.string(),
9
+ /**
10
+ * URL-friendly slug derived from `work` on chat creation. Nullable
11
+ * for legacy rows; the daemon backfills these lazily on first
12
+ * list-load. UNIQUE partial index resolves /runs/<slug> in O(1).
13
+ */
14
+ slug: zod_1.z.string().nullable().default(null),
15
+ work: zod_1.z.string(),
16
+ template_id: zod_1.z.string(),
17
+ status: zod_1.z.enum([
18
+ 'drafting',
19
+ 'reviewing',
20
+ 'approved',
21
+ 'merged',
22
+ 'blocked',
23
+ 'cancelled',
24
+ 'failed',
25
+ 'no_review',
26
+ ]),
27
+ current_phase_idx: zod_1.z.number().int(),
28
+ yolo: zod_1.z.coerce.boolean().default(false),
29
+ attached_files: zod_1.z.string().nullable(),
30
+ repo_path: zod_1.z.string().nullable().default(null),
31
+ pr_url: zod_1.z.string().nullable().default(null),
32
+ ship_error: zod_1.z.string().nullable().default(null),
33
+ artifact: zod_1.z.string().nullable().default(null),
34
+ verdict: zod_1.z.string().nullable().default(null),
35
+ created_at: zod_1.z.number().int(),
36
+ updated_at: zod_1.z.number().int(),
37
+ finished_at: zod_1.z.number().int().nullable(),
38
+ });
39
+ const CreateChatSchema = zod_1.z.object({
40
+ work: zod_1.z.string(),
41
+ template_id: zod_1.z.string(),
42
+ attached_files: zod_1.z.string().optional(),
43
+ /** Absolute path to user's repo for Ship phase. Optional. */
44
+ repo_path: zod_1.z.string().optional(),
45
+ /** Artifact text for review-only templates. Optional at the DB layer; the
46
+ * chat-create endpoint enforces it when the template requires one. */
47
+ artifact: zod_1.z.string().optional(),
48
+ /** Skip ask-user gates for this run. The runner only honours this on the
49
+ * ship phase today; safe to pass on any chat. */
50
+ yolo: zod_1.z.boolean().optional(),
51
+ });
52
+ exports.chats = {
53
+ async create(input) {
54
+ const db = await (0, connection_js_1.getDb)();
55
+ const validated = CreateChatSchema.parse(input);
56
+ const ulid = (0, connection_js_1.generateUlid)();
57
+ const now = Date.now();
58
+ // Generate a unique URL slug BEFORE insert so the returned row is
59
+ // complete (no second SELECT/UPDATE round trip). The collision check
60
+ // uses chats.slugExists which tolerates an in-flight partner row
61
+ // (same slug requested concurrently) — the UNIQUE index is the
62
+ // authoritative race-loser. Retry loop catches the constraint
63
+ // violation, regenerates the slug, and INSERTs again. Capped at 3
64
+ // attempts: if we can't get a unique slug after 3 collisions there's
65
+ // something structurally wrong (clock skew, slug generator bug);
66
+ // fail loud.
67
+ const { generateChatSlug } = await import('../chat-slug.js');
68
+ const MAX_SLUG_ATTEMPTS = 3;
69
+ for (let attempt = 1; attempt <= MAX_SLUG_ATTEMPTS; attempt++) {
70
+ const slug = await generateChatSlug({
71
+ work: validated.work,
72
+ templateId: validated.template_id,
73
+ existsFn: exports.chats.slugExists,
74
+ });
75
+ try {
76
+ await db.execute({
77
+ sql: `
78
+ INSERT INTO chats (id, slug, work, template_id, status, current_phase_idx, yolo, attached_files, repo_path, artifact, created_at, updated_at)
79
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
80
+ `,
81
+ args: [
82
+ ulid,
83
+ slug,
84
+ validated.work,
85
+ validated.template_id,
86
+ 'drafting',
87
+ 0,
88
+ validated.yolo ? 1 : 0,
89
+ validated.attached_files || null,
90
+ validated.repo_path || null,
91
+ validated.artifact || null,
92
+ now,
93
+ now,
94
+ ],
95
+ });
96
+ const row = await exports.chats.getById(ulid);
97
+ if (!row)
98
+ throw new Error(`chats.create: row vanished after insert: ${ulid}`);
99
+ chat_events_bus_js_1.chatEventsBus.emitChange(row.id, 'created');
100
+ return row;
101
+ }
102
+ catch (err) {
103
+ // libsql surfaces UNIQUE violations as Error with message
104
+ // containing "UNIQUE constraint failed: ...idx_chats_slug" (the
105
+ // partial-index name). Retry on this exact pattern; rethrow
106
+ // anything else (FK violations, type errors, conn drops).
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ const isSlugCollision = /UNIQUE constraint failed.*chats\.slug|idx_chats_slug/i.test(message);
109
+ if (!isSlugCollision || attempt === MAX_SLUG_ATTEMPTS)
110
+ throw err;
111
+ }
112
+ }
113
+ // Unreachable — the loop above either returns or throws on the final attempt.
114
+ throw new Error('chats.create: unique slug allocation failed after retries');
115
+ },
116
+ /** Used by generateChatSlug — does any chat already use this slug? */
117
+ async slugExists(slug) {
118
+ const db = await (0, connection_js_1.getDb)();
119
+ const result = await db.execute({
120
+ sql: 'SELECT 1 FROM chats WHERE slug = ? LIMIT 1',
121
+ args: [slug],
122
+ });
123
+ return result.rows.length > 0;
124
+ },
125
+ async getBySlug(slug) {
126
+ const db = await (0, connection_js_1.getDb)();
127
+ const result = await db.execute({
128
+ sql: 'SELECT * FROM chats WHERE slug = ?',
129
+ args: [slug],
130
+ });
131
+ if (result.rows.length === 0)
132
+ return null;
133
+ return ChatRowSchema.parse(result.rows[0]);
134
+ },
135
+ /**
136
+ * Resolve by slug OR id. Falls back to id lookup when the slug query
137
+ * misses, so legacy URLs (`/runs/<ULID>`) keep working forever.
138
+ */
139
+ async getBySlugOrId(slugOrId) {
140
+ const { looksLikeSlug } = await import('../chat-slug.js');
141
+ if (looksLikeSlug(slugOrId)) {
142
+ const bySlug = await exports.chats.getBySlug(slugOrId);
143
+ if (bySlug)
144
+ return bySlug;
145
+ }
146
+ return exports.chats.getById(slugOrId);
147
+ },
148
+ async list(opts) {
149
+ const db = await (0, connection_js_1.getDb)();
150
+ let sql = 'SELECT * FROM chats';
151
+ const args = [];
152
+ if (opts?.status) {
153
+ sql += ' WHERE status = ?';
154
+ args.push(opts.status);
155
+ }
156
+ sql += ' ORDER BY updated_at DESC';
157
+ if (opts?.limit) {
158
+ sql += ' LIMIT ?';
159
+ args.push(opts.limit);
160
+ }
161
+ if (opts?.offset) {
162
+ sql += ' OFFSET ?';
163
+ args.push(opts.offset);
164
+ }
165
+ const result = await db.execute({ sql, args: args });
166
+ return result.rows.map((row) => ChatRowSchema.parse(row));
167
+ },
168
+ async getById(id) {
169
+ const db = await (0, connection_js_1.getDb)();
170
+ const result = await db.execute({ sql: 'SELECT * FROM chats WHERE id = ?', args: [id] });
171
+ if (result.rows.length === 0)
172
+ return null;
173
+ return ChatRowSchema.parse(result.rows[0]);
174
+ },
175
+ async update(id, partial) {
176
+ const db = await (0, connection_js_1.getDb)();
177
+ const chat = await exports.chats.getById(id);
178
+ if (!chat) {
179
+ throw new Error(`Chat ${id} not found`);
180
+ }
181
+ const updated = {
182
+ ...chat,
183
+ ...partial,
184
+ id: chat.id,
185
+ created_at: chat.created_at,
186
+ updated_at: Date.now(),
187
+ };
188
+ await db.execute({
189
+ sql: `
190
+ UPDATE chats
191
+ SET work = ?, template_id = ?, status = ?, current_phase_idx = ?, yolo = ?, attached_files = ?, repo_path = ?, pr_url = ?, ship_error = ?, artifact = ?, verdict = ?, updated_at = ?, finished_at = ?
192
+ WHERE id = ?
193
+ `,
194
+ args: [
195
+ updated.work,
196
+ updated.template_id,
197
+ updated.status,
198
+ updated.current_phase_idx,
199
+ updated.yolo ? 1 : 0,
200
+ updated.attached_files,
201
+ updated.repo_path,
202
+ updated.pr_url,
203
+ updated.ship_error,
204
+ updated.artifact,
205
+ updated.verdict,
206
+ updated.updated_at,
207
+ updated.finished_at,
208
+ id,
209
+ ],
210
+ });
211
+ const row = await exports.chats.getById(id);
212
+ if (!row)
213
+ throw new Error(`chats.update: row vanished: ${id}`);
214
+ chat_events_bus_js_1.chatEventsBus.emitChange(row.id, 'updated');
215
+ return row;
216
+ },
217
+ async cancel(id) {
218
+ return exports.chats.update(id, { status: 'cancelled', finished_at: Date.now() });
219
+ },
220
+ /**
221
+ * Hard-delete a chat. Removes the row + cascades to phase_events.
222
+ * Atomic via libsql transaction so a partial failure can't leave
223
+ * orphaned phase_events. Caller is responsible for filesystem cleanup
224
+ * (chat artifacts in ~/.chorus/chats/<id>) and for ensuring no active
225
+ * session is running (cancel first if needed).
226
+ */
227
+ async delete(id) {
228
+ const db = await (0, connection_js_1.getDb)();
229
+ const tx = await db.transaction('write');
230
+ try {
231
+ // Phase events first to avoid orphans (no FK enforcement, but the
232
+ // chat semantically owns its events).
233
+ await tx.execute({ sql: 'DELETE FROM phase_events WHERE chat_id = ?', args: [id] });
234
+ await tx.execute({ sql: 'DELETE FROM chats WHERE id = ?', args: [id] });
235
+ await tx.commit();
236
+ chat_events_bus_js_1.chatEventsBus.emitChange(id, 'deleted');
237
+ }
238
+ catch (e) {
239
+ await tx.rollback();
240
+ throw e;
241
+ }
242
+ },
243
+ };
244
+ //# sourceMappingURL=chats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chats.js","sourceRoot":"","sources":["../../../src/lib/db/chats.ts"],"names":[],"mappings":"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"}