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,384 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runReviewerHeadless = runReviewerHeadless;
37
+ /**
38
+ * Reviewer streaming execution.
39
+ *
40
+ * Mirrors runDoerHeadless but returns a boolean | null verdict instead of
41
+ * the {content, full} shape — caller (the per-chat reviewer pool) only
42
+ * needs to know agreed / disagreed / failed.
43
+ *
44
+ * Tested by tests/runner-reviewer.test.ts.
45
+ */
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ const template_schema_js_1 = require("../../lib/template-schema.js");
49
+ const permissions_js_1 = require("../../lib/settings/permissions.js");
50
+ const cli_health_js_1 = require("../../lib/cli-health.js");
51
+ const stream_file_writer_js_1 = require("./stream-file-writer.js");
52
+ const verdict_js_1 = require("./verdict.js");
53
+ async function runReviewerHeadless(args) {
54
+ const { shim, chatId, phase, round, reviewerIdx, candidateLineage, candidateModel, agentName, askContent, answerFile, reviewerDir, abortSignal, onEvent, } = args;
55
+ if (!shim.runHeadless)
56
+ return null;
57
+ const perms = await (0, permissions_js_1.getPermissions)();
58
+ const startedAt = Date.now();
59
+ let accumulated = '';
60
+ let finalText;
61
+ let errored = false;
62
+ let capturedUsage;
63
+ // Captured from the first error event so we can write it to
64
+ // answer.md when the subprocess dies before producing any content.
65
+ // Without this, callers reading ~/.chorus/chats/<id>/round-N/
66
+ // reviewer-<agent>/answer.md see a 0-byte file with no clue what
67
+ // went wrong (opencode lock contention, codex quota, etc.).
68
+ let errorSummary;
69
+ fs.writeFileSync(answerFile, '');
70
+ const writer = new stream_file_writer_js_1.StreamFileWriter(answerFile);
71
+ const stream = shim.runHeadless({
72
+ cwd: reviewerDir,
73
+ promptText: askContent,
74
+ model: candidateModel,
75
+ sandbox: perms.sandboxProfile,
76
+ autoApprove: perms.autoApprovePrompts,
77
+ networkAccess: perms.networkAccess,
78
+ abortSignal,
79
+ timeoutMs: phase.timeoutMs ?? template_schema_js_1.DEFAULT_PHASE_TIMEOUT_MS,
80
+ });
81
+ // Safety net: if the stream closes without emitting ANY event (no text,
82
+ // no error, no message_done), the reviewer subprocess silently produced
83
+ // nothing — most often a CLI that wrote model output to /dev/tty instead
84
+ // of the pipe, or one that exited 0 with empty stdout. Without this
85
+ // counter the finally block has no signal to write a failure summary
86
+ // (errorSummary stays undefined), and answer.md ends up 0 bytes — the
87
+ // exact silent failure that hid opencode-cli for two days.
88
+ let eventCount = 0;
89
+ try {
90
+ for await (const event of stream) {
91
+ eventCount += 1;
92
+ if (event.type === 'text_delta') {
93
+ accumulated += event.text;
94
+ writer.write(event.text);
95
+ onEvent({
96
+ chatId,
97
+ type: 'phase_progress',
98
+ payload: {
99
+ phaseId: phase.id,
100
+ round,
101
+ role: 'reviewer',
102
+ agent: `${agentName}-${reviewerIdx}`,
103
+ output: accumulated.slice(-500),
104
+ },
105
+ ts: Date.now(),
106
+ });
107
+ }
108
+ else if (event.type === 'tool_call_start') {
109
+ onEvent({
110
+ chatId,
111
+ type: 'phase_progress',
112
+ payload: {
113
+ phaseId: phase.id,
114
+ round,
115
+ role: 'reviewer',
116
+ agent: `${agentName}-${reviewerIdx}`,
117
+ tool: event.tool,
118
+ },
119
+ ts: Date.now(),
120
+ });
121
+ }
122
+ else if (event.type === 'progress') {
123
+ onEvent({
124
+ chatId,
125
+ type: 'phase_progress',
126
+ payload: {
127
+ phaseId: phase.id,
128
+ round,
129
+ role: 'reviewer',
130
+ agent: `${agentName}-${reviewerIdx}`,
131
+ elapsedMs: event.elapsedMs,
132
+ },
133
+ ts: Date.now(),
134
+ });
135
+ }
136
+ else if (event.type === 'message_done') {
137
+ finalText = event.finalText;
138
+ if (event.usage)
139
+ capturedUsage = event.usage;
140
+ // Same guard as the doer side: don't truncate accumulated deltas
141
+ // when the CLI emits an empty `result` event (Gemini's text-then-
142
+ // disappears bug). Only overwrite when there's real authoritative
143
+ // text; otherwise just append the sentinel to what's already there.
144
+ writer.flushNow();
145
+ if (event.finalText.trim().length === 0) {
146
+ const existing = fs.existsSync(answerFile)
147
+ ? fs.readFileSync(answerFile, 'utf-8')
148
+ : '';
149
+ if (!/\n##\s*DONE\s*\n?$/i.test(existing.trimEnd())) {
150
+ fs.appendFileSync(answerFile, existing.endsWith('\n') ? '\n## DONE\n' : '\n\n## DONE\n');
151
+ }
152
+ }
153
+ else {
154
+ // Don't double-stamp the sentinel. Codex (and any CLI that
155
+ // ends its own output with "## DONE") would otherwise ship
156
+ // an answer with `... ## DONE\n\n\n## DONE\n` — the verdict
157
+ // heuristic doesn't care, but it looks unprofessional in the
158
+ // cockpit and breaks tools that grep for a single sentinel.
159
+ const trimmedTail = event.finalText.replace(/\s+$/, '');
160
+ const alreadyHasSentinel = /\n##\s*DONE\s*$/i.test(trimmedTail);
161
+ const body = alreadyHasSentinel
162
+ ? `${trimmedTail}\n`
163
+ : `${trimmedTail}\n\n## DONE\n`;
164
+ fs.writeFileSync(answerFile, body);
165
+ }
166
+ // Persist runtime stats next to the answer so the cockpit run-
167
+ // artifacts route can surface "12.4s · 3.4k tok" on the card even
168
+ // after a daemon restart or browser reload. Sidecar mirrors the
169
+ // existing _meta.json (transport metadata) shape — write best-
170
+ // effort, ignore errors.
171
+ try {
172
+ fs.writeFileSync(path.join(reviewerDir, '_stats.json'), JSON.stringify({
173
+ durationMs: Date.now() - startedAt,
174
+ ...(capturedUsage ? { usage: capturedUsage } : {}),
175
+ }), 'utf-8');
176
+ }
177
+ catch {
178
+ /* sidecar is informational; ignore write errors */
179
+ }
180
+ // participant_done payload carries identity only. The cockpit
181
+ // refetches /api/run-artifacts on this event to pick up the
182
+ // sidecar-backed stats — see retroactive PR #16 review for why
183
+ // duplicating durationMs/usage in the SSE payload was dead bytes.
184
+ onEvent({
185
+ chatId,
186
+ type: 'participant_done',
187
+ payload: {
188
+ phaseId: phase.id,
189
+ round,
190
+ role: 'reviewer',
191
+ agent: `${agentName}-${reviewerIdx}`,
192
+ },
193
+ ts: Date.now(),
194
+ });
195
+ }
196
+ else if (event.type === 'error') {
197
+ errored = true;
198
+ // Surface OpenRouter HTTP failures (insufficient credits, bad key,
199
+ // rate-limit, upstream outage) as health state so the home-page
200
+ // OpenRouter card flips to a quota/auth/rate-limit badge — same
201
+ // surfacing tmux CLIs already get via error-detector. Best-effort:
202
+ // health write doesn't block the run-page error event.
203
+ const classified = (0, cli_health_js_1.classifyOpenRouterError)(event.kind, event.message);
204
+ if (classified) {
205
+ (0, cli_health_js_1.recordHealth)({
206
+ lineage: 'openrouter',
207
+ status: classified.status,
208
+ message: classified.message,
209
+ }).catch((healthErr) => {
210
+ console.error('[chorus] recordHealth failed for openrouter:', healthErr);
211
+ });
212
+ }
213
+ // First error wins by default — but a more-specific later
214
+ // kind can supersede a vague earlier one. The gemini parser
215
+ // emits a generic `gemini_result_error` from the JSON result
216
+ // line; the on-exit handler then emits a precise
217
+ // `quota_exhausted` from stderr with the reset window. Without
218
+ // this upgrade rule the cockpit shows the vague first message
219
+ // and the user has no idea when their quota resets.
220
+ const VAGUE_KINDS = new Set(['gemini_result_error']);
221
+ const SPECIFIC_KINDS = new Set([
222
+ 'quota_exhausted',
223
+ 'rate_limit',
224
+ 'auth_error',
225
+ 'sandbox_unsupported',
226
+ 'cli_not_in_path',
227
+ ]);
228
+ const isUpgrade = errorSummary &&
229
+ VAGUE_KINDS.has(errorSummary.kind) &&
230
+ SPECIFIC_KINDS.has(event.kind);
231
+ if (!errorSummary || isUpgrade) {
232
+ errorSummary = {
233
+ kind: event.kind,
234
+ message: classified?.message ?? event.message,
235
+ };
236
+ }
237
+ onEvent({
238
+ chatId,
239
+ type: 'cli_error',
240
+ payload: {
241
+ phaseId: phase.id,
242
+ phaseKind: phase.kind,
243
+ phaseIdx: 0,
244
+ round,
245
+ role: 'reviewer',
246
+ agent: `${agentName}-${reviewerIdx}`,
247
+ error: {
248
+ kind: event.kind,
249
+ message: classified?.message ?? event.message,
250
+ ...(classified?.cta ? { cta: classified.cta } : {}),
251
+ lineage: candidateLineage,
252
+ },
253
+ },
254
+ ts: Date.now(),
255
+ });
256
+ }
257
+ }
258
+ }
259
+ catch (err) {
260
+ errored = true;
261
+ const message = err instanceof Error ? err.message : String(err);
262
+ if (!errorSummary) {
263
+ errorSummary = { kind: 'stream_failure', message };
264
+ }
265
+ onEvent({
266
+ chatId,
267
+ type: 'cli_error',
268
+ payload: {
269
+ phaseId: phase.id,
270
+ phaseKind: phase.kind,
271
+ phaseIdx: 0,
272
+ round,
273
+ role: 'reviewer',
274
+ agent: `${agentName}-${reviewerIdx}`,
275
+ error: {
276
+ kind: 'stream_failure',
277
+ message,
278
+ lineage: candidateLineage,
279
+ },
280
+ },
281
+ ts: Date.now(),
282
+ });
283
+ }
284
+ finally {
285
+ writer.flushNow();
286
+ // Stream closed with zero events — the CLI ran but produced nothing
287
+ // we could parse (e.g. opencode 1.14.x writing to /dev/tty instead of
288
+ // the pipe, or any future CLI that exits 0 with empty stdout).
289
+ // Synthesise a no_output failure so:
290
+ // 1. The reviewer card renders the kind+lineage instead of the
291
+ // generic "didn't produce any output" stub.
292
+ // 2. answer.md carries a `## REVIEWER FAILED` block, matching the
293
+ // contract every other failure mode writes.
294
+ // 3. The runner's verdict parser sees a definite "request changes"
295
+ // shape, not an empty string the verdict logic is undefined on.
296
+ if (eventCount === 0 && !errorSummary) {
297
+ errored = true;
298
+ errorSummary = {
299
+ kind: 'no_output',
300
+ message: `${candidateLineage} CLI closed without emitting any output. ` +
301
+ `Likely a transport bug (e.g. opencode 1.14.x writes JSON only to a TTY) ` +
302
+ `or a silent abort. Check the CLI's own log for details.`,
303
+ };
304
+ }
305
+ // When the subprocess died without producing any content, write the
306
+ // error summary to answer.md so the chat dir is self-explanatory.
307
+ // Otherwise post-mortem inspection sees an empty file with no
308
+ // signal — exactly the silent-failure that hid opencode-cli-2's
309
+ // failure on the PR #10 review chat.
310
+ if (errored && accumulated.length === 0 && (!finalText || finalText.length === 0) && errorSummary) {
311
+ try {
312
+ // For quota / rate-limit failures, the error-detector (tmux path)
313
+ // or recordHealth call (HTTP shim path) has already stamped the
314
+ // lineage's cli-health row with `resetAt` if it's known. Pull
315
+ // that here so the cockpit's failure card can render a "Resets
316
+ // at HH:MM" countdown without a second round-trip. Best-effort:
317
+ // resolves to undefined for unknown lineages or cleared health.
318
+ let resetAt;
319
+ try {
320
+ const h = await (0, cli_health_js_1.getHealth)(candidateLineage);
321
+ if (typeof h.resetAt === 'number' && h.resetAt > Date.now()) {
322
+ resetAt = h.resetAt;
323
+ }
324
+ }
325
+ catch {
326
+ /* health lookup is informational */
327
+ }
328
+ fs.writeFileSync(answerFile, `## REVIEWER FAILED\n\n` +
329
+ `**Kind:** ${errorSummary.kind}\n` +
330
+ `**Lineage:** ${candidateLineage}\n` +
331
+ `**Model:** ${candidateModel ?? '(default)'}\n` +
332
+ (resetAt ? `**Resets:** ${new Date(resetAt).toISOString()}\n` : '') +
333
+ `\n${errorSummary.message}\n`);
334
+ }
335
+ catch {
336
+ /* best-effort — don't fail the runner because of a write error */
337
+ }
338
+ }
339
+ // Mirror runDoerHeadless: surface answer.md write failures as a
340
+ // cli_warning so the user sees "stream stopped writing" instead of
341
+ // a quietly truncated reviewer transcript that the verdict parser
342
+ // then chokes on.
343
+ if (writer.isDead()) {
344
+ const err = writer.lastError();
345
+ onEvent({
346
+ chatId,
347
+ type: 'cli_warning',
348
+ payload: {
349
+ phaseId: phase.id,
350
+ round,
351
+ role: 'reviewer',
352
+ agent: `${agentName}-${reviewerIdx}`,
353
+ reason: 'stream_writer_dead',
354
+ message: `answer.md write failed; subsequent deltas dropped: ${err ? err.message : 'unknown'}`,
355
+ cta: 'Check disk space + permissions on ~/.chorus/chats. Re-run when fixed.',
356
+ },
357
+ ts: Date.now(),
358
+ });
359
+ }
360
+ }
361
+ // Prefer answer.md on disk over streamed text. Tool-using CLIs (gemini)
362
+ // put the actual review into the file via a Write tool call and only
363
+ // stream a confirmation message ("Changes have been requested...") that
364
+ // the verdict heuristic can't classify. Reading the file picks up both
365
+ // the tool-written verdict AND any text_delta-appended assistant text,
366
+ // matching what the cockpit and CLI both display to the user.
367
+ let onDisk = '';
368
+ try {
369
+ if (fs.existsSync(answerFile)) {
370
+ onDisk = fs.readFileSync(answerFile, 'utf-8');
371
+ }
372
+ }
373
+ catch {
374
+ /* best-effort — fall through to streamed content */
375
+ }
376
+ const streamed = finalText && finalText.length > 0 ? finalText : accumulated;
377
+ const content = onDisk.trim().length > 0 ? onDisk : streamed;
378
+ if (errored && content.trim().length === 0)
379
+ return null;
380
+ if (content.trim().length === 0)
381
+ return null;
382
+ return (0, verdict_js_1.verdictFromReviewerText)(content);
383
+ }
384
+ //# sourceMappingURL=reviewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../../src/daemon/runner/reviewer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runWithModelFallback = runWithModelFallback;
4
+ exports.runWithChainFallback = runWithChainFallback;
5
+ /**
6
+ * Per-slot model fallback chain.
7
+ *
8
+ * A reviewer or doer slot can list multiple models in `models[]`. When the
9
+ * primary returns null (subprocess produced no answer — quota, rate-limit,
10
+ * crash, empty-stream), this helper falls through to the next model in the
11
+ * list and retries. Auth and lineage selection are stable across the
12
+ * fallback because all models in a slot share the same lineage; only the
13
+ * `--model X` argv changes.
14
+ *
15
+ * Returning `null` from `attempt` is the agreed-upon "this model didn't
16
+ * produce an answer, please try the next one" signal. Throwing is
17
+ * propagated unchanged — a throw means something went wrong outside the
18
+ * model run (e.g. the chat dir disappeared) and falling through wouldn't
19
+ * help. Callers that want exceptions to engage the fallback should catch
20
+ * them inside `attempt` and return null instead.
21
+ *
22
+ * `onFallback(fromModel, toModel, fromIdx)` fires once per transition so
23
+ * the runner can emit a `cli_warning` event with `reason: 'model_fallback'`
24
+ * and the cockpit can show "claude-opus-4-7 → claude-sonnet-4-6" on the
25
+ * card. The index tells the cockpit how deep into the chain we are.
26
+ *
27
+ * If `models` is empty or undefined, exactly one attempt is made with
28
+ * `undefined` as the model — this matches the existing "no model = lineage
29
+ * default" semantics in the shim layer.
30
+ */
31
+ async function runWithModelFallback(models, attempt, onFallback) {
32
+ const list = models && models.length > 0 ? [...models] : [undefined];
33
+ for (let i = 0; i < list.length; i++) {
34
+ const result = await attempt(list[i]);
35
+ if (result !== null)
36
+ return result;
37
+ if (i < list.length - 1) {
38
+ onFallback(list[i], list[i + 1], i);
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ async function runWithChainFallback(chain, attempt, onFallback) {
44
+ if (chain.length === 0)
45
+ return null;
46
+ for (let i = 0; i < chain.length; i++) {
47
+ const result = await attempt(chain[i]);
48
+ if (result !== null)
49
+ return result;
50
+ if (i < chain.length - 1) {
51
+ onFallback(chain[i], chain[i + 1], i);
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+ //# sourceMappingURL=run-with-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-with-fallback.js","sourceRoot":"","sources":["../../../src/daemon/runner/run-with-fallback.ts"],"names":[],"mappings":";;AA0BA,oDAoBC;AAgBD,oDAcC;AA5ED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACI,KAAK,UAAU,oBAAoB,CACxC,MAA4B,EAC5B,OAAyD,EACzD,UAIS;IAET,MAAM,IAAI,GACR,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QACnC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAgBM,KAAK,UAAU,oBAAoB,CACxC,KAA4B,EAC5B,OAAiD,EACjD,UAAuE;IAEvE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QACnC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sanitizeName = sanitizeName;
4
+ /** tmux session names accept [a-zA-Z0-9_-]; drop everything else and clamp length. */
5
+ function sanitizeName(name) {
6
+ return name.replace(/[^a-zA-Z0-9_-]+/g, '_').slice(0, 80);
7
+ }
8
+ //# sourceMappingURL=sanitize-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-name.js","sourceRoot":"","sources":["../../../src/daemon/runner/sanitize-name.ts"],"names":[],"mappings":";;AACA,oCAEC;AAHD,sFAAsF;AACtF,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.StreamFileWriter = void 0;
37
+ /**
38
+ * Buffered append-writer for live LLM streaming.
39
+ *
40
+ * Why this exists: the previous implementation called fs.appendFileSync on
41
+ * every text_delta event from the CLI shim. A typical Opus run emits ~5
42
+ * deltas/sec for ~5 minutes = ~1500 sync disk writes per doer, each one
43
+ * blocking the daemon event loop. Worse, the cockpit polls answer.md every
44
+ * 8s — it doesn't benefit from per-delta granularity.
45
+ *
46
+ * Now we flush whenever the buffer crosses 4KB OR a 750ms quiet timer
47
+ * fires, whichever comes first. Cockpit poll cadence is unchanged but
48
+ * the daemon does ~50 writes per run instead of ~1500.
49
+ *
50
+ * Caller MUST call flushNow() before any direct read of `path` or before
51
+ * fs.writeFileSync overwrites it — the buffer is otherwise lost.
52
+ *
53
+ * Failure semantics: if the underlying appendFileSync ever throws (FS
54
+ * ENOSPC, EACCES, etc.) the writer flips to a "dead" state and surfaces
55
+ * the cause via lastError(). Subsequent write() calls become no-ops; the
56
+ * buffered chunk that failed is dropped (re-trying would just fail again
57
+ * synchronously and leak more memory). The runner inspects isDead() /
58
+ * lastError() in its finally block to surface a cli_error event so the
59
+ * user knows their answer.md is partial — silent loss was the previous
60
+ * behavior and round-2 review flagged it.
61
+ */
62
+ const fs = __importStar(require("fs"));
63
+ class StreamFileWriter {
64
+ filePath;
65
+ flushBytes;
66
+ flushMs;
67
+ buf = '';
68
+ flushTimer = null;
69
+ dead = false;
70
+ lastErr = null;
71
+ constructor(filePath, flushBytes = 4096, flushMs = 750) {
72
+ this.filePath = filePath;
73
+ this.flushBytes = flushBytes;
74
+ this.flushMs = flushMs;
75
+ }
76
+ write(chunk) {
77
+ if (!chunk || this.dead)
78
+ return;
79
+ this.buf += chunk;
80
+ if (this.buf.length >= this.flushBytes) {
81
+ this.flushNow();
82
+ }
83
+ else if (this.flushTimer === null) {
84
+ this.flushTimer = setTimeout(() => this.flushNow(), this.flushMs);
85
+ }
86
+ }
87
+ flushNow() {
88
+ if (this.flushTimer !== null) {
89
+ clearTimeout(this.flushTimer);
90
+ this.flushTimer = null;
91
+ }
92
+ if (this.buf.length === 0 || this.dead)
93
+ return;
94
+ try {
95
+ fs.appendFileSync(this.filePath, this.buf);
96
+ this.buf = '';
97
+ }
98
+ catch (err) {
99
+ // Permanent failure — flip dead, drop the in-flight buffer to free
100
+ // memory, surface via lastError() for the runner to forward as a
101
+ // cli_error so the user sees something instead of a quietly stale
102
+ // answer.md.
103
+ this.dead = true;
104
+ this.lastErr = err instanceof Error ? err : new Error(String(err));
105
+ this.buf = '';
106
+ }
107
+ }
108
+ isDead() {
109
+ return this.dead;
110
+ }
111
+ lastError() {
112
+ return this.lastErr;
113
+ }
114
+ }
115
+ exports.StreamFileWriter = StreamFileWriter;
116
+ //# sourceMappingURL=stream-file-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-file-writer.js","sourceRoot":"","sources":["../../../src/daemon/runner/stream-file-writer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,uCAAyB;AAEzB,MAAa,gBAAgB;IAOR;IACA;IACA;IARX,GAAG,GAAG,EAAE,CAAC;IACT,UAAU,GAA0B,IAAI,CAAC;IACzC,IAAI,GAAG,KAAK,CAAC;IACb,OAAO,GAAiB,IAAI,CAAC;IAErC,YACmB,QAAgB,EAChB,aAAa,IAAI,EACjB,UAAU,GAAG;QAFb,aAAQ,GAAR,QAAQ,CAAQ;QAChB,eAAU,GAAV,UAAU,CAAO;QACjB,YAAO,GAAP,OAAO,CAAM;IAC7B,CAAC;IAEJ,KAAK,CAAC,KAAa;QACjB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QAChC,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC;QAClB,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YACpC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,QAAQ;QACN,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QAC/C,IAAI,CAAC;YACH,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mEAAmE;YACnE,iEAAiE;YACjE,kEAAkE;YAClE,aAAa;YACb,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACjB,IAAI,CAAC,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAjDD,4CAiDC"}