@agimon-ai/po-runner 0.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +257 -0
  3. package/dist/ansiScrub-CsqTSaVT.mjs +71 -0
  4. package/dist/ansiScrub-CsqTSaVT.mjs.map +1 -0
  5. package/dist/ansiScrub-D5gpHDch.cjs +100 -0
  6. package/dist/ansiScrub-D5gpHDch.cjs.map +1 -0
  7. package/dist/ansiScrub.cjs +7 -0
  8. package/dist/ansiScrub.d.mts +26 -0
  9. package/dist/ansiScrub.d.mts.map +1 -0
  10. package/dist/ansiScrub.mjs +2 -0
  11. package/dist/apiContracts.cjs +162 -0
  12. package/dist/apiContracts.cjs.map +1 -0
  13. package/dist/apiContracts.d.mts +53 -0
  14. package/dist/apiContracts.d.mts.map +1 -0
  15. package/dist/apiContracts.mjs +157 -0
  16. package/dist/apiContracts.mjs.map +1 -0
  17. package/dist/bashResult-DNgtahGR.cjs +629 -0
  18. package/dist/bashResult-DNgtahGR.cjs.map +1 -0
  19. package/dist/bashResult-DcrRaBLv.d.mts +49 -0
  20. package/dist/bashResult-DcrRaBLv.d.mts.map +1 -0
  21. package/dist/bashResult-bIVSzoj-.mjs +546 -0
  22. package/dist/bashResult-bIVSzoj-.mjs.map +1 -0
  23. package/dist/bashRunService-CJ02XaAq.mjs +440 -0
  24. package/dist/bashRunService-CJ02XaAq.mjs.map +1 -0
  25. package/dist/bashRunService-Ci0cGdYO.d.mts +71 -0
  26. package/dist/bashRunService-Ci0cGdYO.d.mts.map +1 -0
  27. package/dist/bashRunService-DyqxOSlx.cjs +445 -0
  28. package/dist/bashRunService-DyqxOSlx.cjs.map +1 -0
  29. package/dist/bashRunService.cjs +3 -0
  30. package/dist/bashRunService.d.mts +36 -0
  31. package/dist/bashRunService.d.mts.map +1 -0
  32. package/dist/bashRunService.mjs +2 -0
  33. package/dist/bashSchema.cjs +5 -0
  34. package/dist/bashSchema.d.mts +7 -0
  35. package/dist/bashSchema.d.mts.map +1 -0
  36. package/dist/bashSchema.mjs +2 -0
  37. package/dist/bashTool-B1EwZLo_.d.mts +20 -0
  38. package/dist/bashTool-B1EwZLo_.d.mts.map +1 -0
  39. package/dist/bashTool-C-k7fmyh.mjs +24 -0
  40. package/dist/bashTool-C-k7fmyh.mjs.map +1 -0
  41. package/dist/bashTool-CdgtqVFD.cjs +41 -0
  42. package/dist/bashTool-CdgtqVFD.cjs.map +1 -0
  43. package/dist/bashTool.cjs +13 -0
  44. package/dist/bashTool.cjs.map +1 -0
  45. package/dist/bashTool.d.mts +8 -0
  46. package/dist/bashTool.d.mts.map +1 -0
  47. package/dist/bashTool.mjs +11 -0
  48. package/dist/bashTool.mjs.map +1 -0
  49. package/dist/bin/cli.cjs +78 -0
  50. package/dist/bin/cli.cjs.map +1 -0
  51. package/dist/bin/cli.d.mts +4 -0
  52. package/dist/bin/cli.d.mts.map +1 -0
  53. package/dist/bin/cli.mjs +77 -0
  54. package/dist/bin/cli.mjs.map +1 -0
  55. package/dist/bin/logSink.cjs +102 -0
  56. package/dist/bin/logSink.cjs.map +1 -0
  57. package/dist/bin/logSink.d.mts +4 -0
  58. package/dist/bin/logSink.d.mts.map +1 -0
  59. package/dist/bin/logSink.mjs +99 -0
  60. package/dist/bin/logSink.mjs.map +1 -0
  61. package/dist/bin/runnerHost.cjs +131 -0
  62. package/dist/bin/runnerHost.cjs.map +1 -0
  63. package/dist/bin/runnerHost.d.mts +4 -0
  64. package/dist/bin/runnerHost.d.mts.map +1 -0
  65. package/dist/bin/runnerHost.mjs +127 -0
  66. package/dist/bin/runnerHost.mjs.map +1 -0
  67. package/dist/cliApp-Dj9nwaOv.cjs +244 -0
  68. package/dist/cliApp-Dj9nwaOv.cjs.map +1 -0
  69. package/dist/cliApp-v8Z3rMCi.mjs +237 -0
  70. package/dist/cliApp-v8Z3rMCi.mjs.map +1 -0
  71. package/dist/cliApp.cjs +3 -0
  72. package/dist/cliApp.d.mts +18 -0
  73. package/dist/cliApp.d.mts.map +1 -0
  74. package/dist/cliApp.mjs +2 -0
  75. package/dist/clock-Cv2pddgo.mjs +16 -0
  76. package/dist/clock-Cv2pddgo.mjs.map +1 -0
  77. package/dist/clock-euJkweLq.d.mts +11 -0
  78. package/dist/clock-euJkweLq.d.mts.map +1 -0
  79. package/dist/clock-jqnLWziT.cjs +21 -0
  80. package/dist/clock-jqnLWziT.cjs.map +1 -0
  81. package/dist/clock.cjs +3 -0
  82. package/dist/clock.d.mts +10 -0
  83. package/dist/clock.d.mts.map +1 -0
  84. package/dist/clock.mjs +2 -0
  85. package/dist/compaction-4QSUsBxq.cjs +40 -0
  86. package/dist/compaction-4QSUsBxq.cjs.map +1 -0
  87. package/dist/compaction-DCw80VZj.mjs +29 -0
  88. package/dist/compaction-DCw80VZj.mjs.map +1 -0
  89. package/dist/compaction.cjs +4 -0
  90. package/dist/compaction.d.mts +10 -0
  91. package/dist/compaction.d.mts.map +1 -0
  92. package/dist/compaction.mjs +2 -0
  93. package/dist/config.cjs +41 -0
  94. package/dist/config.d.mts +2 -0
  95. package/dist/config.mjs +2 -0
  96. package/dist/extensions/mcp-ui.mjs +23 -0
  97. package/dist/extensions/mcp-ui.mjs.map +1 -0
  98. package/dist/extensions/mcp.cjs +142 -0
  99. package/dist/extensions/mcp.cjs.map +1 -0
  100. package/dist/extensions/mcp.d.mts +5 -0
  101. package/dist/extensions/mcp.d.mts.map +1 -0
  102. package/dist/extensions/mcp.mjs +114 -0
  103. package/dist/extensions/mcp.mjs.map +1 -0
  104. package/dist/extensions/server.cjs +1156 -0
  105. package/dist/extensions/server.cjs.map +1 -0
  106. package/dist/extensions/server.d.mts +5 -0
  107. package/dist/extensions/server.d.mts.map +1 -0
  108. package/dist/extensions/server.mjs +1146 -0
  109. package/dist/extensions/server.mjs.map +1 -0
  110. package/dist/extensions/web.mjs +1785 -0
  111. package/dist/extensions/web.mjs.map +1 -0
  112. package/dist/headless-BGYfIlNk.mjs +169 -0
  113. package/dist/headless-BGYfIlNk.mjs.map +1 -0
  114. package/dist/headless-CHUsBGhg.cjs +180 -0
  115. package/dist/headless-CHUsBGhg.cjs.map +1 -0
  116. package/dist/index-BZL7keVW.d.mts +31 -0
  117. package/dist/index-BZL7keVW.d.mts.map +1 -0
  118. package/dist/index-BwB_jTI9.d.mts +12 -0
  119. package/dist/index-BwB_jTI9.d.mts.map +1 -0
  120. package/dist/index-D9-IXax4.d.mts +43 -0
  121. package/dist/index-D9-IXax4.d.mts.map +1 -0
  122. package/dist/index-DBz0DxnL.d.mts +39 -0
  123. package/dist/index-DBz0DxnL.d.mts.map +1 -0
  124. package/dist/index-DM7yJpMs.d.mts +25 -0
  125. package/dist/index-DM7yJpMs.d.mts.map +1 -0
  126. package/dist/index-Dhi8asLH.d.mts +22 -0
  127. package/dist/index-Dhi8asLH.d.mts.map +1 -0
  128. package/dist/index.cjs +19 -0
  129. package/dist/index.d.mts +7 -0
  130. package/dist/index.mjs +6 -0
  131. package/dist/launcher-BDm6-DiQ.mjs +150 -0
  132. package/dist/launcher-BDm6-DiQ.mjs.map +1 -0
  133. package/dist/launcher-BG5t9nvG.d.mts +39 -0
  134. package/dist/launcher-BG5t9nvG.d.mts.map +1 -0
  135. package/dist/launcher-SlyE8R3b.cjs +157 -0
  136. package/dist/launcher-SlyE8R3b.cjs.map +1 -0
  137. package/dist/launcher.cjs +3 -0
  138. package/dist/launcher.d.mts +33 -0
  139. package/dist/launcher.d.mts.map +1 -0
  140. package/dist/launcher.mjs +2 -0
  141. package/dist/lifeline-BZaQA0rm.mjs +88 -0
  142. package/dist/lifeline-BZaQA0rm.mjs.map +1 -0
  143. package/dist/lifeline-BuV_erib.cjs +98 -0
  144. package/dist/lifeline-BuV_erib.cjs.map +1 -0
  145. package/dist/lifeline-eJdZV0nN.d.mts +19 -0
  146. package/dist/lifeline-eJdZV0nN.d.mts.map +1 -0
  147. package/dist/lifeline.cjs +6 -0
  148. package/dist/lifeline.d.mts +21 -0
  149. package/dist/lifeline.d.mts.map +1 -0
  150. package/dist/lifeline.mjs +3 -0
  151. package/dist/logFile-C7oQ_v-g.d.mts +16 -0
  152. package/dist/logFile-C7oQ_v-g.d.mts.map +1 -0
  153. package/dist/logFile-CNvepZh0.cjs +67 -0
  154. package/dist/logFile-CNvepZh0.cjs.map +1 -0
  155. package/dist/logFile-KKPbkC-a.mjs +60 -0
  156. package/dist/logFile-KKPbkC-a.mjs.map +1 -0
  157. package/dist/logFile.cjs +3 -0
  158. package/dist/logFile.d.mts +11 -0
  159. package/dist/logFile.d.mts.map +1 -0
  160. package/dist/logFile.mjs +2 -0
  161. package/dist/logReader-B6J7L7Ua.cjs +282 -0
  162. package/dist/logReader-B6J7L7Ua.cjs.map +1 -0
  163. package/dist/logReader-MdW9ffvV.d.mts +43 -0
  164. package/dist/logReader-MdW9ffvV.d.mts.map +1 -0
  165. package/dist/logReader-UfbDbwQl.mjs +257 -0
  166. package/dist/logReader-UfbDbwQl.mjs.map +1 -0
  167. package/dist/logReader.cjs +4 -0
  168. package/dist/logReader.d.mts +3 -0
  169. package/dist/logReader.mjs +2 -0
  170. package/dist/processControl-BAOa3oo_.d.mts +14 -0
  171. package/dist/processControl-BAOa3oo_.d.mts.map +1 -0
  172. package/dist/processControl-DApTpV16.mjs +26 -0
  173. package/dist/processControl-DApTpV16.mjs.map +1 -0
  174. package/dist/processControl-DNJ1WZ9N.cjs +31 -0
  175. package/dist/processControl-DNJ1WZ9N.cjs.map +1 -0
  176. package/dist/processControl.cjs +3 -0
  177. package/dist/processControl.d.mts +10 -0
  178. package/dist/processControl.d.mts.map +1 -0
  179. package/dist/processControl.mjs +2 -0
  180. package/dist/ptyHost-DWWtRrGn.d.mts +38 -0
  181. package/dist/ptyHost-DWWtRrGn.d.mts.map +1 -0
  182. package/dist/ptyHost-DlQtTB8F.mjs +150 -0
  183. package/dist/ptyHost-DlQtTB8F.mjs.map +1 -0
  184. package/dist/ptyHost-MI2Z6WTG.cjs +157 -0
  185. package/dist/ptyHost-MI2Z6WTG.cjs.map +1 -0
  186. package/dist/ptyHost.cjs +3 -0
  187. package/dist/ptyHost.d.mts +26 -0
  188. package/dist/ptyHost.d.mts.map +1 -0
  189. package/dist/ptyHost.mjs +2 -0
  190. package/dist/ptySpawner-B6GsueZd.d.mts +28 -0
  191. package/dist/ptySpawner-B6GsueZd.d.mts.map +1 -0
  192. package/dist/ptySpawner-DEi1KOnd.mjs +17 -0
  193. package/dist/ptySpawner-DEi1KOnd.mjs.map +1 -0
  194. package/dist/ptySpawner-ijw0g5_T.cjs +22 -0
  195. package/dist/ptySpawner-ijw0g5_T.cjs.map +1 -0
  196. package/dist/ptySpawner.cjs +3 -0
  197. package/dist/ptySpawner.d.mts +14 -0
  198. package/dist/ptySpawner.d.mts.map +1 -0
  199. package/dist/ptySpawner.mjs +2 -0
  200. package/dist/reconcile-BugwYFbV.cjs +133 -0
  201. package/dist/reconcile-BugwYFbV.cjs.map +1 -0
  202. package/dist/reconcile-DMeNSxwn.mjs +114 -0
  203. package/dist/reconcile-DMeNSxwn.mjs.map +1 -0
  204. package/dist/reconcile.cjs +5 -0
  205. package/dist/reconcile.d.mts +36 -0
  206. package/dist/reconcile.d.mts.map +1 -0
  207. package/dist/reconcile.mjs +2 -0
  208. package/dist/responseEnvelope.cjs +19 -0
  209. package/dist/responseEnvelope.d.mts +50 -0
  210. package/dist/responseEnvelope.d.mts.map +1 -0
  211. package/dist/responseEnvelope.mjs +3 -0
  212. package/dist/rmuxBackend-CaEWooWn.d.mts +36 -0
  213. package/dist/rmuxBackend-CaEWooWn.d.mts.map +1 -0
  214. package/dist/rmuxBackend-DfyQwJrc.mjs +509 -0
  215. package/dist/rmuxBackend-DfyQwJrc.mjs.map +1 -0
  216. package/dist/rmuxBackend-DlxgqRZ1.cjs +523 -0
  217. package/dist/rmuxBackend-DlxgqRZ1.cjs.map +1 -0
  218. package/dist/rmuxBackend.cjs +4 -0
  219. package/dist/rmuxBackend.d.mts +3 -0
  220. package/dist/rmuxBackend.mjs +2 -0
  221. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  222. package/dist/rtkProcessor-B4_urXQr.mjs +10 -0
  223. package/dist/rtkProcessor-B4_urXQr.mjs.map +1 -0
  224. package/dist/rtkProcessor-B9KPdyzr.mjs +309 -0
  225. package/dist/rtkProcessor-B9KPdyzr.mjs.map +1 -0
  226. package/dist/rtkProcessor-Bc4L4o_8.cjs +335 -0
  227. package/dist/rtkProcessor-Bc4L4o_8.cjs.map +1 -0
  228. package/dist/rtkProcessor-CVZvNhkJ.d.mts +35 -0
  229. package/dist/rtkProcessor-CVZvNhkJ.d.mts.map +1 -0
  230. package/dist/rtkProcessor-CsrCPyTQ.cjs +33 -0
  231. package/dist/rtkProcessor-CsrCPyTQ.cjs.map +1 -0
  232. package/dist/rtkProcessor.cjs +11 -0
  233. package/dist/rtkProcessor.d.mts +7 -0
  234. package/dist/rtkProcessor.d.mts.map +1 -0
  235. package/dist/rtkProcessor.mjs +3 -0
  236. package/dist/runnerConfig-BQBlVAXn.mjs +134 -0
  237. package/dist/runnerConfig-BQBlVAXn.mjs.map +1 -0
  238. package/dist/runnerConfig-BjtulXBI.d.mts +57 -0
  239. package/dist/runnerConfig-BjtulXBI.d.mts.map +1 -0
  240. package/dist/runnerConfig-D2tGq5ZM.cjs +367 -0
  241. package/dist/runnerConfig-D2tGq5ZM.cjs.map +1 -0
  242. package/dist/runnerDependencies-DruByBGS.mjs +647 -0
  243. package/dist/runnerDependencies-DruByBGS.mjs.map +1 -0
  244. package/dist/runnerDependencies-KMZigIxB.cjs +667 -0
  245. package/dist/runnerDependencies-KMZigIxB.cjs.map +1 -0
  246. package/dist/runnerDependencies.cjs +3 -0
  247. package/dist/runnerDependencies.d.mts +2 -0
  248. package/dist/runnerDependencies.mjs +2 -0
  249. package/dist/runnerNamer-B8FH7n2v.mjs +44 -0
  250. package/dist/runnerNamer-B8FH7n2v.mjs.map +1 -0
  251. package/dist/runnerNamer-we1XVYfM.cjs +49 -0
  252. package/dist/runnerNamer-we1XVYfM.cjs.map +1 -0
  253. package/dist/runnerNamer.cjs +3 -0
  254. package/dist/runnerNamer.d.mts +11 -0
  255. package/dist/runnerNamer.d.mts.map +1 -0
  256. package/dist/runnerNamer.mjs +2 -0
  257. package/dist/runnerPaths-BTEuzxw0.cjs +471 -0
  258. package/dist/runnerPaths-BTEuzxw0.cjs.map +1 -0
  259. package/dist/runnerPaths-B_dAAeco.mjs +451 -0
  260. package/dist/runnerPaths-B_dAAeco.mjs.map +1 -0
  261. package/dist/runnerPaths.cjs +3 -0
  262. package/dist/runnerPaths.d.mts +50 -0
  263. package/dist/runnerPaths.d.mts.map +1 -0
  264. package/dist/runnerPaths.mjs +2 -0
  265. package/dist/runnerRegistry-BmkZTJHi.cjs +371 -0
  266. package/dist/runnerRegistry-BmkZTJHi.cjs.map +1 -0
  267. package/dist/runnerRegistry-CeOHEjBv.mjs +339 -0
  268. package/dist/runnerRegistry-CeOHEjBv.mjs.map +1 -0
  269. package/dist/runnerRegistry-CtJiD02R.d.mts +88 -0
  270. package/dist/runnerRegistry-CtJiD02R.d.mts.map +1 -0
  271. package/dist/runnerRegistry.cjs +4 -0
  272. package/dist/runnerRegistry.d.mts +3 -0
  273. package/dist/runnerRegistry.mjs +2 -0
  274. package/dist/runnerSettings-DiV1lvzx.d.mts +34 -0
  275. package/dist/runnerSettings-DiV1lvzx.d.mts.map +1 -0
  276. package/dist/services/lifeline/client.cjs +52 -0
  277. package/dist/services/lifeline/client.cjs.map +1 -0
  278. package/dist/services/lifeline/client.d.mts +22 -0
  279. package/dist/services/lifeline/client.d.mts.map +1 -0
  280. package/dist/services/lifeline/client.mjs +48 -0
  281. package/dist/services/lifeline/client.mjs.map +1 -0
  282. package/dist/services/runnerSupervisor/index.cjs +207 -0
  283. package/dist/services/runnerSupervisor/index.cjs.map +1 -0
  284. package/dist/services/runnerSupervisor/index.d.mts +71 -0
  285. package/dist/services/runnerSupervisor/index.d.mts.map +1 -0
  286. package/dist/services/runnerSupervisor/index.mjs +195 -0
  287. package/dist/services/runnerSupervisor/index.mjs.map +1 -0
  288. package/dist/session-BC43uEyG.cjs +29 -0
  289. package/dist/session-BC43uEyG.cjs.map +1 -0
  290. package/dist/session-Chsb_Nh2.mjs +18 -0
  291. package/dist/session-Chsb_Nh2.mjs.map +1 -0
  292. package/dist/session-DE2Acesp.mjs +6 -0
  293. package/dist/session-DE2Acesp.mjs.map +1 -0
  294. package/dist/session-SfIPtdZO.cjs +11 -0
  295. package/dist/session-SfIPtdZO.cjs.map +1 -0
  296. package/dist/session.cjs +6 -0
  297. package/dist/session.d.mts +11 -0
  298. package/dist/session.d.mts.map +1 -0
  299. package/dist/session.mjs +3 -0
  300. package/dist/spawner-BxFsINXP.cjs +68 -0
  301. package/dist/spawner-BxFsINXP.cjs.map +1 -0
  302. package/dist/spawner-CdrN_HNk.d.mts +35 -0
  303. package/dist/spawner-CdrN_HNk.d.mts.map +1 -0
  304. package/dist/spawner-LoVsn55-.mjs +63 -0
  305. package/dist/spawner-LoVsn55-.mjs.map +1 -0
  306. package/dist/spawner.cjs +3 -0
  307. package/dist/spawner.d.mts +9 -0
  308. package/dist/spawner.d.mts.map +1 -0
  309. package/dist/spawner.mjs +2 -0
  310. package/dist/type-BXM03qSw.d.mts +44 -0
  311. package/dist/type-BXM03qSw.d.mts.map +1 -0
  312. package/dist/type-C31e1gla.d.mts +43 -0
  313. package/dist/type-C31e1gla.d.mts.map +1 -0
  314. package/dist/type-vWQNTahw.d.mts +12 -0
  315. package/dist/type-vWQNTahw.d.mts.map +1 -0
  316. package/dist/types.cjs +0 -0
  317. package/dist/types.d.mts +3 -0
  318. package/dist/types.mjs +1 -0
  319. package/llms.txt +9 -0
  320. package/package.json +303 -0
  321. package/skills/doom-runner/SKILL.md +78 -0
  322. package/src/prompts/po-use-runner/SKILL.md +47 -0
@@ -0,0 +1,451 @@
1
+ import { M as LOG_DIR_ENV, l as getMaxCompletedRunners } from "./runnerConfig-BQBlVAXn.mjs";
2
+ import { t as inheritedSessionId } from "./session-Chsb_Nh2.mjs";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { execFileSync } from "node:child_process";
6
+ import { piAgentDirectory } from "@agimon-ai/po-core/piSettings";
7
+ //#region src/constants/runnerPaths.ts
8
+ const STORE_DIR_NAME = "doom-runner";
9
+ const LOG_DIR_NAME = "logs";
10
+ const STATE_DIR_NAME = "runs";
11
+ const LOG_EXTENSION = ".log";
12
+ const STATE_EXTENSION = ".json";
13
+ const RAW_SUFFIX = ".raw";
14
+ const FILE_ENCODING = "utf8";
15
+ //#endregion
16
+ //#region src/services/runnerPaths/index.ts
17
+ /** Unscrubbed pane bytes, kept beside the log so an attached terminal can replay them. */
18
+ /**
19
+ * How long an entirely empty session directory is left alone before it counts
20
+ * as an orphan. A session that has started but not yet run anything owns such a
21
+ * directory for a moment, and reclaiming it out from under that session would
22
+ * be churn for no gain.
23
+ */
24
+ function git(args, cwd) {
25
+ try {
26
+ const trimmed = execFileSync("git", args, {
27
+ cwd,
28
+ encoding: FILE_ENCODING,
29
+ stdio: [
30
+ "ignore",
31
+ "pipe",
32
+ "ignore"
33
+ ]
34
+ }).trim();
35
+ return trimmed.length > 0 ? trimmed : void 0;
36
+ } catch {
37
+ return;
38
+ }
39
+ }
40
+ function realPath(target) {
41
+ try {
42
+ return fs.realpathSync(target);
43
+ } catch {
44
+ return target;
45
+ }
46
+ }
47
+ /**
48
+ * Newest mtime anywhere one level inside the session, falling back to the
49
+ * directory itself. A session still being written to therefore always looks
50
+ * recent, whatever state its individual records are in.
51
+ */
52
+ function newestEntryTime(directory) {
53
+ let newest = fs.statSync(directory).mtimeMs;
54
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
55
+ const target = path.join(directory, entry.name);
56
+ newest = Math.max(newest, fs.statSync(target).mtimeMs);
57
+ if (!entry.isDirectory()) continue;
58
+ for (const child of fs.readdirSync(target)) newest = Math.max(newest, fs.statSync(path.join(target, child)).mtimeMs);
59
+ }
60
+ return newest;
61
+ }
62
+ async function newestEntryTimeAsync(directory) {
63
+ let newest = (await fs.promises.stat(directory)).mtimeMs;
64
+ for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {
65
+ const target = path.join(directory, entry.name);
66
+ newest = Math.max(newest, (await fs.promises.stat(target)).mtimeMs);
67
+ if (!entry.isDirectory()) continue;
68
+ for (const child of await fs.promises.readdir(target)) newest = Math.max(newest, (await fs.promises.stat(path.join(target, child))).mtimeMs);
69
+ }
70
+ return newest;
71
+ }
72
+ /**
73
+ * Whether any run in this session names a pi process that is still running.
74
+ *
75
+ * One live record is enough: records are only ever written by their owner, so a
76
+ * live host pid means the session is still in use however old its files look.
77
+ */
78
+ function ownerAlive(stateDirectory) {
79
+ let entries;
80
+ try {
81
+ entries = fs.readdirSync(stateDirectory);
82
+ } catch {
83
+ return false;
84
+ }
85
+ for (const entry of entries) {
86
+ if (!entry.endsWith(".json")) continue;
87
+ try {
88
+ const hostPid = JSON.parse(fs.readFileSync(path.join(stateDirectory, entry), FILE_ENCODING)).hostPid;
89
+ if (typeof hostPid !== "number") continue;
90
+ process.kill(hostPid, 0);
91
+ return true;
92
+ } catch {}
93
+ }
94
+ return false;
95
+ }
96
+ async function ownerAliveAsync(stateDirectory) {
97
+ let entries;
98
+ try {
99
+ entries = await fs.promises.readdir(stateDirectory);
100
+ } catch {
101
+ return false;
102
+ }
103
+ for (const entry of entries) {
104
+ if (!entry.endsWith(".json")) continue;
105
+ try {
106
+ const hostPid = JSON.parse(await fs.promises.readFile(path.join(stateDirectory, entry), FILE_ENCODING)).hostPid;
107
+ if (typeof hostPid !== "number") continue;
108
+ process.kill(hostPid, 0);
109
+ return true;
110
+ } catch {}
111
+ }
112
+ return false;
113
+ }
114
+ function completedAt(value) {
115
+ if (typeof value !== "object" || value === null) return void 0;
116
+ const record = value;
117
+ if (record.state !== "completed" || typeof record.exit?.finishedAt !== "string") return void 0;
118
+ const timestamp = Date.parse(record.exit.finishedAt);
119
+ return Number.isFinite(timestamp) ? timestamp : void 0;
120
+ }
121
+ /**
122
+ * Completed records a sweep may delete: everything past the TTL, and everything
123
+ * beyond the newest `keep`.
124
+ *
125
+ * The count bound is what keeps a live session from growing without limit. Its
126
+ * records only reach a multi-day TTL long after the session that is still
127
+ * writing them would have filled its directory, so age alone bounds nothing
128
+ * while the session lasts.
129
+ */
130
+ function expiredCompletedRecords(records, ttlMs, now, keep) {
131
+ return [...records].sort((left, right) => right.finishedAt - left.finishedAt).filter((record, index) => index >= keep || now - record.finishedAt > ttlMs);
132
+ }
133
+ /**
134
+ * Whether the directory holds no files at all, only empty subdirectories.
135
+ *
136
+ * A session that ran something leaves metadata behind, and a live session keeps
137
+ * its lifeline socket in the state directory, so an entirely empty tree names
138
+ * no owner and no history.
139
+ */
140
+ function isEmptySession(directory) {
141
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
142
+ if (!entry.isDirectory()) return false;
143
+ if (fs.readdirSync(path.join(directory, entry.name)).length > 0) return false;
144
+ }
145
+ return true;
146
+ }
147
+ async function isEmptySessionAsync(directory) {
148
+ for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {
149
+ if (!entry.isDirectory()) return false;
150
+ if ((await fs.promises.readdir(path.join(directory, entry.name))).length > 0) return false;
151
+ }
152
+ return true;
153
+ }
154
+ /**
155
+ * The root every session's runner state lives under, before any log-directory
156
+ * override: `<agent dir>/doom-runner/<session id>/{runs,logs}`. A reader in
157
+ * another process, such as the cockpit hub, resolves the same root from its
158
+ * own environment. A relative agent directory resolves against home, not the cwd.
159
+ */
160
+ function resolveRunnerStoreDirectory(env) {
161
+ return path.join(piAgentDirectory(env), STORE_DIR_NAME);
162
+ }
163
+ /** Durable metadata directory for one session. Used by APIs and history reads, not live transport. */
164
+ function runnerStateDirFor(storeDir, sessionId) {
165
+ return path.join(storeDir, sessionId, STATE_DIR_NAME);
166
+ }
167
+ /** Session-scoped runner storage under the Pi agent directory. */
168
+ var RunnerPaths = class {
169
+ cwd;
170
+ env;
171
+ adoptedSessionId;
172
+ repository;
173
+ constructor(cwd = process.cwd(), env = { ...process.env }) {
174
+ this.cwd = cwd;
175
+ this.env = env;
176
+ }
177
+ repositoryPath() {
178
+ this.repository ??= realPath(git(["rev-parse", "--show-toplevel"], this.cwd) ?? this.cwd);
179
+ return this.repository;
180
+ }
181
+ setSessionId(sessionId) {
182
+ const trimmed = sessionId.trim();
183
+ if (!trimmed) throw new Error("doom-runner session id cannot be blank");
184
+ this.adoptedSessionId = trimmed;
185
+ }
186
+ logDirectory(sessionId) {
187
+ const override = this.env[LOG_DIR_ENV]?.trim();
188
+ if (override) return path.resolve(this.cwd, override);
189
+ return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);
190
+ }
191
+ stateDirectory(sessionId) {
192
+ const override = this.env[LOG_DIR_ENV]?.trim();
193
+ if (override) return path.join(path.resolve(this.cwd, override), "..", STATE_DIR_NAME);
194
+ return path.join(this.sessionDirectory(sessionId), STATE_DIR_NAME);
195
+ }
196
+ logPathFor(id, sessionId) {
197
+ return path.join(this.logDirectory(sessionId), `${id}${LOG_EXTENSION}`);
198
+ }
199
+ rotatedLogPathFor(id, sessionId) {
200
+ return `${this.logPathFor(id, sessionId)}.1`;
201
+ }
202
+ rawLogPathFor(id, sessionId) {
203
+ return `${this.logPathFor(id, sessionId)}${RAW_SUFFIX}`;
204
+ }
205
+ statePathFor(id, sessionId) {
206
+ return path.join(this.stateDirectory(sessionId), `${id}${STATE_EXTENSION}`);
207
+ }
208
+ ensureDirectories(sessionId) {
209
+ fs.mkdirSync(this.logDirectory(sessionId), { recursive: true });
210
+ fs.mkdirSync(this.stateDirectory(sessionId), { recursive: true });
211
+ }
212
+ sweepHistory(ttlMs, now = Date.now()) {
213
+ const result = {
214
+ removed: [],
215
+ errors: []
216
+ };
217
+ if (this.env["DOOM_RUNNER_LOG_DIR"]?.trim()) {
218
+ this.sweepStateDirectory(this.stateDirectory(), this.sessionId(), ttlMs, now, result);
219
+ return result;
220
+ }
221
+ const root = this.storeDirectory();
222
+ let sessions;
223
+ try {
224
+ sessions = fs.readdirSync(root);
225
+ } catch (error) {
226
+ if (error.code === "ENOENT") return result;
227
+ result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);
228
+ return result;
229
+ }
230
+ const active = this.currentSessionId();
231
+ for (const sessionId of sessions) {
232
+ if (sessionId === active) {
233
+ this.sweepActiveSession(sessionId, ttlMs, now, result);
234
+ continue;
235
+ }
236
+ this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);
237
+ }
238
+ return result;
239
+ }
240
+ async sweepHistoryAsync(ttlMs, now = Date.now()) {
241
+ const result = {
242
+ removed: [],
243
+ errors: []
244
+ };
245
+ if (this.env["DOOM_RUNNER_LOG_DIR"]?.trim()) {
246
+ await this.sweepStateDirectoryAsync(this.stateDirectory(), this.sessionId(), ttlMs, now, result);
247
+ return result;
248
+ }
249
+ const root = this.storeDirectory();
250
+ let sessions;
251
+ try {
252
+ sessions = await fs.promises.readdir(root);
253
+ } catch (error) {
254
+ if (error.code === "ENOENT") return result;
255
+ result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);
256
+ return result;
257
+ }
258
+ const active = this.currentSessionId();
259
+ for (const sessionId of sessions) {
260
+ if (sessionId === active) {
261
+ await this.sweepActiveSessionAsync(sessionId, ttlMs, now, result);
262
+ continue;
263
+ }
264
+ await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);
265
+ }
266
+ return result;
267
+ }
268
+ legacyDirectory() {
269
+ const commonDirectory = git([
270
+ "rev-parse",
271
+ "--path-format=absolute",
272
+ "--git-common-dir"
273
+ ], this.cwd);
274
+ if (!commonDirectory || !git(["rev-parse", "--show-toplevel"], this.cwd)) return void 0;
275
+ return path.join(realPath(commonDirectory), STORE_DIR_NAME);
276
+ }
277
+ removeLegacyStore() {
278
+ const target = this.legacyDirectory();
279
+ if (!target || path.basename(target) !== "doom-runner" || !fs.existsSync(target)) return void 0;
280
+ fs.rmSync(target, {
281
+ recursive: true,
282
+ force: true
283
+ });
284
+ return target;
285
+ }
286
+ /** The live session, if one is known, so a sweep never deletes underneath itself. */
287
+ currentSessionId() {
288
+ try {
289
+ return this.sessionId();
290
+ } catch {
291
+ return;
292
+ }
293
+ }
294
+ /**
295
+ * Retention inside the session that is still running.
296
+ *
297
+ * The whole-directory sweep cannot be pointed at the live session: it would
298
+ * take the lifeline socket, the supervisor sidecars and the running runners'
299
+ * own metadata with it. Per-record retention is safe there because it only
300
+ * ever removes a record whose state is `completed` together with the two log
301
+ * files that record names, and never the directory itself. A running record
302
+ * has no `exit.finishedAt`, so it is not a candidate at any age.
303
+ */
304
+ sweepActiveSession(sessionId, ttlMs, now, result) {
305
+ this.sweepStateDirectory(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);
306
+ }
307
+ async sweepActiveSessionAsync(sessionId, ttlMs, now, result) {
308
+ await this.sweepStateDirectoryAsync(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);
309
+ }
310
+ /** Removes a session's whole directory once its newest entry is past the TTL. */
311
+ sweepSessionDirectory(directory, ttlMs, now, result) {
312
+ try {
313
+ const newest = newestEntryTime(directory);
314
+ if (now - newest > 3e5 && isEmptySession(directory)) {
315
+ fs.rmSync(directory, {
316
+ recursive: true,
317
+ force: true
318
+ });
319
+ result.removed.push(directory);
320
+ return;
321
+ }
322
+ if (now - newest <= ttlMs) return;
323
+ if (ownerAlive(path.join(directory, "runs"))) return;
324
+ fs.rmSync(directory, {
325
+ recursive: true,
326
+ force: true
327
+ });
328
+ result.removed.push(directory);
329
+ } catch (error) {
330
+ if (error.code !== "ENOENT") result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);
331
+ }
332
+ }
333
+ async sweepSessionDirectoryAsync(directory, ttlMs, now, result) {
334
+ try {
335
+ const newest = await newestEntryTimeAsync(directory);
336
+ if (now - newest > 3e5 && await isEmptySessionAsync(directory)) {
337
+ await fs.promises.rm(directory, {
338
+ recursive: true,
339
+ force: true
340
+ });
341
+ result.removed.push(directory);
342
+ return;
343
+ }
344
+ if (now - newest <= ttlMs) return;
345
+ if (await ownerAliveAsync(path.join(directory, "runs"))) return;
346
+ await fs.promises.rm(directory, {
347
+ recursive: true,
348
+ force: true
349
+ });
350
+ result.removed.push(directory);
351
+ } catch (error) {
352
+ if (error.code !== "ENOENT") result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);
353
+ }
354
+ }
355
+ sessionId(explicit) {
356
+ const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId(this.env);
357
+ if (!sessionId) throw new Error("PI_SESSION_ID is required for doom-runner storage");
358
+ return sessionId;
359
+ }
360
+ storeDirectory() {
361
+ const override = this.env[LOG_DIR_ENV]?.trim();
362
+ if (override) return path.dirname(path.resolve(this.cwd, override));
363
+ return resolveRunnerStoreDirectory(this.env);
364
+ }
365
+ sessionDirectory(sessionId) {
366
+ return path.join(this.storeDirectory(), this.sessionId(sessionId));
367
+ }
368
+ sweepStateDirectory(directory, sessionId, ttlMs, now, result) {
369
+ let entries;
370
+ try {
371
+ entries = fs.readdirSync(directory);
372
+ } catch (error) {
373
+ if (error.code !== "ENOENT") result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);
374
+ return;
375
+ }
376
+ const completed = [];
377
+ for (const entry of entries) {
378
+ if (!entry.endsWith(".json")) continue;
379
+ const statePath = path.join(directory, entry);
380
+ try {
381
+ const finishedAt = completedAt(JSON.parse(fs.readFileSync(statePath, FILE_ENCODING)));
382
+ if (finishedAt === void 0) continue;
383
+ completed.push({
384
+ id: entry.slice(0, -5),
385
+ statePath,
386
+ finishedAt
387
+ });
388
+ } catch (error) {
389
+ result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);
390
+ }
391
+ }
392
+ for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners(this.env))) this.removeHistoryFiles(sessionId, record.id, record.statePath, result);
393
+ }
394
+ async sweepStateDirectoryAsync(directory, sessionId, ttlMs, now, result) {
395
+ let entries;
396
+ try {
397
+ entries = await fs.promises.readdir(directory);
398
+ } catch (error) {
399
+ if (error.code !== "ENOENT") result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);
400
+ return;
401
+ }
402
+ const completed = [];
403
+ for (const entry of entries) {
404
+ if (!entry.endsWith(".json")) continue;
405
+ const statePath = path.join(directory, entry);
406
+ try {
407
+ const finishedAt = completedAt(JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING)));
408
+ if (finishedAt === void 0) continue;
409
+ completed.push({
410
+ id: entry.slice(0, -5),
411
+ statePath,
412
+ finishedAt
413
+ });
414
+ } catch (error) {
415
+ result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);
416
+ }
417
+ }
418
+ for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners(this.env))) await this.removeHistoryFilesAsync(sessionId, record.id, record.statePath, result);
419
+ }
420
+ removeHistoryFiles(sessionId, id, statePath, result) {
421
+ const candidates = [
422
+ statePath,
423
+ this.logPathFor(id, sessionId),
424
+ this.rotatedLogPathFor(id, sessionId),
425
+ this.rawLogPathFor(id, sessionId)
426
+ ];
427
+ for (const candidate of candidates) try {
428
+ fs.unlinkSync(candidate);
429
+ result.removed.push(candidate);
430
+ } catch (error) {
431
+ if (error.code !== "ENOENT") result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);
432
+ }
433
+ }
434
+ async removeHistoryFilesAsync(sessionId, id, statePath, result) {
435
+ const candidates = [
436
+ statePath,
437
+ this.logPathFor(id, sessionId),
438
+ this.rotatedLogPathFor(id, sessionId)
439
+ ];
440
+ for (const candidate of candidates) try {
441
+ await fs.promises.unlink(candidate);
442
+ result.removed.push(candidate);
443
+ } catch (error) {
444
+ if (error.code !== "ENOENT") result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);
445
+ }
446
+ }
447
+ };
448
+ //#endregion
449
+ export { resolveRunnerStoreDirectory as n, runnerStateDirFor as r, RunnerPaths as t };
450
+
451
+ //# sourceMappingURL=runnerPaths-B_dAAeco.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runnerPaths-B_dAAeco.mjs","names":[],"sources":["../src/constants/runnerPaths.ts","../src/services/runnerPaths/index.ts"],"sourcesContent":["export const STORE_DIR_NAME = 'doom-runner';\nexport const LOG_DIR_NAME = 'logs';\nexport const STATE_DIR_NAME = 'runs';\nexport const LOG_EXTENSION = '.log';\nexport const STATE_EXTENSION = '.json';\nexport const ROTATED_SUFFIX = '.1';\nexport const RAW_SUFFIX = '.raw';\nexport const PI_CODING_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nexport const FILE_ENCODING = 'utf8';\nexport const NOT_FOUND_ERROR_CODE = 'ENOENT';\nexport const EMPTY_SESSION_GRACE_MS = 300_000;\n","import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport { LOG_DIR_ENV } from '../../constants/runnerConfig';\nimport {\n EMPTY_SESSION_GRACE_MS,\n FILE_ENCODING,\n LOG_DIR_NAME,\n LOG_EXTENSION,\n NOT_FOUND_ERROR_CODE,\n RAW_SUFFIX,\n ROTATED_SUFFIX,\n STATE_DIR_NAME,\n STATE_EXTENSION,\n STORE_DIR_NAME,\n} from '../../constants/runnerPaths';\nimport { getMaxCompletedRunners } from '../runnerConfig';\nimport { inheritedSessionId } from '../session';\nimport type { IRunnerPaths, LogSweepResult } from './type';\n\n/** Unscrubbed pane bytes, kept beside the log so an attached terminal can replay them. */\n/**\n * How long an entirely empty session directory is left alone before it counts\n * as an orphan. A session that has started but not yet run anything owns such a\n * directory for a moment, and reclaiming it out from under that session would\n * be churn for no gain.\n */\n\nfunction git(args: string[], cwd: string): string | undefined {\n try {\n const output = execFileSync('git', args, { cwd, encoding: FILE_ENCODING, stdio: ['ignore', 'pipe', 'ignore'] });\n const trimmed = output.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction realPath(target: string): string {\n try {\n return fs.realpathSync(target);\n } catch {\n return target;\n }\n}\n\n/**\n * Newest mtime anywhere one level inside the session, falling back to the\n * directory itself. A session still being written to therefore always looks\n * recent, whatever state its individual records are in.\n */\nfunction newestEntryTime(directory: string): number {\n let newest = fs.statSync(directory).mtimeMs;\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, fs.statSync(target).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of fs.readdirSync(target)) {\n newest = Math.max(newest, fs.statSync(path.join(target, child)).mtimeMs);\n }\n }\n return newest;\n}\n\nasync function newestEntryTimeAsync(directory: string): Promise<number> {\n let newest = (await fs.promises.stat(directory)).mtimeMs;\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n const target = path.join(directory, entry.name);\n newest = Math.max(newest, (await fs.promises.stat(target)).mtimeMs);\n if (!entry.isDirectory()) continue;\n for (const child of await fs.promises.readdir(target)) {\n newest = Math.max(newest, (await fs.promises.stat(path.join(target, child))).mtimeMs);\n }\n }\n return newest;\n}\n\n/**\n * Whether any run in this session names a pi process that is still running.\n *\n * One live record is enough: records are only ever written by their owner, so a\n * live host pid means the session is still in use however old its files look.\n */\nfunction ownerAlive(stateDirectory: string): boolean {\n let entries: string[];\n try {\n entries = fs.readdirSync(stateDirectory);\n } catch {\n // No records left to consult, so nothing claims the directory.\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(fs.readFileSync(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nasync function ownerAliveAsync(stateDirectory: string): Promise<boolean> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(stateDirectory);\n } catch {\n return false;\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(path.join(stateDirectory, entry), FILE_ENCODING));\n const hostPid = (value as { hostPid?: unknown }).hostPid;\n if (typeof hostPid !== 'number') continue;\n process.kill(hostPid, 0);\n return true;\n } catch {\n // An unreadable record and a dead pid mean the same thing here: no claim.\n }\n }\n return false;\n}\n\nfunction completedAt(value: unknown): number | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as { state?: unknown; exit?: { finishedAt?: unknown } };\n if (record.state !== 'completed' || typeof record.exit?.finishedAt !== 'string') return undefined;\n const timestamp = Date.parse(record.exit.finishedAt);\n return Number.isFinite(timestamp) ? timestamp : undefined;\n}\n\ninterface CompletedRecord {\n readonly id: string;\n readonly statePath: string;\n readonly finishedAt: number;\n}\n\n/**\n * Completed records a sweep may delete: everything past the TTL, and everything\n * beyond the newest `keep`.\n *\n * The count bound is what keeps a live session from growing without limit. Its\n * records only reach a multi-day TTL long after the session that is still\n * writing them would have filled its directory, so age alone bounds nothing\n * while the session lasts.\n */\nfunction expiredCompletedRecords(\n records: readonly CompletedRecord[],\n ttlMs: number,\n now: number,\n keep: number,\n): CompletedRecord[] {\n return [...records]\n .sort((left, right) => right.finishedAt - left.finishedAt)\n .filter((record, index) => index >= keep || now - record.finishedAt > ttlMs);\n}\n\n/**\n * Whether the directory holds no files at all, only empty subdirectories.\n *\n * A session that ran something leaves metadata behind, and a live session keeps\n * its lifeline socket in the state directory, so an entirely empty tree names\n * no owner and no history.\n */\nfunction isEmptySession(directory: string): boolean {\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if (fs.readdirSync(path.join(directory, entry.name)).length > 0) return false;\n }\n return true;\n}\n\nasync function isEmptySessionAsync(directory: string): Promise<boolean> {\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if ((await fs.promises.readdir(path.join(directory, entry.name))).length > 0) return false;\n }\n return true;\n}\n\n/**\n * The root every session's runner state lives under, before any log-directory\n * override: `<agent dir>/doom-runner/<session id>/{runs,logs}`. A reader in\n * another process, such as the cockpit hub, resolves the same root from its\n * own environment. A relative agent directory resolves against home, not the cwd.\n */\nexport function resolveRunnerStoreDirectory(env: Readonly<Record<string, string | undefined>>): string {\n return path.join(piAgentDirectory(env), STORE_DIR_NAME);\n}\n\n/** Durable metadata directory for one session. Used by APIs and history reads, not live transport. */\nexport function runnerStateDirFor(storeDir: string, sessionId: string): string {\n return path.join(storeDir, sessionId, STATE_DIR_NAME);\n}\n\n/** Session-scoped runner storage under the Pi agent directory. */\nexport class RunnerPaths implements IRunnerPaths {\n private adoptedSessionId: string | undefined;\n private repository: string | undefined;\n\n constructor(\n private readonly cwd: string = process.cwd(),\n private readonly env: Readonly<Record<string, string | undefined>> = { ...process.env },\n ) {}\n\n repositoryPath(): string {\n this.repository ??= realPath(git(['rev-parse', '--show-toplevel'], this.cwd) ?? this.cwd);\n return this.repository;\n }\n\n setSessionId(sessionId: string): void {\n const trimmed = sessionId.trim();\n if (!trimmed) throw new Error('doom-runner session id cannot be blank');\n this.adoptedSessionId = trimmed;\n }\n\n logDirectory(sessionId?: string): string {\n const override = this.env[LOG_DIR_ENV]?.trim();\n if (override) return path.resolve(this.cwd, override);\n return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);\n }\n\n stateDirectory(sessionId?: string): string {\n const override = this.env[LOG_DIR_ENV]?.trim();\n if (override) return path.join(path.resolve(this.cwd, override), '..', STATE_DIR_NAME);\n return path.join(this.sessionDirectory(sessionId), STATE_DIR_NAME);\n }\n\n logPathFor(id: string, sessionId?: string): string {\n return path.join(this.logDirectory(sessionId), `${id}${LOG_EXTENSION}`);\n }\n\n rotatedLogPathFor(id: string, sessionId?: string): string {\n return `${this.logPathFor(id, sessionId)}${ROTATED_SUFFIX}`;\n }\n\n rawLogPathFor(id: string, sessionId?: string): string {\n return `${this.logPathFor(id, sessionId)}${RAW_SUFFIX}`;\n }\n\n statePathFor(id: string, sessionId?: string): string {\n return path.join(this.stateDirectory(sessionId), `${id}${STATE_EXTENSION}`);\n }\n\n ensureDirectories(sessionId?: string): void {\n fs.mkdirSync(this.logDirectory(sessionId), { recursive: true });\n fs.mkdirSync(this.stateDirectory(sessionId), { recursive: true });\n }\n\n sweepHistory(ttlMs: number, now: number = Date.now()): LogSweepResult {\n const result: LogSweepResult = { removed: [], errors: [] };\n // A pinned log directory belongs to whoever pinned it, so that branch only\n // ever removes the files this package put there.\n if (this.env[LOG_DIR_ENV]?.trim()) {\n this.sweepStateDirectory(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = fs.readdirSync(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n // Sweeping whole session directories rather than the three files a completed\n // record names is what keeps sockets, sidecars, runs that never reached a\n // terminal state, and the emptied directories themselves from accumulating.\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) {\n this.sweepActiveSession(sessionId, ttlMs, now, result);\n continue;\n }\n this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n async sweepHistoryAsync(ttlMs: number, now: number = Date.now()): Promise<LogSweepResult> {\n const result: LogSweepResult = { removed: [], errors: [] };\n if (this.env[LOG_DIR_ENV]?.trim()) {\n await this.sweepStateDirectoryAsync(this.stateDirectory(), this.sessionId(), ttlMs, now, result);\n return result;\n }\n\n const root = this.storeDirectory();\n let sessions: string[];\n try {\n sessions = await fs.promises.readdir(root);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === NOT_FOUND_ERROR_CODE) return result;\n result.errors.push(`Could not read runner history directory ${root}: ${String(error)}`);\n return result;\n }\n\n const active = this.currentSessionId();\n for (const sessionId of sessions) {\n if (sessionId === active) {\n await this.sweepActiveSessionAsync(sessionId, ttlMs, now, result);\n continue;\n }\n await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n legacyDirectory(): string | undefined {\n const commonDirectory = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], this.cwd);\n if (!commonDirectory || !git(['rev-parse', '--show-toplevel'], this.cwd)) return undefined;\n return path.join(realPath(commonDirectory), STORE_DIR_NAME);\n }\n\n removeLegacyStore(): string | undefined {\n const target = this.legacyDirectory();\n if (!target || path.basename(target) !== STORE_DIR_NAME || !fs.existsSync(target)) return undefined;\n fs.rmSync(target, { recursive: true, force: true });\n return target;\n }\n\n /** The live session, if one is known, so a sweep never deletes underneath itself. */\n private currentSessionId(): string | undefined {\n try {\n return this.sessionId();\n } catch {\n // A CLI invocation outside a session still sweeps; it just has nothing to spare.\n return undefined;\n }\n }\n\n /**\n * Retention inside the session that is still running.\n *\n * The whole-directory sweep cannot be pointed at the live session: it would\n * take the lifeline socket, the supervisor sidecars and the running runners'\n * own metadata with it. Per-record retention is safe there because it only\n * ever removes a record whose state is `completed` together with the two log\n * files that record names, and never the directory itself. A running record\n * has no `exit.finishedAt`, so it is not a candidate at any age.\n */\n private sweepActiveSession(sessionId: string, ttlMs: number, now: number, result: LogSweepResult): void {\n this.sweepStateDirectory(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n private async sweepActiveSessionAsync(\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n await this.sweepStateDirectoryAsync(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n /** Removes a session's whole directory once its newest entry is past the TTL. */\n private sweepSessionDirectory(directory: string, ttlMs: number, now: number, result: LogSweepResult): void {\n try {\n const newest = newestEntryTime(directory);\n // An orphan left by a session that never ran anything holds nothing worth\n // a retention window, so it goes as soon as it is clearly not in use.\n if (now - newest > EMPTY_SESSION_GRACE_MS && isEmptySession(directory)) {\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n // An idle session can go quiet for longer than the TTL, and deleting one\n // takes its lifeline socket with it, which would read to its own runners\n // as an owner that had died. Its records name the owner, so ask.\n if (ownerAlive(path.join(directory, STATE_DIR_NAME))) return;\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private async sweepSessionDirectoryAsync(\n directory: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n try {\n const newest = await newestEntryTimeAsync(directory);\n if (now - newest > EMPTY_SESSION_GRACE_MS && (await isEmptySessionAsync(directory))) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n if (await ownerAliveAsync(path.join(directory, STATE_DIR_NAME))) return;\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private sessionId(explicit?: string): string {\n const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId(this.env);\n if (!sessionId) throw new Error('PI_SESSION_ID is required for doom-runner storage');\n return sessionId;\n }\n\n private storeDirectory(): string {\n const override = this.env[LOG_DIR_ENV]?.trim();\n if (override) return path.dirname(path.resolve(this.cwd, override));\n return resolveRunnerStoreDirectory(this.env);\n }\n\n private sessionDirectory(sessionId?: string): string {\n return path.join(this.storeDirectory(), this.sessionId(sessionId));\n }\n\n private sweepStateDirectory(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): void {\n let entries: string[];\n try {\n entries = fs.readdirSync(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(fs.readFileSync(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners(this.env))) {\n this.removeHistoryFiles(sessionId, record.id, record.statePath, result);\n }\n }\n\n private async sweepStateDirectoryAsync(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners(this.env))) {\n await this.removeHistoryFilesAsync(sessionId, record.id, record.statePath, result);\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [\n statePath,\n this.logPathFor(id, sessionId),\n this.rotatedLogPathFor(id, sessionId),\n this.rawLogPathFor(id, sessionId),\n ];\n for (const candidate of candidates) {\n try {\n fs.unlinkSync(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n\n private async removeHistoryFilesAsync(\n sessionId: string,\n id: string,\n statePath: string,\n result: LogSweepResult,\n ): Promise<void> {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\n for (const candidate of candidates) {\n try {\n await fs.promises.unlink(candidate);\n result.removed.push(candidate);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not remove runner history ${candidate}: ${String(error)}`);\n }\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runnerPaths = require("./runnerPaths-BTEuzxw0.cjs");
3
+ exports.RunnerPaths = require_runnerPaths.RunnerPaths;
@@ -0,0 +1,50 @@
1
+ import { n as LogSweepResult, t as IRunnerPaths } from "./type-BXM03qSw.mjs";
2
+ //#region src/services/runnerPaths/index.d.ts
3
+ /** Session-scoped runner storage under the Pi agent directory. */
4
+ export declare class RunnerPaths implements IRunnerPaths {
5
+ private readonly cwd;
6
+ private readonly env;
7
+ private adoptedSessionId;
8
+ private repository;
9
+ constructor(cwd?: string, env?: Readonly<Record<string, string | undefined>>);
10
+ repositoryPath(): string;
11
+ setSessionId(sessionId: string): void;
12
+ logDirectory(sessionId?: string): string;
13
+ stateDirectory(sessionId?: string): string;
14
+ logPathFor(id: string, sessionId?: string): string;
15
+ rotatedLogPathFor(id: string, sessionId?: string): string;
16
+ rawLogPathFor(id: string, sessionId?: string): string;
17
+ statePathFor(id: string, sessionId?: string): string;
18
+ ensureDirectories(sessionId?: string): void;
19
+ sweepHistory(ttlMs: number, now?: number): LogSweepResult;
20
+ sweepHistoryAsync(ttlMs: number, now?: number): Promise<LogSweepResult>;
21
+ legacyDirectory(): string | undefined;
22
+ removeLegacyStore(): string | undefined;
23
+ /** The live session, if one is known, so a sweep never deletes underneath itself. */
24
+ private currentSessionId;
25
+ /**
26
+ * Retention inside the session that is still running.
27
+ *
28
+ * The whole-directory sweep cannot be pointed at the live session: it would
29
+ * take the lifeline socket, the supervisor sidecars and the running runners'
30
+ * own metadata with it. Per-record retention is safe there because it only
31
+ * ever removes a record whose state is `completed` together with the two log
32
+ * files that record names, and never the directory itself. A running record
33
+ * has no `exit.finishedAt`, so it is not a candidate at any age.
34
+ */
35
+ private sweepActiveSession;
36
+ private sweepActiveSessionAsync;
37
+ /** Removes a session's whole directory once its newest entry is past the TTL. */
38
+ private sweepSessionDirectory;
39
+ private sweepSessionDirectoryAsync;
40
+ private sessionId;
41
+ private storeDirectory;
42
+ private sessionDirectory;
43
+ private sweepStateDirectory;
44
+ private sweepStateDirectoryAsync;
45
+ private removeHistoryFiles;
46
+ private removeHistoryFilesAsync;
47
+ }
48
+ //#endregion
49
+ export type { IRunnerPaths, LogSweepResult };
50
+ //# sourceMappingURL=runnerPaths.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runnerPaths.d.mts","names":[],"sources":["../src/services/runnerPaths/index.ts"],"mappings":";;;qBA8Ma,uBAAuB;mBAKf;mBACA;UALX;UACA;EAER,YACmB,cACA,MAAK,SAAS;EAGjC;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,cAAc,YAAY;EAI1B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EAgCjD,kBAAkB,eAAe,eAA2B,QAAQ;EA4B1E;EAMA;;UAQQ;;;;;;;;;;;UAmBA;UAIM;;UAUN;UAwBM;UAwBN;UAMA;UAMA;UAIA;UAoCM;UAoCN;UAmBM"}
@@ -0,0 +1,2 @@
1
+ import { t as RunnerPaths } from "./runnerPaths-B_dAAeco.mjs";
2
+ export { RunnerPaths };