@agimon-ai/doompi-runner 0.0.1-alpha.37 → 0.0.1-alpha.38

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 (131) hide show
  1. package/README.md +15 -4
  2. package/dist/sessionApi.cjs +1 -0
  3. package/dist/sessionApi.d.cts +2 -0
  4. package/dist/sessionApi.d.mts +2 -0
  5. package/dist/sessionApi.mjs +1 -0
  6. package/dist/src/adapters/LogTail/LogTail.cjs +3 -0
  7. package/dist/src/adapters/LogTail/LogTail.cjs.map +1 -0
  8. package/dist/src/adapters/LogTail/LogTail.mjs +3 -0
  9. package/dist/src/adapters/LogTail/LogTail.mjs.map +1 -0
  10. package/dist/src/adapters/PtyBackendChain/PtyBackendChain.cjs +2 -0
  11. package/dist/src/adapters/PtyBackendChain/PtyBackendChain.cjs.map +1 -0
  12. package/dist/src/adapters/PtyBackendChain/PtyBackendChain.mjs +2 -0
  13. package/dist/src/adapters/PtyBackendChain/PtyBackendChain.mjs.map +1 -0
  14. package/dist/src/adapters/RtkProcessor/RtkProcessor.cjs +1 -1
  15. package/dist/src/adapters/RtkProcessor/RtkProcessor.cjs.map +1 -1
  16. package/dist/src/adapters/RtkProcessor/RtkProcessor.d.cts +9 -0
  17. package/dist/src/adapters/RtkProcessor/RtkProcessor.d.cts.map +1 -1
  18. package/dist/src/adapters/RtkProcessor/RtkProcessor.d.mts +9 -0
  19. package/dist/src/adapters/RtkProcessor/RtkProcessor.d.mts.map +1 -1
  20. package/dist/src/adapters/RtkProcessor/RtkProcessor.mjs +1 -1
  21. package/dist/src/adapters/RtkProcessor/RtkProcessor.mjs.map +1 -1
  22. package/dist/src/adapters/RunnerPaths.cjs +1 -1
  23. package/dist/src/adapters/RunnerPaths.cjs.map +1 -1
  24. package/dist/src/adapters/RunnerPaths.d.cts.map +1 -1
  25. package/dist/src/adapters/RunnerPaths.d.mts.map +1 -1
  26. package/dist/src/adapters/RunnerPaths.mjs +1 -1
  27. package/dist/src/adapters/RunnerPaths.mjs.map +1 -1
  28. package/dist/src/adapters/RunnerRegistry/RunnerRegistry.cjs +1 -1
  29. package/dist/src/adapters/RunnerRegistry/RunnerRegistry.cjs.map +1 -1
  30. package/dist/src/adapters/RunnerRegistry/RunnerRegistry.d.cts.map +1 -1
  31. package/dist/src/adapters/RunnerRegistry/RunnerRegistry.d.mts.map +1 -1
  32. package/dist/src/adapters/RunnerRegistry/RunnerRegistry.mjs +1 -1
  33. package/dist/src/adapters/RunnerRegistry/RunnerRegistry.mjs.map +1 -1
  34. package/dist/src/adapters/TmuxBackend/TmuxBackend.cjs +2 -0
  35. package/dist/src/adapters/TmuxBackend/TmuxBackend.cjs.map +1 -0
  36. package/dist/src/adapters/TmuxBackend/TmuxBackend.mjs +2 -0
  37. package/dist/src/adapters/TmuxBackend/TmuxBackend.mjs.map +1 -0
  38. package/dist/src/adapters/TmuxBackend/TmuxClient.cjs +2 -0
  39. package/dist/src/adapters/TmuxBackend/TmuxClient.cjs.map +1 -0
  40. package/dist/src/adapters/TmuxBackend/TmuxClient.mjs +2 -0
  41. package/dist/src/adapters/TmuxBackend/TmuxClient.mjs.map +1 -0
  42. package/dist/src/adapters/pi/extension.cjs +2 -2
  43. package/dist/src/adapters/pi/extension.cjs.map +1 -1
  44. package/dist/src/adapters/pi/extension.d.cts.map +1 -1
  45. package/dist/src/adapters/pi/extension.d.mts.map +1 -1
  46. package/dist/src/adapters/pi/extension.mjs +2 -2
  47. package/dist/src/adapters/pi/extension.mjs.map +1 -1
  48. package/dist/src/adapters/runnerLogApi.cjs +2 -0
  49. package/dist/src/adapters/runnerLogApi.cjs.map +1 -0
  50. package/dist/src/adapters/runnerLogApi.d.cts +28 -0
  51. package/dist/src/adapters/runnerLogApi.d.cts.map +1 -0
  52. package/dist/src/adapters/runnerLogApi.d.mts +28 -0
  53. package/dist/src/adapters/runnerLogApi.d.mts.map +1 -0
  54. package/dist/src/adapters/runnerLogApi.mjs +2 -0
  55. package/dist/src/adapters/runnerLogApi.mjs.map +1 -0
  56. package/dist/src/adapters/webRunnerWatcher.cjs +2 -0
  57. package/dist/src/adapters/webRunnerWatcher.cjs.map +1 -0
  58. package/dist/src/adapters/webRunnerWatcher.d.cts +22 -0
  59. package/dist/src/adapters/webRunnerWatcher.d.cts.map +1 -0
  60. package/dist/src/adapters/webRunnerWatcher.d.mts +22 -0
  61. package/dist/src/adapters/webRunnerWatcher.d.mts.map +1 -0
  62. package/dist/src/adapters/webRunnerWatcher.mjs +2 -0
  63. package/dist/src/adapters/webRunnerWatcher.mjs.map +1 -0
  64. package/dist/src/adapters/webRunnersChannel.cjs +2 -0
  65. package/dist/src/adapters/webRunnersChannel.cjs.map +1 -0
  66. package/dist/src/adapters/webRunnersChannel.d.cts +15 -0
  67. package/dist/src/adapters/webRunnersChannel.d.cts.map +1 -0
  68. package/dist/src/adapters/webRunnersChannel.d.mts +15 -0
  69. package/dist/src/adapters/webRunnersChannel.d.mts.map +1 -0
  70. package/dist/src/adapters/webRunnersChannel.mjs +2 -0
  71. package/dist/src/adapters/webRunnersChannel.mjs.map +1 -0
  72. package/dist/src/commands/cli/cliApp.cjs +2 -2
  73. package/dist/src/commands/cli/cliApp.cjs.map +1 -1
  74. package/dist/src/commands/cli/cliApp.mjs +2 -2
  75. package/dist/src/commands/cli/cliApp.mjs.map +1 -1
  76. package/dist/src/container/index.cjs +1 -1
  77. package/dist/src/container/index.cjs.map +1 -1
  78. package/dist/src/container/index.d.cts.map +1 -1
  79. package/dist/src/container/index.d.mts.map +1 -1
  80. package/dist/src/container/index.mjs +1 -1
  81. package/dist/src/container/index.mjs.map +1 -1
  82. package/dist/src/services/runs/reconcile.cjs +1 -1
  83. package/dist/src/services/runs/reconcile.cjs.map +1 -1
  84. package/dist/src/services/runs/reconcile.d.cts.map +1 -1
  85. package/dist/src/services/runs/reconcile.d.mts.map +1 -1
  86. package/dist/src/services/runs/reconcile.mjs +1 -1
  87. package/dist/src/services/runs/reconcile.mjs.map +1 -1
  88. package/dist/src/services/runs/runnerRecord.cjs +2 -0
  89. package/dist/src/services/runs/runnerRecord.cjs.map +1 -0
  90. package/dist/src/services/runs/runnerRecord.mjs +2 -0
  91. package/dist/src/services/runs/runnerRecord.mjs.map +1 -0
  92. package/dist/src/services/runs/runnersCommand.cjs +2 -0
  93. package/dist/src/services/runs/runnersCommand.cjs.map +1 -0
  94. package/dist/src/services/runs/runnersCommand.mjs +2 -0
  95. package/dist/src/services/runs/runnersCommand.mjs.map +1 -0
  96. package/dist/src/services/webRunnerRuns.cjs +2 -0
  97. package/dist/src/services/webRunnerRuns.cjs.map +1 -0
  98. package/dist/src/services/webRunnerRuns.mjs +2 -0
  99. package/dist/src/services/webRunnerRuns.mjs.map +1 -0
  100. package/dist/src/types/logTail.d.cts +23 -0
  101. package/dist/src/types/logTail.d.cts.map +1 -0
  102. package/dist/src/types/logTail.d.mts +23 -0
  103. package/dist/src/types/logTail.d.mts.map +1 -0
  104. package/dist/src/types/webRunnerLog.cjs +2 -0
  105. package/dist/src/types/webRunnerLog.cjs.map +1 -0
  106. package/dist/src/types/webRunnerLog.mjs +2 -0
  107. package/dist/src/types/webRunnerLog.mjs.map +1 -0
  108. package/dist/src/types/webRunners.cjs +2 -0
  109. package/dist/src/types/webRunners.cjs.map +1 -0
  110. package/dist/src/types/webRunners.d.cts +38 -0
  111. package/dist/src/types/webRunners.d.cts.map +1 -0
  112. package/dist/src/types/webRunners.d.mts +38 -0
  113. package/dist/src/types/webRunners.d.mts.map +1 -0
  114. package/dist/src/types/webRunners.mjs +2 -0
  115. package/dist/src/types/webRunners.mjs.map +1 -0
  116. package/dist/webHub.cjs +1 -0
  117. package/dist/webHub.d.cts +2 -0
  118. package/dist/webHub.d.mts +2 -0
  119. package/dist/webHub.mjs +1 -0
  120. package/package.json +56 -18
  121. package/src/types/webRunnerLog.ts +95 -0
  122. package/src/types/webRunners.ts +39 -0
  123. package/web/BashToolMessage.tsx +156 -0
  124. package/web/RunnerLogPanel.tsx +263 -0
  125. package/web/RunnersActivitySection.tsx +107 -0
  126. package/web/bashToolFormat.ts +216 -0
  127. package/web/format.ts +42 -0
  128. package/web/index.ts +21 -0
  129. package/web/logApi.ts +81 -0
  130. package/web/runnersStore.ts +50 -0
  131. package/web/tsconfig.json +23 -0
package/README.md CHANGED
@@ -12,12 +12,23 @@ inline; long commands can be promoted to a background runner instead of blocking
12
12
  ## Requirements
13
13
 
14
14
  - Node.js 22.19.0 or newer
15
- - Pi 0.84.2 and Pi TUI 0.84.2
15
+ - Pi 0.84.3 and Pi TUI 0.84.3
16
16
  - `/bin/bash`
17
17
  - macOS or Linux on arm64 or x64 for bundled RMUX and RTK support
18
18
 
19
- Linux native loading also depends on a compatible system loader and libc. Non-interactive commands
20
- fall back to a supervised subprocess when RMUX is unavailable. Interactive commands require RMUX.
19
+ Linux native loading also depends on a compatible system loader and libc. The bundled binaries are
20
+ glibc-linked, so a musl host such as Alpine cannot run them.
21
+
22
+ RTK is optional. Where its binary is absent or cannot start, runner results carry the raw log with
23
+ an "RTK is unavailable" note instead of processed output. That verdict is reached once per process
24
+ rather than retried per command, so an unusable binary costs one failed start rather than one on
25
+ every run.
26
+
27
+ Panes come from the first backend that answers: bundled RMUX, then `rmux` on PATH, then `tmux` on
28
+ PATH. Only when none is available does a non-interactive command fall back to a supervised
29
+ subprocess, and interactive commands need one of them. A tmux-backed run is supervised by the same
30
+ host process as an RMUX one, so exit metadata and logs behave the same; its session names carry a
31
+ `doom-tmux-` prefix rather than `doom-runner-`.
21
32
 
22
33
  ## Install
23
34
 
@@ -70,7 +81,7 @@ doom-runner stop <runner-id>
70
81
  doom-runner stop-all
71
82
  ```
72
83
 
73
- The `input` command requires a running interactive process backed by RMUX.
84
+ The `input` command requires a running interactive process backed by RMUX or tmux.
74
85
 
75
86
  Stop runs that are no longer required.
76
87
 
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src/adapters/runnerLogApi.cjs");exports.api=e.api,exports.createRunnerLogApi=e.createRunnerLogApi;
@@ -0,0 +1,2 @@
1
+ import { api, createRunnerLogApi } from "./src/adapters/runnerLogApi.cjs";
2
+ export { api, createRunnerLogApi };
@@ -0,0 +1,2 @@
1
+ import { api, createRunnerLogApi } from "./src/adapters/runnerLogApi.mjs";
2
+ export { api, createRunnerLogApi };
@@ -0,0 +1 @@
1
+ import{api as e,createRunnerLogApi as t}from"./src/adapters/runnerLogApi.mjs";export{e as api,t as createRunnerLogApi};
@@ -0,0 +1,3 @@
1
+ const e=require("../../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);var n=class{follow(e,n){let r=n.from,i=``,a=!1,o=a=>{if(a<r&&(r=0,i=``),a===r)return;let o=a-r,s=Buffer.alloc(o),c=t.default.openSync(e,`r`),l;try{l=t.default.readSync(c,s,0,o,r)}finally{t.default.closeSync(c)}if(r=a,l<=0)return;i+=s.subarray(0,l).toString(`utf8`);let u=i.split(`
2
+ `);i=u.pop()??``,u.length>0&&n.onLines(u)},s=setInterval(()=>{if(!a)try{o(t.default.statSync(e).size)}catch(e){if(e.code===`ENOENT`)return;n.onError(e instanceof Error?e:Error(String(e)))}},250);return s.unref?.(),{close(){a||(a=!0,clearInterval(s))}}}};exports.LogTail=n;
3
+ //# sourceMappingURL=LogTail.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogTail.cjs","names":["fs"],"sources":["../../../../src/adapters/LogTail/LogTail.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { ILogTail, LogTailHandle, LogTailOptions } from '../../types/logTail.ts';\n\n/** How often the tail restats the file; the same cadence the CLI's --follow uses. */\nconst POLL_MS = 250;\n\n/**\n * Polling tail: read the delta between the last offset and the file's current\n * size, and hand back only complete lines.\n *\n * Same reliability posture as this package's runner watcher: the poll is the\n * mechanism, not an accelerator for a change notification. fs.watchFile only\n * fires when it observes a difference from a baseline it took itself, so a\n * write that lands before its first stat is a change it never reports; a\n * restat every tick has no such window.\n *\n * A write that lands mid-line is common, so the trailing fragment is held\n * until its newline arrives rather than emitted as a line that will change.\n */\nexport class LogTail implements ILogTail {\n follow(logPath: string, options: LogTailOptions): LogTailHandle {\n let offset = options.from;\n let pending = '';\n let closed = false;\n\n const drain = (size: number): void => {\n // A shrunken file was rotated or truncated; start over rather than\n // reading from an offset that now points into different content.\n if (size < offset) {\n offset = 0;\n pending = '';\n }\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(logPath, 'r');\n let bytesRead: number;\n try {\n bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n offset = size;\n if (bytesRead <= 0) return;\n pending += buffer.subarray(0, bytesRead).toString('utf8');\n const lines = pending.split('\\n');\n pending = lines.pop() ?? '';\n if (lines.length > 0) options.onLines(lines);\n };\n\n const tick = (): void => {\n if (closed) return;\n try {\n drain(fs.statSync(logPath).size);\n } catch (error) {\n // The runner has not opened its log yet; the next tick settles it.\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n const timer = setInterval(tick, POLL_MS);\n // Following a log must never be the reason a process stays alive.\n timer.unref?.();\n return {\n close() {\n if (closed) return;\n closed = true;\n clearInterval(timer);\n },\n };\n }\n}\n"],"mappings":"qGAmBA,IAAa,EAAb,KAAyC,CACvC,OAAO,EAAiB,EAAwC,CAC9D,IAAI,EAAS,EAAQ,KACjB,EAAU,GACV,EAAS,GAEP,EAAS,GAAuB,CAOpC,GAJI,EAAO,IACT,EAAS,EACT,EAAU,IAER,IAAS,EAAQ,OACrB,IAAM,EAAS,EAAO,EAChB,EAAS,OAAO,MAAM,CAAM,EAC5B,EAAaA,EAAAA,QAAG,SAAS,EAAS,GAAG,EACvC,EACJ,GAAI,CACF,EAAYA,EAAAA,QAAG,SAAS,EAAY,EAAQ,EAAG,EAAQ,CAAM,CAC/D,QAAU,CACR,EAAA,QAAG,UAAU,CAAU,CACzB,CAEA,GADA,EAAS,EACL,GAAa,EAAG,OACpB,GAAW,EAAO,SAAS,EAAG,CAAS,CAAC,CAAC,SAAS,MAAM,EACxD,IAAM,EAAQ,EAAQ,MAAM;CAAI,EAChC,EAAU,EAAM,IAAI,GAAK,GACrB,EAAM,OAAS,GAAG,EAAQ,QAAQ,CAAK,CAC7C,EAaM,EAAQ,gBAXW,CACnB,MACJ,GAAI,CACF,EAAMA,EAAAA,QAAG,SAAS,CAAO,CAAC,CAAC,IAAI,CACjC,OAAS,EAAO,CAEd,GAAK,EAAgC,OAAS,SAAU,OACxD,EAAQ,QAAQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAAC,CAC3E,CACF,EAEgC,GAAO,EAGvC,OADA,EAAM,QAAQ,EACP,CACL,OAAQ,CACF,IACJ,EAAS,GACT,cAAc,CAAK,EACrB,CACF,CACF,CACF"}
@@ -0,0 +1,3 @@
1
+ import e from"node:fs";var t=class{follow(t,n){let r=n.from,i=``,a=!1,o=a=>{if(a<r&&(r=0,i=``),a===r)return;let o=a-r,s=Buffer.alloc(o),c=e.openSync(t,`r`),l;try{l=e.readSync(c,s,0,o,r)}finally{e.closeSync(c)}if(r=a,l<=0)return;i+=s.subarray(0,l).toString(`utf8`);let u=i.split(`
2
+ `);i=u.pop()??``,u.length>0&&n.onLines(u)},s=setInterval(()=>{if(!a)try{o(e.statSync(t).size)}catch(e){if(e.code===`ENOENT`)return;n.onError(e instanceof Error?e:Error(String(e)))}},250);return s.unref?.(),{close(){a||(a=!0,clearInterval(s))}}}};export{t as LogTail};
3
+ //# sourceMappingURL=LogTail.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogTail.mjs","names":[],"sources":["../../../../src/adapters/LogTail/LogTail.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { ILogTail, LogTailHandle, LogTailOptions } from '../../types/logTail.ts';\n\n/** How often the tail restats the file; the same cadence the CLI's --follow uses. */\nconst POLL_MS = 250;\n\n/**\n * Polling tail: read the delta between the last offset and the file's current\n * size, and hand back only complete lines.\n *\n * Same reliability posture as this package's runner watcher: the poll is the\n * mechanism, not an accelerator for a change notification. fs.watchFile only\n * fires when it observes a difference from a baseline it took itself, so a\n * write that lands before its first stat is a change it never reports; a\n * restat every tick has no such window.\n *\n * A write that lands mid-line is common, so the trailing fragment is held\n * until its newline arrives rather than emitted as a line that will change.\n */\nexport class LogTail implements ILogTail {\n follow(logPath: string, options: LogTailOptions): LogTailHandle {\n let offset = options.from;\n let pending = '';\n let closed = false;\n\n const drain = (size: number): void => {\n // A shrunken file was rotated or truncated; start over rather than\n // reading from an offset that now points into different content.\n if (size < offset) {\n offset = 0;\n pending = '';\n }\n if (size === offset) return;\n const length = size - offset;\n const buffer = Buffer.alloc(length);\n const descriptor = fs.openSync(logPath, 'r');\n let bytesRead: number;\n try {\n bytesRead = fs.readSync(descriptor, buffer, 0, length, offset);\n } finally {\n fs.closeSync(descriptor);\n }\n offset = size;\n if (bytesRead <= 0) return;\n pending += buffer.subarray(0, bytesRead).toString('utf8');\n const lines = pending.split('\\n');\n pending = lines.pop() ?? '';\n if (lines.length > 0) options.onLines(lines);\n };\n\n const tick = (): void => {\n if (closed) return;\n try {\n drain(fs.statSync(logPath).size);\n } catch (error) {\n // The runner has not opened its log yet; the next tick settles it.\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n options.onError(error instanceof Error ? error : new Error(String(error)));\n }\n };\n\n const timer = setInterval(tick, POLL_MS);\n // Following a log must never be the reason a process stays alive.\n timer.unref?.();\n return {\n close() {\n if (closed) return;\n closed = true;\n clearInterval(timer);\n },\n };\n }\n}\n"],"mappings":"uBAmBA,IAAa,EAAb,KAAyC,CACvC,OAAO,EAAiB,EAAwC,CAC9D,IAAI,EAAS,EAAQ,KACjB,EAAU,GACV,EAAS,GAEP,EAAS,GAAuB,CAOpC,GAJI,EAAO,IACT,EAAS,EACT,EAAU,IAER,IAAS,EAAQ,OACrB,IAAM,EAAS,EAAO,EAChB,EAAS,OAAO,MAAM,CAAM,EAC5B,EAAa,EAAG,SAAS,EAAS,GAAG,EACvC,EACJ,GAAI,CACF,EAAY,EAAG,SAAS,EAAY,EAAQ,EAAG,EAAQ,CAAM,CAC/D,QAAU,CACR,EAAG,UAAU,CAAU,CACzB,CAEA,GADA,EAAS,EACL,GAAa,EAAG,OACpB,GAAW,EAAO,SAAS,EAAG,CAAS,CAAC,CAAC,SAAS,MAAM,EACxD,IAAM,EAAQ,EAAQ,MAAM;CAAI,EAChC,EAAU,EAAM,IAAI,GAAK,GACrB,EAAM,OAAS,GAAG,EAAQ,QAAQ,CAAK,CAC7C,EAaM,EAAQ,gBAXW,CACnB,MACJ,GAAI,CACF,EAAM,EAAG,SAAS,CAAO,CAAC,CAAC,IAAI,CACjC,OAAS,EAAO,CAEd,GAAK,EAAgC,OAAS,SAAU,OACxD,EAAQ,QAAQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAAC,CAC3E,CACF,EAEgC,GAAO,EAGvC,OADA,EAAM,QAAQ,EACP,CACL,OAAQ,CACF,IACJ,EAAS,GACT,cAAc,CAAK,EACrB,CACF,CACF,CACF"}
@@ -0,0 +1,2 @@
1
+ require("../TmuxBackend/TmuxBackend.cjs");var e=class{rmux;tmux;constructor(e,t){this.rmux=e,this.tmux=t}async launch(e){return await this.rmux.launch(e)??await this.tmux.launch(e)}watch(e,t,n){return this.owner(t).watch(e,t,n)}stop(e,t){return this.owner(e).stop(e,t)}input(e,t){return this.owner(e).input(e,t)}readOutcome(e,t){return this.rmux.readOutcome(e,t)??this.tmux.readOutcome(e,t)}get(e){return this.rmux.get(e)??this.tmux.get(e)}owner(e){return e.startsWith(`doom-tmux-`)?this.tmux:this.rmux}};exports.PtyBackendChain=e;
2
+ //# sourceMappingURL=PtyBackendChain.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PtyBackendChain.cjs","names":[],"sources":["../../../../src/adapters/PtyBackendChain/PtyBackendChain.ts"],"sourcesContent":["import type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\nimport type { ExitResult } from '../../types/spawner';\nimport { TMUX_TARGET_PREFIX } from '../TmuxBackend/TmuxBackend.ts';\n\n/**\n * Presents the available PTY backends as one, preferring RMUX.\n *\n * A launch takes the first backend that accepts it, so a host without a\n * compatible RMUX binary still gets panes rather than falling through to the\n * plain subprocess launcher. Every later call routes on the target's prefix\n * instead of asking each backend in turn: a backend cannot tell \"this session\n * is gone\" from \"this session was never mine\", and guessing would let one\n * report success for the other's run.\n */\nexport class PtyBackendChain implements IRmuxBackend {\n constructor(\n private readonly rmux: IRmuxBackend,\n private readonly tmux: IRmuxBackend,\n ) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n return (await this.rmux.launch(request)) ?? (await this.tmux.launch(request));\n }\n\n watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n return this.owner(target).watch(id, target, sessionId);\n }\n\n stop(target: string, expectedPid: number): Promise<boolean> {\n return this.owner(target).stop(target, expectedPid);\n }\n\n input(target: string, text: string): Promise<boolean> {\n return this.owner(target).input(target, text);\n }\n\n /** Both backends persist terminal evidence at the same path, so either answers. */\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return this.rmux.readOutcome(id, sessionId) ?? this.tmux.readOutcome(id, sessionId);\n }\n\n get(name: string): PtyRun | undefined {\n return this.rmux.get(name) ?? this.tmux.get(name);\n }\n\n private owner(target: string): IRmuxBackend {\n return target.startsWith(TMUX_TARGET_PREFIX) ? this.tmux : this.rmux;\n }\n}\n"],"mappings":"0CAgBA,IAAa,EAAb,KAAqD,CAEhC,KACA,KAFnB,YACE,EACA,EACA,CAFiB,KAAA,KAAA,EACA,KAAA,KAAA,CAChB,CAEH,MAAM,OAAO,EAA4D,CACvE,OAAQ,MAAM,KAAK,KAAK,OAAO,CAAO,GAAO,MAAM,KAAK,KAAK,OAAO,CAAO,CAC7E,CAEA,MAAM,EAAY,EAAgB,EAAqD,CACrF,OAAO,KAAK,MAAM,CAAM,CAAC,CAAC,MAAM,EAAI,EAAQ,CAAS,CACvD,CAEA,KAAK,EAAgB,EAAuC,CAC1D,OAAO,KAAK,MAAM,CAAM,CAAC,CAAC,KAAK,EAAQ,CAAW,CACpD,CAEA,MAAM,EAAgB,EAAgC,CACpD,OAAO,KAAK,MAAM,CAAM,CAAC,CAAC,MAAM,EAAQ,CAAI,CAC9C,CAGA,YAAY,EAAY,EAA2C,CACjE,OAAO,KAAK,KAAK,YAAY,EAAI,CAAS,GAAK,KAAK,KAAK,YAAY,EAAI,CAAS,CACpF,CAEA,IAAI,EAAkC,CACpC,OAAO,KAAK,KAAK,IAAI,CAAI,GAAK,KAAK,KAAK,IAAI,CAAI,CAClD,CAEA,MAAc,EAA8B,CAC1C,OAAO,EAAO,WAAA,YAA6B,EAAI,KAAK,KAAO,KAAK,IAClE,CACF"}
@@ -0,0 +1,2 @@
1
+ import"../TmuxBackend/TmuxBackend.mjs";var e=class{rmux;tmux;constructor(e,t){this.rmux=e,this.tmux=t}async launch(e){return await this.rmux.launch(e)??await this.tmux.launch(e)}watch(e,t,n){return this.owner(t).watch(e,t,n)}stop(e,t){return this.owner(e).stop(e,t)}input(e,t){return this.owner(e).input(e,t)}readOutcome(e,t){return this.rmux.readOutcome(e,t)??this.tmux.readOutcome(e,t)}get(e){return this.rmux.get(e)??this.tmux.get(e)}owner(e){return e.startsWith(`doom-tmux-`)?this.tmux:this.rmux}};export{e as PtyBackendChain};
2
+ //# sourceMappingURL=PtyBackendChain.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PtyBackendChain.mjs","names":[],"sources":["../../../../src/adapters/PtyBackendChain/PtyBackendChain.ts"],"sourcesContent":["import type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\nimport type { ExitResult } from '../../types/spawner';\nimport { TMUX_TARGET_PREFIX } from '../TmuxBackend/TmuxBackend.ts';\n\n/**\n * Presents the available PTY backends as one, preferring RMUX.\n *\n * A launch takes the first backend that accepts it, so a host without a\n * compatible RMUX binary still gets panes rather than falling through to the\n * plain subprocess launcher. Every later call routes on the target's prefix\n * instead of asking each backend in turn: a backend cannot tell \"this session\n * is gone\" from \"this session was never mine\", and guessing would let one\n * report success for the other's run.\n */\nexport class PtyBackendChain implements IRmuxBackend {\n constructor(\n private readonly rmux: IRmuxBackend,\n private readonly tmux: IRmuxBackend,\n ) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n return (await this.rmux.launch(request)) ?? (await this.tmux.launch(request));\n }\n\n watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n return this.owner(target).watch(id, target, sessionId);\n }\n\n stop(target: string, expectedPid: number): Promise<boolean> {\n return this.owner(target).stop(target, expectedPid);\n }\n\n input(target: string, text: string): Promise<boolean> {\n return this.owner(target).input(target, text);\n }\n\n /** Both backends persist terminal evidence at the same path, so either answers. */\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return this.rmux.readOutcome(id, sessionId) ?? this.tmux.readOutcome(id, sessionId);\n }\n\n get(name: string): PtyRun | undefined {\n return this.rmux.get(name) ?? this.tmux.get(name);\n }\n\n private owner(target: string): IRmuxBackend {\n return target.startsWith(TMUX_TARGET_PREFIX) ? this.tmux : this.rmux;\n }\n}\n"],"mappings":"uCAgBA,IAAa,EAAb,KAAqD,CAEhC,KACA,KAFnB,YACE,EACA,EACA,CAFiB,KAAA,KAAA,EACA,KAAA,KAAA,CAChB,CAEH,MAAM,OAAO,EAA4D,CACvE,OAAQ,MAAM,KAAK,KAAK,OAAO,CAAO,GAAO,MAAM,KAAK,KAAK,OAAO,CAAO,CAC7E,CAEA,MAAM,EAAY,EAAgB,EAAqD,CACrF,OAAO,KAAK,MAAM,CAAM,CAAC,CAAC,MAAM,EAAI,EAAQ,CAAS,CACvD,CAEA,KAAK,EAAgB,EAAuC,CAC1D,OAAO,KAAK,MAAM,CAAM,CAAC,CAAC,KAAK,EAAQ,CAAW,CACpD,CAEA,MAAM,EAAgB,EAAgC,CACpD,OAAO,KAAK,MAAM,CAAM,CAAC,CAAC,MAAM,EAAQ,CAAI,CAC9C,CAGA,YAAY,EAAY,EAA2C,CACjE,OAAO,KAAK,KAAK,YAAY,EAAI,CAAS,GAAK,KAAK,KAAK,YAAY,EAAI,CAAS,CACpF,CAEA,IAAI,EAAkC,CACpC,OAAO,KAAK,KAAK,IAAI,CAAI,GAAK,KAAK,KAAK,IAAI,CAAI,CAClD,CAEA,MAAc,EAA8B,CAC1C,OAAO,EAAO,WAAA,YAA6B,EAAI,KAAK,KAAO,KAAK,IAClE,CACF"}
@@ -1,2 +1,2 @@
1
- const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../types/rtkProcessor.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:module"),o=require("node:child_process");const s=/^[A-Za-z0-9_./:@%+=,-]+$/u,c=/[\r\n;&|<>`$\\'"(){}*?!]/u,l=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),u=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],d={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var f=class{resolveBinary;timeoutMs;constructor(e=h,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let t=p(e.command);if(!t)return{kind:`skipped`};let i;try{i=r.default.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:n.RTK_FAILED_WARNING}}if(i>10485760)return{kind:`fallback`,warning:n.RTK_OVERSIZED_WARNING};let a;try{if(a=this.resolveBinary(),!a)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};r.default.statSync(a).mode&73||r.default.chmodSync(a,493)}catch{return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING}}return this.pipe(a,t,e.logPath,i)}pipe(e,i,a,s){return new Promise(c=>{let l;try{l=(0,o.spawn)(e,[`pipe`,`-f`,i],{stdio:`pipe`})}catch{c({kind:`fallback`,warning:n.RTK_FAILED_WARNING});return}let u=r.default.createReadStream(a),d=t.getResultMaxBytes()*2,f=Buffer.alloc(0),p=Buffer.alloc(0),m=0,h=0,v,y=!1,b,x,S=e=>{y||(y=!0,clearTimeout(w),x&&clearTimeout(x),u.destroy(),c(e))},C=e=>{if(y)return;b=e;let t=!1;try{t=l.kill(`SIGKILL`)}catch{S({kind:`fallback`,warning:e});return}if(!t){S({kind:`fallback`,warning:e});return}x??=setTimeout(()=>S({kind:`fallback`,warning:e}),1e3)},w=setTimeout(()=>C(n.RTK_TIMEOUT_WARNING),this.timeoutMs);l.stdout.on(`data`,e=>{m+=e.byteLength;for(let t of e)t===10&&(h+=1);v=e.at(-1),f.byteLength<d&&(f=Buffer.concat([f,e.subarray(0,d-f.byteLength)])),p=g(p,e,d)}),l.stderr.resume(),l.once(`error`,()=>C(n.RTK_FAILED_WARNING)),l.stdin.once(`error`,()=>C(n.RTK_FAILED_WARNING)),u.once(`error`,()=>C(n.RTK_FAILED_WARNING)),l.once(`close`,e=>{if(b){S({kind:`fallback`,warning:b});return}let t=_(p);if(e!==0||s>0&&t.trim().length===0){S({kind:`fallback`,warning:n.RTK_FAILED_WARNING});return}let r=h+ +(m>0&&v!==10);S({kind:`processed`,result:{filter:i,head:m>p.byteLength?f.toString(`utf8`):``,output:t,bytes:m,lines:r}})}),u.pipe(l.stdin)})}};function p(e){if(c.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!s.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>l.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||y(n)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||y(n)||!n.includes(`--no-heading`)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function m(e,t){let n=`${e}-${t}`,r=d[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function h(){let e=m(process.platform,process.arch);if(e)try{let t=(0,a.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${e}/package.json`);return i.default.join(i.default.dirname(t),`vendor`,`bin`,`rtk`)}catch{return}}function g(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function _(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function v(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function y(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||u.some(t=>e===t||e.startsWith(`${t}=`)))}exports.RTK_STDIN_MAX_BYTES=10485760,exports.RtkProcessor=f,exports.classifyRtkFilter=p,exports.rtkPackageForTarget=m;
1
+ const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../types/rtkProcessor.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:module"),o=require("node:child_process");const s=/^[A-Za-z0-9_./:@%+=,-]+$/u,c=/[\r\n;&|<>`$\\'"(){}*?!]/u,l=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),u=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],d={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var f=class{resolveBinary;timeoutMs;unrunnable=!1;constructor(e=h,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let t=p(e.command);if(!t)return{kind:`skipped`};let i;try{i=r.default.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:n.RTK_FAILED_WARNING}}if(i>10485760)return{kind:`fallback`,warning:n.RTK_OVERSIZED_WARNING};if(this.unrunnable)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};let a;try{if(a=this.resolveBinary(),!a)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};r.default.statSync(a).mode&73||r.default.chmodSync(a,493)}catch{return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING}}return this.pipe(a,t,e.logPath,i)}pipe(e,i,a,s){return new Promise(c=>{let l;try{l=(0,o.spawn)(e,[`pipe`,`-f`,i],{stdio:`pipe`})}catch{this.unrunnable=!0,c({kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING});return}let u=r.default.createReadStream(a),d=t.getResultMaxBytes()*2,f=Buffer.alloc(0),p=Buffer.alloc(0),m=0,h=0,v,y=!1,b,x,S=e=>{y||(y=!0,clearTimeout(w),x&&clearTimeout(x),u.destroy(),c(e))},C=e=>{if(y)return;b=e;let t=!1;try{t=l.kill(`SIGKILL`)}catch{S({kind:`fallback`,warning:e});return}if(!t){S({kind:`fallback`,warning:e});return}x??=setTimeout(()=>S({kind:`fallback`,warning:e}),1e3)},w=setTimeout(()=>C(n.RTK_TIMEOUT_WARNING),this.timeoutMs);l.stdout.on(`data`,e=>{m+=e.byteLength;for(let t of e)t===10&&(h+=1);v=e.at(-1),f.byteLength<d&&(f=Buffer.concat([f,e.subarray(0,d-f.byteLength)])),p=g(p,e,d)}),l.stderr.resume(),l.once(`error`,e=>{if(e.code===`ENOENT`||e.code===`EACCES`||e.code===`ENOEXEC`){this.unrunnable=!0,C(n.RTK_UNAVAILABLE_WARNING);return}C(n.RTK_FAILED_WARNING)}),l.stdin.once(`error`,()=>C(n.RTK_FAILED_WARNING)),u.once(`error`,()=>C(n.RTK_FAILED_WARNING)),l.once(`close`,e=>{if(b){S({kind:`fallback`,warning:b});return}let t=_(p);if(e!==0||s>0&&t.trim().length===0){S({kind:`fallback`,warning:n.RTK_FAILED_WARNING});return}let r=h+ +(m>0&&v!==10);S({kind:`processed`,result:{filter:i,head:m>p.byteLength?f.toString(`utf8`):``,output:t,bytes:m,lines:r}})}),u.pipe(l.stdin)})}};function p(e){if(c.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!s.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>l.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||y(n)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||y(n)||!n.includes(`--no-heading`)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function m(e,t){let n=`${e}-${t}`,r=d[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function h(){let e=m(process.platform,process.arch);if(e)try{let t=(0,a.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${e}/package.json`);return i.default.join(i.default.dirname(t),`vendor`,`bin`,`rtk`)}catch{return}}function g(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function _(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function v(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function y(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||u.some(t=>e===t||e.startsWith(`${t}=`)))}exports.RTK_STDIN_MAX_BYTES=10485760,exports.RtkProcessor=f,exports.classifyRtkFilter=p,exports.rtkPackageForTarget=m;
2
2
  //# sourceMappingURL=RtkProcessor.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.cjs","names":["fs","RTK_FAILED_WARNING","RTK_OVERSIZED_WARNING","RTK_UNAVAILABLE_WARNING","spawn","getResultMaxBytes","RTK_TIMEOUT_WARNING","createRequire","path"],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n resolve({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', () => stop(RTK_FAILED_WARNING));\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RtkProcessor.cjs","names":["fs","RTK_FAILED_WARNING","RTK_OVERSIZED_WARNING","RTK_UNAVAILABLE_WARNING","spawn","getResultMaxBytes","RTK_TIMEOUT_WARNING","createRequire","path"],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"4RAgBA,MAMM,EAAuB,4BACvB,EAAwB,4BACxB,EAAgC,gVA6BtC,EACM,EAAyC,CAC7C,SACA,SACA,cACA,UACA,WACA,kBACA,mBACA,YACA,gBACA,mBACA,WACF,EACM,EAAwD,CAC5D,eAAgB,4CAChB,aAAc,0CACd,cAAe,2CACf,YAAa,wCACf,EAIA,IAAa,EAAb,KAAmD,CAY9B,cACA,UAJnB,WAAqB,GAErB,YACE,EAAiD,EACjD,EAA6B,IAC7B,CAFiB,KAAA,cAAA,EACA,KAAA,UAAA,CAChB,CAEH,MAAM,QAAQ,EAAuD,CACnE,IAAM,EAAS,EAAkB,EAAQ,OAAO,EAChD,GAAI,CAAC,EAAQ,MAAO,CAAE,KAAM,SAAU,EAEtC,IAAI,EACJ,GAAI,CACF,EAAaA,EAAAA,QAAG,SAAS,EAAQ,OAAO,CAAC,CAAC,IAC5C,MAAQ,CACN,MAAO,CAAE,KAAM,WAAY,QAASC,EAAAA,kBAAmB,CACzD,CACA,GAAI,EAAA,SAAkC,MAAO,CAAE,KAAM,WAAY,QAASC,EAAAA,qBAAsB,EAEhG,GAAI,KAAK,WAAY,MAAO,CAAE,KAAM,WAAY,QAASC,EAAAA,uBAAwB,EAEjF,IAAI,EACJ,GAAI,CAEF,GADA,EAAS,KAAK,cAAc,EACxB,CAAC,EAAQ,MAAO,CAAE,KAAM,WAAY,QAASA,EAAAA,uBAAwB,EACpEH,EAAAA,QAAG,SAAS,CAAM,CAAC,CAAC,KAAO,IAAc,EAAA,QAAG,UAAU,EAAQ,GAAe,CACpF,MAAQ,CACN,MAAO,CAAE,KAAM,WAAY,QAASG,EAAAA,uBAAwB,CAC9D,CAEA,OAAO,KAAK,KAAK,EAAQ,EAAQ,EAAQ,QAAS,CAAU,CAC9D,CAEA,KAAa,EAAgB,EAAmB,EAAiB,EAA+C,CAC9G,OAAO,IAAI,QAAS,GAAY,CAC9B,IAAI,EACJ,GAAI,CACF,GAAA,EAAQC,EAAAA,MAAAA,CAAM,EAAQ,CAAC,OAAQ,KAAM,CAAM,EAAG,CAAE,MAAO,MAAO,CAAC,CACjE,MAAQ,CACN,KAAK,WAAa,GAClB,EAAQ,CAAE,KAAM,WAAY,QAASD,EAAAA,uBAAwB,CAAC,EAC9D,MACF,CAEA,IAAM,EAAQH,EAAAA,QAAG,iBAAiB,CAAO,EACnC,EAAcK,EAAAA,kBAAkB,EAAI,EACtC,EAAgC,OAAO,MAAM,CAAC,EAC9C,EAAkC,OAAO,MAAM,CAAC,EAChD,EAAc,EACd,EAAe,EACf,EACA,EAAU,GACV,EACA,EAEE,EAAU,GAAmC,CAC7C,IACJ,EAAU,GACV,aAAa,CAAK,EACd,GAAe,aAAa,CAAa,EAC7C,EAAM,QAAQ,EACd,EAAQ,CAAM,EAChB,EACM,EAAQ,GAA0B,CACtC,GAAI,EAAS,OACb,EAAkB,EAClB,IAAI,EAAS,GACb,GAAI,CACF,EAAS,EAAM,KAAK,SAAS,CAC/B,MAAQ,CACN,EAAO,CAAE,KAAM,WAAY,SAAQ,CAAC,EACpC,MACF,CACA,GAAI,CAAC,EAAQ,CACX,EAAO,CAAE,KAAM,WAAY,SAAQ,CAAC,EACpC,MACF,CACA,IAAkB,eAAiB,EAAO,CAAE,KAAM,WAAY,SAAQ,CAAC,EAAG,GAAgB,CAC5F,EACM,EAAQ,eAAiB,EAAKC,EAAAA,mBAAmB,EAAG,KAAK,SAAS,EAExE,EAAM,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAe,EAAM,WACrB,IAAK,IAAM,KAAQ,EAAW,IAAS,KAAM,GAAgB,GAC7D,EAAW,EAAM,GAAG,EAAE,EAClB,EAAK,WAAa,IACpB,EAAO,OAAO,OAAO,CAAC,EAAM,EAAM,SAAS,EAAG,EAAc,EAAK,UAAU,CAAC,CAAC,GAE/E,EAAS,EAAW,EAAQ,EAAO,CAAW,CAChD,CAAC,EACD,EAAM,OAAO,OAAO,EACpB,EAAM,KAAK,QAAU,GAAiC,CAGpD,GAAI,EAAM,OAAS,UAAY,EAAM,OAAS,UAAY,EAAM,OAAS,UAAW,CAClF,KAAK,WAAa,GAClB,EAAKH,EAAAA,uBAAuB,EAC5B,MACF,CACA,EAAKF,EAAAA,kBAAkB,CACzB,CAAC,EACD,EAAM,MAAM,KAAK,YAAe,EAAKA,EAAAA,kBAAkB,CAAC,EACxD,EAAM,KAAK,YAAe,EAAKA,EAAAA,kBAAkB,CAAC,EAClD,EAAM,KAAK,QAAU,GAAS,CAC5B,GAAI,EAAiB,CACnB,EAAO,CAAE,KAAM,WAAY,QAAS,CAAgB,CAAC,EACrD,MACF,CACA,IAAM,EAAkB,EAAe,CAAM,EAC7C,GAAI,IAAS,GAAM,EAAa,GAAK,EAAgB,KAAK,CAAC,CAAC,SAAW,EAAI,CACzE,EAAO,CAAE,KAAM,WAAY,QAASA,EAAAA,kBAAmB,CAAC,EACxD,MACF,CACA,IAAM,EAAQ,GAAgB,IAAc,GAAK,IAAa,IAC9D,EAAO,CACL,KAAM,YACN,OAAQ,CACN,SAGA,KAAM,EAAc,EAAO,WAAa,EAAK,SAAS,MAAM,EAAI,GAChE,OAAQ,EACR,MAAO,EACP,OACF,CACF,CAAC,CACH,CAAC,EAED,EAAM,KAAK,EAAM,KAAK,CACxB,CAAC,CACH,CACF,EAMA,SAAgB,EAAkB,EAAwC,CACxE,GAAI,EAAsB,KAAK,CAAO,EAAG,OACzC,IAAM,EAAU,EAAQ,KAAK,EAC7B,GAAI,CAAC,EAAS,OACd,IAAM,EAAS,EAAQ,MAAM,SAAS,EAClC,MAAO,KAAM,GAAU,CAAC,EAAqB,KAAK,CAAK,CAAC,EAE5D,IAAI,EAAO,KAAO,SAAW,EAAO,KAAO,OAIzC,OAHI,EAAO,SAAS,IAAI,GAAK,EAAO,KAAM,GAAU,EAAM,WAAW,kBAAkB,GAAK,IAAU,QAAQ,EAC5G,OAEK,aAET,GAAI,EAAO,KAAO,SAEhB,OADI,EAAO,KAAM,GAAU,EAAM,WAAW,eAAe,CAAC,EAAG,OACxD,SAET,GAAI,EAAO,KAAO,OAAS,EAAO,KAAO,OAQvC,OANE,EAAO,KAAM,GACX,EAA8B,KAAM,GAAW,IAAU,GAAU,EAAM,WAAW,GAAG,EAAO,EAAE,CAAC,CACnG,EAEA,OAEK,WAET,GAAI,EAAO,KAAO,OAEhB,OADI,EAAO,OAAS,GAAK,EAA6B,CAAM,EAAG,OACxD,EAAqB,EAAQ,IAAK,eAAe,GAAK,EAAqB,EAAQ,IAAK,iBAAiB,EAC5G,OACA,IAAA,GAEN,GAAI,EAAO,KAAO,KAEhB,OADI,EAAO,OAAS,GAAK,EAA6B,CAAM,GAAK,CAAC,EAAO,SAAS,cAAc,EAAG,OAC5F,EAAqB,EAAQ,IAAK,eAAe,GAAK,EAAqB,EAAQ,IAAK,iBAAiB,EAC5G,OACA,IAAA,GAEN,GAAI,EAAO,KAAO,MAAQ,EAAO,KAAO,OAAQ,CAC9C,IAAI,EAAa,GACjB,IAAK,IAAM,KAAS,EAAO,MAAM,CAAC,GAC5B,IAAU,SAAW,IAAU,gBAAc,EAAa,IAC1D,IAAU,gBAAe,EAAa,IAE5C,OAAO,EAAa,UAAY,IAAA,EAClC,CACA,GAAI,EAAO,KAAO,QAAU,EAAO,KAAO,QAKxC,OAJmB,EAAO,MACvB,EAAO,IACN,IAAU,wBAA2B,IAAU,mBAAqB,EAAO,EAAQ,KAAO,MAE9E,EAAI,aAAe,IAAA,EAxCrC,CA2CF,CAEA,SAAgB,EAAoB,EAAkB,EAA0C,CAC9F,IAAM,EAAS,GAAG,EAAS,GAAG,IACxB,EAAc,EAAoB,GAExC,OADK,GAAa,QAAQ,YAAY,4DAA4D,GAAQ,EACnG,CACT,CAEA,SAAS,GAAoC,CAC3C,IAAM,EAAc,EAAoB,QAAQ,SAAU,QAAQ,IAAI,EACjE,KACL,GAAI,CAEF,IAAM,GAAA,EADUM,EAAAA,cAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,IACO,CAAC,CAAC,QAAQ,GAAG,EAAY,cAAc,EAC9D,OAAOC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,CAAQ,EAAG,SAAU,MAAO,KAAK,CACjE,MAAQ,CACN,MACF,CACF,CAEA,SAAS,EACP,EACA,EACA,EACyB,CACzB,GAAI,EAAM,YAAc,EAAO,OAAO,OAAO,KAAK,EAAM,SAAS,EAAM,WAAa,CAAK,CAAC,EAC1F,IAAM,EAAW,OAAO,OAAO,CAAC,EAAS,CAAK,CAAC,EAC/C,OAAO,EAAS,WAAa,EAAQ,OAAO,KAAK,EAAS,SAAS,EAAS,WAAa,CAAK,CAAC,EAAI,CACrG,CAEA,SAAS,EAAe,EAAyC,CAC/D,IAAI,EAAQ,EACZ,KAAO,EAAQ,EAAO,aAAe,EAAO,GAAU,MAAU,KAAM,GAAS,EAC/E,OAAO,EAAO,SAAS,CAAK,CAAC,CAAC,SAAS,MAAM,CAC/C,CACA,SAAS,EAAqB,EAA2B,EAAe,EAAuB,CAC7F,OAAO,EAAO,KACX,GAAU,IAAU,GAAS,EAAM,WAAW,GAAG,GAAK,CAAC,EAAM,WAAW,IAAI,GAAK,EAAM,MAAM,CAAC,CAAC,CAAC,SAAS,CAAK,CACjH,CACF,CAEA,SAAS,EAA6B,EAAoC,CACxE,OAAO,EAAO,KACX,GACE,EAAM,WAAW,GAAG,GAAK,CAAC,EAAM,WAAW,IAAI,GAAK,iBAAiB,KAAK,EAAM,MAAM,CAAC,CAAC,GACzF,EAAuC,KAAM,GAAW,IAAU,GAAU,EAAM,WAAW,GAAG,EAAO,EAAE,CAAC,CAC9G,CACF"}
@@ -5,6 +5,15 @@ type BinaryResolver = () => string | undefined;
5
5
  declare class RtkProcessor implements IRtkProcessor {
6
6
  private readonly resolveBinary;
7
7
  private readonly timeoutMs;
8
+ /**
9
+ * Latched once a binary proves unrunnable on this host.
10
+ *
11
+ * A glibc build installed on a musl host satisfies os and cpu, so it
12
+ * resolves and then fails to exec. Without this the same doomed spawn is
13
+ * paid on every command, and each one is reported as a processing failure
14
+ * rather than the absence it actually is.
15
+ */
16
+ private unrunnable;
8
17
  constructor(resolveBinary?: BinaryResolver, timeoutMs?: number);
9
18
  process(request: RtkProcessRequest): Promise<RtkProcessResult>;
10
19
  private pipe;
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.d.cts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KAyDR;cAEQ,wBAAwB;mBAEhB;mBACA;EAFnB,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UAwB3C;;;;;;iBA8FM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
1
+ {"version":3,"file":"RtkProcessor.d.cts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KAyDR;cAEQ,wBAAwB;mBAYhB;mBACA;;;;;;;;;UAJX;EAER,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UA0B3C;;;;;;iBAwGM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
@@ -5,6 +5,15 @@ type BinaryResolver = () => string | undefined;
5
5
  declare class RtkProcessor implements IRtkProcessor {
6
6
  private readonly resolveBinary;
7
7
  private readonly timeoutMs;
8
+ /**
9
+ * Latched once a binary proves unrunnable on this host.
10
+ *
11
+ * A glibc build installed on a musl host satisfies os and cpu, so it
12
+ * resolves and then fails to exec. Without this the same doomed spawn is
13
+ * paid on every command, and each one is reported as a processing failure
14
+ * rather than the absence it actually is.
15
+ */
16
+ private unrunnable;
8
17
  constructor(resolveBinary?: BinaryResolver, timeoutMs?: number);
9
18
  process(request: RtkProcessRequest): Promise<RtkProcessResult>;
10
19
  private pipe;
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.d.mts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KAyDR;cAEQ,wBAAwB;mBAEhB;mBACA;EAFnB,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UAwB3C;;;;;;iBA8FM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
1
+ {"version":3,"file":"RtkProcessor.d.mts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KAyDR;cAEQ,wBAAwB;mBAYhB;mBACA;;;;;;;;;UAJX;EAER,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UA0B3C;;;;;;iBAwGM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
@@ -1,2 +1,2 @@
1
- import{getResultMaxBytes as e}from"../../types/config.mjs";import{RTK_FAILED_WARNING as t,RTK_OVERSIZED_WARNING as n,RTK_TIMEOUT_WARNING as r,RTK_UNAVAILABLE_WARNING as i}from"../../types/rtkProcessor.mjs";import{createRequire as a}from"node:module";import o from"node:fs";import s from"node:path";import{spawn as c}from"node:child_process";const l=10485760,u=/^[A-Za-z0-9_./:@%+=,-]+$/u,d=/[\r\n;&|<>`$\\'"(){}*?!]/u,f=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),p=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],m={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var h=class{resolveBinary;timeoutMs;constructor(e=v,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let r=g(e.command);if(!r)return{kind:`skipped`};let a;try{a=o.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:t}}if(a>10485760)return{kind:`fallback`,warning:n};let s;try{if(s=this.resolveBinary(),!s)return{kind:`fallback`,warning:i};o.statSync(s).mode&73||o.chmodSync(s,493)}catch{return{kind:`fallback`,warning:i}}return this.pipe(s,r,e.logPath,a)}pipe(n,i,a,s){return new Promise(l=>{let u;try{u=c(n,[`pipe`,`-f`,i],{stdio:`pipe`})}catch{l({kind:`fallback`,warning:t});return}let d=o.createReadStream(a),f=e()*2,p=Buffer.alloc(0),m=Buffer.alloc(0),h=0,g=0,_,v=!1,x,S,C=e=>{v||(v=!0,clearTimeout(T),S&&clearTimeout(S),d.destroy(),l(e))},w=e=>{if(v)return;x=e;let t=!1;try{t=u.kill(`SIGKILL`)}catch{C({kind:`fallback`,warning:e});return}if(!t){C({kind:`fallback`,warning:e});return}S??=setTimeout(()=>C({kind:`fallback`,warning:e}),1e3)},T=setTimeout(()=>w(r),this.timeoutMs);u.stdout.on(`data`,e=>{h+=e.byteLength;for(let t of e)t===10&&(g+=1);_=e.at(-1),p.byteLength<f&&(p=Buffer.concat([p,e.subarray(0,f-p.byteLength)])),m=y(m,e,f)}),u.stderr.resume(),u.once(`error`,()=>w(t)),u.stdin.once(`error`,()=>w(t)),d.once(`error`,()=>w(t)),u.once(`close`,e=>{if(x){C({kind:`fallback`,warning:x});return}let n=b(m);if(e!==0||s>0&&n.trim().length===0){C({kind:`fallback`,warning:t});return}let r=g+ +(h>0&&_!==10);C({kind:`processed`,result:{filter:i,head:h>m.byteLength?p.toString(`utf8`):``,output:n,bytes:h,lines:r}})}),d.pipe(u.stdin)})}};function g(e){if(d.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!u.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>f.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||S(n)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||S(n)||!n.includes(`--no-heading`)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function _(e,t){let n=`${e}-${t}`,r=m[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function v(){let e=_(process.platform,process.arch);if(e)try{let t=a(import.meta.url).resolve(`${e}/package.json`);return s.join(s.dirname(t),`vendor`,`bin`,`rtk`)}catch{return}}function y(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function b(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function x(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function S(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||p.some(t=>e===t||e.startsWith(`${t}=`)))}export{l as RTK_STDIN_MAX_BYTES,h as RtkProcessor,g as classifyRtkFilter,_ as rtkPackageForTarget};
1
+ import{getResultMaxBytes as e}from"../../types/config.mjs";import{RTK_FAILED_WARNING as t,RTK_OVERSIZED_WARNING as n,RTK_TIMEOUT_WARNING as r,RTK_UNAVAILABLE_WARNING as i}from"../../types/rtkProcessor.mjs";import{createRequire as a}from"node:module";import o from"node:fs";import s from"node:path";import{spawn as c}from"node:child_process";const l=10485760,u=/^[A-Za-z0-9_./:@%+=,-]+$/u,d=/[\r\n;&|<>`$\\'"(){}*?!]/u,f=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),p=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],m={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var h=class{resolveBinary;timeoutMs;unrunnable=!1;constructor(e=v,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let r=g(e.command);if(!r)return{kind:`skipped`};let a;try{a=o.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:t}}if(a>10485760)return{kind:`fallback`,warning:n};if(this.unrunnable)return{kind:`fallback`,warning:i};let s;try{if(s=this.resolveBinary(),!s)return{kind:`fallback`,warning:i};o.statSync(s).mode&73||o.chmodSync(s,493)}catch{return{kind:`fallback`,warning:i}}return this.pipe(s,r,e.logPath,a)}pipe(n,a,s,l){return new Promise(u=>{let d;try{d=c(n,[`pipe`,`-f`,a],{stdio:`pipe`})}catch{this.unrunnable=!0,u({kind:`fallback`,warning:i});return}let f=o.createReadStream(s),p=e()*2,m=Buffer.alloc(0),h=Buffer.alloc(0),g=0,_=0,v,x=!1,S,C,w=e=>{x||(x=!0,clearTimeout(E),C&&clearTimeout(C),f.destroy(),u(e))},T=e=>{if(x)return;S=e;let t=!1;try{t=d.kill(`SIGKILL`)}catch{w({kind:`fallback`,warning:e});return}if(!t){w({kind:`fallback`,warning:e});return}C??=setTimeout(()=>w({kind:`fallback`,warning:e}),1e3)},E=setTimeout(()=>T(r),this.timeoutMs);d.stdout.on(`data`,e=>{g+=e.byteLength;for(let t of e)t===10&&(_+=1);v=e.at(-1),m.byteLength<p&&(m=Buffer.concat([m,e.subarray(0,p-m.byteLength)])),h=y(h,e,p)}),d.stderr.resume(),d.once(`error`,e=>{if(e.code===`ENOENT`||e.code===`EACCES`||e.code===`ENOEXEC`){this.unrunnable=!0,T(i);return}T(t)}),d.stdin.once(`error`,()=>T(t)),f.once(`error`,()=>T(t)),d.once(`close`,e=>{if(S){w({kind:`fallback`,warning:S});return}let n=b(h);if(e!==0||l>0&&n.trim().length===0){w({kind:`fallback`,warning:t});return}let r=_+ +(g>0&&v!==10);w({kind:`processed`,result:{filter:a,head:g>h.byteLength?m.toString(`utf8`):``,output:n,bytes:g,lines:r}})}),f.pipe(d.stdin)})}};function g(e){if(d.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!u.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>f.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||S(n)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||S(n)||!n.includes(`--no-heading`)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function _(e,t){let n=`${e}-${t}`,r=m[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function v(){let e=_(process.platform,process.arch);if(e)try{let t=a(import.meta.url).resolve(`${e}/package.json`);return s.join(s.dirname(t),`vendor`,`bin`,`rtk`)}catch{return}}function y(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function b(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function x(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function S(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||p.some(t=>e===t||e.startsWith(`${t}=`)))}export{l as RTK_STDIN_MAX_BYTES,h as RtkProcessor,g as classifyRtkFilter,_ as rtkPackageForTarget};
2
2
  //# sourceMappingURL=RtkProcessor.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.mjs","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n resolve({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', () => stop(RTK_FAILED_WARNING));\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RtkProcessor.mjs","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../types/config.cjs"),n=require("../services/runs/session.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("node:child_process");const s=`doom-runner`,c=`runs`,l=`.json`,u=`utf8`,d=`ENOENT`;function f(e,t){try{let n=(0,o.execFileSync)(`git`,e,{cwd:t,encoding:u,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return n.length>0?n:void 0}catch{return}}function p(e){try{return r.default.realpathSync(e)}catch{return e}}function m(e){let t=e.PI_CODING_AGENT_DIR?.trim();return t===`~`?a.default.homedir():t?.startsWith(`~/`)?i.default.join(a.default.homedir(),t.slice(2)):t?i.default.resolve(t):i.default.join(a.default.homedir(),`.pi`,`agent`)}function h(e){let t=r.default.statSync(e).mtimeMs;for(let n of r.default.readdirSync(e,{withFileTypes:!0})){let a=i.default.join(e,n.name);if(t=Math.max(t,r.default.statSync(a).mtimeMs),n.isDirectory())for(let e of r.default.readdirSync(a))t=Math.max(t,r.default.statSync(i.default.join(a,e)).mtimeMs)}return t}async function g(e){let t=(await r.default.promises.stat(e)).mtimeMs;for(let n of await r.default.promises.readdir(e,{withFileTypes:!0})){let a=i.default.join(e,n.name);if(t=Math.max(t,(await r.default.promises.stat(a)).mtimeMs),n.isDirectory())for(let e of await r.default.promises.readdir(a))t=Math.max(t,(await r.default.promises.stat(i.default.join(a,e))).mtimeMs)}return t}function _(e){let t;try{t=r.default.readdirSync(e)}catch{return!1}for(let n of t)if(n.endsWith(l))try{let t=JSON.parse(r.default.readFileSync(i.default.join(e,n),u)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}async function v(e){let t;try{t=await r.default.promises.readdir(e)}catch{return!1}for(let n of t)if(n.endsWith(l))try{let t=JSON.parse(await r.default.promises.readFile(i.default.join(e,n),u)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}function y(e){if(typeof e!=`object`||!e)return;let t=e;if(t.state!==`completed`||typeof t.exit?.finishedAt!=`string`)return;let n=Date.parse(t.exit.finishedAt);return Number.isFinite(n)?n:void 0}var b=class{adoptedSessionId;repository;repositoryPath(){return this.repository??=p(f([`rev-parse`,`--show-toplevel`],process.cwd())??process.cwd()),this.repository}setSessionId(e){let t=e.trim();if(!t)throw Error(`doom-runner session id cannot be blank`);this.adoptedSessionId=t}logDirectory(e){let n=process.env[t.LOG_DIR_ENV]?.trim();return n?i.default.resolve(n):i.default.join(this.sessionDirectory(e),`logs`)}stateDirectory(e){let n=process.env[t.LOG_DIR_ENV]?.trim();return n?i.default.join(i.default.resolve(n),`..`,c):i.default.join(this.sessionDirectory(e),c)}logPathFor(e,t){return i.default.join(this.logDirectory(t),`${e}.log`)}rotatedLogPathFor(e,t){return`${this.logPathFor(e,t)}.1`}statePathFor(e,t){return i.default.join(this.stateDirectory(t),`${e}${l}`)}ensureDirectories(e){r.default.mkdirSync(this.logDirectory(e),{recursive:!0}),r.default.mkdirSync(this.stateDirectory(e),{recursive:!0})}sweepHistory(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return this.sweepStateDirectory(this.stateDirectory(),this.sessionId(),e,t,n),n;let a=this.storeDirectory(),o;try{o=r.default.readdirSync(a)}catch(e){return e.code===d||n.errors.push(`Could not read runner history directory ${a}: ${String(e)}`),n}let s=this.currentSessionId();for(let r of o)r!==s&&this.sweepSessionDirectory(i.default.join(a,r),e,t,n);return n}async sweepHistoryAsync(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return await this.sweepStateDirectoryAsync(this.stateDirectory(),this.sessionId(),e,t,n),n;let a=this.storeDirectory(),o;try{o=await r.default.promises.readdir(a)}catch(e){return e.code===d||n.errors.push(`Could not read runner history directory ${a}: ${String(e)}`),n}let s=this.currentSessionId();for(let r of o)r!==s&&await this.sweepSessionDirectoryAsync(i.default.join(a,r),e,t,n);return n}legacyDirectory(){let e=f([`rev-parse`,`--path-format=absolute`,`--git-common-dir`],process.cwd());return e?i.default.join(e,s):void 0}removeLegacyStore(){let e=this.legacyDirectory();if(!(!e||i.default.basename(e)!==s||!r.default.existsSync(e)))return r.default.rmSync(e,{recursive:!0,force:!0}),e}currentSessionId(){try{return this.sessionId()}catch{return}}sweepSessionDirectory(e,t,n,a){try{if(n-h(e)<=t||_(i.default.join(e,c)))return;r.default.rmSync(e,{recursive:!0,force:!0}),a.removed.push(e)}catch(t){t.code!==d&&a.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}async sweepSessionDirectoryAsync(e,t,n,a){try{if(n-await g(e)<=t||await v(i.default.join(e,c)))return;await r.default.promises.rm(e,{recursive:!0,force:!0}),a.removed.push(e)}catch(t){t.code!==d&&a.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}sessionId(e){let t=e?.trim()||this.adoptedSessionId||n.inheritedSessionId();if(!t)throw Error(`PI_SESSION_ID is required for doom-runner storage`);return t}storeDirectory(){let e=process.env[t.LOG_DIR_ENV]?.trim();return e?i.default.dirname(i.default.resolve(e)):i.default.join(m(process.env),s)}sessionDirectory(e){return i.default.join(this.storeDirectory(),this.sessionId(e))}sweepStateDirectory(e,t,n,a,o){let s;try{s=r.default.readdirSync(e)}catch(t){t.code!==d&&o.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(l))continue;let s=i.default.join(e,c);try{let e=y(JSON.parse(r.default.readFileSync(s,u)));if(e===void 0||a-e<=n)continue;let i=c.slice(0,-5);this.removeHistoryFiles(t,i,s,o)}catch(e){o.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}async sweepStateDirectoryAsync(e,t,n,a,o){let s;try{s=await r.default.promises.readdir(e)}catch(t){t.code!==d&&o.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(l))continue;let s=i.default.join(e,c);try{let e=y(JSON.parse(await r.default.promises.readFile(s,u)));if(e===void 0||a-e<=n)continue;let i=c.slice(0,-5);await this.removeHistoryFilesAsync(t,i,s,o)}catch(e){o.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}removeHistoryFiles(e,t,n,i){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{r.default.unlinkSync(e),i.removed.push(e)}catch(t){t.code!==d&&i.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}async removeHistoryFilesAsync(e,t,n,i){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{await r.default.promises.unlink(e),i.removed.push(e)}catch(t){t.code!==d&&i.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}};exports.RunnerPaths=b;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../types/config.cjs"),n=require("../services/runs/session.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:os");a=e.__toESM(a,1);let o=require("node:child_process");const s=`doom-runner`,c=`runs`,l=`.json`,u=`utf8`,d=`ENOENT`;function f(e,t){try{let n=(0,o.execFileSync)(`git`,e,{cwd:t,encoding:u,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return n.length>0?n:void 0}catch{return}}function p(e){try{return r.default.realpathSync(e)}catch{return e}}function m(e){let t=e.PI_CODING_AGENT_DIR?.trim();return t===`~`?a.default.homedir():t?.startsWith(`~/`)?i.default.join(a.default.homedir(),t.slice(2)):t?i.default.resolve(t):i.default.join(a.default.homedir(),`.pi`,`agent`)}function h(e){let t=r.default.statSync(e).mtimeMs;for(let n of r.default.readdirSync(e,{withFileTypes:!0})){let a=i.default.join(e,n.name);if(t=Math.max(t,r.default.statSync(a).mtimeMs),n.isDirectory())for(let e of r.default.readdirSync(a))t=Math.max(t,r.default.statSync(i.default.join(a,e)).mtimeMs)}return t}async function g(e){let t=(await r.default.promises.stat(e)).mtimeMs;for(let n of await r.default.promises.readdir(e,{withFileTypes:!0})){let a=i.default.join(e,n.name);if(t=Math.max(t,(await r.default.promises.stat(a)).mtimeMs),n.isDirectory())for(let e of await r.default.promises.readdir(a))t=Math.max(t,(await r.default.promises.stat(i.default.join(a,e))).mtimeMs)}return t}function _(e){let t;try{t=r.default.readdirSync(e)}catch{return!1}for(let n of t)if(n.endsWith(l))try{let t=JSON.parse(r.default.readFileSync(i.default.join(e,n),u)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}async function v(e){let t;try{t=await r.default.promises.readdir(e)}catch{return!1}for(let n of t)if(n.endsWith(l))try{let t=JSON.parse(await r.default.promises.readFile(i.default.join(e,n),u)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}function y(e){if(typeof e!=`object`||!e)return;let t=e;if(t.state!==`completed`||typeof t.exit?.finishedAt!=`string`)return;let n=Date.parse(t.exit.finishedAt);return Number.isFinite(n)?n:void 0}function b(e){return i.default.join(m(e),s)}var x=class{adoptedSessionId;repository;repositoryPath(){return this.repository??=p(f([`rev-parse`,`--show-toplevel`],process.cwd())??process.cwd()),this.repository}setSessionId(e){let t=e.trim();if(!t)throw Error(`doom-runner session id cannot be blank`);this.adoptedSessionId=t}logDirectory(e){let n=process.env[t.LOG_DIR_ENV]?.trim();return n?i.default.resolve(n):i.default.join(this.sessionDirectory(e),`logs`)}stateDirectory(e){let n=process.env[t.LOG_DIR_ENV]?.trim();return n?i.default.join(i.default.resolve(n),`..`,c):i.default.join(this.sessionDirectory(e),c)}logPathFor(e,t){return i.default.join(this.logDirectory(t),`${e}.log`)}rotatedLogPathFor(e,t){return`${this.logPathFor(e,t)}.1`}statePathFor(e,t){return i.default.join(this.stateDirectory(t),`${e}${l}`)}ensureDirectories(e){r.default.mkdirSync(this.logDirectory(e),{recursive:!0}),r.default.mkdirSync(this.stateDirectory(e),{recursive:!0})}sweepHistory(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return this.sweepStateDirectory(this.stateDirectory(),this.sessionId(),e,t,n),n;let a=this.storeDirectory(),o;try{o=r.default.readdirSync(a)}catch(e){return e.code===d||n.errors.push(`Could not read runner history directory ${a}: ${String(e)}`),n}let s=this.currentSessionId();for(let r of o)r!==s&&this.sweepSessionDirectory(i.default.join(a,r),e,t,n);return n}async sweepHistoryAsync(e,t=Date.now()){let n={removed:[],errors:[]};if(process.env.DOOM_RUNNER_LOG_DIR?.trim())return await this.sweepStateDirectoryAsync(this.stateDirectory(),this.sessionId(),e,t,n),n;let a=this.storeDirectory(),o;try{o=await r.default.promises.readdir(a)}catch(e){return e.code===d||n.errors.push(`Could not read runner history directory ${a}: ${String(e)}`),n}let s=this.currentSessionId();for(let r of o)r!==s&&await this.sweepSessionDirectoryAsync(i.default.join(a,r),e,t,n);return n}legacyDirectory(){let e=f([`rev-parse`,`--path-format=absolute`,`--git-common-dir`],process.cwd());return e?i.default.join(e,s):void 0}removeLegacyStore(){let e=this.legacyDirectory();if(!(!e||i.default.basename(e)!==s||!r.default.existsSync(e)))return r.default.rmSync(e,{recursive:!0,force:!0}),e}currentSessionId(){try{return this.sessionId()}catch{return}}sweepSessionDirectory(e,t,n,a){try{if(n-h(e)<=t||_(i.default.join(e,c)))return;r.default.rmSync(e,{recursive:!0,force:!0}),a.removed.push(e)}catch(t){t.code!==d&&a.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}async sweepSessionDirectoryAsync(e,t,n,a){try{if(n-await g(e)<=t||await v(i.default.join(e,c)))return;await r.default.promises.rm(e,{recursive:!0,force:!0}),a.removed.push(e)}catch(t){t.code!==d&&a.errors.push(`Could not sweep runner session ${e}: ${String(t)}`)}}sessionId(e){let t=e?.trim()||this.adoptedSessionId||n.inheritedSessionId();if(!t)throw Error(`PI_SESSION_ID is required for doom-runner storage`);return t}storeDirectory(){let e=process.env[t.LOG_DIR_ENV]?.trim();return e?i.default.dirname(i.default.resolve(e)):b(process.env)}sessionDirectory(e){return i.default.join(this.storeDirectory(),this.sessionId(e))}sweepStateDirectory(e,t,n,a,o){let s;try{s=r.default.readdirSync(e)}catch(t){t.code!==d&&o.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(l))continue;let s=i.default.join(e,c);try{let e=y(JSON.parse(r.default.readFileSync(s,u)));if(e===void 0||a-e<=n)continue;let i=c.slice(0,-5);this.removeHistoryFiles(t,i,s,o)}catch(e){o.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}async sweepStateDirectoryAsync(e,t,n,a,o){let s;try{s=await r.default.promises.readdir(e)}catch(t){t.code!==d&&o.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}for(let c of s){if(!c.endsWith(l))continue;let s=i.default.join(e,c);try{let e=y(JSON.parse(await r.default.promises.readFile(s,u)));if(e===void 0||a-e<=n)continue;let i=c.slice(0,-5);await this.removeHistoryFilesAsync(t,i,s,o)}catch(e){o.errors.push(`Could not sweep runner metadata ${s}: ${String(e)}`)}}}removeHistoryFiles(e,t,n,i){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{r.default.unlinkSync(e),i.removed.push(e)}catch(t){t.code!==d&&i.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}async removeHistoryFilesAsync(e,t,n,i){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{await r.default.promises.unlink(e),i.removed.push(e)}catch(t){t.code!==d&&i.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}};exports.RunnerPaths=x,exports.resolveRunnerStoreDirectory=b;
2
2
  //# sourceMappingURL=RunnerPaths.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RunnerPaths.cjs","names":["execFileSync","fs","os","path","LOG_DIR_ENV","inheritedSessionId"],"sources":["../../../src/adapters/RunnerPaths.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { IRunnerPaths, LogSweepResult } from '../services/RunnerPaths/types.ts';\nimport { inheritedSessionId } from '../services/runs/session.ts';\nimport { LOG_DIR_ENV } from '../types/config.ts';\n\nconst STORE_DIR_NAME = 'doom-runner';\nconst DEFAULT_CONFIG_DIR_NAME = '.pi';\nconst AGENT_DIR_NAME = 'agent';\nconst LOG_DIR_NAME = 'logs';\nconst STATE_DIR_NAME = 'runs';\nconst LOG_EXTENSION = '.log';\nconst STATE_EXTENSION = '.json';\nconst ROTATED_SUFFIX = '.1';\nconst PI_CODING_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nconst HOME_ALIAS = '~';\nconst HOME_ALIAS_PREFIX = '~/';\nconst FILE_ENCODING = 'utf8';\nconst NOT_FOUND_ERROR_CODE = 'ENOENT';\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\nfunction resolveAgentDirectory(env: NodeJS.ProcessEnv): string {\n const configured = env[PI_CODING_AGENT_DIR_ENV]?.trim();\n if (configured === HOME_ALIAS) return os.homedir();\n if (configured?.startsWith(HOME_ALIAS_PREFIX)) {\n return path.join(os.homedir(), configured.slice(HOME_ALIAS_PREFIX.length));\n }\n return configured ? path.resolve(configured) : path.join(os.homedir(), DEFAULT_CONFIG_DIR_NAME, AGENT_DIR_NAME);\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\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 repositoryPath(): string {\n this.repository ??= realPath(git(['rev-parse', '--show-toplevel'], process.cwd()) ?? process.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 = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.resolve(override);\n return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);\n }\n\n stateDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.join(path.resolve(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 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 (process.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) continue;\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 (process.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) continue;\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'], process.cwd());\n return commonDirectory ? path.join(commonDirectory, STORE_DIR_NAME) : undefined;\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 /** 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 if (now - newestEntryTime(directory) <= 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 if (now - (await newestEntryTimeAsync(directory)) <= 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();\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 = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.dirname(path.resolve(override));\n return path.join(resolveAgentDirectory(process.env), STORE_DIR_NAME);\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 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 || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n this.removeHistoryFiles(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\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 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 || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n await this.removeHistoryFilesAsync(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\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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"}
1
+ {"version":3,"file":"RunnerPaths.cjs","names":["execFileSync","fs","os","path","LOG_DIR_ENV","inheritedSessionId"],"sources":["../../../src/adapters/RunnerPaths.ts"],"sourcesContent":["import { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type { IRunnerPaths, LogSweepResult } from '../services/RunnerPaths/types.ts';\nimport { inheritedSessionId } from '../services/runs/session.ts';\nimport { LOG_DIR_ENV } from '../types/config.ts';\n\nconst STORE_DIR_NAME = 'doom-runner';\nconst DEFAULT_CONFIG_DIR_NAME = '.pi';\nconst AGENT_DIR_NAME = 'agent';\nconst LOG_DIR_NAME = 'logs';\nconst STATE_DIR_NAME = 'runs';\nconst LOG_EXTENSION = '.log';\nconst STATE_EXTENSION = '.json';\nconst ROTATED_SUFFIX = '.1';\nconst PI_CODING_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';\nconst HOME_ALIAS = '~';\nconst HOME_ALIAS_PREFIX = '~/';\nconst FILE_ENCODING = 'utf8';\nconst NOT_FOUND_ERROR_CODE = 'ENOENT';\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\nfunction resolveAgentDirectory(env: NodeJS.ProcessEnv): string {\n const configured = env[PI_CODING_AGENT_DIR_ENV]?.trim();\n if (configured === HOME_ALIAS) return os.homedir();\n if (configured?.startsWith(HOME_ALIAS_PREFIX)) {\n return path.join(os.homedir(), configured.slice(HOME_ALIAS_PREFIX.length));\n }\n return configured ? path.resolve(configured) : path.join(os.homedir(), DEFAULT_CONFIG_DIR_NAME, AGENT_DIR_NAME);\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\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.\n */\nexport function resolveRunnerStoreDirectory(env: NodeJS.ProcessEnv): string {\n return path.join(resolveAgentDirectory(env), STORE_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 repositoryPath(): string {\n this.repository ??= realPath(git(['rev-parse', '--show-toplevel'], process.cwd()) ?? process.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 = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.resolve(override);\n return path.join(this.sessionDirectory(sessionId), LOG_DIR_NAME);\n }\n\n stateDirectory(sessionId?: string): string {\n const override = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.join(path.resolve(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 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 (process.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) continue;\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 (process.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) continue;\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'], process.cwd());\n return commonDirectory ? path.join(commonDirectory, STORE_DIR_NAME) : undefined;\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 /** 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 if (now - newestEntryTime(directory) <= 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 if (now - (await newestEntryTimeAsync(directory)) <= 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();\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 = process.env[LOG_DIR_ENV]?.trim();\n if (override) return path.dirname(path.resolve(override));\n return resolveRunnerStoreDirectory(process.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 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 || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n this.removeHistoryFiles(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\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 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 || now - finishedAt <= ttlMs) continue;\n const id = entry.slice(0, -STATE_EXTENSION.length);\n await this.removeHistoryFilesAsync(sessionId, id, statePath, result);\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [statePath, this.logPathFor(id, sessionId), this.rotatedLogPathFor(id, sessionId)];\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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RunnerPaths.d.cts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;cA8Ia,uBAAuB;UAC1B;UACA;EAER;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EA6BjD,kBAAkB,eAAe,eAA2B,QAAQ;EAyB1E;EAKA;;UAQQ;;UAUA;UAgBM;UAkBN;UAMA;UAMA;UAIA;UAgCM;UAgCN;UAcM"}
1
+ {"version":3,"file":"RunnerPaths.d.cts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;cAwJa,uBAAuB;UAC1B;UACA;EAER;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EA6BjD,kBAAkB,eAAe,eAA2B,QAAQ;EAyB1E;EAKA;;UAQQ;;UAUA;UAgBM;UAkBN;UAMA;UAMA;UAIA;UAgCM;UAgCN;UAcM"}
@@ -1 +1 @@
1
- {"version":3,"file":"RunnerPaths.d.mts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;cA8Ia,uBAAuB;UAC1B;UACA;EAER;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EA6BjD,kBAAkB,eAAe,eAA2B,QAAQ;EAyB1E;EAKA;;UAQQ;;UAUA;UAgBM;UAkBN;UAMA;UAMA;UAIA;UAgCM;UAgCN;UAcM"}
1
+ {"version":3,"file":"RunnerPaths.d.mts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;cAwJa,uBAAuB;UAC1B;UACA;EAER;EAKA,aAAa;EAMb,aAAa;EAMb,eAAe;EAMf,WAAW,YAAY;EAIvB,kBAAkB,YAAY;EAI9B,aAAa,YAAY;EAIzB,kBAAkB;EAKlB,aAAa,eAAe,eAA2B;EA6BjD,kBAAkB,eAAe,eAA2B,QAAQ;EAyB1E;EAKA;;UAQQ;;UAUA;UAgBM;UAkBN;UAMA;UAMA;UAIA;UAgCM;UAgCN;UAcM"}