@agimon-ai/doompi-runner 0.0.1-alpha.44 → 0.0.1-alpha.46
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.
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +1 -1
- package/dist/src/adapters/LogFile/LogFile.cjs +1 -1
- package/dist/src/adapters/LogFile/LogFile.cjs.map +1 -1
- package/dist/src/adapters/LogFile/LogFile.d.cts.map +1 -1
- package/dist/src/adapters/LogFile/LogFile.d.mts.map +1 -1
- package/dist/src/adapters/LogFile/LogFile.mjs +1 -1
- package/dist/src/adapters/LogFile/LogFile.mjs.map +1 -1
- package/dist/src/adapters/RunnerPaths.cjs +1 -1
- package/dist/src/adapters/RunnerPaths.cjs.map +1 -1
- package/dist/src/adapters/RunnerPaths.d.cts +12 -0
- package/dist/src/adapters/RunnerPaths.d.cts.map +1 -1
- package/dist/src/adapters/RunnerPaths.d.mts +12 -0
- package/dist/src/adapters/RunnerPaths.d.mts.map +1 -1
- package/dist/src/adapters/RunnerPaths.mjs +1 -1
- package/dist/src/adapters/RunnerPaths.mjs.map +1 -1
- package/dist/src/adapters/pi/extension.cjs +2 -2
- package/dist/src/adapters/pi/extension.cjs.map +1 -1
- package/dist/src/adapters/pi/extension.d.cts.map +1 -1
- package/dist/src/adapters/pi/extension.d.mts.map +1 -1
- package/dist/src/adapters/pi/extension.mjs +2 -2
- package/dist/src/adapters/pi/extension.mjs.map +1 -1
- package/dist/src/adapters/webRunnerWatcher.cjs +1 -1
- package/dist/src/adapters/webRunnerWatcher.cjs.map +1 -1
- package/dist/src/adapters/webRunnerWatcher.d.cts +18 -1
- package/dist/src/adapters/webRunnerWatcher.d.cts.map +1 -1
- package/dist/src/adapters/webRunnerWatcher.d.mts +18 -1
- package/dist/src/adapters/webRunnerWatcher.d.mts.map +1 -1
- package/dist/src/adapters/webRunnerWatcher.mjs +1 -1
- package/dist/src/adapters/webRunnerWatcher.mjs.map +1 -1
- package/dist/src/types/config.cjs +1 -1
- package/dist/src/types/config.cjs.map +1 -1
- package/dist/src/types/config.d.cts +12 -1
- package/dist/src/types/config.d.cts.map +1 -1
- package/dist/src/types/config.d.mts +12 -1
- package/dist/src/types/config.d.mts.map +1 -1
- package/dist/src/types/config.mjs +1 -1
- package/dist/src/types/config.mjs.map +1 -1
- package/dist/src/types/logFile.d.cts +1 -1
- package/dist/src/types/logFile.d.mts +1 -1
- package/dist/src/types/webRunnerLog.cjs.map +1 -1
- package/dist/src/types/webRunnerLog.mjs.map +1 -1
- package/package.json +19 -18
- package/src/exports/webClient.ts +1 -0
- package/src/types/webRunnerLog.ts +1 -1
- package/{web → src/web}/RunnerLogPanel.tsx +2 -2
- package/{web → src/web}/RunnersActivitySection.tsx +1 -1
- package/{web → src/web}/logApi.ts +1 -1
- package/{web → src/web}/runnersStore.ts +1 -1
- package/{web → src/web}/tsconfig.json +1 -1
- /package/{web → src/web}/BashToolMessage.tsx +0 -0
- /package/{web → src/web}/bashToolFormat.ts +0 -0
- /package/{web → src/web}/format.ts +0 -0
- /package/{web → src/web}/index.ts +0 -0
package/dist/config.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src/types/config.cjs");exports.BG_THRESHOLD_MS_ENV=e.BG_THRESHOLD_MS_ENV,exports.DEFAULT_BG_THRESHOLD_MS=e.DEFAULT_BG_THRESHOLD_MS,exports.DEFAULT_ERROR_BUDGET_RATIO=e.DEFAULT_ERROR_BUDGET_RATIO,exports.DEFAULT_ERROR_MAX_ENTRIES=e.DEFAULT_ERROR_MAX_ENTRIES,exports.DEFAULT_ERROR_MAX_VARIANTS_JOINED=e.DEFAULT_ERROR_MAX_VARIANTS_JOINED,exports.DEFAULT_HEAD_RATIO=e.DEFAULT_HEAD_RATIO,exports.DEFAULT_LOG_MAX_BYTES=e.DEFAULT_LOG_MAX_BYTES,exports.DEFAULT_LOG_TTL_MS=e.DEFAULT_LOG_TTL_MS,exports.DEFAULT_RESULT_MAX_BYTES=e.DEFAULT_RESULT_MAX_BYTES,exports.DEFAULT_RESULT_MAX_LINES=e.DEFAULT_RESULT_MAX_LINES,exports.DEFAULT_RESULT_MAX_TOKENS=e.DEFAULT_RESULT_MAX_TOKENS,exports.DEFAULT_SUCCESS_RESULT_MAX_BYTES=e.DEFAULT_SUCCESS_RESULT_MAX_BYTES,exports.DEFAULT_SUCCESS_RESULT_MAX_TOKENS=e.DEFAULT_SUCCESS_RESULT_MAX_TOKENS,exports.LOG_DIR_ENV=e.LOG_DIR_ENV,exports.LOG_MAX_BYTES_ENV=e.LOG_MAX_BYTES_ENV,exports.LOG_TTL_MS_ENV=e.LOG_TTL_MS_ENV,exports.RESULT_MAX_BYTES_ENV=e.RESULT_MAX_BYTES_ENV,exports.RESULT_MAX_TOKENS_ENV=e.RESULT_MAX_TOKENS_ENV,exports.SUCCESS_RESULT_MAX_BYTES_ENV=e.SUCCESS_RESULT_MAX_BYTES_ENV,exports.SUCCESS_RESULT_MAX_TOKENS_ENV=e.SUCCESS_RESULT_MAX_TOKENS_ENV,exports.getBackgroundThresholdMs=e.getBackgroundThresholdMs,exports.getErrorBudgetRatio=e.getErrorBudgetRatio,exports.getErrorMaxEntries=e.getErrorMaxEntries,exports.getErrorMaxVariantsJoined=e.getErrorMaxVariantsJoined,exports.getErrorPatterns=e.getErrorPatterns,exports.getHeadRatio=e.getHeadRatio,exports.getLogMaxBytes=e.getLogMaxBytes,exports.getLogTtlMs=e.getLogTtlMs,exports.getResultMaxBytes=e.getResultMaxBytes,exports.getResultMaxLines=e.getResultMaxLines,exports.getResultMaxTokens=e.getResultMaxTokens,exports.getRunnerSettings=e.getRunnerSettings,exports.getSuccessResultMaxBytes=e.getSuccessResultMaxBytes,exports.getSuccessResultMaxTokens=e.getSuccessResultMaxTokens,exports.setRunnerSettings=e.setRunnerSettings;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./src/types/config.cjs");exports.BG_THRESHOLD_MS_ENV=e.BG_THRESHOLD_MS_ENV,exports.DEFAULT_BG_THRESHOLD_MS=e.DEFAULT_BG_THRESHOLD_MS,exports.DEFAULT_ERROR_BUDGET_RATIO=e.DEFAULT_ERROR_BUDGET_RATIO,exports.DEFAULT_ERROR_MAX_ENTRIES=e.DEFAULT_ERROR_MAX_ENTRIES,exports.DEFAULT_ERROR_MAX_VARIANTS_JOINED=e.DEFAULT_ERROR_MAX_VARIANTS_JOINED,exports.DEFAULT_HEAD_RATIO=e.DEFAULT_HEAD_RATIO,exports.DEFAULT_LOG_MAX_BYTES=e.DEFAULT_LOG_MAX_BYTES,exports.DEFAULT_LOG_TTL_MS=e.DEFAULT_LOG_TTL_MS,exports.DEFAULT_MAX_COMPLETED_RUNNERS=e.DEFAULT_MAX_COMPLETED_RUNNERS,exports.DEFAULT_RESULT_MAX_BYTES=e.DEFAULT_RESULT_MAX_BYTES,exports.DEFAULT_RESULT_MAX_LINES=e.DEFAULT_RESULT_MAX_LINES,exports.DEFAULT_RESULT_MAX_TOKENS=e.DEFAULT_RESULT_MAX_TOKENS,exports.DEFAULT_SUCCESS_RESULT_MAX_BYTES=e.DEFAULT_SUCCESS_RESULT_MAX_BYTES,exports.DEFAULT_SUCCESS_RESULT_MAX_TOKENS=e.DEFAULT_SUCCESS_RESULT_MAX_TOKENS,exports.LOG_DIR_ENV=e.LOG_DIR_ENV,exports.LOG_MAX_BYTES_ENV=e.LOG_MAX_BYTES_ENV,exports.LOG_TTL_MS_ENV=e.LOG_TTL_MS_ENV,exports.MAX_COMPLETED_RUNNERS_ENV=e.MAX_COMPLETED_RUNNERS_ENV,exports.RESULT_MAX_BYTES_ENV=e.RESULT_MAX_BYTES_ENV,exports.RESULT_MAX_TOKENS_ENV=e.RESULT_MAX_TOKENS_ENV,exports.SUCCESS_RESULT_MAX_BYTES_ENV=e.SUCCESS_RESULT_MAX_BYTES_ENV,exports.SUCCESS_RESULT_MAX_TOKENS_ENV=e.SUCCESS_RESULT_MAX_TOKENS_ENV,exports.getBackgroundThresholdMs=e.getBackgroundThresholdMs,exports.getErrorBudgetRatio=e.getErrorBudgetRatio,exports.getErrorMaxEntries=e.getErrorMaxEntries,exports.getErrorMaxVariantsJoined=e.getErrorMaxVariantsJoined,exports.getErrorPatterns=e.getErrorPatterns,exports.getHeadRatio=e.getHeadRatio,exports.getLogMaxBytes=e.getLogMaxBytes,exports.getLogTtlMs=e.getLogTtlMs,exports.getMaxCompletedRunners=e.getMaxCompletedRunners,exports.getResultMaxBytes=e.getResultMaxBytes,exports.getResultMaxLines=e.getResultMaxLines,exports.getResultMaxTokens=e.getResultMaxTokens,exports.getRunnerSettings=e.getRunnerSettings,exports.getSuccessResultMaxBytes=e.getSuccessResultMaxBytes,exports.getSuccessResultMaxTokens=e.getSuccessResultMaxTokens,exports.setRunnerSettings=e.setRunnerSettings;
|
package/dist/config.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings } from "./src/types/config.cjs";
|
|
2
|
-
export { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings };
|
|
1
|
+
import { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_MAX_COMPLETED_RUNNERS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, MAX_COMPLETED_RUNNERS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getMaxCompletedRunners, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings } from "./src/types/config.cjs";
|
|
2
|
+
export { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_MAX_COMPLETED_RUNNERS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, MAX_COMPLETED_RUNNERS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getMaxCompletedRunners, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings };
|
package/dist/config.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings } from "./src/types/config.mjs";
|
|
2
|
-
export { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings };
|
|
1
|
+
import { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_MAX_COMPLETED_RUNNERS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, MAX_COMPLETED_RUNNERS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getMaxCompletedRunners, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings } from "./src/types/config.mjs";
|
|
2
|
+
export { BG_THRESHOLD_MS_ENV, DEFAULT_BG_THRESHOLD_MS, DEFAULT_ERROR_BUDGET_RATIO, DEFAULT_ERROR_MAX_ENTRIES, DEFAULT_ERROR_MAX_VARIANTS_JOINED, DEFAULT_HEAD_RATIO, DEFAULT_LOG_MAX_BYTES, DEFAULT_LOG_TTL_MS, DEFAULT_MAX_COMPLETED_RUNNERS, DEFAULT_RESULT_MAX_BYTES, DEFAULT_RESULT_MAX_LINES, DEFAULT_RESULT_MAX_TOKENS, DEFAULT_SUCCESS_RESULT_MAX_BYTES, DEFAULT_SUCCESS_RESULT_MAX_TOKENS, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, MAX_COMPLETED_RUNNERS_ENV, RESULT_MAX_BYTES_ENV, RESULT_MAX_TOKENS_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_TOKENS_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getMaxCompletedRunners, getResultMaxBytes, getResultMaxLines, getResultMaxTokens, getRunnerSettings, getSuccessResultMaxBytes, getSuccessResultMaxTokens, setRunnerSettings };
|
package/dist/config.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BG_THRESHOLD_MS_ENV as e,DEFAULT_BG_THRESHOLD_MS as t,DEFAULT_ERROR_BUDGET_RATIO as n,DEFAULT_ERROR_MAX_ENTRIES as r,DEFAULT_ERROR_MAX_VARIANTS_JOINED as i,DEFAULT_HEAD_RATIO as a,DEFAULT_LOG_MAX_BYTES as o,DEFAULT_LOG_TTL_MS as s,
|
|
1
|
+
import{BG_THRESHOLD_MS_ENV as e,DEFAULT_BG_THRESHOLD_MS as t,DEFAULT_ERROR_BUDGET_RATIO as n,DEFAULT_ERROR_MAX_ENTRIES as r,DEFAULT_ERROR_MAX_VARIANTS_JOINED as i,DEFAULT_HEAD_RATIO as a,DEFAULT_LOG_MAX_BYTES as o,DEFAULT_LOG_TTL_MS as s,DEFAULT_MAX_COMPLETED_RUNNERS as c,DEFAULT_RESULT_MAX_BYTES as l,DEFAULT_RESULT_MAX_LINES as u,DEFAULT_RESULT_MAX_TOKENS as d,DEFAULT_SUCCESS_RESULT_MAX_BYTES as f,DEFAULT_SUCCESS_RESULT_MAX_TOKENS as p,LOG_DIR_ENV as m,LOG_MAX_BYTES_ENV as h,LOG_TTL_MS_ENV as g,MAX_COMPLETED_RUNNERS_ENV as _,RESULT_MAX_BYTES_ENV as v,RESULT_MAX_TOKENS_ENV as y,SUCCESS_RESULT_MAX_BYTES_ENV as b,SUCCESS_RESULT_MAX_TOKENS_ENV as x,getBackgroundThresholdMs as S,getErrorBudgetRatio as C,getErrorMaxEntries as w,getErrorMaxVariantsJoined as T,getErrorPatterns as E,getHeadRatio as D,getLogMaxBytes as O,getLogTtlMs as k,getMaxCompletedRunners as A,getResultMaxBytes as j,getResultMaxLines as M,getResultMaxTokens as N,getRunnerSettings as P,getSuccessResultMaxBytes as F,getSuccessResultMaxTokens as I,setRunnerSettings as L}from"./src/types/config.mjs";export{e as BG_THRESHOLD_MS_ENV,t as DEFAULT_BG_THRESHOLD_MS,n as DEFAULT_ERROR_BUDGET_RATIO,r as DEFAULT_ERROR_MAX_ENTRIES,i as DEFAULT_ERROR_MAX_VARIANTS_JOINED,a as DEFAULT_HEAD_RATIO,o as DEFAULT_LOG_MAX_BYTES,s as DEFAULT_LOG_TTL_MS,c as DEFAULT_MAX_COMPLETED_RUNNERS,l as DEFAULT_RESULT_MAX_BYTES,u as DEFAULT_RESULT_MAX_LINES,d as DEFAULT_RESULT_MAX_TOKENS,f as DEFAULT_SUCCESS_RESULT_MAX_BYTES,p as DEFAULT_SUCCESS_RESULT_MAX_TOKENS,m as LOG_DIR_ENV,h as LOG_MAX_BYTES_ENV,g as LOG_TTL_MS_ENV,_ as MAX_COMPLETED_RUNNERS_ENV,v as RESULT_MAX_BYTES_ENV,y as RESULT_MAX_TOKENS_ENV,b as SUCCESS_RESULT_MAX_BYTES_ENV,x as SUCCESS_RESULT_MAX_TOKENS_ENV,S as getBackgroundThresholdMs,C as getErrorBudgetRatio,w as getErrorMaxEntries,T as getErrorMaxVariantsJoined,E as getErrorPatterns,D as getHeadRatio,O as getLogMaxBytes,k as getLogTtlMs,A as getMaxCompletedRunners,j as getResultMaxBytes,M as getResultMaxLines,N as getResultMaxTokens,P as getRunnerSettings,F as getSuccessResultMaxBytes,I as getSuccessResultMaxTokens,L as setRunnerSettings};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../../_virtual/_rolldown/runtime.cjs");let
|
|
1
|
+
const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs");let n=require("node:fs");n=e.__toESM(n,1);var r=class{paths;constructor(e){this.paths=e}open(e){this.paths.ensureDirectories();let r=this.paths.logPathFor(e),i=this.paths.rotatedLogPathFor(e),a=t.getLogMaxBytes(),o=n.default.openSync(r,`w`),s=0,c=!1,l=()=>{n.default.closeSync(o);try{n.default.renameSync(r,i)}catch(e){process.emitWarning(`Could not rotate runner log ${r}: ${String(e)}`)}o=n.default.openSync(r,`w`),s=0};return{path:r,append(e){let t=Buffer.from(e,`utf8`);s>0&&s+t.byteLength>a&&l(),n.default.writeSync(o,t),s+=t.byteLength},size(){return s},close(){if(!c){c=!0;try{n.default.closeSync(o)}catch(e){process.emitWarning(`Could not close runner log ${r}: ${String(e)}`)}}}}}};exports.LogFile=r;
|
|
2
2
|
//# sourceMappingURL=LogFile.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogFile.cjs","names":["fs"],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport type { ILogFile, LogWriter } from '../../types/logFile';\n\nexport class LogFile implements ILogFile {\n constructor(private readonly paths: IRunnerPaths) {}\n\n open(id: string): LogWriter {\n this.paths.ensureDirectories();\n const logPath = this.paths.logPathFor(id);\n\n // A reused name starts clean: the previous runner's output is already\n // available to whoever wanted it, and mixing two runs in one file makes\n // both unreadable.\n
|
|
1
|
+
{"version":3,"file":"LogFile.cjs","names":["getLogMaxBytes","fs"],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport { getLogMaxBytes } from '../../types/config.ts';\nimport type { ILogFile, LogWriter } from '../../types/logFile';\n\nexport class LogFile implements ILogFile {\n constructor(private readonly paths: IRunnerPaths) {}\n\n open(id: string): LogWriter {\n this.paths.ensureDirectories();\n const logPath = this.paths.logPathFor(id);\n const rotatedPath = this.paths.rotatedLogPathFor(id);\n const maxBytes = getLogMaxBytes();\n\n // A reused name starts clean: the previous runner's output is already\n // available to whoever wanted it, and mixing two runs in one file makes\n // both unreadable.\n let handle = fs.openSync(logPath, 'w');\n let written = 0;\n let closed = false;\n\n /**\n * A run that keeps producing output for hours would otherwise grow one file\n * without limit. Rotating keeps the newest window at the advertised path\n * and the window before it alongside, so on-disk cost is bounded at roughly\n * twice the ceiling, the path a result advertises stays valid, and the line\n * counts a reader reports still describe the file it actually read.\n */\n const rotate = (): void => {\n fs.closeSync(handle);\n try {\n fs.renameSync(logPath, rotatedPath);\n } catch (error) {\n // Holding the ceiling matters more than keeping the previous window,\n // so the new file is opened either way and the loss is reported.\n process.emitWarning(`Could not rotate runner log ${logPath}: ${String(error)}`);\n }\n handle = fs.openSync(logPath, 'w');\n written = 0;\n };\n\n return {\n path: logPath,\n append(text: string): void {\n const chunk = Buffer.from(text, 'utf8');\n // An empty file always takes the chunk, so a single write larger than\n // the ceiling is stored rather than rotated away into nothing.\n if (written > 0 && written + chunk.byteLength > maxBytes) rotate();\n fs.writeSync(handle, chunk);\n written += chunk.byteLength;\n },\n size(): number {\n return written;\n },\n close(): void {\n // Closing twice is normal: a launch that fails closes the log before\n // the exit handler does. Only a first close that fails is news.\n if (closed) return;\n closed = true;\n try {\n fs.closeSync(handle);\n } catch (error) {\n process.emitWarning(`Could not close runner log ${logPath}: ${String(error)}`);\n }\n },\n };\n }\n}\n"],"mappings":"yIAKA,IAAa,EAAb,KAAyC,CACV,MAA7B,YAAY,EAAsC,CAArB,KAAA,MAAA,CAAsB,CAEnD,KAAK,EAAuB,CAC1B,KAAK,MAAM,kBAAkB,EAC7B,IAAM,EAAU,KAAK,MAAM,WAAW,CAAE,EAClC,EAAc,KAAK,MAAM,kBAAkB,CAAE,EAC7C,EAAWA,EAAAA,eAAe,EAK5B,EAASC,EAAAA,QAAG,SAAS,EAAS,GAAG,EACjC,EAAU,EACV,EAAS,GASP,MAAqB,CACzB,EAAA,QAAG,UAAU,CAAM,EACnB,GAAI,CACF,EAAA,QAAG,WAAW,EAAS,CAAW,CACpC,OAAS,EAAO,CAGd,QAAQ,YAAY,+BAA+B,EAAQ,IAAI,OAAO,CAAK,GAAG,CAChF,CACA,EAASA,EAAAA,QAAG,SAAS,EAAS,GAAG,EACjC,EAAU,CACZ,EAEA,MAAO,CACL,KAAM,EACN,OAAO,EAAoB,CACzB,IAAM,EAAQ,OAAO,KAAK,EAAM,MAAM,EAGlC,EAAU,GAAK,EAAU,EAAM,WAAa,GAAU,EAAO,EACjE,EAAA,QAAG,UAAU,EAAQ,CAAK,EAC1B,GAAW,EAAM,UACnB,EACA,MAAe,CACb,OAAO,CACT,EACA,OAAc,CAGR,MACJ,GAAS,GACT,GAAI,CACF,EAAA,QAAG,UAAU,CAAM,CACrB,OAAS,EAAO,CACd,QAAQ,YAAY,8BAA8B,EAAQ,IAAI,OAAO,CAAK,GAAG,CAC/E,CALS,CAMX,CACF,CACF,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogFile.d.cts","names":[],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"LogFile.d.cts","names":[],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"mappings":";;;cAKa,mBAAmB;mBACD;EAA7B,YAA6B,OAAO;EAEpC,KAAK,aAAa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogFile.d.mts","names":[],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"LogFile.d.mts","names":[],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"mappings":";;;cAKa,mBAAmB;mBACD;EAA7B,YAA6B,OAAO;EAEpC,KAAK,aAAa"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"node:fs";var
|
|
1
|
+
import{getLogMaxBytes as e}from"../../types/config.mjs";import t from"node:fs";var n=class{paths;constructor(e){this.paths=e}open(n){this.paths.ensureDirectories();let r=this.paths.logPathFor(n),i=this.paths.rotatedLogPathFor(n),a=e(),o=t.openSync(r,`w`),s=0,c=!1,l=()=>{t.closeSync(o);try{t.renameSync(r,i)}catch(e){process.emitWarning(`Could not rotate runner log ${r}: ${String(e)}`)}o=t.openSync(r,`w`),s=0};return{path:r,append(e){let n=Buffer.from(e,`utf8`);s>0&&s+n.byteLength>a&&l(),t.writeSync(o,n),s+=n.byteLength},size(){return s},close(){if(!c){c=!0;try{t.closeSync(o)}catch(e){process.emitWarning(`Could not close runner log ${r}: ${String(e)}`)}}}}}};export{n as LogFile};
|
|
2
2
|
//# sourceMappingURL=LogFile.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogFile.mjs","names":[],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport type { ILogFile, LogWriter } from '../../types/logFile';\n\nexport class LogFile implements ILogFile {\n constructor(private readonly paths: IRunnerPaths) {}\n\n open(id: string): LogWriter {\n this.paths.ensureDirectories();\n const logPath = this.paths.logPathFor(id);\n\n // A reused name starts clean: the previous runner's output is already\n // available to whoever wanted it, and mixing two runs in one file makes\n // both unreadable.\n
|
|
1
|
+
{"version":3,"file":"LogFile.mjs","names":[],"sources":["../../../../src/adapters/LogFile/LogFile.ts"],"sourcesContent":["import fs from 'node:fs';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport { getLogMaxBytes } from '../../types/config.ts';\nimport type { ILogFile, LogWriter } from '../../types/logFile';\n\nexport class LogFile implements ILogFile {\n constructor(private readonly paths: IRunnerPaths) {}\n\n open(id: string): LogWriter {\n this.paths.ensureDirectories();\n const logPath = this.paths.logPathFor(id);\n const rotatedPath = this.paths.rotatedLogPathFor(id);\n const maxBytes = getLogMaxBytes();\n\n // A reused name starts clean: the previous runner's output is already\n // available to whoever wanted it, and mixing two runs in one file makes\n // both unreadable.\n let handle = fs.openSync(logPath, 'w');\n let written = 0;\n let closed = false;\n\n /**\n * A run that keeps producing output for hours would otherwise grow one file\n * without limit. Rotating keeps the newest window at the advertised path\n * and the window before it alongside, so on-disk cost is bounded at roughly\n * twice the ceiling, the path a result advertises stays valid, and the line\n * counts a reader reports still describe the file it actually read.\n */\n const rotate = (): void => {\n fs.closeSync(handle);\n try {\n fs.renameSync(logPath, rotatedPath);\n } catch (error) {\n // Holding the ceiling matters more than keeping the previous window,\n // so the new file is opened either way and the loss is reported.\n process.emitWarning(`Could not rotate runner log ${logPath}: ${String(error)}`);\n }\n handle = fs.openSync(logPath, 'w');\n written = 0;\n };\n\n return {\n path: logPath,\n append(text: string): void {\n const chunk = Buffer.from(text, 'utf8');\n // An empty file always takes the chunk, so a single write larger than\n // the ceiling is stored rather than rotated away into nothing.\n if (written > 0 && written + chunk.byteLength > maxBytes) rotate();\n fs.writeSync(handle, chunk);\n written += chunk.byteLength;\n },\n size(): number {\n return written;\n },\n close(): void {\n // Closing twice is normal: a launch that fails closes the log before\n // the exit handler does. Only a first close that fails is news.\n if (closed) return;\n closed = true;\n try {\n fs.closeSync(handle);\n } catch (error) {\n process.emitWarning(`Could not close runner log ${logPath}: ${String(error)}`);\n }\n },\n };\n }\n}\n"],"mappings":"+EAKA,IAAa,EAAb,KAAyC,CACV,MAA7B,YAAY,EAAsC,CAArB,KAAA,MAAA,CAAsB,CAEnD,KAAK,EAAuB,CAC1B,KAAK,MAAM,kBAAkB,EAC7B,IAAM,EAAU,KAAK,MAAM,WAAW,CAAE,EAClC,EAAc,KAAK,MAAM,kBAAkB,CAAE,EAC7C,EAAW,EAAe,EAK5B,EAAS,EAAG,SAAS,EAAS,GAAG,EACjC,EAAU,EACV,EAAS,GASP,MAAqB,CACzB,EAAG,UAAU,CAAM,EACnB,GAAI,CACF,EAAG,WAAW,EAAS,CAAW,CACpC,OAAS,EAAO,CAGd,QAAQ,YAAY,+BAA+B,EAAQ,IAAI,OAAO,CAAK,GAAG,CAChF,CACA,EAAS,EAAG,SAAS,EAAS,GAAG,EACjC,EAAU,CACZ,EAEA,MAAO,CACL,KAAM,EACN,OAAO,EAAoB,CACzB,IAAM,EAAQ,OAAO,KAAK,EAAM,MAAM,EAGlC,EAAU,GAAK,EAAU,EAAM,WAAa,GAAU,EAAO,EACjE,EAAG,UAAU,EAAQ,CAAK,EAC1B,GAAW,EAAM,UACnB,EACA,MAAe,CACb,OAAO,CACT,EACA,OAAc,CAGR,MACJ,GAAS,GACT,GAAI,CACF,EAAG,UAAU,CAAM,CACrB,OAAS,EAAO,CACd,QAAQ,YAAY,8BAA8B,EAAQ,IAAI,OAAO,CAAK,GAAG,CAC/E,CALS,CAMX,CACF,CACF,CACF"}
|
|
@@ -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
|
|
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`,f=3e5;function p(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 m(e){try{return r.default.realpathSync(e)}catch{return e}}function h(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 g(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 _(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 v(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 y(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 b(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 x(e,t,n,r){return[...e].sort((e,t)=>t.finishedAt-e.finishedAt).filter((e,i)=>i>=r||n-e.finishedAt>t)}function S(e){for(let t of r.default.readdirSync(e,{withFileTypes:!0}))if(!t.isDirectory()||r.default.readdirSync(i.default.join(e,t.name)).length>0)return!1;return!0}async function C(e){for(let t of await r.default.promises.readdir(e,{withFileTypes:!0}))if(!t.isDirectory()||(await r.default.promises.readdir(i.default.join(e,t.name))).length>0)return!1;return!0}function w(e){return i.default.join(h(e),s)}var T=class{adoptedSessionId;repository;repositoryPath(){return this.repository??=m(p([`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){if(r===s){this.sweepActiveSession(r,e,t,n);continue}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){if(r===s){await this.sweepActiveSessionAsync(r,e,t,n);continue}await this.sweepSessionDirectoryAsync(i.default.join(a,r),e,t,n)}return n}legacyDirectory(){let e=p([`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}}sweepActiveSession(e,t,n,r){this.sweepStateDirectory(this.stateDirectory(e),e,t,n,r)}async sweepActiveSessionAsync(e,t,n,r){await this.sweepStateDirectoryAsync(this.stateDirectory(e),e,t,n,r)}sweepSessionDirectory(e,t,n,a){try{let o=g(e);if(n-o>f&&S(e)){r.default.rmSync(e,{recursive:!0,force:!0}),a.removed.push(e);return}if(n-o<=t||v(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{let o=await _(e);if(n-o>f&&await C(e)){await r.default.promises.rm(e,{recursive:!0,force:!0}),a.removed.push(e);return}if(n-o<=t||await y(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)):w(process.env)}sessionDirectory(e){return i.default.join(this.storeDirectory(),this.sessionId(e))}sweepStateDirectory(e,n,a,o,s){let c;try{c=r.default.readdirSync(e)}catch(t){t.code!==d&&s.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}let f=[];for(let t of c){if(!t.endsWith(l))continue;let n=i.default.join(e,t);try{let e=b(JSON.parse(r.default.readFileSync(n,u)));if(e===void 0)continue;f.push({id:t.slice(0,-5),statePath:n,finishedAt:e})}catch(e){s.errors.push(`Could not sweep runner metadata ${n}: ${String(e)}`)}}for(let e of x(f,a,o,t.getMaxCompletedRunners()))this.removeHistoryFiles(n,e.id,e.statePath,s)}async sweepStateDirectoryAsync(e,n,a,o,s){let c;try{c=await r.default.promises.readdir(e)}catch(t){t.code!==d&&s.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}let f=[];for(let t of c){if(!t.endsWith(l))continue;let n=i.default.join(e,t);try{let e=b(JSON.parse(await r.default.promises.readFile(n,u)));if(e===void 0)continue;f.push({id:t.slice(0,-5),statePath:n,finishedAt:e})}catch(e){s.errors.push(`Could not sweep runner metadata ${n}: ${String(e)}`)}}for(let e of x(f,a,o,t.getMaxCompletedRunners()))await this.removeHistoryFilesAsync(n,e.id,e.statePath,s)}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=T,exports.resolveRunnerStoreDirectory=w;
|
|
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/**\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
|
+
{"version":3,"file":"RunnerPaths.cjs","names":["execFileSync","fs","os","path","LOG_DIR_ENV","inheritedSessionId","getMaxCompletedRunners"],"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 { getMaxCompletedRunners, 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/**\n * How long an entirely empty session directory is left alone before it counts\n * as an orphan. A session that has started but not yet run anything owns such a\n * directory for a moment, and reclaiming it out from under that session would\n * be churn for no gain.\n */\nconst EMPTY_SESSION_GRACE_MS = 5 * 60 * 1000;\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\ninterface CompletedRecord {\n readonly id: string;\n readonly statePath: string;\n readonly finishedAt: number;\n}\n\n/**\n * Completed records a sweep may delete: everything past the TTL, and everything\n * beyond the newest `keep`.\n *\n * The count bound is what keeps a live session from growing without limit. Its\n * records only reach a multi-day TTL long after the session that is still\n * writing them would have filled its directory, so age alone bounds nothing\n * while the session lasts.\n */\nfunction expiredCompletedRecords(\n records: readonly CompletedRecord[],\n ttlMs: number,\n now: number,\n keep: number,\n): CompletedRecord[] {\n return [...records]\n .sort((left, right) => right.finishedAt - left.finishedAt)\n .filter((record, index) => index >= keep || now - record.finishedAt > ttlMs);\n}\n\n/**\n * Whether the directory holds no files at all, only empty subdirectories.\n *\n * A session that ran something leaves metadata behind, and a live session keeps\n * its lifeline socket in the state directory, so an entirely empty tree names\n * no owner and no history.\n */\nfunction isEmptySession(directory: string): boolean {\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if (fs.readdirSync(path.join(directory, entry.name)).length > 0) return false;\n }\n return true;\n}\n\nasync function isEmptySessionAsync(directory: string): Promise<boolean> {\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if ((await fs.promises.readdir(path.join(directory, entry.name))).length > 0) return false;\n }\n return true;\n}\n\n/**\n * The root every session's runner state lives under, before any log-directory\n * override: `<agent dir>/doom-runner/<session id>/{runs,logs}`. A reader in\n * another process, such as the cockpit hub, resolves the same root from its\n * own environment.\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) {\n this.sweepActiveSession(sessionId, ttlMs, now, result);\n continue;\n }\n this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n async sweepHistoryAsync(ttlMs: number, now: number = Date.now()): Promise<LogSweepResult> {\n const result: LogSweepResult = { removed: [], errors: [] };\n if (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) {\n await this.sweepActiveSessionAsync(sessionId, ttlMs, now, result);\n continue;\n }\n await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n legacyDirectory(): string | undefined {\n const commonDirectory = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], 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 /**\n * Retention inside the session that is still running.\n *\n * The whole-directory sweep cannot be pointed at the live session: it would\n * take the lifeline socket, the supervisor sidecars and the running runners'\n * own metadata with it. Per-record retention is safe there because it only\n * ever removes a record whose state is `completed` together with the two log\n * files that record names, and never the directory itself. A running record\n * has no `exit.finishedAt`, so it is not a candidate at any age.\n */\n private sweepActiveSession(sessionId: string, ttlMs: number, now: number, result: LogSweepResult): void {\n this.sweepStateDirectory(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n private async sweepActiveSessionAsync(\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n await this.sweepStateDirectoryAsync(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n /** Removes a session's whole directory once its newest entry is past the TTL. */\n private sweepSessionDirectory(directory: string, ttlMs: number, now: number, result: LogSweepResult): void {\n try {\n const newest = newestEntryTime(directory);\n // An orphan left by a session that never ran anything holds nothing worth\n // a retention window, so it goes as soon as it is clearly not in use.\n if (now - newest > EMPTY_SESSION_GRACE_MS && isEmptySession(directory)) {\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n // An idle session can go quiet for longer than the TTL, and deleting one\n // takes its lifeline socket with it, which would read to its own runners\n // as an owner that had died. Its records name the owner, so ask.\n if (ownerAlive(path.join(directory, STATE_DIR_NAME))) return;\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private async sweepSessionDirectoryAsync(\n directory: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n try {\n const newest = await newestEntryTimeAsync(directory);\n if (now - newest > EMPTY_SESSION_GRACE_MS && (await isEmptySessionAsync(directory))) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n if (await ownerAliveAsync(path.join(directory, STATE_DIR_NAME))) return;\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private sessionId(explicit?: string): string {\n const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId();\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 const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(fs.readFileSync(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners())) {\n this.removeHistoryFiles(sessionId, record.id, record.statePath, result);\n }\n }\n\n private async sweepStateDirectoryAsync(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners())) {\n await this.removeHistoryFilesAsync(sessionId, record.id, record.statePath, result);\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [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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"}
|
|
@@ -18,6 +18,18 @@ declare class RunnerPaths implements IRunnerPaths {
|
|
|
18
18
|
removeLegacyStore(): string | undefined;
|
|
19
19
|
/** The live session, if one is known, so a sweep never deletes underneath itself. */
|
|
20
20
|
private currentSessionId;
|
|
21
|
+
/**
|
|
22
|
+
* Retention inside the session that is still running.
|
|
23
|
+
*
|
|
24
|
+
* The whole-directory sweep cannot be pointed at the live session: it would
|
|
25
|
+
* take the lifeline socket, the supervisor sidecars and the running runners'
|
|
26
|
+
* own metadata with it. Per-record retention is safe there because it only
|
|
27
|
+
* ever removes a record whose state is `completed` together with the two log
|
|
28
|
+
* files that record names, and never the directory itself. A running record
|
|
29
|
+
* has no `exit.finishedAt`, so it is not a candidate at any age.
|
|
30
|
+
*/
|
|
31
|
+
private sweepActiveSession;
|
|
32
|
+
private sweepActiveSessionAsync;
|
|
21
33
|
/** Removes a session's whole directory once its newest entry is past the TTL. */
|
|
22
34
|
private sweepSessionDirectory;
|
|
23
35
|
private sweepSessionDirectoryAsync;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerPaths.d.cts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"RunnerPaths.d.cts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;cAgNa,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;EAgCjD,kBAAkB,eAAe,eAA2B,QAAQ;EA4B1E;EAKA;;UAQQ;;;;;;;;;;;UAmBA;UAIM;;UAUN;UAwBM;UAwBN;UAMA;UAMA;UAIA;UAoCM;UAoCN;UAcM"}
|
|
@@ -18,6 +18,18 @@ declare class RunnerPaths implements IRunnerPaths {
|
|
|
18
18
|
removeLegacyStore(): string | undefined;
|
|
19
19
|
/** The live session, if one is known, so a sweep never deletes underneath itself. */
|
|
20
20
|
private currentSessionId;
|
|
21
|
+
/**
|
|
22
|
+
* Retention inside the session that is still running.
|
|
23
|
+
*
|
|
24
|
+
* The whole-directory sweep cannot be pointed at the live session: it would
|
|
25
|
+
* take the lifeline socket, the supervisor sidecars and the running runners'
|
|
26
|
+
* own metadata with it. Per-record retention is safe there because it only
|
|
27
|
+
* ever removes a record whose state is `completed` together with the two log
|
|
28
|
+
* files that record names, and never the directory itself. A running record
|
|
29
|
+
* has no `exit.finishedAt`, so it is not a candidate at any age.
|
|
30
|
+
*/
|
|
31
|
+
private sweepActiveSession;
|
|
32
|
+
private sweepActiveSessionAsync;
|
|
21
33
|
/** Removes a session's whole directory once its newest entry is past the TTL. */
|
|
22
34
|
private sweepSessionDirectory;
|
|
23
35
|
private sweepSessionDirectoryAsync;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerPaths.d.mts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"RunnerPaths.d.mts","names":[],"sources":["../../../src/adapters/RunnerPaths.ts"],"mappings":";;;cAgNa,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;EAgCjD,kBAAkB,eAAe,eAA2B,QAAQ;EA4B1E;EAKA;;UAQQ;;;;;;;;;;;UAmBA;UAIM;;UAUN;UAwBM;UAwBN;UAMA;UAMA;UAIA;UAoCM;UAoCN;UAcM"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{LOG_DIR_ENV as e}from"../types/config.mjs";import{inheritedSessionId as
|
|
1
|
+
import{LOG_DIR_ENV as e,getMaxCompletedRunners as t}from"../types/config.mjs";import{inheritedSessionId as n}from"../services/runs/session.mjs";import r from"node:fs";import i from"node:path";import a from"node:os";import{execFileSync as o}from"node:child_process";const s=`doom-runner`,c=`runs`,l=`.json`,u=`utf8`,d=`ENOENT`,f=3e5;function p(e,t){try{let n=o(`git`,e,{cwd:t,encoding:u,stdio:[`ignore`,`pipe`,`ignore`]}).trim();return n.length>0?n:void 0}catch{return}}function m(e){try{return r.realpathSync(e)}catch{return e}}function h(e){let t=e.PI_CODING_AGENT_DIR?.trim();return t===`~`?a.homedir():t?.startsWith(`~/`)?i.join(a.homedir(),t.slice(2)):t?i.resolve(t):i.join(a.homedir(),`.pi`,`agent`)}function g(e){let t=r.statSync(e).mtimeMs;for(let n of r.readdirSync(e,{withFileTypes:!0})){let a=i.join(e,n.name);if(t=Math.max(t,r.statSync(a).mtimeMs),n.isDirectory())for(let e of r.readdirSync(a))t=Math.max(t,r.statSync(i.join(a,e)).mtimeMs)}return t}async function _(e){let t=(await r.promises.stat(e)).mtimeMs;for(let n of await r.promises.readdir(e,{withFileTypes:!0})){let a=i.join(e,n.name);if(t=Math.max(t,(await r.promises.stat(a)).mtimeMs),n.isDirectory())for(let e of await r.promises.readdir(a))t=Math.max(t,(await r.promises.stat(i.join(a,e))).mtimeMs)}return t}function v(e){let t;try{t=r.readdirSync(e)}catch{return!1}for(let n of t)if(n.endsWith(l))try{let t=JSON.parse(r.readFileSync(i.join(e,n),u)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}async function y(e){let t;try{t=await r.promises.readdir(e)}catch{return!1}for(let n of t)if(n.endsWith(l))try{let t=JSON.parse(await r.promises.readFile(i.join(e,n),u)).hostPid;if(typeof t!=`number`)continue;return process.kill(t,0),!0}catch{}return!1}function b(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 x(e,t,n,r){return[...e].sort((e,t)=>t.finishedAt-e.finishedAt).filter((e,i)=>i>=r||n-e.finishedAt>t)}function S(e){for(let t of r.readdirSync(e,{withFileTypes:!0}))if(!t.isDirectory()||r.readdirSync(i.join(e,t.name)).length>0)return!1;return!0}async function C(e){for(let t of await r.promises.readdir(e,{withFileTypes:!0}))if(!t.isDirectory()||(await r.promises.readdir(i.join(e,t.name))).length>0)return!1;return!0}function w(e){return i.join(h(e),s)}var T=class{adoptedSessionId;repository;repositoryPath(){return this.repository??=m(p([`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(t){let n=process.env[e]?.trim();return n?i.resolve(n):i.join(this.sessionDirectory(t),`logs`)}stateDirectory(t){let n=process.env[e]?.trim();return n?i.join(i.resolve(n),`..`,c):i.join(this.sessionDirectory(t),c)}logPathFor(e,t){return i.join(this.logDirectory(t),`${e}.log`)}rotatedLogPathFor(e,t){return`${this.logPathFor(e,t)}.1`}statePathFor(e,t){return i.join(this.stateDirectory(t),`${e}${l}`)}ensureDirectories(e){r.mkdirSync(this.logDirectory(e),{recursive:!0}),r.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.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){if(r===s){this.sweepActiveSession(r,e,t,n);continue}this.sweepSessionDirectory(i.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.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){if(r===s){await this.sweepActiveSessionAsync(r,e,t,n);continue}await this.sweepSessionDirectoryAsync(i.join(a,r),e,t,n)}return n}legacyDirectory(){let e=p([`rev-parse`,`--path-format=absolute`,`--git-common-dir`],process.cwd());return e?i.join(e,s):void 0}removeLegacyStore(){let e=this.legacyDirectory();if(!(!e||i.basename(e)!==s||!r.existsSync(e)))return r.rmSync(e,{recursive:!0,force:!0}),e}currentSessionId(){try{return this.sessionId()}catch{return}}sweepActiveSession(e,t,n,r){this.sweepStateDirectory(this.stateDirectory(e),e,t,n,r)}async sweepActiveSessionAsync(e,t,n,r){await this.sweepStateDirectoryAsync(this.stateDirectory(e),e,t,n,r)}sweepSessionDirectory(e,t,n,a){try{let o=g(e);if(n-o>f&&S(e)){r.rmSync(e,{recursive:!0,force:!0}),a.removed.push(e);return}if(n-o<=t||v(i.join(e,c)))return;r.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{let o=await _(e);if(n-o>f&&await C(e)){await r.promises.rm(e,{recursive:!0,force:!0}),a.removed.push(e);return}if(n-o<=t||await y(i.join(e,c)))return;await r.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();if(!t)throw Error(`PI_SESSION_ID is required for doom-runner storage`);return t}storeDirectory(){let t=process.env[e]?.trim();return t?i.dirname(i.resolve(t)):w(process.env)}sessionDirectory(e){return i.join(this.storeDirectory(),this.sessionId(e))}sweepStateDirectory(e,n,a,o,s){let c;try{c=r.readdirSync(e)}catch(t){t.code!==d&&s.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}let f=[];for(let t of c){if(!t.endsWith(l))continue;let n=i.join(e,t);try{let e=b(JSON.parse(r.readFileSync(n,u)));if(e===void 0)continue;f.push({id:t.slice(0,-5),statePath:n,finishedAt:e})}catch(e){s.errors.push(`Could not sweep runner metadata ${n}: ${String(e)}`)}}for(let e of x(f,a,o,t()))this.removeHistoryFiles(n,e.id,e.statePath,s)}async sweepStateDirectoryAsync(e,n,a,o,s){let c;try{c=await r.promises.readdir(e)}catch(t){t.code!==d&&s.errors.push(`Could not read runner state directory ${e}: ${String(t)}`);return}let f=[];for(let t of c){if(!t.endsWith(l))continue;let n=i.join(e,t);try{let e=b(JSON.parse(await r.promises.readFile(n,u)));if(e===void 0)continue;f.push({id:t.slice(0,-5),statePath:n,finishedAt:e})}catch(e){s.errors.push(`Could not sweep runner metadata ${n}: ${String(e)}`)}}for(let e of x(f,a,o,t()))await this.removeHistoryFilesAsync(n,e.id,e.statePath,s)}removeHistoryFiles(e,t,n,i){let a=[n,this.logPathFor(t,e),this.rotatedLogPathFor(t,e)];for(let e of a)try{r.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.promises.unlink(e),i.removed.push(e)}catch(t){t.code!==d&&i.errors.push(`Could not remove runner history ${e}: ${String(t)}`)}}};export{T as RunnerPaths,w as resolveRunnerStoreDirectory};
|
|
2
2
|
//# sourceMappingURL=RunnerPaths.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerPaths.mjs","names":[],"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":"6OAQA,MAAM,EAAiB,cAIjB,EAAiB,OAEjB,EAAkB,QAKlB,EAAgB,OAChB,EAAuB,SAE7B,SAAS,EAAI,EAAgB,EAAiC,CAC5D,GAAI,CAEF,IAAM,EADS,EAAa,MAAO,EAAM,CAAE,MAAK,SAAU,EAAe,MAAO,CAAC,SAAU,OAAQ,QAAQ,CAAE,CACxF,CAAC,CAAC,KAAK,EAC5B,OAAO,EAAQ,OAAS,EAAI,EAAU,IAAA,EACxC,MAAQ,CACN,MACF,CACF,CAEA,SAAS,EAAS,EAAwB,CACxC,GAAI,CACF,OAAO,EAAG,aAAa,CAAM,CAC/B,MAAQ,CACN,OAAO,CACT,CACF,CAEA,SAAS,EAAsB,EAAgC,CAC7D,IAAM,EAAa,EAAI,qBAA0B,KAAK,EAKtD,OAJI,IAAe,IAAmB,EAAG,QAAQ,EAC7C,GAAY,WAAW,IAAiB,EACnC,EAAK,KAAK,EAAG,QAAQ,EAAG,EAAW,MAAM,CAAwB,CAAC,EAEpE,EAAa,EAAK,QAAQ,CAAU,EAAI,EAAK,KAAK,EAAG,QAAQ,EAAG,MAAyB,OAAc,CAChH,CAOA,SAAS,EAAgB,EAA2B,CAClD,IAAI,EAAS,EAAG,SAAS,CAAS,CAAC,CAAC,QACpC,IAAK,IAAM,KAAS,EAAG,YAAY,EAAW,CAAE,cAAe,EAAK,CAAC,EAAG,CACtE,IAAM,EAAS,EAAK,KAAK,EAAW,EAAM,IAAI,EAC9C,KAAS,KAAK,IAAI,EAAQ,EAAG,SAAS,CAAM,CAAC,CAAC,OAAO,EAChD,EAAM,YAAY,EACvB,IAAK,IAAM,KAAS,EAAG,YAAY,CAAM,EACvC,EAAS,KAAK,IAAI,EAAQ,EAAG,SAAS,EAAK,KAAK,EAAQ,CAAK,CAAC,CAAC,CAAC,OAAO,CAE3E,CACA,OAAO,CACT,CAEA,eAAe,EAAqB,EAAoC,CACtE,IAAI,GAAU,MAAM,EAAG,SAAS,KAAK,CAAS,EAAA,CAAG,QACjD,IAAK,IAAM,KAAS,MAAM,EAAG,SAAS,QAAQ,EAAW,CAAE,cAAe,EAAK,CAAC,EAAG,CACjF,IAAM,EAAS,EAAK,KAAK,EAAW,EAAM,IAAI,EAC9C,KAAS,KAAK,IAAI,GAAS,MAAM,EAAG,SAAS,KAAK,CAAM,EAAA,CAAG,OAAO,EAC7D,EAAM,YAAY,EACvB,IAAK,IAAM,KAAS,MAAM,EAAG,SAAS,QAAQ,CAAM,EAClD,EAAS,KAAK,IAAI,GAAS,MAAM,EAAG,SAAS,KAAK,EAAK,KAAK,EAAQ,CAAK,CAAC,EAAA,CAAG,OAAO,CAExF,CACA,OAAO,CACT,CAQA,SAAS,EAAW,EAAiC,CACnD,IAAI,EACJ,GAAI,CACF,EAAU,EAAG,YAAY,CAAc,CACzC,MAAQ,CAEN,MAAO,EACT,CAEA,IAAK,IAAM,KAAS,EACb,KAAM,SAAS,CAAe,EACnC,GAAI,CAEF,IAAM,EADiB,KAAK,MAAM,EAAG,aAAa,EAAK,KAAK,EAAgB,CAAK,EAAG,CAAa,CAC5E,CAAC,CAA2B,QACjD,GAAI,OAAO,GAAY,SAAU,SAEjC,OADA,QAAQ,KAAK,EAAS,CAAC,EAChB,EACT,MAAQ,CAER,CAEF,MAAO,EACT,CAEA,eAAe,EAAgB,EAA0C,CACvE,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAG,SAAS,QAAQ,CAAc,CACpD,MAAQ,CACN,MAAO,EACT,CAEA,IAAK,IAAM,KAAS,EACb,KAAM,SAAS,CAAe,EACnC,GAAI,CAEF,IAAM,EADiB,KAAK,MAAM,MAAM,EAAG,SAAS,SAAS,EAAK,KAAK,EAAgB,CAAK,EAAG,CAAa,CACvF,CAAC,CAA2B,QACjD,GAAI,OAAO,GAAY,SAAU,SAEjC,OADA,QAAQ,KAAK,EAAS,CAAC,EAChB,EACT,MAAQ,CAER,CAEF,MAAO,EACT,CAEA,SAAS,EAAY,EAAoC,CACvD,GAAI,OAAO,GAAU,WAAY,EAAgB,OACjD,IAAM,EAAS,EACf,GAAI,EAAO,QAAU,aAAe,OAAO,EAAO,MAAM,YAAe,SAAU,OACjF,IAAM,EAAY,KAAK,MAAM,EAAO,KAAK,UAAU,EACnD,OAAO,OAAO,SAAS,CAAS,EAAI,EAAY,IAAA,EAClD,CAQA,SAAgB,EAA4B,EAAgC,CAC1E,OAAO,EAAK,KAAK,EAAsB,CAAG,EAAG,CAAc,CAC7D,CAGA,IAAa,EAAb,KAAiD,CAC/C,iBACA,WAEA,gBAAyB,CAEvB,MADA,MAAK,aAAe,EAAS,EAAI,CAAC,YAAa,iBAAiB,EAAG,QAAQ,IAAI,CAAC,GAAK,QAAQ,IAAI,CAAC,EAC3F,KAAK,UACd,CAEA,aAAa,EAAyB,CACpC,IAAM,EAAU,EAAU,KAAK,EAC/B,GAAI,CAAC,EAAS,MAAU,MAAM,wCAAwC,EACtE,KAAK,iBAAmB,CAC1B,CAEA,aAAa,EAA4B,CACvC,IAAM,EAAW,QAAQ,IAAI,EAAY,EAAE,KAAK,EAEhD,OADI,EAAiB,EAAK,QAAQ,CAAQ,EACnC,EAAK,KAAK,KAAK,iBAAiB,CAAS,EAAG,MAAY,CACjE,CAEA,eAAe,EAA4B,CACzC,IAAM,EAAW,QAAQ,IAAI,EAAY,EAAE,KAAK,EAEhD,OADI,EAAiB,EAAK,KAAK,EAAK,QAAQ,CAAQ,EAAG,KAAM,CAAc,EACpE,EAAK,KAAK,KAAK,iBAAiB,CAAS,EAAG,CAAc,CACnE,CAEA,WAAW,EAAY,EAA4B,CACjD,OAAO,EAAK,KAAK,KAAK,aAAa,CAAS,EAAG,GAAG,OAAoB,CACxE,CAEA,kBAAkB,EAAY,EAA4B,CACxD,MAAO,GAAG,KAAK,WAAW,EAAI,CAAS,KACzC,CAEA,aAAa,EAAY,EAA4B,CACnD,OAAO,EAAK,KAAK,KAAK,eAAe,CAAS,EAAG,GAAG,IAAK,GAAiB,CAC5E,CAEA,kBAAkB,EAA0B,CAC1C,EAAG,UAAU,KAAK,aAAa,CAAS,EAAG,CAAE,UAAW,EAAK,CAAC,EAC9D,EAAG,UAAU,KAAK,eAAe,CAAS,EAAG,CAAE,UAAW,EAAK,CAAC,CAClE,CAEA,aAAa,EAAe,EAAc,KAAK,IAAI,EAAmB,CACpE,IAAM,EAAyB,CAAE,QAAS,CAAC,EAAG,OAAQ,CAAC,CAAE,EAGzD,GAAI,QAAQ,IAAA,qBAAkB,KAAK,EAEjC,OADA,KAAK,oBAAoB,KAAK,eAAe,EAAG,KAAK,UAAU,EAAG,EAAO,EAAK,CAAM,EAC7E,EAET,IAAM,EAAO,KAAK,eAAe,EAC7B,EACJ,GAAI,CACF,EAAW,EAAG,YAAY,CAAI,CAChC,OAAS,EAAO,CAGd,OAFK,EAAgC,OAAS,GAC9C,EAAO,OAAO,KAAK,2CAA2C,EAAK,IAAI,OAAO,CAAK,GAAG,EADX,CAG7E,CAKA,IAAM,EAAS,KAAK,iBAAiB,EACrC,IAAK,IAAM,KAAa,EAClB,IAAc,GAClB,KAAK,sBAAsB,EAAK,KAAK,EAAM,CAAS,EAAG,EAAO,EAAK,CAAM,EAE3E,OAAO,CACT,CAEA,MAAM,kBAAkB,EAAe,EAAc,KAAK,IAAI,EAA4B,CACxF,IAAM,EAAyB,CAAE,QAAS,CAAC,EAAG,OAAQ,CAAC,CAAE,EACzD,GAAI,QAAQ,IAAA,qBAAkB,KAAK,EAEjC,OADA,MAAM,KAAK,yBAAyB,KAAK,eAAe,EAAG,KAAK,UAAU,EAAG,EAAO,EAAK,CAAM,EACxF,EAGT,IAAM,EAAO,KAAK,eAAe,EAC7B,EACJ,GAAI,CACF,EAAW,MAAM,EAAG,SAAS,QAAQ,CAAI,CAC3C,OAAS,EAAO,CAGd,OAFK,EAAgC,OAAS,GAC9C,EAAO,OAAO,KAAK,2CAA2C,EAAK,IAAI,OAAO,CAAK,GAAG,EADX,CAG7E,CAEA,IAAM,EAAS,KAAK,iBAAiB,EACrC,IAAK,IAAM,KAAa,EAClB,IAAc,GAClB,MAAM,KAAK,2BAA2B,EAAK,KAAK,EAAM,CAAS,EAAG,EAAO,EAAK,CAAM,EAEtF,OAAO,CACT,CAEA,iBAAsC,CACpC,IAAM,EAAkB,EAAI,CAAC,YAAa,yBAA0B,kBAAkB,EAAG,QAAQ,IAAI,CAAC,EACtG,OAAO,EAAkB,EAAK,KAAK,EAAiB,CAAc,EAAI,IAAA,EACxE,CAEA,mBAAwC,CACtC,IAAM,EAAS,KAAK,gBAAgB,EAChC,MAAC,GAAU,EAAK,SAAS,CAAM,IAAM,GAAkB,CAAC,EAAG,WAAW,CAAM,GAEhF,OADA,EAAG,OAAO,EAAQ,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EAC3C,CACT,CAGA,kBAA+C,CAC7C,GAAI,CACF,OAAO,KAAK,UAAU,CACxB,MAAQ,CAEN,MACF,CACF,CAGA,sBAA8B,EAAmB,EAAe,EAAa,EAA8B,CACzG,GAAI,CAKF,GAJI,EAAM,EAAgB,CAAS,GAAK,GAIpC,EAAW,EAAK,KAAK,EAAW,CAAc,CAAC,EAAG,OACtD,EAAG,OAAO,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACrD,EAAO,QAAQ,KAAK,CAAS,CAC/B,OAAS,EAAO,CACT,EAAgC,OAAS,GAC5C,EAAO,OAAO,KAAK,kCAAkC,EAAU,IAAI,OAAO,CAAK,GAAG,CAEtF,CACF,CAEA,MAAc,2BACZ,EACA,EACA,EACA,EACe,CACf,GAAI,CAEF,GADI,EAAO,MAAM,EAAqB,CAAS,GAAM,GACjD,MAAM,EAAgB,EAAK,KAAK,EAAW,CAAc,CAAC,EAAG,OACjE,MAAM,EAAG,SAAS,GAAG,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EAChE,EAAO,QAAQ,KAAK,CAAS,CAC/B,OAAS,EAAO,CACT,EAAgC,OAAS,GAC5C,EAAO,OAAO,KAAK,kCAAkC,EAAU,IAAI,OAAO,CAAK,GAAG,CAEtF,CACF,CAEA,UAAkB,EAA2B,CAC3C,IAAM,EAAY,GAAU,KAAK,GAAK,KAAK,kBAAoB,EAAmB,EAClF,GAAI,CAAC,EAAW,MAAU,MAAM,mDAAmD,EACnF,OAAO,CACT,CAEA,gBAAiC,CAC/B,IAAM,EAAW,QAAQ,IAAI,EAAY,EAAE,KAAK,EAEhD,OADI,EAAiB,EAAK,QAAQ,EAAK,QAAQ,CAAQ,CAAC,EACjD,EAA4B,QAAQ,GAAG,CAChD,CAEA,iBAAyB,EAA4B,CACnD,OAAO,EAAK,KAAK,KAAK,eAAe,EAAG,KAAK,UAAU,CAAS,CAAC,CACnE,CAEA,oBACE,EACA,EACA,EACA,EACA,EACM,CACN,IAAI,EACJ,GAAI,CACF,EAAU,EAAG,YAAY,CAAS,CACpC,OAAS,EAAO,CACT,EAAgC,OAAS,GAC5C,EAAO,OAAO,KAAK,yCAAyC,EAAU,IAAI,OAAO,CAAK,GAAG,EAE3F,MACF,CAEA,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,CAAC,EAAM,SAAS,CAAe,EAAG,SACtC,IAAM,EAAY,EAAK,KAAK,EAAW,CAAK,EAC5C,GAAI,CAEF,IAAM,EAAa,EADI,KAAK,MAAM,EAAG,aAAa,EAAW,CAAa,CACvC,CAAC,EACpC,GAAI,IAAe,IAAA,IAAa,EAAM,GAAc,EAAO,SAC3D,IAAM,EAAK,EAAM,MAAM,EAAG,EAAuB,EACjD,KAAK,mBAAmB,EAAW,EAAI,EAAW,CAAM,CAC1D,OAAS,EAAO,CACd,EAAO,OAAO,KAAK,mCAAmC,EAAU,IAAI,OAAO,CAAK,GAAG,CACrF,CACF,CACF,CAEA,MAAc,yBACZ,EACA,EACA,EACA,EACA,EACe,CACf,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAG,SAAS,QAAQ,CAAS,CAC/C,OAAS,EAAO,CACT,EAAgC,OAAS,GAC5C,EAAO,OAAO,KAAK,yCAAyC,EAAU,IAAI,OAAO,CAAK,GAAG,EAE3F,MACF,CAEA,IAAK,IAAM,KAAS,EAAS,CAC3B,GAAI,CAAC,EAAM,SAAS,CAAe,EAAG,SACtC,IAAM,EAAY,EAAK,KAAK,EAAW,CAAK,EAC5C,GAAI,CAEF,IAAM,EAAa,EADI,KAAK,MAAM,MAAM,EAAG,SAAS,SAAS,EAAW,CAAa,CAClD,CAAC,EACpC,GAAI,IAAe,IAAA,IAAa,EAAM,GAAc,EAAO,SAC3D,IAAM,EAAK,EAAM,MAAM,EAAG,EAAuB,EACjD,MAAM,KAAK,wBAAwB,EAAW,EAAI,EAAW,CAAM,CACrE,OAAS,EAAO,CACd,EAAO,OAAO,KAAK,mCAAmC,EAAU,IAAI,OAAO,CAAK,GAAG,CACrF,CACF,CACF,CAEA,mBAA2B,EAAmB,EAAY,EAAmB,EAA8B,CACzG,IAAM,EAAa,CAAC,EAAW,KAAK,WAAW,EAAI,CAAS,EAAG,KAAK,kBAAkB,EAAI,CAAS,CAAC,EACpG,IAAK,IAAM,KAAa,EACtB,GAAI,CACF,EAAG,WAAW,CAAS,EACvB,EAAO,QAAQ,KAAK,CAAS,CAC/B,OAAS,EAAO,CACT,EAAgC,OAAS,GAC5C,EAAO,OAAO,KAAK,mCAAmC,EAAU,IAAI,OAAO,CAAK,GAAG,CAEvF,CAEJ,CAEA,MAAc,wBACZ,EACA,EACA,EACA,EACe,CACf,IAAM,EAAa,CAAC,EAAW,KAAK,WAAW,EAAI,CAAS,EAAG,KAAK,kBAAkB,EAAI,CAAS,CAAC,EACpG,IAAK,IAAM,KAAa,EACtB,GAAI,CACF,MAAM,EAAG,SAAS,OAAO,CAAS,EAClC,EAAO,QAAQ,KAAK,CAAS,CAC/B,OAAS,EAAO,CACT,EAAgC,OAAS,GAC5C,EAAO,OAAO,KAAK,mCAAmC,EAAU,IAAI,OAAO,CAAK,GAAG,CAEvF,CAEJ,CACF"}
|
|
1
|
+
{"version":3,"file":"RunnerPaths.mjs","names":[],"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 { getMaxCompletedRunners, 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/**\n * How long an entirely empty session directory is left alone before it counts\n * as an orphan. A session that has started but not yet run anything owns such a\n * directory for a moment, and reclaiming it out from under that session would\n * be churn for no gain.\n */\nconst EMPTY_SESSION_GRACE_MS = 5 * 60 * 1000;\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\ninterface CompletedRecord {\n readonly id: string;\n readonly statePath: string;\n readonly finishedAt: number;\n}\n\n/**\n * Completed records a sweep may delete: everything past the TTL, and everything\n * beyond the newest `keep`.\n *\n * The count bound is what keeps a live session from growing without limit. Its\n * records only reach a multi-day TTL long after the session that is still\n * writing them would have filled its directory, so age alone bounds nothing\n * while the session lasts.\n */\nfunction expiredCompletedRecords(\n records: readonly CompletedRecord[],\n ttlMs: number,\n now: number,\n keep: number,\n): CompletedRecord[] {\n return [...records]\n .sort((left, right) => right.finishedAt - left.finishedAt)\n .filter((record, index) => index >= keep || now - record.finishedAt > ttlMs);\n}\n\n/**\n * Whether the directory holds no files at all, only empty subdirectories.\n *\n * A session that ran something leaves metadata behind, and a live session keeps\n * its lifeline socket in the state directory, so an entirely empty tree names\n * no owner and no history.\n */\nfunction isEmptySession(directory: string): boolean {\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if (fs.readdirSync(path.join(directory, entry.name)).length > 0) return false;\n }\n return true;\n}\n\nasync function isEmptySessionAsync(directory: string): Promise<boolean> {\n for (const entry of await fs.promises.readdir(directory, { withFileTypes: true })) {\n if (!entry.isDirectory()) return false;\n if ((await fs.promises.readdir(path.join(directory, entry.name))).length > 0) return false;\n }\n return true;\n}\n\n/**\n * The root every session's runner state lives under, before any log-directory\n * override: `<agent dir>/doom-runner/<session id>/{runs,logs}`. A reader in\n * another process, such as the cockpit hub, resolves the same root from its\n * own environment.\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) {\n this.sweepActiveSession(sessionId, ttlMs, now, result);\n continue;\n }\n this.sweepSessionDirectory(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n async sweepHistoryAsync(ttlMs: number, now: number = Date.now()): Promise<LogSweepResult> {\n const result: LogSweepResult = { removed: [], errors: [] };\n if (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) {\n await this.sweepActiveSessionAsync(sessionId, ttlMs, now, result);\n continue;\n }\n await this.sweepSessionDirectoryAsync(path.join(root, sessionId), ttlMs, now, result);\n }\n return result;\n }\n\n legacyDirectory(): string | undefined {\n const commonDirectory = git(['rev-parse', '--path-format=absolute', '--git-common-dir'], 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 /**\n * Retention inside the session that is still running.\n *\n * The whole-directory sweep cannot be pointed at the live session: it would\n * take the lifeline socket, the supervisor sidecars and the running runners'\n * own metadata with it. Per-record retention is safe there because it only\n * ever removes a record whose state is `completed` together with the two log\n * files that record names, and never the directory itself. A running record\n * has no `exit.finishedAt`, so it is not a candidate at any age.\n */\n private sweepActiveSession(sessionId: string, ttlMs: number, now: number, result: LogSweepResult): void {\n this.sweepStateDirectory(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n private async sweepActiveSessionAsync(\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n await this.sweepStateDirectoryAsync(this.stateDirectory(sessionId), sessionId, ttlMs, now, result);\n }\n\n /** Removes a session's whole directory once its newest entry is past the TTL. */\n private sweepSessionDirectory(directory: string, ttlMs: number, now: number, result: LogSweepResult): void {\n try {\n const newest = newestEntryTime(directory);\n // An orphan left by a session that never ran anything holds nothing worth\n // a retention window, so it goes as soon as it is clearly not in use.\n if (now - newest > EMPTY_SESSION_GRACE_MS && isEmptySession(directory)) {\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n // An idle session can go quiet for longer than the TTL, and deleting one\n // takes its lifeline socket with it, which would read to its own runners\n // as an owner that had died. Its records name the owner, so ask.\n if (ownerAlive(path.join(directory, STATE_DIR_NAME))) return;\n fs.rmSync(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private async sweepSessionDirectoryAsync(\n directory: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n try {\n const newest = await newestEntryTimeAsync(directory);\n if (now - newest > EMPTY_SESSION_GRACE_MS && (await isEmptySessionAsync(directory))) {\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n return;\n }\n if (now - newest <= ttlMs) return;\n if (await ownerAliveAsync(path.join(directory, STATE_DIR_NAME))) return;\n await fs.promises.rm(directory, { recursive: true, force: true });\n result.removed.push(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not sweep runner session ${directory}: ${String(error)}`);\n }\n }\n }\n\n private sessionId(explicit?: string): string {\n const sessionId = explicit?.trim() || this.adoptedSessionId || inheritedSessionId();\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 const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(fs.readFileSync(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners())) {\n this.removeHistoryFiles(sessionId, record.id, record.statePath, result);\n }\n }\n\n private async sweepStateDirectoryAsync(\n directory: string,\n sessionId: string,\n ttlMs: number,\n now: number,\n result: LogSweepResult,\n ): Promise<void> {\n let entries: string[];\n try {\n entries = await fs.promises.readdir(directory);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== NOT_FOUND_ERROR_CODE) {\n result.errors.push(`Could not read runner state directory ${directory}: ${String(error)}`);\n }\n return;\n }\n\n const completed: CompletedRecord[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(STATE_EXTENSION)) continue;\n const statePath = path.join(directory, entry);\n try {\n const value: unknown = JSON.parse(await fs.promises.readFile(statePath, FILE_ENCODING));\n const finishedAt = completedAt(value);\n if (finishedAt === undefined) continue;\n completed.push({ id: entry.slice(0, -STATE_EXTENSION.length), statePath, finishedAt });\n } catch (error) {\n result.errors.push(`Could not sweep runner metadata ${statePath}: ${String(error)}`);\n }\n }\n\n for (const record of expiredCompletedRecords(completed, ttlMs, now, getMaxCompletedRunners())) {\n await this.removeHistoryFilesAsync(sessionId, record.id, record.statePath, result);\n }\n }\n\n private removeHistoryFiles(sessionId: string, id: string, statePath: string, result: LogSweepResult): void {\n const candidates = [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,3 +1,3 @@
|
|
|
1
|
-
const e=require("../../services/runs/compaction.cjs"),t=require("../../types/config.cjs"),n=require("../../container/index.cjs"),r=require("../../services/runs/reconcile.cjs"),i=require("../../commands/bash/bashTool.cjs"),a=require("../../services/runs/runnersCommand.cjs"),o=require("../../tui/format.cjs"),s=require("../../tui/runnerSpace.cjs"),c=require("../RunnerSettings/RunnerSettingsLoader.cjs");let l=require("@agimon-ai/doompi-extension-contracts/child-process"),u=require("@agimon-ai/doompi-extension-contracts/cordis-host"),d=require("@agimon-ai/doompi-extension-contracts/help"),f=require("@agimon-ai/doompi-extension-contracts/readiness"),p=require("@agimon-ai/doompi-extension-contracts/ui-hub"),ee=require("@agimon-ai/doompi-telemetry");const m=`@agimon-ai/doompi-runner`,te=`runners`,h=`completed`,ne=`stopped`,g=`doom-runner-runners`;function re(e){try{let n=e.isProjectTrusted?.()??!1,{settings:r,issues:i}=new c.RunnerSettingsLoader().load(e.cwd,n);t.setRunnerSettings(r);for(let e of i)process.emitWarning(`${c.RUNNER_SETTINGS_FILE}: ${e}`)}catch(e){process.emitWarning(`Could not read ${c.RUNNER_SETTINGS_FILE}: ${String(e)}`),t.setRunnerSettings({})}}function _(c,u){let d=n.createRunnerContainer(),_=d.runnerRegistry,v=async()=>{try{_.close()}catch(e){process.emitWarning(`Could not close a partially installed doom-runner registry: ${String(e)}`)}};c.effect(()=>()=>v(),`${m}/runtime`);let y=d.launcher,b=d.rmuxBackend,ie=d.logReader,x=d.ptyHost,ae=d.bashRunService,S=d.paths,C=d.processControl,w=d.lifeline,T=!0,E=0,D,O,k=[],A,j,M,N=!1,P=!1,F,I,L,R,z,B=Promise.resolve(),
|
|
2
|
-
`);u.sendMessage({customType:`doom-runner-finished`,content:o,display:!0},{triggerTurn:!0,deliverAs:`steer`}),A&&
|
|
1
|
+
const e=require("../../services/runs/compaction.cjs"),t=require("../../types/config.cjs"),n=require("../../container/index.cjs"),r=require("../../services/runs/reconcile.cjs"),i=require("../../commands/bash/bashTool.cjs"),a=require("../../services/runs/runnersCommand.cjs"),o=require("../../tui/format.cjs"),s=require("../../tui/runnerSpace.cjs"),c=require("../RunnerSettings/RunnerSettingsLoader.cjs");let l=require("@agimon-ai/doompi-extension-contracts/child-process"),u=require("@agimon-ai/doompi-extension-contracts/cordis-host"),d=require("@agimon-ai/doompi-extension-contracts/help"),f=require("@agimon-ai/doompi-extension-contracts/readiness"),p=require("@agimon-ai/doompi-extension-contracts/ui-hub"),ee=require("@agimon-ai/doompi-telemetry");const m=`@agimon-ai/doompi-runner`,te=`runners`,h=`completed`,ne=`stopped`,g=`doom-runner-runners`;function re(e){try{let n=e.isProjectTrusted?.()??!1,{settings:r,issues:i}=new c.RunnerSettingsLoader().load(e.cwd,n);t.setRunnerSettings(r);for(let e of i)process.emitWarning(`${c.RUNNER_SETTINGS_FILE}: ${e}`)}catch(e){process.emitWarning(`Could not read ${c.RUNNER_SETTINGS_FILE}: ${String(e)}`),t.setRunnerSettings({})}}function _(c,u){let d=n.createRunnerContainer(),_=d.runnerRegistry,v=async()=>{try{_.close()}catch(e){process.emitWarning(`Could not close a partially installed doom-runner registry: ${String(e)}`)}};c.effect(()=>()=>v(),`${m}/runtime`);let y=d.launcher,b=d.rmuxBackend,ie=d.logReader,x=d.ptyHost,ae=d.bashRunService,S=d.paths,C=d.processControl,w=d.lifeline,T=!0,E=0,D,O,k=[],A,j,M,N=!1,P=!1,F,I,L,R,z,B,V=Promise.resolve(),H,U=!1,W=!1,G,K=new Set,q=new Set,J=new Set,Y=e=>(J.add(e),e.then(()=>J.delete(e),()=>J.delete(e)),e),X=async()=>{if(await V,!T||!N||!D)throw Error(`doom-runner requires an active Pi session`)},oe={run:async e=>(await X(),Y(ae.run(e)))},Z=(e,t=D)=>T&&!P&&e===E&&t===D,se=e=>(M??=(0,ee.createDoomTelemetry)({serviceName:`doom-runner`,packageName:`@agimon-ai/doompi-runner`,cwd:e.cwd,env:process.env,enableLogs:!0,enableTraces:!0}),M),ce=async e=>{let t=D,n=E;if(!t||!Z(n,t))return;let i=await _.list();if(!Z(n,t))return;let a=i;if(e){let e=await r.reconcileActiveRunners({registry:_,launcher:y,rmuxBackend:b,processControl:C,currentHostPid:process.pid,startup:!1,active:i});for(let t of e.errors)process.emitWarning(t);if(e.reclaimed.length>0){let t=new Set(e.reclaimed);a=i.filter(e=>!t.has(e.id))}}if(!Z(n,t))return;k=a.filter(e=>e.sessionId===t);let s=(O?a.filter(e=>(e.rootSessionId??e.sessionId)===O):[]).length;G!==s&&(G=s,j?.update(o.formatRunnerFooterContribution(s)),A?.hasUI&&A.ui.setStatus(g,o.formatRunnerStatus(s)));let c=[...q],l=await Promise.all(c.map(e=>_.get(e,t)));if(Z(n,t))for(let[e,n]of l.entries()){let r=c[e];if(!r)continue;if(!n||n.sessionId!==t){q.delete(r);continue}if(n.state!==h||(q.delete(n.id),K.has(n.id)))continue;K.add(n.id);let i=n.exit?.reason??h,a=n.exit?.code===null||n.exit?.code===void 0?``:`, exit code ${n.exit.code}`,o=[`Background runner ${n.name} exited: ${i}${a}.`,`Runner ID: ${n.id}`,`Log: ${n.logPath}`,`Inspect: doom-runner logs ${n.id}`].join(`
|
|
2
|
+
`);u.sendMessage({customType:`doom-runner-finished`,content:o,display:!0},{triggerTurn:!0,deliverAs:`steer`}),A&&Y(se(A).recordEvent(`doom_runner.process_finished`,{outcome:`completed`,"runner.exit_code":n.exit?.code??0,"runner.backend":n.backend,...Number.isFinite(Date.parse(n.startedAt))?{duration_ms:Math.max(0,Date.now()-Date.parse(n.startedAt))}:{}}))}},Q=(e=!0)=>{if(!D||!N||P)return Promise.resolve();if(F)return U=!0,W||=e,F;let t=(async()=>{let t=e;do U=!1,W=!1,await ce(t),t=W;while(U&&!P)})().finally(()=>{F===t&&(F=void 0)});return F=t,t},le=e=>{P||(A&&Y(se(A).recordError(`doom_runner.refresh_failed`,e)),process.emitWarning(`Could not refresh doom-runner: ${String(e)}`))},ue=e=>{Q(e).catch(le)},de=()=>{L||I||P||(L=setImmediate(()=>{if(L=void 0,P)return;let e=(async()=>{let e=S.sweepHistoryAsync?await S.sweepHistoryAsync(t.getLogTtlMs()):S.sweepHistory(t.getLogTtlMs());for(let t of e.errors)process.emitWarning(t)})().catch(e=>process.emitWarning(`Could not sweep doom-runner history: ${String(e)}`)).finally(()=>{I===e&&(I=void 0)});I=e}),L.unref?.())},$=async(e,t)=>{try{await t()}catch(t){process.emitWarning(`Doom-runner cleanup could not ${e}: ${String(t)}`)}};v=async()=>{if(P)return;T=!1,P=!0,E+=1,N=!1,R&&clearInterval(R),R=void 0,z&&clearInterval(z),z=void 0,L&&clearImmediate(L),L=void 0;let e=B;B=void 0,e&&await $(`unsubscribe from the runner registry`,e);let t=H;H=void 0,t&&await $(`cancel standalone readiness work`,()=>t.dispose()),await Promise.allSettled(J);let n=F;n&&await $(`settle the runner refresh`,()=>n);let i=I;i&&await $(`settle runner history cleanup`,()=>i),await $(`dispose runner PTYs`,()=>x.disposeAll());let a=[];if(D)try{a=await _.listBySession(D)}catch(e){process.emitWarning(`Could not list runners during doom-runner shutdown: ${String(e)}`)}await Promise.all(a.map(async e=>{try{let t=await r.stopRunnerProcess(e,y,b);if(!t&&C.isAlive(e.pid)){process.emitWarning(`Could not stop runner ${e.id} during session shutdown`);return}await _.complete(e.id,{reason:ne,code:null,signal:t?`SIGTERM`:null,stopReason:`session ended`},e.sessionId)}catch(t){process.emitWarning(`Could not clean up runner ${e.id}: ${String(t)}`)}}));let o=A,s=M;M=void 0,A=void 0,D=void 0,O=void 0,k=[],K.clear(),q.clear(),await Promise.all([$(`dispose the runner lifeline`,()=>w.dispose()),...o?.hasUI?[$(`clear the runner status`,()=>o.ui.setStatus(g,void 0))]:[]]),await $(`close the runner registry`,()=>_.close()),s&&(await $(`record the runner session finish`,()=>s.recordEvent(`doom_runner.session_finished`,{outcome:`stopped`})),await $(`stop runner telemetry`,()=>s.shutdown()))},c.inject([p.DOOM_UI_HUB_SERVICE],e=>{let t=(0,p.requireDoomUiHub)(e).registerFooter({source:m,id:`runner-count`,order:10});return j=t,()=>{t.dispose(),j===t&&(j=void 0)}}),B=_.subscribe(()=>ue(!1)),R=setInterval(()=>ue(!0),500),R.unref?.(),z=setInterval(()=>de(),6e5),z.unref?.(),i.registerBashTool(u,{bashRunService:oe,getSessionId:async()=>{if(await X(),!D)throw Error(`doom-runner requires an active Pi session`);return D},onRunnerStarted:e=>{!T||!N||(q.add(e),ue(!1))}}),e.registerRunnerCompactionRecovery(u,{getSessionId:async()=>{try{return await X(),D}catch{return}},listBySession:async e=>{try{await X()}catch{return[]}return T?_.listBySession(e):[]}});let fe=async(e,t,n,r)=>{if(!Z(e,t))return!1;let i=k.find(e=>e.id===n);return i?(i.backend===`rmux`&&i.backendTarget?await b.stop(i.backendTarget,i.pid):await y.stop(i.pid),!Z(e,t)||(await _.complete(i.id,{reason:ne,code:null,signal:null,stopReason:r},i.sessionId),Z(e,t)&&await Q(),!0)):!1};u.registerCommand(te,{description:"Open Runner Space: background processes started by bash. `/runners stop <id> [reason]` stops one.",handler:async(e,t)=>{if(!T)return;let n=E,r=D;if(!r)return;let i=a.parseRunnersCommand(e);if(i.kind===`stop`){if(!i.id){t.ui.notify(`Usage: /runners stop <runner-id> [reason]`,`error`);return}if(await X(),await Q(),!Z(n,r))return;let e=await fe(n,r,i.id,i.reason);if(!Z(n,r))return;e?t.ui.notify(`Stopped runner ${i.id}.`,`info`):t.ui.notify(`No active runner ${i.id} in this session.`,`error`);return}if(!t.hasUI){t.ui.notify(`/runners requires interactive mode`,`error`);return}await X(),await Q(),!(!r||!Z(n,r))&&await s.openRunnerSpace(t,{getRunners:()=>Z(n,r)?k:[],getPtyRun:e=>Z(n,r)?b.get(e)??x.get(e):void 0,readLog:e=>Z(n,r)?ie.read(e,{lines:1e3}).text:``,stopRunner:async(e,t)=>{await fe(n,r,e,t)}})}}),c.inject([p.DOOM_UI_HUB_SERVICE],e=>{let t=(0,p.requireDoomUiHub)(e).registerLeader({source:m,bindings:[{id:`runners.open`,path:[{key:`r`,label:`runners`,detail:`background processes`,order:67},{key:`l`,label:`list`,detail:`runners in this session`}],command:{name:te}}]});return()=>t.dispose()}),u.on(`session_start`,(e,t)=>{if(!T)return;let n=t;re(n);let i=n.sessionManager.getSessionId(),a=D,o=A,s=++E;D=i,O=(0,l.resolveRootSessionId)(i),A=n,N=!1,G=void 0,K.clear(),q.clear();let u=async e=>{let t=()=>!e.aborted&&Z(s,i);if(!t())return[];if(a&&a!==i){await $(`dispose the previous session PTYs`,()=>x.disposeAll()),await $(`dispose the previous session lifeline`,()=>w.dispose()),o?.hasUI&&await $(`clear the previous session status`,()=>o.ui.setStatus(g,void 0));let e=M;if(M=void 0,e&&await $(`stop the previous session telemetry`,()=>e.shutdown()),!t())return[]}let c=se(n);if(await c.recordEvent(`doom_runner.session_started`,{outcome:`started`}),!t()||(S.setSessionId(i),await w.arm(i),!t()))return[];let l=await _.listAll(i);if(!t())return[];for(let e of l)e.state===h?K.add(e.id):e.promoted&&q.add(e.id);let u=await r.cleanupLegacyRunnerStore({registry:_,launcher:y,rmuxBackend:b,processControl:C,currentHostPid:process.pid,paths:S});if(!t())return[];let d=await r.reconcileActiveRunners({registry:_,launcher:y,rmuxBackend:b,processControl:C,currentHostPid:process.pid,startup:!0});if(!t()||(N=!0,await Q(!1),!t()))return[];de();let f=[...u.errors,...d.errors],p=u.reclaimed.length+d.reclaimed.length;return p>0&&n.hasUI&&n.ui.notify(`Reclaimed ${p} stale runner record(s)`,`warning`),await c.recordEvent(`doom_runner.reconciled`,{"runner.reclaimed_count":p,"runner.error_count":f.length,outcome:f.length===0?`completed`:`degraded`}),f},d=V.catch(()=>void 0),p=(async()=>{await d,Z(s,i)&&await((0,f.readDoomReadinessCoordinator)(c)??(H??=(0,f.createDoomReadinessCoordinator)({notify:e=>{process.emitWarning(`${e.packageId} initialization ${e.state}: ${e.error?.message??e.diagnostics.join(`; `)}`)}}))).start(m,`${i}:${s}`,async e=>({value:void 0,diagnostics:await u(e)})).wait()})();V=p,Y(p).catch(()=>void 0)})}async function v(e){let t=await(0,u.connectDoomCordisHost)(e,m),n=t.root.plugin(y,{pi:e});try{await n}catch(e){try{await n.dispose()}finally{await t.dispose()}throw e}let r;e.on(`session_shutdown`,()=>r??=(async()=>{try{await n.dispose()}finally{await t.dispose()}})())}function y(e,t){e.inject([d.DOOM_HELP_SERVICE],e=>{let t=(0,d.requireDoomHelpService)(e).register({source:m,moduleUrl:require("url").pathToFileURL(__filename).href,skills:[{name:`doompi-use-runner`,description:`Use Doom Pi Runner to supervise shell commands, inspect durable logs, provide interactive input, and stop background runs.`}]});return()=>t.dispose()}),_(e,t.pi)}exports.default=v,exports.runnerExtension=v,exports.installRunnerRuntime=_;
|
|
3
3
|
//# sourceMappingURL=extension.cjs.map
|