@wascartech/devbot 0.0.0-stage → 2.1.1

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 (376) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +638 -2
  3. package/dist/access-grant-store.js +99 -0
  4. package/dist/access-grant-store.js.map +1 -0
  5. package/dist/access-resolve.js +11 -0
  6. package/dist/access-resolve.js.map +1 -0
  7. package/dist/access-rule-store.js +99 -0
  8. package/dist/access-rule-store.js.map +1 -0
  9. package/dist/admin/access-routes.js +73 -0
  10. package/dist/admin/access-routes.js.map +1 -0
  11. package/dist/admin/access-rule-routes.js +107 -0
  12. package/dist/admin/access-rule-routes.js.map +1 -0
  13. package/dist/admin/custom-repos-routes.js +93 -0
  14. package/dist/admin/custom-repos-routes.js.map +1 -0
  15. package/dist/admin/env-routes.js +74 -0
  16. package/dist/admin/env-routes.js.map +1 -0
  17. package/dist/admin/global-settings-routes.js +254 -0
  18. package/dist/admin/global-settings-routes.js.map +1 -0
  19. package/dist/admin/mcp-cleanup.js +12 -0
  20. package/dist/admin/mcp-cleanup.js.map +1 -0
  21. package/dist/admin/mcp-routes.js +169 -0
  22. package/dist/admin/mcp-routes.js.map +1 -0
  23. package/dist/admin/query-params.js +80 -0
  24. package/dist/admin/query-params.js.map +1 -0
  25. package/dist/admin/routes.js +150 -0
  26. package/dist/admin/routes.js.map +1 -0
  27. package/dist/admin/run-history-routes.js +44 -0
  28. package/dist/admin/run-history-routes.js.map +1 -0
  29. package/dist/admin/run-routes.js +99 -0
  30. package/dist/admin/run-routes.js.map +1 -0
  31. package/dist/admin/settings-routes.js +119 -0
  32. package/dist/admin/settings-routes.js.map +1 -0
  33. package/dist/admin/stats-routes.js +51 -0
  34. package/dist/admin/stats-routes.js.map +1 -0
  35. package/dist/admin/task-routes.js +213 -0
  36. package/dist/admin/task-routes.js.map +1 -0
  37. package/dist/admin/test-routes.js +38 -0
  38. package/dist/admin/test-routes.js.map +1 -0
  39. package/dist/admin/volume-routes.js +82 -0
  40. package/dist/admin/volume-routes.js.map +1 -0
  41. package/dist/admin/webhook-routes.js +174 -0
  42. package/dist/admin/webhook-routes.js.map +1 -0
  43. package/dist/agents.js +48 -0
  44. package/dist/agents.js.map +1 -0
  45. package/dist/assignee-value.js +6 -0
  46. package/dist/assignee-value.js.map +1 -0
  47. package/dist/attachments.js +76 -0
  48. package/dist/attachments.js.map +1 -0
  49. package/dist/auto-commands.js +88 -0
  50. package/dist/auto-commands.js.map +1 -0
  51. package/dist/auto-features.js +32 -0
  52. package/dist/auto-features.js.map +1 -0
  53. package/dist/base-branch-store.js +40 -0
  54. package/dist/base-branch-store.js.map +1 -0
  55. package/dist/base-branches.js +9 -0
  56. package/dist/base-branches.js.map +1 -0
  57. package/dist/build-workflow.js +116 -0
  58. package/dist/build-workflow.js.map +1 -0
  59. package/dist/change-request-lifecycle.js +16 -0
  60. package/dist/change-request-lifecycle.js.map +1 -0
  61. package/dist/change-request-store.js +72 -0
  62. package/dist/change-request-store.js.map +1 -0
  63. package/dist/ci-failure.js +80 -0
  64. package/dist/ci-failure.js.map +1 -0
  65. package/dist/cli.js +3 -0
  66. package/dist/cli.js.map +1 -0
  67. package/dist/config.js +17 -0
  68. package/dist/config.js.map +1 -0
  69. package/dist/conflict-fix.js +178 -0
  70. package/dist/conflict-fix.js.map +1 -0
  71. package/dist/cookie.js +70 -0
  72. package/dist/cookie.js.map +1 -0
  73. package/dist/crypto-util.js +40 -0
  74. package/dist/crypto-util.js.map +1 -0
  75. package/dist/custom-repos-store.js +90 -0
  76. package/dist/custom-repos-store.js.map +1 -0
  77. package/dist/custom-repos-sync.js +118 -0
  78. package/dist/custom-repos-sync.js.map +1 -0
  79. package/dist/db.js +12 -0
  80. package/dist/db.js.map +1 -0
  81. package/dist/devbot-scripts.js +18 -0
  82. package/dist/devbot-scripts.js.map +1 -0
  83. package/dist/devcontainer.js +150 -0
  84. package/dist/devcontainer.js.map +1 -0
  85. package/dist/env-config-store.js +132 -0
  86. package/dist/env-config-store.js.map +1 -0
  87. package/dist/error-log-store.js +193 -0
  88. package/dist/error-log-store.js.map +1 -0
  89. package/dist/git.js +605 -0
  90. package/dist/git.js.map +1 -0
  91. package/dist/global-queue.js +127 -0
  92. package/dist/global-queue.js.map +1 -0
  93. package/dist/global-settings-store.js +307 -0
  94. package/dist/global-settings-store.js.map +1 -0
  95. package/dist/help.js +41 -0
  96. package/dist/help.js.map +1 -0
  97. package/dist/http-csp.js +15 -0
  98. package/dist/http-csp.js.map +1 -0
  99. package/dist/http-util.js +40 -0
  100. package/dist/http-util.js.map +1 -0
  101. package/dist/idempotency.js +50 -0
  102. package/dist/idempotency.js.map +1 -0
  103. package/dist/lifecycle.js +234 -0
  104. package/dist/lifecycle.js.map +1 -0
  105. package/dist/logger.js +33 -0
  106. package/dist/logger.js.map +1 -0
  107. package/dist/lsp-servers.js +81 -0
  108. package/dist/lsp-servers.js.map +1 -0
  109. package/dist/mcp/auth.js +62 -0
  110. package/dist/mcp/auth.js.map +1 -0
  111. package/dist/mcp/effective-mcp.js +42 -0
  112. package/dist/mcp/effective-mcp.js.map +1 -0
  113. package/dist/mcp/oauth-refresh.js +138 -0
  114. package/dist/mcp/oauth-refresh.js.map +1 -0
  115. package/dist/mcp/peer-sync.js +109 -0
  116. package/dist/mcp/peer-sync.js.map +1 -0
  117. package/dist/mcp/registry.js +54 -0
  118. package/dist/mcp/registry.js.map +1 -0
  119. package/dist/mcp/runner-bridge.js +93 -0
  120. package/dist/mcp/runner-bridge.js.map +1 -0
  121. package/dist/mcp/server.js +173 -0
  122. package/dist/mcp/server.js.map +1 -0
  123. package/dist/mcp/tools/_helpers.js +88 -0
  124. package/dist/mcp/tools/_helpers.js.map +1 -0
  125. package/dist/mcp/tools/attachments.js +117 -0
  126. package/dist/mcp/tools/attachments.js.map +1 -0
  127. package/dist/mcp/tools/cicd.js +105 -0
  128. package/dist/mcp/tools/cicd.js.map +1 -0
  129. package/dist/mcp/tools/discovery.js +34 -0
  130. package/dist/mcp/tools/discovery.js.map +1 -0
  131. package/dist/mcp/tools/issues.js +56 -0
  132. package/dist/mcp/tools/issues.js.map +1 -0
  133. package/dist/mcp/tools/merge_requests.js +67 -0
  134. package/dist/mcp/tools/merge_requests.js.map +1 -0
  135. package/dist/mcp/tools/metadata.js +127 -0
  136. package/dist/mcp/tools/metadata.js.map +1 -0
  137. package/dist/mcp/tools/peer_sync.js +43 -0
  138. package/dist/mcp/tools/peer_sync.js.map +1 -0
  139. package/dist/mcp/tools/repository.js +178 -0
  140. package/dist/mcp/tools/repository.js.map +1 -0
  141. package/dist/mcp/tools/work_tracking.js +125 -0
  142. package/dist/mcp/tools/work_tracking.js.map +1 -0
  143. package/dist/mcp/tools/workitems.js +30 -0
  144. package/dist/mcp/tools/workitems.js.map +1 -0
  145. package/dist/mcp/volume-files.js +187 -0
  146. package/dist/mcp/volume-files.js.map +1 -0
  147. package/dist/mcp-auth-store.js +134 -0
  148. package/dist/mcp-auth-store.js.map +1 -0
  149. package/dist/mcp-config-store.js +83 -0
  150. package/dist/mcp-config-store.js.map +1 -0
  151. package/dist/mention.js +512 -0
  152. package/dist/mention.js.map +1 -0
  153. package/dist/migrate.js +438 -0
  154. package/dist/migrate.js.map +1 -0
  155. package/dist/mr-assignee.js +71 -0
  156. package/dist/mr-assignee.js.map +1 -0
  157. package/dist/mr-content.js +90 -0
  158. package/dist/mr-content.js.map +1 -0
  159. package/dist/oauth-routes.js +54 -0
  160. package/dist/oauth-routes.js.map +1 -0
  161. package/dist/onboarding-allowlist.js +32 -0
  162. package/dist/onboarding-allowlist.js.map +1 -0
  163. package/dist/opencode.js +291 -0
  164. package/dist/opencode.js.map +1 -0
  165. package/dist/outbound-webhook-store.js +229 -0
  166. package/dist/outbound-webhook-store.js.map +1 -0
  167. package/dist/outbound-webhook.js +314 -0
  168. package/dist/outbound-webhook.js.map +1 -0
  169. package/dist/path-sandbox.js +200 -0
  170. package/dist/path-sandbox.js.map +1 -0
  171. package/dist/platform/git-auth.js +34 -0
  172. package/dist/platform/git-auth.js.map +1 -0
  173. package/dist/platform/gitealike/attachments.js +62 -0
  174. package/dist/platform/gitealike/attachments.js.map +1 -0
  175. package/dist/platform/gitealike/base.js +935 -0
  176. package/dist/platform/gitealike/base.js.map +1 -0
  177. package/dist/platform/gitealike/forgejo.js +16 -0
  178. package/dist/platform/gitealike/forgejo.js.map +1 -0
  179. package/dist/platform/gitealike/gitea.js +16 -0
  180. package/dist/platform/gitealike/gitea.js.map +1 -0
  181. package/dist/platform/gitealike/http.js +152 -0
  182. package/dist/platform/gitealike/http.js.map +1 -0
  183. package/dist/platform/gitealike/schemas.js +284 -0
  184. package/dist/platform/gitealike/schemas.js.map +1 -0
  185. package/dist/platform/github/attachments.js +52 -0
  186. package/dist/platform/github/attachments.js.map +1 -0
  187. package/dist/platform/github/http.js +153 -0
  188. package/dist/platform/github/http.js.map +1 -0
  189. package/dist/platform/github/index.js +936 -0
  190. package/dist/platform/github/index.js.map +1 -0
  191. package/dist/platform/github/schemas.js +308 -0
  192. package/dist/platform/github/schemas.js.map +1 -0
  193. package/dist/platform/github/zip.js +85 -0
  194. package/dist/platform/github/zip.js.map +1 -0
  195. package/dist/platform/gitlab/admin.js +105 -0
  196. package/dist/platform/gitlab/admin.js.map +1 -0
  197. package/dist/platform/gitlab/api.js +229 -0
  198. package/dist/platform/gitlab/api.js.map +1 -0
  199. package/dist/platform/gitlab/attachments.js +79 -0
  200. package/dist/platform/gitlab/attachments.js.map +1 -0
  201. package/dist/platform/gitlab/http.js +165 -0
  202. package/dist/platform/gitlab/http.js.map +1 -0
  203. package/dist/platform/gitlab/index.js +630 -0
  204. package/dist/platform/gitlab/index.js.map +1 -0
  205. package/dist/platform/gitlab/oauth.js +58 -0
  206. package/dist/platform/gitlab/oauth.js.map +1 -0
  207. package/dist/platform/gitlab/read.js +182 -0
  208. package/dist/platform/gitlab/read.js.map +1 -0
  209. package/dist/platform/gitlab/schemas-read.js +274 -0
  210. package/dist/platform/gitlab/schemas-read.js.map +1 -0
  211. package/dist/platform/gitlab/schemas.js +251 -0
  212. package/dist/platform/gitlab/schemas.js.map +1 -0
  213. package/dist/platform/gitlab/signature.js +52 -0
  214. package/dist/platform/gitlab/signature.js.map +1 -0
  215. package/dist/platform/index.js +38 -0
  216. package/dist/platform/index.js.map +1 -0
  217. package/dist/platform/port.js +26 -0
  218. package/dist/platform/port.js.map +1 -0
  219. package/dist/platform/types.js +80 -0
  220. package/dist/platform/types.js.map +1 -0
  221. package/dist/podman.js +119 -0
  222. package/dist/podman.js.map +1 -0
  223. package/dist/project-sync.js +69 -0
  224. package/dist/project-sync.js.map +1 -0
  225. package/dist/prompt.js +47 -0
  226. package/dist/prompt.js.map +1 -0
  227. package/dist/queue-store.js +236 -0
  228. package/dist/queue-store.js.map +1 -0
  229. package/dist/queue.js +129 -0
  230. package/dist/queue.js.map +1 -0
  231. package/dist/redact.js +23 -0
  232. package/dist/redact.js.map +1 -0
  233. package/dist/run-files.js +297 -0
  234. package/dist/run-files.js.map +1 -0
  235. package/dist/run-filters.js +54 -0
  236. package/dist/run-filters.js.map +1 -0
  237. package/dist/run-history-store.js +229 -0
  238. package/dist/run-history-store.js.map +1 -0
  239. package/dist/run-log-sink.js +270 -0
  240. package/dist/run-log-sink.js.map +1 -0
  241. package/dist/run-log.js +97 -0
  242. package/dist/run-log.js.map +1 -0
  243. package/dist/run-usage.js +29 -0
  244. package/dist/run-usage.js.map +1 -0
  245. package/dist/run-view.js +112 -0
  246. package/dist/run-view.js.map +1 -0
  247. package/dist/runner.js +1094 -0
  248. package/dist/runner.js.map +1 -0
  249. package/dist/runtime-apply.js +26 -0
  250. package/dist/runtime-apply.js.map +1 -0
  251. package/dist/runtime-settings.js +71 -0
  252. package/dist/runtime-settings.js.map +1 -0
  253. package/dist/runtime-state.js +19 -0
  254. package/dist/runtime-state.js.map +1 -0
  255. package/dist/server.js +119 -0
  256. package/dist/server.js.map +1 -0
  257. package/dist/settings-store.js +320 -0
  258. package/dist/settings-store.js.map +1 -0
  259. package/dist/status.js +121 -0
  260. package/dist/status.js.map +1 -0
  261. package/dist/stop.js +56 -0
  262. package/dist/stop.js.map +1 -0
  263. package/dist/sweeps.js +87 -0
  264. package/dist/sweeps.js.map +1 -0
  265. package/dist/task-log.js +279 -0
  266. package/dist/task-log.js.map +1 -0
  267. package/dist/task-view.js +62 -0
  268. package/dist/task-view.js.map +1 -0
  269. package/dist/token-store.js +68 -0
  270. package/dist/token-store.js.map +1 -0
  271. package/dist/types/access.js +74 -0
  272. package/dist/types/access.js.map +1 -0
  273. package/dist/types/custom-repos.js +28 -0
  274. package/dist/types/custom-repos.js.map +1 -0
  275. package/dist/types/env-config.js +25 -0
  276. package/dist/types/env-config.js.map +1 -0
  277. package/dist/types/env.js +190 -0
  278. package/dist/types/env.js.map +1 -0
  279. package/dist/types/global-settings.js +178 -0
  280. package/dist/types/global-settings.js.map +1 -0
  281. package/dist/types/lsp-servers.js +145 -0
  282. package/dist/types/lsp-servers.js.map +1 -0
  283. package/dist/types/mcp-config.js +77 -0
  284. package/dist/types/mcp-config.js.map +1 -0
  285. package/dist/types/opencode.js +59 -0
  286. package/dist/types/opencode.js.map +1 -0
  287. package/dist/types/outbound-webhook.js +56 -0
  288. package/dist/types/outbound-webhook.js.map +1 -0
  289. package/dist/types/project-settings.js +114 -0
  290. package/dist/types/project-settings.js.map +1 -0
  291. package/dist/types/provider-registry.js +37 -0
  292. package/dist/types/provider-registry.js.map +1 -0
  293. package/dist/types/runtime-settings.js +78 -0
  294. package/dist/types/runtime-settings.js.map +1 -0
  295. package/dist/usage-stats.js +159 -0
  296. package/dist/usage-stats.js.map +1 -0
  297. package/dist/version.js +19 -0
  298. package/dist/version.js.map +1 -0
  299. package/dist/volume-gc.js +193 -0
  300. package/dist/volume-gc.js.map +1 -0
  301. package/dist/volume-manage.js +191 -0
  302. package/dist/volume-manage.js.map +1 -0
  303. package/dist/volume-store.js +122 -0
  304. package/dist/volume-store.js.map +1 -0
  305. package/dist/webhook.js +1106 -0
  306. package/dist/webhook.js.map +1 -0
  307. package/dist/work-item-auto-store.js +75 -0
  308. package/dist/work-item-auto-store.js.map +1 -0
  309. package/dist/work-item-branch-store.js +37 -0
  310. package/dist/work-item-branch-store.js.map +1 -0
  311. package/migrations/0001_initial.sql +59 -0
  312. package/migrations/0002_queue_status.sql +15 -0
  313. package/migrations/0003_work_item_base_branch.sql +14 -0
  314. package/migrations/0004_project_settings.sql +15 -0
  315. package/migrations/0005_global_settings.sql +34 -0
  316. package/migrations/0006_model_variants.sql +8 -0
  317. package/migrations/0007_container_error_logs.sql +21 -0
  318. package/migrations/0008_project_access_grants.sql +16 -0
  319. package/migrations/0009_drop_imported_legacy.sql +4 -0
  320. package/migrations/0010_container_error_log_model.sql +6 -0
  321. package/migrations/0011_container_error_log_title.sql +5 -0
  322. package/migrations/0012_pending_task_cancel.sql +6 -0
  323. package/migrations/0013_builder_image.sql +6 -0
  324. package/migrations/0014_build_workflow.sql +7 -0
  325. package/migrations/0015_access_rules.sql +24 -0
  326. package/migrations/0016_trigger_policy.sql +17 -0
  327. package/migrations/0017_attachment_upload.sql +8 -0
  328. package/migrations/0018_work_item_branch.sql +11 -0
  329. package/migrations/0019_auto_push_mr.sql +18 -0
  330. package/migrations/0020_outbound_webhooks.sql +47 -0
  331. package/migrations/0021_runtime_settings.sql +12 -0
  332. package/migrations/0022_auto_resolve_conflicts.sql +17 -0
  333. package/migrations/0023_ci_fail_auto_report.sql +27 -0
  334. package/migrations/0024_work_item_auto_override.sql +22 -0
  335. package/migrations/0025_run_history.sql +37 -0
  336. package/migrations/0026_run_history_task_seq.sql +6 -0
  337. package/migrations/0027_custom_repos.sql +11 -0
  338. package/migrations/0028_run_usage.sql +14 -0
  339. package/migrations/0029_drop_ocr_langs.sql +6 -0
  340. package/migrations/0030_mr_assignee.sql +9 -0
  341. package/migrations/0031_lsp_servers.sql +14 -0
  342. package/opencode-config/managed/devbot-agent-policy.md +61 -0
  343. package/opencode-config/managed/opencode.json +77 -0
  344. package/opencode-config/opencode.json +211 -0
  345. package/opencode-config/plugins/devbot-path-guard.js +224 -0
  346. package/package.json +158 -4
  347. package/public/assets/DashboardSidebarCollapse-rIuzCmVm.js +1 -0
  348. package/public/assets/DashboardToolbar-DFCUqZ4Z.js +1 -0
  349. package/public/assets/ErrorLogModal.vue_vue_type_script_setup_true_lang-BCyNIf1E.js +1 -0
  350. package/public/assets/ErrorLogsPage-DyYK2Rop.js +1 -0
  351. package/public/assets/LogSlideover.vue_vue_type_script_setup_true_lang-33xie7TE.js +1 -0
  352. package/public/assets/OverviewPage-CFfa0uwg.js +1 -0
  353. package/public/assets/PageGrid-CBROIU87.js +1 -0
  354. package/public/assets/PeriodRangePicker.vue_vue_type_script_setup_true_lang-DiFUNem7.js +1 -0
  355. package/public/assets/ProjectDetailPage-CreyvUit.js +1 -0
  356. package/public/assets/ProjectsPage-CFx8-elJ.js +1 -0
  357. package/public/assets/RunDetailSlideover.vue_vue_type_script_setup_true_lang-DopYYmCT.js +1 -0
  358. package/public/assets/RunsPage-CRnnXU4P.js +1 -0
  359. package/public/assets/Select-Bvx32Diz.js +1 -0
  360. package/public/assets/SelectMenu-BjMxc4VB.js +1 -0
  361. package/public/assets/SettingsPage-Bb6LM3wO.js +60 -0
  362. package/public/assets/TimeCell.vue_vue_type_script_setup_true_lang-DW_UB0Fu.js +1 -0
  363. package/public/assets/UsagePage-BLMDEsv-.js +332 -0
  364. package/public/assets/UsagePage-DodEeAjB.css +1 -0
  365. package/public/assets/VolumesPage-CxcLc3L2.js +1 -0
  366. package/public/assets/WebhookConfigPanel.vue_vue_type_script_setup_true_lang-B2V2GF6b.js +1 -0
  367. package/public/assets/WorkItemCell.vue_vue_type_script_setup_true_lang-BQn6vTjd.js +1 -0
  368. package/public/assets/index-BMbMibyb.js +4 -0
  369. package/public/assets/index-C5RBBrPj.js +30 -0
  370. package/public/assets/index-CgX29UgC.css +1 -0
  371. package/public/assets/usage-format-jsb31_O8.js +1 -0
  372. package/public/assets/useManagedProjects-8rtjRqVJ.js +1 -0
  373. package/public/assets/useRouteTab-DfwrcMic.js +1 -0
  374. package/public/favicon.ico +0 -0
  375. package/public/favicon.png +0 -0
  376. package/public/index.html +15 -0
@@ -0,0 +1,270 @@
1
+ import { appendFileSync, mkdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { join, resolve, sep } from "node:path";
3
+ import { config } from "./config.js";
4
+ import { log } from "./logger.js";
5
+ import { redact } from "./redact.js";
6
+ // Per-run log sink registry (work item #61). Kept in its own module with no
7
+ // dependency on task-log/store so `podman.ts` can tee container output here
8
+ // without importing the formatter (which imports podman — a cycle). The
9
+ // formatting and retention paths live in src/run-log.ts, which re-exports this
10
+ // module's API.
11
+ //
12
+ // One active run per work item (the queue is per-work-item FIFO), so a Map keyed
13
+ // by workItemId cannot collide. MCP-triggered phase containers can run alongside
14
+ // the primary run container; their output interleaves chronologically under
15
+ // their delimiters. Output is redacted before it touches disk.
16
+ // Whole-file cap. A chatty run is stopped here with a truncation marker; the
17
+ // row's log_truncated flag tells the UI to say so.
18
+ const DEFAULT_MAX_CHARS = 2_000_000;
19
+ // Streaming chunks are written line-buffered so a secret split across two
20
+ // `data` events is redacted as one unit (redact() matches whole tokens; a
21
+ // half-token crossing a chunk boundary would otherwise leak its other half).
22
+ // A tail longer than this with no newline is flushed redacted as a unit so an
23
+ // unbounded line cannot grow the buffer without limit.
24
+ const MAX_STREAM_CARRY = 64_000;
25
+ const sinks = new Map();
26
+ let override = null;
27
+ export function setConfigForTest(o) {
28
+ override = o;
29
+ }
30
+ export function logDir() {
31
+ return override?.dir ?? config.RUN_LOG_DIR;
32
+ }
33
+ function maxChars() {
34
+ return override?.maxChars ?? DEFAULT_MAX_CHARS;
35
+ }
36
+ function instanceId() {
37
+ return override?.instanceId ?? config.INSTANCE_ID;
38
+ }
39
+ // Filename for a run. seq is AUTOINCREMENT and never reused, so a filename can
40
+ // never collide with an earlier run's.
41
+ export function runLogFileName(seq) {
42
+ const prefix = instanceId() ? `${instanceId()}-` : "";
43
+ return `${prefix}run-${seq}.log`;
44
+ }
45
+ // Resolve a stored filename to an absolute path inside the log dir. A stored
46
+ // value containing a separator or escaping the dir is rejected so a corrupt DB
47
+ // row can never read (or delete) an arbitrary file. Returns null when unsafe.
48
+ export function resolveLogPath(file) {
49
+ if (!file || file.includes("/") || file.includes("\\") || file.includes(".."))
50
+ return null;
51
+ const dir = resolve(logDir());
52
+ const full = resolve(dir, file);
53
+ if (full !== join(dir, file) || !full.startsWith(dir + sep))
54
+ return null;
55
+ return full;
56
+ }
57
+ function ensureDir() {
58
+ mkdirSync(logDir(), { recursive: true, mode: 0o700 });
59
+ }
60
+ function existsSafe(file) {
61
+ try {
62
+ return statSync(file).isFile();
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ }
68
+ function sizeSafe(file) {
69
+ try {
70
+ return statSync(file).size;
71
+ }
72
+ catch {
73
+ return 0;
74
+ }
75
+ }
76
+ // Append a complete, redacted block. `appendRaw` assumes the caller has already
77
+ // made a redaction-safe unit (a whole phase body or a whole line).
78
+ function appendRaw(sink, text) {
79
+ if (!text || sink.truncated)
80
+ return;
81
+ const redacted = redact(text);
82
+ const room = maxChars() - sink.chars;
83
+ if (redacted.length > room) {
84
+ sink.truncated = true;
85
+ try {
86
+ appendFileSync(sink.file, "\n…[run log truncated at limit]\n");
87
+ sink.wrote = true;
88
+ }
89
+ catch (e) {
90
+ log.warn("run log truncate marker failed", { file: sink.file, err: e });
91
+ }
92
+ return;
93
+ }
94
+ sink.chars += redacted.length;
95
+ try {
96
+ appendFileSync(sink.file, redacted);
97
+ if (redacted.length > 0)
98
+ sink.wrote = true;
99
+ }
100
+ catch (e) {
101
+ log.warn("run log append failed", { file: sink.file, err: e });
102
+ }
103
+ }
104
+ // Open a new run's log file. A retry of the same seq appends an attempt header to
105
+ // the same file (the file is per seq, not per attempt).
106
+ export function beginRunLog(workItemId, seq) {
107
+ try {
108
+ ensureDir();
109
+ const file = join(logDir(), runLogFileName(seq));
110
+ const exists = existsSafe(file);
111
+ if (!exists)
112
+ writeFileSync(file, "", { mode: 0o600 });
113
+ const sink = {
114
+ workItemId,
115
+ seq,
116
+ file,
117
+ chars: exists ? sizeSafe(file) : 0,
118
+ truncated: false,
119
+ wrote: exists && sizeSafe(file) > 0,
120
+ carry: "",
121
+ };
122
+ sinks.set(workItemId, sink);
123
+ if (exists)
124
+ appendRaw(sink, "\n=== attempt ===\n");
125
+ }
126
+ catch (e) {
127
+ log.warn("beginRunLog failed", { workItemId, seq, err: e });
128
+ }
129
+ }
130
+ // Append a phase's buffered output under a delimiter. Used directly by tests and
131
+ // by the podman() hook via appendPodmanPhase.
132
+ export function appendPhase(phase, exitCode, stdout, stderr) {
133
+ // No workItemId in the signature (see the plan's interface): production
134
+ // callers go through appendPodmanPhase, which resolves it from the container
135
+ // name. Direct callers are single-sink tests/helpers, so target the sole sink.
136
+ const only = sinks.size === 1 ? [...sinks.values()][0] : undefined;
137
+ if (!only)
138
+ return;
139
+ emitPhase(only, phase, exitCode, stdout, stderr);
140
+ }
141
+ // podman() hook: parse `devbot-wi-<id>-<phase>` and tee into that item's sink.
142
+ export function appendPodmanPhase(containerName, exitCode, stdout, stderr) {
143
+ const parsed = parseWorkItemContainer(containerName);
144
+ if (!parsed)
145
+ return;
146
+ const sink = sinks.get(parsed.workItemId);
147
+ if (!sink)
148
+ return;
149
+ flushCarry(sink);
150
+ emitPhase(sink, parsed.phase, exitCode, stdout, stderr);
151
+ }
152
+ function emitPhase(sink, phase, exitCode, stdout, stderr) {
153
+ appendRaw(sink, `\n--- phase: ${phase} (exit ${exitCode}) ---\n`);
154
+ appendRaw(sink, stdout);
155
+ if (stderr)
156
+ appendRaw(sink, stderr);
157
+ }
158
+ // Open a phase whose output arrives as a stream rather than a buffered result
159
+ // (the opencode run container, streamed by runPodmanCapture). Writes the
160
+ // delimiter; the caller tees chunks with appendChunk and closes the phase with
161
+ // endStreamedPhase once the exit code is known.
162
+ export function startStreamedPhase(containerName) {
163
+ const parsed = parseWorkItemContainer(containerName);
164
+ if (!parsed)
165
+ return;
166
+ const sink = sinks.get(parsed.workItemId);
167
+ if (!sink)
168
+ return;
169
+ appendRaw(sink, `\n--- phase: ${parsed.phase} ---\n`);
170
+ }
171
+ // Close a streamed phase with its exit code, matching the buffered delimiter's
172
+ // `(exit N)` shape.
173
+ export function endStreamedPhase(containerName, exitCode) {
174
+ const parsed = parseWorkItemContainer(containerName);
175
+ if (!parsed)
176
+ return;
177
+ const sink = sinks.get(parsed.workItemId);
178
+ if (!sink)
179
+ return;
180
+ flushCarry(sink);
181
+ appendRaw(sink, `\n--- phase: ${parsed.phase} exit ${exitCode} ---\n`);
182
+ }
183
+ // Streaming chunks from runPodmanCapture (the agent run). Buffered to line
184
+ // boundaries so redact() sees whole tokens across arbitrary `data` event splits.
185
+ export function appendChunk(workItemId, chunk) {
186
+ const sink = sinks.get(workItemId);
187
+ if (!sink)
188
+ return;
189
+ sink.carry += chunk;
190
+ let nl;
191
+ while ((nl = sink.carry.indexOf("\n")) >= 0) {
192
+ appendRaw(sink, sink.carry.slice(0, nl + 1));
193
+ sink.carry = sink.carry.slice(nl + 1);
194
+ }
195
+ if (sink.carry.length > MAX_STREAM_CARRY)
196
+ flushCarry(sink);
197
+ }
198
+ // Redact + write any buffered partial line as one unit.
199
+ function flushCarry(sink) {
200
+ if (!sink.carry)
201
+ return;
202
+ appendRaw(sink, sink.carry);
203
+ sink.carry = "";
204
+ }
205
+ // Close a specific run's sink and return the stored filename. Production uses
206
+ // this (concurrent work items each own a sink). No active sink is a no-op. A run
207
+ // that captured no bytes returns logFile="" and its empty file is removed.
208
+ export function finishRunLogFor(workItemId) {
209
+ const sink = sinks.get(workItemId);
210
+ if (!sink)
211
+ return { logFile: "", truncated: false, hasContent: false };
212
+ return finishSink(sink);
213
+ }
214
+ // Single-sink convenience (tests). Drains every sink and returns the last one's
215
+ // result; production must use finishRunLogFor so concurrent runs are isolated.
216
+ export function finishRunLog() {
217
+ let result = { logFile: "", truncated: false, hasContent: false };
218
+ for (const [key, sink] of sinks) {
219
+ result = finishSink(sink);
220
+ sinks.delete(key);
221
+ }
222
+ return result;
223
+ }
224
+ function finishSink(sink) {
225
+ flushCarry(sink);
226
+ sinks.delete(sink.workItemId);
227
+ if (!sink.wrote) {
228
+ // Nothing captured: drop the empty file so it does not linger for the TTL,
229
+ // and report no file so the UI disables the Logs button.
230
+ try {
231
+ unlinkSync(sink.file);
232
+ }
233
+ catch { /* already gone */ }
234
+ return { logFile: "", truncated: sink.truncated, hasContent: false };
235
+ }
236
+ return { logFile: runLogFileName(sink.seq), truncated: sink.truncated, hasContent: true };
237
+ }
238
+ export function activeRunLogWorkItem(workItemId) {
239
+ return sinks.get(workItemId)?.seq ?? null;
240
+ }
241
+ // True when a live sink owns this filename (a run currently in flight). The TTL
242
+ // purge must not unlink it mid-run even if its mtime is past the cutoff.
243
+ export function isActiveLogFile(file) {
244
+ for (const sink of sinks.values()) {
245
+ if (runLogFileName(sink.seq) === file)
246
+ return true;
247
+ }
248
+ return false;
249
+ }
250
+ export function resetRunLogsForTest() {
251
+ sinks.clear();
252
+ override = null;
253
+ }
254
+ // Parse `devbot-wi-<id>-<phase>`. Container names never carry the INSTANCE_ID
255
+ // (only volume names do — see src/git.ts), so the prefix is fixed. The numeric
256
+ // re-check after the prefix means `devbot-wi-5-` can never match `devbot-wi-50-…`.
257
+ export function parseWorkItemContainer(name) {
258
+ const prefix = "devbot-wi-";
259
+ if (!name.startsWith(prefix))
260
+ return null;
261
+ const rest = name.slice(prefix.length);
262
+ const dash = rest.indexOf("-");
263
+ if (dash <= 0)
264
+ return null;
265
+ const workItemId = Number(rest.slice(0, dash));
266
+ if (!Number.isInteger(workItemId) || workItemId <= 0)
267
+ return null;
268
+ return { workItemId, phase: rest.slice(dash + 1) };
269
+ }
270
+ //# sourceMappingURL=run-log-sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-log-sink.js","sourceRoot":"","sources":["../src/run-log-sink.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ import { readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { log } from "./logger.js";
4
+ import { parseLogLines } from "./task-log.js";
5
+ import { clearLogFile, listExpiredLogRows } from "./run-history-store.js";
6
+ import { logDir, resolveLogPath, isActiveLogFile } from "./run-log-sink.js";
7
+ // Archived run-log reads and retention (work item #61). The capture sink lives
8
+ // in run-log-sink.ts (imported by podman.ts without pulling in the formatter);
9
+ // this module adds the read + purge paths that need the formatter/store.
10
+ export { beginRunLog, appendPhase, appendPodmanPhase, appendChunk, startStreamedPhase, endStreamedPhase, finishRunLog, finishRunLogFor, activeRunLogWorkItem, resetRunLogsForTest, setConfigForTest, runLogFileName, parseWorkItemContainer, } from "./run-log-sink.js";
11
+ // Read and format an archived run log. Returns reason "none" for a blank
12
+ // filename and "expired" for a missing file (the TTL sweep unlinked it). Text is
13
+ // redacted + capped + formatted by parseLogLines, the same renderer the live and
14
+ // error-log views use.
15
+ export function readArchivedLog(file) {
16
+ if (!file)
17
+ return { available: false, text: "", truncated: false, reason: "none" };
18
+ const full = resolveLogPath(file);
19
+ if (!full) {
20
+ log.warn("run log path rejected", { file });
21
+ return { available: false, text: "", truncated: false, reason: "expired" };
22
+ }
23
+ let raw;
24
+ try {
25
+ raw = readFileSync(full, "utf8");
26
+ }
27
+ catch {
28
+ return { available: false, text: "", truncated: false, reason: "expired" };
29
+ }
30
+ const { text, truncated } = parseLogLines(raw);
31
+ return { available: true, text, truncated, reason: "" };
32
+ }
33
+ // Delete log files older than `hours` (0 = keep forever) and blank the matching
34
+ // rows' log_file. Rows are kept so the history page stays a durable record.
35
+ // Also unlinks orphaned run-*.log files older than the cutoff that no row
36
+ // references (a crash mid-run leaves a file before its row exists). Returns the
37
+ // number of files removed.
38
+ export function purgeRunLogs(hours) {
39
+ if (hours <= 0)
40
+ return 0;
41
+ let removed = 0;
42
+ // finished_at and file mtimes are epoch MILLISECONDS; the cutoff must match.
43
+ const cutoffMs = Date.now() - hours * 3600 * 1000;
44
+ try {
45
+ const expired = listExpiredLogRows(cutoffMs);
46
+ const ids = [];
47
+ for (const row of expired) {
48
+ const full = resolveLogPath(row.logFile);
49
+ if (full) {
50
+ try {
51
+ unlinkSync(full);
52
+ removed++;
53
+ }
54
+ catch { /* already gone */ }
55
+ }
56
+ ids.push(row.id);
57
+ }
58
+ if (ids.length > 0)
59
+ clearLogFile(ids);
60
+ removed += purgeOrphans(cutoffMs);
61
+ }
62
+ catch (e) {
63
+ log.warn("run log purge failed", { hours, err: e });
64
+ }
65
+ return removed;
66
+ }
67
+ // Unlink run-*.log files older than the cutoff. Referenced-and-expired files
68
+ // were already unlinked by the row pass, so this removes anything still on disk
69
+ // past the TTL — including orphans from a crash that left a file before its row.
70
+ function purgeOrphans(cutoffMs) {
71
+ const dir = resolve(logDir());
72
+ let names;
73
+ try {
74
+ names = readdirSync(dir);
75
+ }
76
+ catch {
77
+ return 0;
78
+ }
79
+ let removed = 0;
80
+ for (const name of names) {
81
+ if (!/^(?:[a-zA-Z0-9-]+-)?run-\d+\.log$/.test(name))
82
+ continue;
83
+ // Never unlink a file a run is currently writing.
84
+ if (isActiveLogFile(name))
85
+ continue;
86
+ const full = join(dir, name);
87
+ try {
88
+ if (statSync(full).mtimeMs >= cutoffMs)
89
+ continue;
90
+ unlinkSync(full);
91
+ removed++;
92
+ }
93
+ catch { /* raced or gone */ }
94
+ }
95
+ return removed;
96
+ }
97
+ //# sourceMappingURL=run-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-log.js","sourceRoot":"","sources":["../src/run-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { zeroUsage } from "./opencode.js";
2
+ // Per-work-item token usage accumulator (work item #66). One run per work item
3
+ // is the queue invariant, and every OpenCode container in that run (the agent
4
+ // run plus conflict resolvers) reports through runPodmanCapture, so a Map keyed
5
+ // by workItemId cannot collide. The webhook drains it once per settled run after
6
+ // finality is known; retries reuse the same seq and therefore accumulate here.
7
+ const usage = new Map();
8
+ export function addUsage(workItemId, u) {
9
+ const cur = usage.get(workItemId) ?? zeroUsage();
10
+ cur.input += u.input;
11
+ cur.output += u.output;
12
+ cur.reasoning += u.reasoning;
13
+ cur.cacheRead += u.cacheRead;
14
+ cur.cacheWrite += u.cacheWrite;
15
+ cur.costUsd += u.costUsd;
16
+ usage.set(workItemId, cur);
17
+ }
18
+ export function peekUsage(workItemId) {
19
+ return { ...(usage.get(workItemId) ?? zeroUsage()) };
20
+ }
21
+ export function drainUsage(workItemId) {
22
+ const u = peekUsage(workItemId);
23
+ usage.delete(workItemId);
24
+ return u;
25
+ }
26
+ export function resetRunUsageForTest() {
27
+ usage.clear();
28
+ }
29
+ //# sourceMappingURL=run-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-usage.js","sourceRoot":"","sources":["../src/run-usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,eAAe,CAAC;AAEzD,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,iFAAiF;AACjF,+EAA+E;AAC/E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE1C,MAAM,UAAU,QAAQ,CAAC,UAAkB,EAAE,CAAW;IACtD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,SAAS,EAAE,CAAC;IACjD,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;IACrB,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;IACvB,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC;IAC7B,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC;IAC7B,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC;IAC/B,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC;IACzB,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,SAAS,EAAE,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,MAAM,CAAC,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAChC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,112 @@
1
+ // Cap on how deep the merged "all" view can be paged. Both sources are fetched
2
+ // with offset 0 up to this window; `settled` has unbounded direct pagination for
3
+ // deeper history. Keeps the two-pointer merge from fetching an unbounded offset
4
+ // from each source on every page request.
5
+ export const MAX_MERGE_WINDOW = 1000;
6
+ // Live logs only exist while a phase container runs (queued/running). A settled
7
+ // task reads its archived file when one was captured. A `waiting` task is a
8
+ // claimed task blocked on the global gate before any container starts — no logs
9
+ // yet, but it is still in-flight, so treat it as live (the poll yields
10
+ // available:false until a container appears).
11
+ export function logSourceFor(taskStatus, historyLogFile) {
12
+ if (taskStatus === "queued" || taskStatus === "waiting" || taskStatus === "running")
13
+ return "live";
14
+ if (historyLogFile)
15
+ return "archive";
16
+ return "none";
17
+ }
18
+ export function taskViewToRunView(t) {
19
+ return {
20
+ seq: t.seq,
21
+ projectId: t.projectId,
22
+ projectPath: t.projectPath,
23
+ workItemIid: t.workItemIid,
24
+ workItemId: t.workItemId,
25
+ workItemUrl: t.workItemUrl,
26
+ workItemTitle: t.title,
27
+ mode: t.mode,
28
+ model: t.model,
29
+ variant: t.variant,
30
+ state: t.status,
31
+ // The active phase is discovered at read time by the live-log route; the
32
+ // list has no phase label.
33
+ phase: "",
34
+ enqueuedAt: t.enqueuedAt,
35
+ startedAt: t.claimedAt,
36
+ finishedAt: null,
37
+ durationMs: null,
38
+ attempts: t.attempts,
39
+ position: t.position,
40
+ canStop: true,
41
+ historyId: null,
42
+ logFile: "",
43
+ logTruncated: false,
44
+ message: "",
45
+ exitCode: null,
46
+ logKind: logSourceFor(t.status, null),
47
+ inputTokens: 0,
48
+ outputTokens: 0,
49
+ reasoningTokens: 0,
50
+ cacheReadTokens: 0,
51
+ cacheWriteTokens: 0,
52
+ costUsd: 0,
53
+ };
54
+ }
55
+ export function historyToRunView(r) {
56
+ const durationMs = r.startedAt && r.finishedAt && r.finishedAt >= r.startedAt ? r.finishedAt - r.startedAt : null;
57
+ return {
58
+ seq: r.taskSeq ?? 0,
59
+ projectId: r.projectId,
60
+ projectPath: "",
61
+ workItemIid: r.workItemIid,
62
+ workItemId: r.workItemId,
63
+ workItemUrl: "",
64
+ workItemTitle: r.workItemTitle,
65
+ mode: r.mode,
66
+ model: r.model,
67
+ variant: r.variant,
68
+ state: r.status,
69
+ phase: r.phase,
70
+ enqueuedAt: r.enqueuedAt,
71
+ startedAt: r.startedAt || null,
72
+ finishedAt: r.finishedAt || null,
73
+ durationMs,
74
+ attempts: r.attempts,
75
+ position: null,
76
+ canStop: false,
77
+ historyId: r.id,
78
+ logFile: r.logFile,
79
+ logTruncated: r.logTruncated,
80
+ message: r.message,
81
+ exitCode: r.exitCode,
82
+ logKind: logSourceFor(null, r.logFile),
83
+ inputTokens: r.inputTokens,
84
+ outputTokens: r.outputTokens,
85
+ reasoningTokens: r.reasoningTokens,
86
+ cacheReadTokens: r.cacheReadTokens,
87
+ cacheWriteTokens: r.cacheWriteTokens,
88
+ costUsd: r.costUsd,
89
+ };
90
+ }
91
+ // Merge in-flight and settled RunViews into one seq-DESC page. A seq present in
92
+ // both (the few-ms window between recording history and deleting the queue row)
93
+ // resolves to the settled row, which has the finished state and log reference.
94
+ // Rows with no task seq (settled history with taskSeq null) have no shared key,
95
+ // so they are never deduped against each other and sort last.
96
+ export function mergeRunViews(active, settled, limit, offset) {
97
+ const bySeq = new Map();
98
+ const unkeyed = [];
99
+ const put = (v) => {
100
+ if (v.seq > 0)
101
+ bySeq.set(v.seq, v);
102
+ else
103
+ unkeyed.push(v);
104
+ };
105
+ for (const v of active)
106
+ put(v);
107
+ for (const v of settled)
108
+ put(v); // settled wins
109
+ const keyed = [...bySeq.values()].sort((a, b) => b.seq - a.seq);
110
+ return [...keyed, ...unkeyed].slice(offset, offset + limit);
111
+ }
112
+ //# sourceMappingURL=run-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-view.js","sourceRoot":"","sources":["../src/run-view.ts"],"names":[],"mappings":"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"}