@agimon-ai/doompi-runner 0.0.1-alpha.31 → 0.0.1-alpha.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +18 -4
  2. package/dist/config.cjs +1 -1
  3. package/dist/config.d.cts +2 -2
  4. package/dist/config.d.mts +2 -2
  5. package/dist/config.mjs +1 -1
  6. package/dist/src/adapters/RunnerSettings/RunnerSettingsLoader.cjs +1 -1
  7. package/dist/src/adapters/RunnerSettings/RunnerSettingsLoader.cjs.map +1 -1
  8. package/dist/src/adapters/RunnerSettings/RunnerSettingsLoader.mjs +1 -1
  9. package/dist/src/adapters/RunnerSettings/RunnerSettingsLoader.mjs.map +1 -1
  10. package/dist/src/commands/bash/bashTool.cjs +2 -2
  11. package/dist/src/commands/bash/bashTool.cjs.map +1 -1
  12. package/dist/src/commands/bash/bashTool.d.cts.map +1 -1
  13. package/dist/src/commands/bash/bashTool.d.mts.map +1 -1
  14. package/dist/src/commands/bash/bashTool.mjs +5 -5
  15. package/dist/src/commands/bash/bashTool.mjs.map +1 -1
  16. package/dist/src/commands/bash/responseEnvelope.cjs +10 -10
  17. package/dist/src/commands/bash/responseEnvelope.cjs.map +1 -1
  18. package/dist/src/commands/bash/responseEnvelope.d.cts +4 -3
  19. package/dist/src/commands/bash/responseEnvelope.d.cts.map +1 -1
  20. package/dist/src/commands/bash/responseEnvelope.d.mts +4 -3
  21. package/dist/src/commands/bash/responseEnvelope.d.mts.map +1 -1
  22. package/dist/src/commands/bash/responseEnvelope.mjs +10 -10
  23. package/dist/src/commands/bash/responseEnvelope.mjs.map +1 -1
  24. package/dist/src/services/TokenEstimate/tokenEstimate.cjs +2 -0
  25. package/dist/src/services/TokenEstimate/tokenEstimate.cjs.map +1 -0
  26. package/dist/src/services/TokenEstimate/tokenEstimate.mjs +2 -0
  27. package/dist/src/services/TokenEstimate/tokenEstimate.mjs.map +1 -0
  28. package/dist/src/types/config.cjs +1 -1
  29. package/dist/src/types/config.cjs.map +1 -1
  30. package/dist/src/types/config.d.cts +14 -1
  31. package/dist/src/types/config.d.cts.map +1 -1
  32. package/dist/src/types/config.d.mts +14 -1
  33. package/dist/src/types/config.d.mts.map +1 -1
  34. package/dist/src/types/config.mjs +1 -1
  35. package/dist/src/types/config.mjs.map +1 -1
  36. package/dist/src/types/runnerSettings.d.cts +4 -0
  37. package/dist/src/types/runnerSettings.d.cts.map +1 -1
  38. package/dist/src/types/runnerSettings.d.mts +4 -0
  39. package/dist/src/types/runnerSettings.d.mts.map +1 -1
  40. package/package.json +12 -12
package/README.md CHANGED
@@ -108,9 +108,21 @@ exit status 0
108
108
  Full log: /path/to/run.log (17.0 KB, 604 lines); inspect with doom-runner logs run-a
109
109
  ```
110
110
 
111
- `DOOM_RUNNER_RESULT_MAX_BYTES` moves the failure budget and `DOOM_RUNNER_SUCCESS_RESULT_MAX_BYTES`
112
- the success one. A pragma overrides either, which is how one noisy successful command can still ask
113
- for the wider result.
111
+ Three ceilings apply and whichever binds first wins: bytes, lines, and tokens. Tokens are there
112
+ because bytes do not track context cost. Measured against `gpt-tokenizer`, real text runs from about
113
+ 1.4 characters per token for base64 to 4.7 for English prose, so 8 KiB of one costs several times
114
+ what 8 KiB of the other does. The defaults are 2,048 tokens on failure and 512 on success.
115
+
116
+ Token counts are estimated rather than tokenized: truncation runs on every command and is
117
+ synchronous, so loading a vocabulary would charge every session for a table most never need. The
118
+ estimator models how BPE behaves, with punctuation forcing boundaries, natural words cheap, and
119
+ letter-digit runs expensive. Its worst error against `gpt-tokenizer` is a factor of about 1.46
120
+ either way, which is why the byte ceiling stays behind it as the hard bound.
121
+
122
+ `DOOM_RUNNER_RESULT_MAX_BYTES`, `DOOM_RUNNER_RESULT_MAX_TOKENS`, and their
123
+ `DOOM_RUNNER_SUCCESS_*` counterparts move these for one invocation. A pragma overrides all of
124
+ them, which is how one noisy successful command can still ask for the wider result. Raising only
125
+ `maxResultBytes` will not help when the token ceiling is the one binding.
114
126
 
115
127
  Error lines found in the dropped span are rescued into that marker's block, up to ten entries.
116
128
  Matching looks for a severity word in the first 60 characters, so `[widget] build: error TS2322`
@@ -135,7 +147,7 @@ One command that needs a wider result can say so on its first line. The pragma i
135
147
  so it stays harmless to the command itself:
136
148
 
137
149
  ```bash
138
- # @doom: {"maxResultBytes": 32768, "maxResultLines": 300}
150
+ # @doom: {"maxResultBytes": 32768, "maxResultTokens": 8192, "maxResultLines": 300}
139
151
  pnpm build
140
152
  ```
141
153
 
@@ -148,6 +160,8 @@ trusted, and an unrecognized or out-of-range key is reported rather than silentl
148
160
  "maxResultBytes": 16384,
149
161
  "maxResultLines": 200,
150
162
  "successMaxResultBytes": 2048,
163
+ "maxResultTokens": 2048,
164
+ "successMaxResultTokens": 512,
151
165
  "headRatio": 0.2,
152
166
  "errorPatterns": ["^FEHLER", "^\\s*FAILED\\b"],
153
167
  "errorMaxEntries": 10,
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_SUCCESS_RESULT_MAX_BYTES=e.DEFAULT_SUCCESS_RESULT_MAX_BYTES,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.SUCCESS_RESULT_MAX_BYTES_ENV=e.SUCCESS_RESULT_MAX_BYTES_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.getRunnerSettings=e.getRunnerSettings,exports.getSuccessResultMaxBytes=e.getSuccessResultMaxBytes,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_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;
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_SUCCESS_RESULT_MAX_BYTES, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getRunnerSettings, getSuccessResultMaxBytes, 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_SUCCESS_RESULT_MAX_BYTES, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getRunnerSettings, getSuccessResultMaxBytes, 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_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 };
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_SUCCESS_RESULT_MAX_BYTES, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getRunnerSettings, getSuccessResultMaxBytes, 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_SUCCESS_RESULT_MAX_BYTES, LOG_DIR_ENV, LOG_MAX_BYTES_ENV, LOG_TTL_MS_ENV, RESULT_MAX_BYTES_ENV, SUCCESS_RESULT_MAX_BYTES_ENV, getBackgroundThresholdMs, getErrorBudgetRatio, getErrorMaxEntries, getErrorMaxVariantsJoined, getErrorPatterns, getHeadRatio, getLogMaxBytes, getLogTtlMs, getResultMaxBytes, getResultMaxLines, getRunnerSettings, getSuccessResultMaxBytes, 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_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 };
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,DEFAULT_RESULT_MAX_BYTES as c,DEFAULT_RESULT_MAX_LINES as l,DEFAULT_SUCCESS_RESULT_MAX_BYTES as u,LOG_DIR_ENV as d,LOG_MAX_BYTES_ENV as f,LOG_TTL_MS_ENV as p,RESULT_MAX_BYTES_ENV as m,SUCCESS_RESULT_MAX_BYTES_ENV as h,getBackgroundThresholdMs as g,getErrorBudgetRatio as _,getErrorMaxEntries as v,getErrorMaxVariantsJoined as y,getErrorPatterns as b,getHeadRatio as x,getLogMaxBytes as S,getLogTtlMs as C,getResultMaxBytes as w,getResultMaxLines as T,getRunnerSettings as E,getSuccessResultMaxBytes as D,setRunnerSettings as O}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_RESULT_MAX_BYTES,l as DEFAULT_RESULT_MAX_LINES,u as DEFAULT_SUCCESS_RESULT_MAX_BYTES,d as LOG_DIR_ENV,f as LOG_MAX_BYTES_ENV,p as LOG_TTL_MS_ENV,m as RESULT_MAX_BYTES_ENV,h as SUCCESS_RESULT_MAX_BYTES_ENV,g as getBackgroundThresholdMs,_ as getErrorBudgetRatio,v as getErrorMaxEntries,y as getErrorMaxVariantsJoined,b as getErrorPatterns,x as getHeadRatio,S as getLogMaxBytes,C as getLogTtlMs,w as getResultMaxBytes,T as getResultMaxLines,E as getRunnerSettings,D as getSuccessResultMaxBytes,O as setRunnerSettings};
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_RESULT_MAX_BYTES as c,DEFAULT_RESULT_MAX_LINES as l,DEFAULT_RESULT_MAX_TOKENS as u,DEFAULT_SUCCESS_RESULT_MAX_BYTES as d,DEFAULT_SUCCESS_RESULT_MAX_TOKENS as f,LOG_DIR_ENV as p,LOG_MAX_BYTES_ENV as m,LOG_TTL_MS_ENV as h,RESULT_MAX_BYTES_ENV as g,RESULT_MAX_TOKENS_ENV as _,SUCCESS_RESULT_MAX_BYTES_ENV as v,SUCCESS_RESULT_MAX_TOKENS_ENV as y,getBackgroundThresholdMs as b,getErrorBudgetRatio as x,getErrorMaxEntries as S,getErrorMaxVariantsJoined as C,getErrorPatterns as w,getHeadRatio as T,getLogMaxBytes as E,getLogTtlMs as D,getResultMaxBytes as O,getResultMaxLines as k,getResultMaxTokens as A,getRunnerSettings as j,getSuccessResultMaxBytes as M,getSuccessResultMaxTokens as N,setRunnerSettings as P}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_RESULT_MAX_BYTES,l as DEFAULT_RESULT_MAX_LINES,u as DEFAULT_RESULT_MAX_TOKENS,d as DEFAULT_SUCCESS_RESULT_MAX_BYTES,f as DEFAULT_SUCCESS_RESULT_MAX_TOKENS,p as LOG_DIR_ENV,m as LOG_MAX_BYTES_ENV,h as LOG_TTL_MS_ENV,g as RESULT_MAX_BYTES_ENV,_ as RESULT_MAX_TOKENS_ENV,v as SUCCESS_RESULT_MAX_BYTES_ENV,y as SUCCESS_RESULT_MAX_TOKENS_ENV,b as getBackgroundThresholdMs,x as getErrorBudgetRatio,S as getErrorMaxEntries,C as getErrorMaxVariantsJoined,w as getErrorPatterns,T as getHeadRatio,E as getLogMaxBytes,D as getLogTtlMs,O as getResultMaxBytes,k as getResultMaxLines,A as getResultMaxTokens,j as getRunnerSettings,M as getSuccessResultMaxBytes,N as getSuccessResultMaxTokens,P as setRunnerSettings};
@@ -1,2 +1,2 @@
1
- const e=require("../../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("@earendil-works/pi-coding-agent");const i=`doompi-runner.json`,a={settings:{},issues:[]},o=[`headRatio`,`errorBudgetRatio`],s=[`maxResultBytes`,`maxResultLines`,`successMaxResultBytes`,`errorMaxEntries`,`errorMaxVariantsJoined`],c=new Set([...o,...s,`errorPatterns`]),l=262144,u={maxResultBytes:l,successMaxResultBytes:l,maxResultLines:5e3,errorMaxEntries:100,errorMaxVariantsJoined:100};var d=class{load(e,o){if(!o)return a;let s;try{s=t.default.readFileSync(n.default.join(e,r.CONFIG_DIR_NAME,i),`utf8`)}catch(e){return e.code===`ENOENT`?a:{settings:{},issues:[`${i} could not be read`]}}let c;try{c=JSON.parse(s)}catch{return{settings:{},issues:[`${i} is not valid JSON`]}}return f(c)}};function f(e){if(typeof e!=`object`||!e||Array.isArray(e))return{settings:{},issues:[`${i} must contain a JSON object`]};let t=e,n={},r=[];for(let e of Object.keys(t))c.has(e)||r.push(`unknown key ${e}`);for(let e of s){let i=t[e];if(i!==void 0){if(typeof i!=`number`||!Number.isInteger(i)||i<=0){r.push(`${e} must be a positive integer`);continue}n[e]=Math.min(i,u[e]??i)}}for(let e of o){let i=t[e];if(i!==void 0){if(typeof i!=`number`||!Number.isFinite(i)||i<=0||i>=1){r.push(`${e} must be a number above 0 and below 1`);continue}n[e]=i}}let a=t.errorPatterns;if(a!==void 0){if(!Array.isArray(a)||a.some(e=>typeof e!=`string`))r.push(`errorPatterns must be an array of strings`);else{let e=[];for(let t of a.slice(0,32)){if(t.length>512){r.push(`errorPatterns entry is too long`);continue}try{new RegExp(t,`iu`),e.push(t)}catch{r.push(`errorPatterns entry is not a valid regular expression: ${t}`)}}e.length>0&&(n.errorPatterns=e)}}return{settings:n,issues:r}}exports.RUNNER_SETTINGS_FILE=i,exports.RunnerSettingsLoader=d;
1
+ const e=require("../../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("@earendil-works/pi-coding-agent");const i=`doompi-runner.json`,a={settings:{},issues:[]},o=[`headRatio`,`errorBudgetRatio`],s=[`maxResultBytes`,`maxResultLines`,`successMaxResultBytes`,`maxResultTokens`,`successMaxResultTokens`,`errorMaxEntries`,`errorMaxVariantsJoined`],c=new Set([...o,...s,`errorPatterns`]),l=262144,u=1e5,d={maxResultBytes:l,successMaxResultBytes:l,maxResultTokens:u,successMaxResultTokens:u,maxResultLines:5e3,errorMaxEntries:100,errorMaxVariantsJoined:100};var f=class{load(e,o){if(!o)return a;let s;try{s=t.default.readFileSync(n.default.join(e,r.CONFIG_DIR_NAME,i),`utf8`)}catch(e){return e.code===`ENOENT`?a:{settings:{},issues:[`${i} could not be read`]}}let c;try{c=JSON.parse(s)}catch{return{settings:{},issues:[`${i} is not valid JSON`]}}return p(c)}};function p(e){if(typeof e!=`object`||!e||Array.isArray(e))return{settings:{},issues:[`${i} must contain a JSON object`]};let t=e,n={},r=[];for(let e of Object.keys(t))c.has(e)||r.push(`unknown key ${e}`);for(let e of s){let i=t[e];if(i!==void 0){if(typeof i!=`number`||!Number.isInteger(i)||i<=0){r.push(`${e} must be a positive integer`);continue}n[e]=Math.min(i,d[e]??i)}}for(let e of o){let i=t[e];if(i!==void 0){if(typeof i!=`number`||!Number.isFinite(i)||i<=0||i>=1){r.push(`${e} must be a number above 0 and below 1`);continue}n[e]=i}}let a=t.errorPatterns;if(a!==void 0){if(!Array.isArray(a)||a.some(e=>typeof e!=`string`))r.push(`errorPatterns must be an array of strings`);else{let e=[];for(let t of a.slice(0,32)){if(t.length>512){r.push(`errorPatterns entry is too long`);continue}try{new RegExp(t,`iu`),e.push(t)}catch{r.push(`errorPatterns entry is not a valid regular expression: ${t}`)}}e.length>0&&(n.errorPatterns=e)}}return{settings:n,issues:r}}exports.RUNNER_SETTINGS_FILE=i,exports.RunnerSettingsLoader=f;
2
2
  //# sourceMappingURL=RunnerSettingsLoader.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RunnerSettingsLoader.cjs","names":["fs","path","CONFIG_DIR_NAME"],"sources":["../../../../src/adapters/RunnerSettings/RunnerSettingsLoader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\nexport const RUNNER_SETTINGS_FILE = 'doompi-runner.json';\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst MAX_RESULT_BYTES_CEILING = 262_144;\nconst MAX_RESULT_LINES_CEILING = 5_000;\nconst MAX_ERROR_ENTRIES_CEILING = 100;\nconst MAX_PATTERNS = 32;\nconst MAX_PATTERN_LENGTH = 512;\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n"],"mappings":"kMAMA,MAAa,EAAuB,qBAE9B,EAA4B,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,CAAE,EACvD,EAAa,CAAC,YAAa,kBAAkB,EAC7C,EAAa,CACjB,iBACA,iBACA,wBACA,kBACA,wBACF,EACM,EAAa,IAAI,IAAY,CAAC,GAAG,EAAY,GAAG,EAAY,eAAe,CAAC,EAE5E,EAA2B,OAK3B,EAA6C,CACjD,eAAgB,EAChB,sBAAuB,EACvB,eAAgB,IAChB,gBAAiB,IACjB,uBAAwB,GAC1B,EAEA,IAAa,EAAb,KAAmE,CACjE,KAAK,EAAa,EAAsC,CAEtD,GAAI,CAAC,EAAS,OAAO,EAErB,IAAI,EACJ,GAAI,CACF,EAAMA,EAAAA,QAAG,aAAaC,EAAAA,QAAK,KAAK,EAAKC,EAAAA,gBAAiB,CAAoB,EAAG,MAAM,CACrF,OAAS,EAAO,CAEd,OADK,EAAgC,OAAS,SAAiB,EACxD,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CAEA,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAG,CACzB,MAAQ,CACN,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CACA,OAAO,EAAS,CAAM,CACxB,CACF,EAEA,SAAS,EAAS,EAAqC,CACrD,GAAI,OAAO,GAAW,WAAY,GAAmB,MAAM,QAAQ,CAAM,EACvE,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,4BAA4B,CAAE,EAGxF,IAAM,EAAS,EACT,EAAoC,CAAC,EACrC,EAAmB,CAAC,EAG1B,IAAK,IAAM,KAAO,OAAO,KAAK,CAAM,EAAQ,EAAW,IAAI,CAAG,GAAG,EAAO,KAAK,eAAe,GAAK,EAEjG,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,UAAU,CAAK,GAAK,GAAS,EAAG,CACvE,EAAO,KAAK,GAAG,EAAI,4BAA4B,EAC/C,QACF,CACA,EAAS,GAAO,KAAK,IAAI,EAAO,EAAS,IAAQ,CAAK,CADtD,CAEF,CAEA,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,GAAK,GAAS,EAAG,CACpF,EAAO,KAAK,GAAG,EAAI,sCAAsC,EACzD,QACF,CACA,EAAS,GAAO,CADhB,CAEF,CAEA,IAAM,EAAW,EAAO,cACxB,GAAI,IAAa,IAAA,GAAW,CAC1B,GAAI,CAAC,MAAM,QAAQ,CAAQ,GAAK,EAAS,KAAM,GAAU,OAAO,GAAU,QAAQ,EAChF,EAAO,KAAK,2CAA2C,MAClD,CACL,IAAM,EAAqB,CAAC,EAC5B,IAAK,IAAM,KAAW,EAAS,MAAM,EAAG,EAAY,EAAe,CACjE,GAAI,EAAQ,OAAS,IAAoB,CACvC,EAAO,KAAK,iCAAiC,EAC7C,QACF,CACA,GAAI,CACF,IAAI,OAAO,EAAS,IAAI,EACxB,EAAS,KAAK,CAAO,CACvB,MAAQ,CACN,EAAO,KAAK,0DAA0D,GAAS,CACjF,CACF,CACI,EAAS,OAAS,IAAG,EAAS,cAAgB,EACpD,CACF,CAEA,MAAO,CAAY,WAA4B,QAAO,CACxD"}
1
+ {"version":3,"file":"RunnerSettingsLoader.cjs","names":["fs","path","CONFIG_DIR_NAME"],"sources":["../../../../src/adapters/RunnerSettings/RunnerSettingsLoader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\nexport const RUNNER_SETTINGS_FILE = 'doompi-runner.json';\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'maxResultTokens',\n 'successMaxResultTokens',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst MAX_RESULT_BYTES_CEILING = 262_144;\nconst MAX_RESULT_LINES_CEILING = 5_000;\nconst MAX_RESULT_TOKENS_CEILING = 100_000;\nconst MAX_ERROR_ENTRIES_CEILING = 100;\nconst MAX_PATTERNS = 32;\nconst MAX_PATTERN_LENGTH = 512;\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultTokens: MAX_RESULT_TOKENS_CEILING,\n successMaxResultTokens: MAX_RESULT_TOKENS_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n"],"mappings":"kMAMA,MAAa,EAAuB,qBAE9B,EAA4B,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,CAAE,EACvD,EAAa,CAAC,YAAa,kBAAkB,EAC7C,EAAa,CACjB,iBACA,iBACA,wBACA,kBACA,yBACA,kBACA,wBACF,EACM,EAAa,IAAI,IAAY,CAAC,GAAG,EAAY,GAAG,EAAY,eAAe,CAAC,EAE5E,EAA2B,OAE3B,EAA4B,IAI5B,EAA6C,CACjD,eAAgB,EAChB,sBAAuB,EACvB,gBAAiB,EACjB,uBAAwB,EACxB,eAAgB,IAChB,gBAAiB,IACjB,uBAAwB,GAC1B,EAEA,IAAa,EAAb,KAAmE,CACjE,KAAK,EAAa,EAAsC,CAEtD,GAAI,CAAC,EAAS,OAAO,EAErB,IAAI,EACJ,GAAI,CACF,EAAMA,EAAAA,QAAG,aAAaC,EAAAA,QAAK,KAAK,EAAKC,EAAAA,gBAAiB,CAAoB,EAAG,MAAM,CACrF,OAAS,EAAO,CAEd,OADK,EAAgC,OAAS,SAAiB,EACxD,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CAEA,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAG,CACzB,MAAQ,CACN,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CACA,OAAO,EAAS,CAAM,CACxB,CACF,EAEA,SAAS,EAAS,EAAqC,CACrD,GAAI,OAAO,GAAW,WAAY,GAAmB,MAAM,QAAQ,CAAM,EACvE,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,4BAA4B,CAAE,EAGxF,IAAM,EAAS,EACT,EAAoC,CAAC,EACrC,EAAmB,CAAC,EAG1B,IAAK,IAAM,KAAO,OAAO,KAAK,CAAM,EAAQ,EAAW,IAAI,CAAG,GAAG,EAAO,KAAK,eAAe,GAAK,EAEjG,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,UAAU,CAAK,GAAK,GAAS,EAAG,CACvE,EAAO,KAAK,GAAG,EAAI,4BAA4B,EAC/C,QACF,CACA,EAAS,GAAO,KAAK,IAAI,EAAO,EAAS,IAAQ,CAAK,CADtD,CAEF,CAEA,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,GAAK,GAAS,EAAG,CACpF,EAAO,KAAK,GAAG,EAAI,sCAAsC,EACzD,QACF,CACA,EAAS,GAAO,CADhB,CAEF,CAEA,IAAM,EAAW,EAAO,cACxB,GAAI,IAAa,IAAA,GAAW,CAC1B,GAAI,CAAC,MAAM,QAAQ,CAAQ,GAAK,EAAS,KAAM,GAAU,OAAO,GAAU,QAAQ,EAChF,EAAO,KAAK,2CAA2C,MAClD,CACL,IAAM,EAAqB,CAAC,EAC5B,IAAK,IAAM,KAAW,EAAS,MAAM,EAAG,EAAY,EAAe,CACjE,GAAI,EAAQ,OAAS,IAAoB,CACvC,EAAO,KAAK,iCAAiC,EAC7C,QACF,CACA,GAAI,CACF,IAAI,OAAO,EAAS,IAAI,EACxB,EAAS,KAAK,CAAO,CACvB,MAAQ,CACN,EAAO,KAAK,0DAA0D,GAAS,CACjF,CACF,CACI,EAAS,OAAS,IAAG,EAAS,cAAgB,EACpD,CACF,CAEA,MAAO,CAAY,WAA4B,QAAO,CACxD"}
@@ -1,2 +1,2 @@
1
- import e from"node:fs";import t from"node:path";import{CONFIG_DIR_NAME as n}from"@earendil-works/pi-coding-agent";const r=`doompi-runner.json`,i={settings:{},issues:[]},a=[`headRatio`,`errorBudgetRatio`],o=[`maxResultBytes`,`maxResultLines`,`successMaxResultBytes`,`errorMaxEntries`,`errorMaxVariantsJoined`],s=new Set([...a,...o,`errorPatterns`]),c=262144,l={maxResultBytes:c,successMaxResultBytes:c,maxResultLines:5e3,errorMaxEntries:100,errorMaxVariantsJoined:100};var u=class{load(a,o){if(!o)return i;let s;try{s=e.readFileSync(t.join(a,n,r),`utf8`)}catch(e){return e.code===`ENOENT`?i:{settings:{},issues:[`${r} could not be read`]}}let c;try{c=JSON.parse(s)}catch{return{settings:{},issues:[`${r} is not valid JSON`]}}return d(c)}};function d(e){if(typeof e!=`object`||!e||Array.isArray(e))return{settings:{},issues:[`${r} must contain a JSON object`]};let t=e,n={},i=[];for(let e of Object.keys(t))s.has(e)||i.push(`unknown key ${e}`);for(let e of o){let r=t[e];if(r!==void 0){if(typeof r!=`number`||!Number.isInteger(r)||r<=0){i.push(`${e} must be a positive integer`);continue}n[e]=Math.min(r,l[e]??r)}}for(let e of a){let r=t[e];if(r!==void 0){if(typeof r!=`number`||!Number.isFinite(r)||r<=0||r>=1){i.push(`${e} must be a number above 0 and below 1`);continue}n[e]=r}}let c=t.errorPatterns;if(c!==void 0){if(!Array.isArray(c)||c.some(e=>typeof e!=`string`))i.push(`errorPatterns must be an array of strings`);else{let e=[];for(let t of c.slice(0,32)){if(t.length>512){i.push(`errorPatterns entry is too long`);continue}try{new RegExp(t,`iu`),e.push(t)}catch{i.push(`errorPatterns entry is not a valid regular expression: ${t}`)}}e.length>0&&(n.errorPatterns=e)}}return{settings:n,issues:i}}export{r as RUNNER_SETTINGS_FILE,u as RunnerSettingsLoader};
1
+ import e from"node:fs";import t from"node:path";import{CONFIG_DIR_NAME as n}from"@earendil-works/pi-coding-agent";const r=`doompi-runner.json`,i={settings:{},issues:[]},a=[`headRatio`,`errorBudgetRatio`],o=[`maxResultBytes`,`maxResultLines`,`successMaxResultBytes`,`maxResultTokens`,`successMaxResultTokens`,`errorMaxEntries`,`errorMaxVariantsJoined`],s=new Set([...a,...o,`errorPatterns`]),c=262144,l=1e5,u={maxResultBytes:c,successMaxResultBytes:c,maxResultTokens:l,successMaxResultTokens:l,maxResultLines:5e3,errorMaxEntries:100,errorMaxVariantsJoined:100};var d=class{load(a,o){if(!o)return i;let s;try{s=e.readFileSync(t.join(a,n,r),`utf8`)}catch(e){return e.code===`ENOENT`?i:{settings:{},issues:[`${r} could not be read`]}}let c;try{c=JSON.parse(s)}catch{return{settings:{},issues:[`${r} is not valid JSON`]}}return f(c)}};function f(e){if(typeof e!=`object`||!e||Array.isArray(e))return{settings:{},issues:[`${r} must contain a JSON object`]};let t=e,n={},i=[];for(let e of Object.keys(t))s.has(e)||i.push(`unknown key ${e}`);for(let e of o){let r=t[e];if(r!==void 0){if(typeof r!=`number`||!Number.isInteger(r)||r<=0){i.push(`${e} must be a positive integer`);continue}n[e]=Math.min(r,u[e]??r)}}for(let e of a){let r=t[e];if(r!==void 0){if(typeof r!=`number`||!Number.isFinite(r)||r<=0||r>=1){i.push(`${e} must be a number above 0 and below 1`);continue}n[e]=r}}let c=t.errorPatterns;if(c!==void 0){if(!Array.isArray(c)||c.some(e=>typeof e!=`string`))i.push(`errorPatterns must be an array of strings`);else{let e=[];for(let t of c.slice(0,32)){if(t.length>512){i.push(`errorPatterns entry is too long`);continue}try{new RegExp(t,`iu`),e.push(t)}catch{i.push(`errorPatterns entry is not a valid regular expression: ${t}`)}}e.length>0&&(n.errorPatterns=e)}}return{settings:n,issues:i}}export{r as RUNNER_SETTINGS_FILE,d as RunnerSettingsLoader};
2
2
  //# sourceMappingURL=RunnerSettingsLoader.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RunnerSettingsLoader.mjs","names":[],"sources":["../../../../src/adapters/RunnerSettings/RunnerSettingsLoader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\nexport const RUNNER_SETTINGS_FILE = 'doompi-runner.json';\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst MAX_RESULT_BYTES_CEILING = 262_144;\nconst MAX_RESULT_LINES_CEILING = 5_000;\nconst MAX_ERROR_ENTRIES_CEILING = 100;\nconst MAX_PATTERNS = 32;\nconst MAX_PATTERN_LENGTH = 512;\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n"],"mappings":"kHAMA,MAAa,EAAuB,qBAE9B,EAA4B,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,CAAE,EACvD,EAAa,CAAC,YAAa,kBAAkB,EAC7C,EAAa,CACjB,iBACA,iBACA,wBACA,kBACA,wBACF,EACM,EAAa,IAAI,IAAY,CAAC,GAAG,EAAY,GAAG,EAAY,eAAe,CAAC,EAE5E,EAA2B,OAK3B,EAA6C,CACjD,eAAgB,EAChB,sBAAuB,EACvB,eAAgB,IAChB,gBAAiB,IACjB,uBAAwB,GAC1B,EAEA,IAAa,EAAb,KAAmE,CACjE,KAAK,EAAa,EAAsC,CAEtD,GAAI,CAAC,EAAS,OAAO,EAErB,IAAI,EACJ,GAAI,CACF,EAAM,EAAG,aAAa,EAAK,KAAK,EAAK,EAAiB,CAAoB,EAAG,MAAM,CACrF,OAAS,EAAO,CAEd,OADK,EAAgC,OAAS,SAAiB,EACxD,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CAEA,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAG,CACzB,MAAQ,CACN,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CACA,OAAO,EAAS,CAAM,CACxB,CACF,EAEA,SAAS,EAAS,EAAqC,CACrD,GAAI,OAAO,GAAW,WAAY,GAAmB,MAAM,QAAQ,CAAM,EACvE,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,4BAA4B,CAAE,EAGxF,IAAM,EAAS,EACT,EAAoC,CAAC,EACrC,EAAmB,CAAC,EAG1B,IAAK,IAAM,KAAO,OAAO,KAAK,CAAM,EAAQ,EAAW,IAAI,CAAG,GAAG,EAAO,KAAK,eAAe,GAAK,EAEjG,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,UAAU,CAAK,GAAK,GAAS,EAAG,CACvE,EAAO,KAAK,GAAG,EAAI,4BAA4B,EAC/C,QACF,CACA,EAAS,GAAO,KAAK,IAAI,EAAO,EAAS,IAAQ,CAAK,CADtD,CAEF,CAEA,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,GAAK,GAAS,EAAG,CACpF,EAAO,KAAK,GAAG,EAAI,sCAAsC,EACzD,QACF,CACA,EAAS,GAAO,CADhB,CAEF,CAEA,IAAM,EAAW,EAAO,cACxB,GAAI,IAAa,IAAA,GAAW,CAC1B,GAAI,CAAC,MAAM,QAAQ,CAAQ,GAAK,EAAS,KAAM,GAAU,OAAO,GAAU,QAAQ,EAChF,EAAO,KAAK,2CAA2C,MAClD,CACL,IAAM,EAAqB,CAAC,EAC5B,IAAK,IAAM,KAAW,EAAS,MAAM,EAAG,EAAY,EAAe,CACjE,GAAI,EAAQ,OAAS,IAAoB,CACvC,EAAO,KAAK,iCAAiC,EAC7C,QACF,CACA,GAAI,CACF,IAAI,OAAO,EAAS,IAAI,EACxB,EAAS,KAAK,CAAO,CACvB,MAAQ,CACN,EAAO,KAAK,0DAA0D,GAAS,CACjF,CACF,CACI,EAAS,OAAS,IAAG,EAAS,cAAgB,EACpD,CACF,CAEA,MAAO,CAAY,WAA4B,QAAO,CACxD"}
1
+ {"version":3,"file":"RunnerSettingsLoader.mjs","names":[],"sources":["../../../../src/adapters/RunnerSettings/RunnerSettingsLoader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_DIR_NAME } from '@earendil-works/pi-coding-agent';\nimport type { IRunnerSettingsLoader, RunnerSettings, RunnerSettingsLoad } from '../../types/runnerSettings';\n\n/** Pi's convention is one JSON file per extension under the config directory. */\nexport const RUNNER_SETTINGS_FILE = 'doompi-runner.json';\n\nconst EMPTY: RunnerSettingsLoad = { settings: {}, issues: [] };\nconst RATIO_KEYS = ['headRatio', 'errorBudgetRatio'] as const;\nconst COUNT_KEYS = [\n 'maxResultBytes',\n 'maxResultLines',\n 'successMaxResultBytes',\n 'maxResultTokens',\n 'successMaxResultTokens',\n 'errorMaxEntries',\n 'errorMaxVariantsJoined',\n] as const;\nconst KNOWN_KEYS = new Set<string>([...RATIO_KEYS, ...COUNT_KEYS, 'errorPatterns']);\n/** Ceilings so a project file cannot flood the model's context. */\nconst MAX_RESULT_BYTES_CEILING = 262_144;\nconst MAX_RESULT_LINES_CEILING = 5_000;\nconst MAX_RESULT_TOKENS_CEILING = 100_000;\nconst MAX_ERROR_ENTRIES_CEILING = 100;\nconst MAX_PATTERNS = 32;\nconst MAX_PATTERN_LENGTH = 512;\nconst CEILINGS: Readonly<Record<string, number>> = {\n maxResultBytes: MAX_RESULT_BYTES_CEILING,\n successMaxResultBytes: MAX_RESULT_BYTES_CEILING,\n maxResultTokens: MAX_RESULT_TOKENS_CEILING,\n successMaxResultTokens: MAX_RESULT_TOKENS_CEILING,\n maxResultLines: MAX_RESULT_LINES_CEILING,\n errorMaxEntries: MAX_ERROR_ENTRIES_CEILING,\n errorMaxVariantsJoined: MAX_ERROR_ENTRIES_CEILING,\n};\n\nexport class RunnerSettingsLoader implements IRunnerSettingsLoader {\n load(cwd: string, trusted: boolean): RunnerSettingsLoad {\n // Project-local configuration is untrusted input until the project is.\n if (!trusted) return EMPTY;\n\n let raw: string;\n try {\n raw = fs.readFileSync(path.join(cwd, CONFIG_DIR_NAME, RUNNER_SETTINGS_FILE), 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return EMPTY;\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} could not be read`] };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} is not valid JSON`] };\n }\n return validate(parsed);\n }\n}\n\nfunction validate(parsed: unknown): RunnerSettingsLoad {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return { settings: {}, issues: [`${RUNNER_SETTINGS_FILE} must contain a JSON object`] };\n }\n\n const source = parsed as Record<string, unknown>;\n const settings: Record<string, unknown> = {};\n const issues: string[] = [];\n // A rejected key is reported rather than dropped: a silent typo reads as a\n // setting that simply does not work.\n for (const key of Object.keys(source)) if (!KNOWN_KEYS.has(key)) issues.push(`unknown key ${key}`);\n\n for (const key of COUNT_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n issues.push(`${key} must be a positive integer`);\n continue;\n }\n settings[key] = Math.min(value, CEILINGS[key] ?? value);\n }\n\n for (const key of RATIO_KEYS) {\n const value = source[key];\n if (value === undefined) continue;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value >= 1) {\n issues.push(`${key} must be a number above 0 and below 1`);\n continue;\n }\n settings[key] = value;\n }\n\n const patterns = source.errorPatterns;\n if (patterns !== undefined) {\n if (!Array.isArray(patterns) || patterns.some((entry) => typeof entry !== 'string')) {\n issues.push('errorPatterns must be an array of strings');\n } else {\n const accepted: string[] = [];\n for (const pattern of patterns.slice(0, MAX_PATTERNS) as string[]) {\n if (pattern.length > MAX_PATTERN_LENGTH) {\n issues.push('errorPatterns entry is too long');\n continue;\n }\n try {\n new RegExp(pattern, 'iu');\n accepted.push(pattern);\n } catch {\n issues.push(`errorPatterns entry is not a valid regular expression: ${pattern}`);\n }\n }\n if (accepted.length > 0) settings.errorPatterns = accepted;\n }\n }\n\n return { settings: settings as RunnerSettings, issues };\n}\n"],"mappings":"kHAMA,MAAa,EAAuB,qBAE9B,EAA4B,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,CAAE,EACvD,EAAa,CAAC,YAAa,kBAAkB,EAC7C,EAAa,CACjB,iBACA,iBACA,wBACA,kBACA,yBACA,kBACA,wBACF,EACM,EAAa,IAAI,IAAY,CAAC,GAAG,EAAY,GAAG,EAAY,eAAe,CAAC,EAE5E,EAA2B,OAE3B,EAA4B,IAI5B,EAA6C,CACjD,eAAgB,EAChB,sBAAuB,EACvB,gBAAiB,EACjB,uBAAwB,EACxB,eAAgB,IAChB,gBAAiB,IACjB,uBAAwB,GAC1B,EAEA,IAAa,EAAb,KAAmE,CACjE,KAAK,EAAa,EAAsC,CAEtD,GAAI,CAAC,EAAS,OAAO,EAErB,IAAI,EACJ,GAAI,CACF,EAAM,EAAG,aAAa,EAAK,KAAK,EAAK,EAAiB,CAAoB,EAAG,MAAM,CACrF,OAAS,EAAO,CAEd,OADK,EAAgC,OAAS,SAAiB,EACxD,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CAEA,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAG,CACzB,MAAQ,CACN,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,mBAAmB,CAAE,CAC/E,CACA,OAAO,EAAS,CAAM,CACxB,CACF,EAEA,SAAS,EAAS,EAAqC,CACrD,GAAI,OAAO,GAAW,WAAY,GAAmB,MAAM,QAAQ,CAAM,EACvE,MAAO,CAAE,SAAU,CAAC,EAAG,OAAQ,CAAC,GAAG,EAAqB,4BAA4B,CAAE,EAGxF,IAAM,EAAS,EACT,EAAoC,CAAC,EACrC,EAAmB,CAAC,EAG1B,IAAK,IAAM,KAAO,OAAO,KAAK,CAAM,EAAQ,EAAW,IAAI,CAAG,GAAG,EAAO,KAAK,eAAe,GAAK,EAEjG,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,UAAU,CAAK,GAAK,GAAS,EAAG,CACvE,EAAO,KAAK,GAAG,EAAI,4BAA4B,EAC/C,QACF,CACA,EAAS,GAAO,KAAK,IAAI,EAAO,EAAS,IAAQ,CAAK,CADtD,CAEF,CAEA,IAAK,IAAM,KAAO,EAAY,CAC5B,IAAM,EAAQ,EAAO,GACjB,OAAU,IAAA,GACd,IAAI,OAAO,GAAU,UAAY,CAAC,OAAO,SAAS,CAAK,GAAK,GAAS,GAAK,GAAS,EAAG,CACpF,EAAO,KAAK,GAAG,EAAI,sCAAsC,EACzD,QACF,CACA,EAAS,GAAO,CADhB,CAEF,CAEA,IAAM,EAAW,EAAO,cACxB,GAAI,IAAa,IAAA,GAAW,CAC1B,GAAI,CAAC,MAAM,QAAQ,CAAQ,GAAK,EAAS,KAAM,GAAU,OAAO,GAAU,QAAQ,EAChF,EAAO,KAAK,2CAA2C,MAClD,CACL,IAAM,EAAqB,CAAC,EAC5B,IAAK,IAAM,KAAW,EAAS,MAAM,EAAG,EAAY,EAAe,CACjE,GAAI,EAAQ,OAAS,IAAoB,CACvC,EAAO,KAAK,iCAAiC,EAC7C,QACF,CACA,GAAI,CACF,IAAI,OAAO,EAAS,IAAI,EACxB,EAAS,KAAK,CAAO,CACvB,MAAQ,CACN,EAAO,KAAK,0DAA0D,GAAS,CACjF,CACF,CACI,EAAS,OAAS,IAAG,EAAS,cAAgB,EACpD,CACF,CAEA,MAAO,CAAY,WAA4B,QAAO,CACxD"}
@@ -1,6 +1,6 @@
1
1
  const e=require("../../types/config.cjs"),t=require("../../services/AnsiScrub/ansiScrub.cjs"),n=require("./responseEnvelope.cjs"),r=require("../../schemas/bashTool.cjs"),i=require("../../tui/bashRender.cjs"),a=1e3,o={requested:`Started in the background`,threshold:`Still running after the background threshold`,interactive:`Started interactively`},s=`Execute shell commands with bounded foreground output and supervised background runners`;function c(t=e.getBackgroundThresholdMs()){return[`A command still running after ${Math.round(t/a)} seconds remains active as a background runner with an id and streaming log path.`,`Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.`,`Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.`,`On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.`,`Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.`]}function l(e,t){e.registerTool({name:r.BASH_TOOL_NAME,label:r.BASH_TOOL_LABEL,description:`Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.`,promptSnippet:s,promptGuidelines:c(),parameters:r.BashParamsSchema,renderShell:`self`,async execute(e,r,i,o,s){let{command:c,timeout:l,background:u,interactive:d,name:p}=r,m;o&&o(n.textResult(`Starting ${d===!0?`interactive runner`:u===!0?`background runner`:`command`}...`)),u!==!0&&d!==!0&&o&&(m=e=>o(n.textResult(e)));let h;try{h=await t.bashRunService.run({command:c,timeoutMs:l===void 0?void 0:l*a,background:u,interactive:d,name:p,...m?{onOutput:m}:{},sessionId:await t.getSessionId()})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error([`Could not execute command: ${t}`,`Next: verify the command, runtime, and working directory. Retry only after correcting the cause.`].join(`
2
2
  `),{cause:e})}return h.kind===`promoted`&&t.onRunnerStarted(h.id),f(h,n.parseResultPragma(r.command))},renderCall(e,t,n){return i.renderBashCall(e,t)},renderResult(e,t,n,r){return i.renderBashResult(e,{...t,isError:r.isError},n)}})}function u(e){return e.timedOut===!0||e.signal!==null||e.exitCode!==null&&e.exitCode!==0}function d(e){if(e.timedOut===!0)return`Timed out: exceeded the requested timeout.`;if(e.signal!==null)return`Signal: ${e.signal}`;if(e.exitCode===null)return`Exit status unavailable.`;if(e.exitCode!==0)return`Exit: ${e.exitCode}`}function f(r,i={}){if(r.kind===`failed`)throw Error([`Could not start runner "${r.name}": ${r.error}`,`Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.`].join(`
3
3
  `));if(r.kind===`promoted`){let e=[`${o[r.reason]}: runner "${r.name}" (${r.id}).`,`Streaming log: ${r.logPath}`,`Inspect: doom-runner logs ${r.id}`].join(`
4
- `);return n.textResult(e,{id:r.id,runner:r.name,pid:r.pid,logPath:r.logPath,promoted:!0,reason:r.reason})}let a=!u(r),s=i.maxLines??e.getResultMaxLines(),c=i.maxBytes??(a?e.getSuccessResultMaxBytes():e.getResultMaxBytes()),l=n.summarizeLog(r.logPath,s,c),f=!r.rtkOutput&&l.tail.length===0&&r.output.length>0,p,m,h,g,_;if(r.rtkOutput){let e=Buffer.byteLength(r.rtkOutput.output,`utf8`)<r.rtkOutput.bytes?n.composeExcerpt(r.rtkOutput.head,r.rtkOutput.output,r.rtkOutput.lines,s,c):n.boundExcerpt(r.rtkOutput.output,s,c);p=e.text,m=e.lines,h=r.rtkOutput.lines,g=r.rtkOutput.bytes,_=h>m||g>Buffer.byteLength(p,`utf8`)}else p=f?r.output:l.tail,m=f?n.countLines(p):l.tailLines,h=Math.max(l.lines,m),g=f?Buffer.byteLength(p,`utf8`):l.bytes,_=!f&&(h>m||g>Buffer.byteLength(p,`utf8`));let v=t.stripAnsi(p).replace(/\r?\n$/,``),y=u(r),b=d(r),x=[];if(v.length===0)x.push(b===void 0?`Completed with no output.`:`No output.`);else if(_){let e=r.rtkOutput?`RTK ${r.rtkOutput.filter} excerpt`:`Log excerpt`;x.push(`${e} (${m.toLocaleString(`en-US`)} of ${h.toLocaleString(`en-US`)} lines):\n${v}`)}else x.push(v);if(b!==void 0&&x.push(b),r.rtkWarning&&x.push(r.rtkWarning),_){let e=r.rtkOutput?`Complete raw log`:`Full log`;x.push(`${e}: ${r.logPath} (${n.formatSize(l.bytes)}, ${l.lines.toLocaleString(`en-US`)} lines); inspect with doom-runner logs ${r.id}`)}else y&&v.length===0&&x.push(`Log: ${r.logPath}`,`Next: run one read-only diagnostic; retry only after correcting the cause.`);let S=n.boundResultText(x.join(`
5
- `),c),C={id:r.id,runner:r.name,exitCode:r.exitCode,logPath:r.logPath,backend:r.backend,fileSize:l.bytes,lines:f?m:l.lines,tail:p,tailLines:m,...r.rtkOutput?{rtkFilter:r.rtkOutput.filter,rtkOutputBytes:r.rtkOutput.bytes,rtkOutputLines:r.rtkOutput.lines}:{},...r.rtkWarning?{rtkWarning:r.rtkWarning}:{},...r.timedOut?{timedOut:!0}:{}};if(y)throw Error(S);return n.textResult(S,C)}exports.BASH_PROMPT_SNIPPET=s,exports.bashPromptGuidelines=c,exports.formatRunResult=f,exports.registerBashTool=l;
4
+ `);return n.textResult(e,{id:r.id,runner:r.name,pid:r.pid,logPath:r.logPath,promoted:!0,reason:r.reason})}let a=!u(r),s=i.maxLines??e.getResultMaxLines(),c=i.maxBytes??(a?e.getSuccessResultMaxBytes():e.getResultMaxBytes()),l=i.maxTokens??(a?e.getSuccessResultMaxTokens():e.getResultMaxTokens()),f=n.summarizeLog(r.logPath,s,c,l),p=!r.rtkOutput&&f.tail.length===0&&r.output.length>0,m,h,g,_,v;if(r.rtkOutput){let e=Buffer.byteLength(r.rtkOutput.output,`utf8`)<r.rtkOutput.bytes?n.composeExcerpt(r.rtkOutput.head,r.rtkOutput.output,r.rtkOutput.lines,s,c,[],l):n.boundExcerpt(r.rtkOutput.output,s,c,l);m=e.text,h=e.lines,g=r.rtkOutput.lines,_=r.rtkOutput.bytes,v=g>h||_>Buffer.byteLength(m,`utf8`)}else m=p?r.output:f.tail,h=p?n.countLines(m):f.tailLines,g=Math.max(f.lines,h),_=p?Buffer.byteLength(m,`utf8`):f.bytes,v=!p&&(g>h||_>Buffer.byteLength(m,`utf8`));let y=t.stripAnsi(m).replace(/\r?\n$/,``),b=u(r),x=d(r),S=[];if(y.length===0)S.push(x===void 0?`Completed with no output.`:`No output.`);else if(v){let e=r.rtkOutput?`RTK ${r.rtkOutput.filter} excerpt`:`Log excerpt`;S.push(`${e} (${h.toLocaleString(`en-US`)} of ${g.toLocaleString(`en-US`)} lines):\n${y}`)}else S.push(y);if(x!==void 0&&S.push(x),r.rtkWarning&&S.push(r.rtkWarning),v){let e=r.rtkOutput?`Complete raw log`:`Full log`;S.push(`${e}: ${r.logPath} (${n.formatSize(f.bytes)}, ${f.lines.toLocaleString(`en-US`)} lines); inspect with doom-runner logs ${r.id}`)}else b&&y.length===0&&S.push(`Log: ${r.logPath}`,`Next: run one read-only diagnostic; retry only after correcting the cause.`);let C=n.boundResultText(S.join(`
5
+ `),c),w={id:r.id,runner:r.name,exitCode:r.exitCode,logPath:r.logPath,backend:r.backend,fileSize:f.bytes,lines:p?h:f.lines,tail:m,tailLines:h,...r.rtkOutput?{rtkFilter:r.rtkOutput.filter,rtkOutputBytes:r.rtkOutput.bytes,rtkOutputLines:r.rtkOutput.lines}:{},...r.rtkWarning?{rtkWarning:r.rtkWarning}:{},...r.timedOut?{timedOut:!0}:{}};if(b)throw Error(C);return n.textResult(C,w)}exports.BASH_PROMPT_SNIPPET=s,exports.bashPromptGuidelines=c,exports.formatRunResult=f,exports.registerBashTool=l;
6
6
  //# sourceMappingURL=bashTool.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.cjs","names":["getBackgroundThresholdMs","BASH_TOOL_NAME","BASH_TOOL_LABEL","BashParamsSchema","textResult","parseResultPragma","renderBashCall","renderBashResult","getResultMaxLines","getSuccessResultMaxBytes","getResultMaxBytes","summarizeLog","composeExcerpt","boundExcerpt","countLines","stripAnsi","formatSize","boundResultText"],"sources":["../../../../src/commands/bash/bashTool.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME, type BashParams, BashParamsSchema } from '../../schemas/bashTool.ts';\nimport { stripAnsi } from '../../services/AnsiScrub/ansiScrub';\nimport type { BashRunResult, CompletedRun, IBashRunService } from '../../types/bashRunService';\nimport { renderBashCall, renderBashResult } from '../../tui/bashRender.ts';\nimport {\n getBackgroundThresholdMs,\n getResultMaxBytes,\n getResultMaxLines,\n getSuccessResultMaxBytes,\n} from '../../types/config.ts';\nimport {\n boundExcerpt,\n boundResultText,\n composeExcerpt,\n parseResultPragma,\n type ResultBudget,\n countLines,\n formatSize,\n summarizeLog,\n type ToolResult,\n textResult,\n} from './responseEnvelope.ts';\n\nconst MS_PER_SECOND = 1000;\n\nconst PROMOTION_REASONS: Record<'requested' | 'threshold' | 'interactive', string> = {\n requested: 'Started in the background',\n threshold: 'Still running after the background threshold',\n interactive: 'Started interactively',\n};\n\nexport const BASH_PROMPT_SNIPPET =\n 'Execute shell commands with bounded foreground output and supervised background runners';\n\n/** Written at registration time so the stated threshold matches the configured one. */\nexport function bashPromptGuidelines(thresholdMs = getBackgroundThresholdMs()): string[] {\n return [\n `A command still running after ${Math.round(thresholdMs / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log path.`,\n 'Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.',\n 'Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.',\n 'On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.',\n 'Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.',\n ];\n}\n\nexport interface BashToolDependencies {\n bashRunService: IBashRunService;\n getSessionId(): string | Promise<string>;\n /** Called after a runner is promoted, so UI state can refresh. */\n onRunnerStarted(id: string): void;\n}\n\n/**\n * Registers a tool named `bash`, replacing pi's built-in.\n *\n * The name is deliberate: hooks, guardrails and doom-pi's dispatcher all key on\n * `bash`, and they keep working only while the replacement keeps the name.\n */\nexport function registerBashTool(pi: ExtensionAPI, dependencies: BashToolDependencies): void {\n pi.registerTool({\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.',\n promptSnippet: BASH_PROMPT_SNIPPET,\n promptGuidelines: bashPromptGuidelines(),\n parameters: BashParamsSchema,\n // Bash output already carries its own status glyphs and ANSI colours. Owning\n // the shell keeps Pi from filling every successful command with the global\n // toolSuccessBg, which overwhelms long logs and diffs.\n renderShell: 'self',\n\n async execute(_toolCallId, params, _signal, onUpdate, _ctx): Promise<ToolResult> {\n const { command, timeout, background, interactive, name } = params as BashParams;\n let onOutput: ((output: string) => void) | undefined;\n if (onUpdate) {\n const mode =\n interactive === true ? 'interactive runner' : background === true ? 'background runner' : 'command';\n onUpdate(textResult(`Starting ${mode}...`));\n }\n if (background !== true && interactive !== true && onUpdate) {\n onOutput = (output) => onUpdate(textResult(output));\n }\n let result: BashRunResult;\n try {\n result = await dependencies.bashRunService.run({\n command,\n timeoutMs: timeout === undefined ? undefined : timeout * MS_PER_SECOND,\n background,\n interactive,\n name,\n ...(onOutput ? { onOutput } : {}),\n sessionId: await dependencies.getSessionId(),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n [\n `Could not execute command: ${message}`,\n 'Next: verify the command, runtime, and working directory. Retry only after correcting the cause.',\n ].join('\\n'),\n { cause: error },\n );\n }\n\n if (result.kind === 'promoted') dependencies.onRunnerStarted(result.id);\n return formatRunResult(result, parseResultPragma(params.command));\n },\n\n // Without these, pi falls back to echoing the raw command and the tail end of\n // the result text, which is the metadata footer rather than the log output.\n renderCall(args, theme, _context) {\n return renderBashCall(args as BashParams, theme);\n },\n\n renderResult(result, options, theme, context) {\n return renderBashResult(result, { ...options, isError: context.isError }, theme);\n },\n });\n}\n\nfunction completionFailed(result: CompletedRun): boolean {\n return result.timedOut === true || result.signal !== null || (result.exitCode !== null && result.exitCode !== 0);\n}\n\nfunction completionStatus(result: CompletedRun): string | undefined {\n if (result.timedOut === true) return 'Timed out: exceeded the requested timeout.';\n if (result.signal !== null) return `Signal: ${result.signal}`;\n if (result.exitCode === null) return 'Exit status unavailable.';\n if (result.exitCode !== 0) return `Exit: ${result.exitCode}`;\n return undefined;\n}\n\nexport function formatRunResult(result: BashRunResult, budget: ResultBudget = {}): ToolResult {\n if (result.kind === 'failed') {\n throw new Error(\n [\n `Could not start runner \"${result.name}\": ${result.error}`,\n 'Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.',\n ].join('\\n'),\n );\n }\n\n if (result.kind === 'promoted') {\n const reason = PROMOTION_REASONS[result.reason];\n const body = [\n `${reason}: runner \"${result.name}\" (${result.id}).`,\n `Streaming log: ${result.logPath}`,\n `Inspect: doom-runner logs ${result.id}`,\n ].join('\\n');\n\n return textResult(body, {\n id: result.id,\n runner: result.name,\n pid: result.pid,\n logPath: result.logPath,\n promoted: true,\n reason: result.reason,\n });\n }\n\n // Same shape either way; a success just buys less of it. Exiting 0 has already\n // reported the outcome, so its output is worth a fraction of a failure's.\n const succeeded = !completionFailed(result);\n const maxLines = budget.maxLines ?? getResultMaxLines();\n // An explicit pragma still wins: asking for a wider result is the point of it.\n const maxBytes = budget.maxBytes ?? (succeeded ? getSuccessResultMaxBytes() : getResultMaxBytes());\n const log = summarizeLog(result.logPath, maxLines, maxBytes);\n const useCapturedOutput = !result.rtkOutput && log.tail.length === 0 && result.output.length > 0;\n let tail: string;\n let tailLines: number;\n let outputLines: number;\n let outputBytes: number;\n let truncated: boolean;\n if (result.rtkOutput) {\n const clipped = Buffer.byteLength(result.rtkOutput.output, 'utf8') < result.rtkOutput.bytes;\n const bounded = clipped\n ? composeExcerpt(result.rtkOutput.head, result.rtkOutput.output, result.rtkOutput.lines, maxLines, maxBytes)\n : boundExcerpt(result.rtkOutput.output, maxLines, maxBytes);\n tail = bounded.text;\n tailLines = bounded.lines;\n outputLines = result.rtkOutput.lines;\n outputBytes = result.rtkOutput.bytes;\n truncated = outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8');\n } else {\n tail = useCapturedOutput ? result.output : log.tail;\n tailLines = useCapturedOutput ? countLines(tail) : log.tailLines;\n outputLines = Math.max(log.lines, tailLines);\n outputBytes = useCapturedOutput ? Buffer.byteLength(tail, 'utf8') : log.bytes;\n truncated = !useCapturedOutput && (outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8'));\n }\n const plainTail = stripAnsi(tail).replace(/\\r?\\n$/, '');\n const failed = completionFailed(result);\n const status = completionStatus(result);\n const textLines: string[] = [];\n\n if (plainTail.length === 0) {\n textLines.push(status === undefined ? 'Completed with no output.' : 'No output.');\n } else if (truncated) {\n const label = result.rtkOutput ? `RTK ${result.rtkOutput.filter} excerpt` : 'Log excerpt';\n textLines.push(\n `${label} (${tailLines.toLocaleString('en-US')} of ${outputLines.toLocaleString('en-US')} lines):\\n${plainTail}`,\n );\n } else {\n textLines.push(plainTail);\n }\n if (status !== undefined) textLines.push(status);\n if (result.rtkWarning) textLines.push(result.rtkWarning);\n\n if (truncated) {\n const label = result.rtkOutput ? 'Complete raw log' : 'Full log';\n textLines.push(\n `${label}: ${result.logPath} (${formatSize(log.bytes)}, ${log.lines.toLocaleString('en-US')} lines); inspect with doom-runner logs ${result.id}`,\n );\n } else if (failed && plainTail.length === 0) {\n textLines.push(\n `Log: ${result.logPath}`,\n 'Next: run one read-only diagnostic; retry only after correcting the cause.',\n );\n }\n\n const text = boundResultText(textLines.join('\\n'), maxBytes);\n const details = {\n id: result.id,\n runner: result.name,\n exitCode: result.exitCode,\n logPath: result.logPath,\n backend: result.backend,\n fileSize: log.bytes,\n lines: useCapturedOutput ? tailLines : log.lines,\n tail,\n tailLines,\n ...(result.rtkOutput\n ? {\n rtkFilter: result.rtkOutput.filter,\n rtkOutputBytes: result.rtkOutput.bytes,\n rtkOutputLines: result.rtkOutput.lines,\n }\n : {}),\n ...(result.rtkWarning ? { rtkWarning: result.rtkWarning } : {}),\n ...(result.timedOut ? { timedOut: true } : {}),\n };\n\n if (failed) throw new Error(text);\n return textResult(text, details);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bashTool.cjs","names":["getBackgroundThresholdMs","BASH_TOOL_NAME","BASH_TOOL_LABEL","BashParamsSchema","textResult","parseResultPragma","renderBashCall","renderBashResult","getResultMaxLines","getSuccessResultMaxBytes","getResultMaxBytes","getSuccessResultMaxTokens","getResultMaxTokens","summarizeLog","composeExcerpt","boundExcerpt","countLines","stripAnsi","formatSize","boundResultText"],"sources":["../../../../src/commands/bash/bashTool.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME, type BashParams, BashParamsSchema } from '../../schemas/bashTool.ts';\nimport { stripAnsi } from '../../services/AnsiScrub/ansiScrub';\nimport type { BashRunResult, CompletedRun, IBashRunService } from '../../types/bashRunService';\nimport { renderBashCall, renderBashResult } from '../../tui/bashRender.ts';\nimport {\n getBackgroundThresholdMs,\n getResultMaxBytes,\n getResultMaxLines,\n getResultMaxTokens,\n getSuccessResultMaxBytes,\n getSuccessResultMaxTokens,\n} from '../../types/config.ts';\nimport {\n boundExcerpt,\n boundResultText,\n composeExcerpt,\n parseResultPragma,\n type ResultBudget,\n countLines,\n formatSize,\n summarizeLog,\n type ToolResult,\n textResult,\n} from './responseEnvelope.ts';\n\nconst MS_PER_SECOND = 1000;\n\nconst PROMOTION_REASONS: Record<'requested' | 'threshold' | 'interactive', string> = {\n requested: 'Started in the background',\n threshold: 'Still running after the background threshold',\n interactive: 'Started interactively',\n};\n\nexport const BASH_PROMPT_SNIPPET =\n 'Execute shell commands with bounded foreground output and supervised background runners';\n\n/** Written at registration time so the stated threshold matches the configured one. */\nexport function bashPromptGuidelines(thresholdMs = getBackgroundThresholdMs()): string[] {\n return [\n `A command still running after ${Math.round(thresholdMs / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log path.`,\n 'Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.',\n 'Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.',\n 'On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.',\n 'Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.',\n ];\n}\n\nexport interface BashToolDependencies {\n bashRunService: IBashRunService;\n getSessionId(): string | Promise<string>;\n /** Called after a runner is promoted, so UI state can refresh. */\n onRunnerStarted(id: string): void;\n}\n\n/**\n * Registers a tool named `bash`, replacing pi's built-in.\n *\n * The name is deliberate: hooks, guardrails and doom-pi's dispatcher all key on\n * `bash`, and they keep working only while the replacement keeps the name.\n */\nexport function registerBashTool(pi: ExtensionAPI, dependencies: BashToolDependencies): void {\n pi.registerTool({\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.',\n promptSnippet: BASH_PROMPT_SNIPPET,\n promptGuidelines: bashPromptGuidelines(),\n parameters: BashParamsSchema,\n // Bash output already carries its own status glyphs and ANSI colours. Owning\n // the shell keeps Pi from filling every successful command with the global\n // toolSuccessBg, which overwhelms long logs and diffs.\n renderShell: 'self',\n\n async execute(_toolCallId, params, _signal, onUpdate, _ctx): Promise<ToolResult> {\n const { command, timeout, background, interactive, name } = params as BashParams;\n let onOutput: ((output: string) => void) | undefined;\n if (onUpdate) {\n const mode =\n interactive === true ? 'interactive runner' : background === true ? 'background runner' : 'command';\n onUpdate(textResult(`Starting ${mode}...`));\n }\n if (background !== true && interactive !== true && onUpdate) {\n onOutput = (output) => onUpdate(textResult(output));\n }\n let result: BashRunResult;\n try {\n result = await dependencies.bashRunService.run({\n command,\n timeoutMs: timeout === undefined ? undefined : timeout * MS_PER_SECOND,\n background,\n interactive,\n name,\n ...(onOutput ? { onOutput } : {}),\n sessionId: await dependencies.getSessionId(),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n [\n `Could not execute command: ${message}`,\n 'Next: verify the command, runtime, and working directory. Retry only after correcting the cause.',\n ].join('\\n'),\n { cause: error },\n );\n }\n\n if (result.kind === 'promoted') dependencies.onRunnerStarted(result.id);\n return formatRunResult(result, parseResultPragma(params.command));\n },\n\n // Without these, pi falls back to echoing the raw command and the tail end of\n // the result text, which is the metadata footer rather than the log output.\n renderCall(args, theme, _context) {\n return renderBashCall(args as BashParams, theme);\n },\n\n renderResult(result, options, theme, context) {\n return renderBashResult(result, { ...options, isError: context.isError }, theme);\n },\n });\n}\n\nfunction completionFailed(result: CompletedRun): boolean {\n return result.timedOut === true || result.signal !== null || (result.exitCode !== null && result.exitCode !== 0);\n}\n\nfunction completionStatus(result: CompletedRun): string | undefined {\n if (result.timedOut === true) return 'Timed out: exceeded the requested timeout.';\n if (result.signal !== null) return `Signal: ${result.signal}`;\n if (result.exitCode === null) return 'Exit status unavailable.';\n if (result.exitCode !== 0) return `Exit: ${result.exitCode}`;\n return undefined;\n}\n\nexport function formatRunResult(result: BashRunResult, budget: ResultBudget = {}): ToolResult {\n if (result.kind === 'failed') {\n throw new Error(\n [\n `Could not start runner \"${result.name}\": ${result.error}`,\n 'Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.',\n ].join('\\n'),\n );\n }\n\n if (result.kind === 'promoted') {\n const reason = PROMOTION_REASONS[result.reason];\n const body = [\n `${reason}: runner \"${result.name}\" (${result.id}).`,\n `Streaming log: ${result.logPath}`,\n `Inspect: doom-runner logs ${result.id}`,\n ].join('\\n');\n\n return textResult(body, {\n id: result.id,\n runner: result.name,\n pid: result.pid,\n logPath: result.logPath,\n promoted: true,\n reason: result.reason,\n });\n }\n\n // Same shape either way; a success just buys less of it. Exiting 0 has already\n // reported the outcome, so its output is worth a fraction of a failure's.\n const succeeded = !completionFailed(result);\n const maxLines = budget.maxLines ?? getResultMaxLines();\n // An explicit pragma still wins: asking for a wider result is the point of it.\n const maxBytes = budget.maxBytes ?? (succeeded ? getSuccessResultMaxBytes() : getResultMaxBytes());\n const maxTokens = budget.maxTokens ?? (succeeded ? getSuccessResultMaxTokens() : getResultMaxTokens());\n const log = summarizeLog(result.logPath, maxLines, maxBytes, maxTokens);\n const useCapturedOutput = !result.rtkOutput && log.tail.length === 0 && result.output.length > 0;\n let tail: string;\n let tailLines: number;\n let outputLines: number;\n let outputBytes: number;\n let truncated: boolean;\n if (result.rtkOutput) {\n const clipped = Buffer.byteLength(result.rtkOutput.output, 'utf8') < result.rtkOutput.bytes;\n const bounded = clipped\n ? composeExcerpt(\n result.rtkOutput.head,\n result.rtkOutput.output,\n result.rtkOutput.lines,\n maxLines,\n maxBytes,\n [],\n maxTokens,\n )\n : boundExcerpt(result.rtkOutput.output, maxLines, maxBytes, maxTokens);\n tail = bounded.text;\n tailLines = bounded.lines;\n outputLines = result.rtkOutput.lines;\n outputBytes = result.rtkOutput.bytes;\n truncated = outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8');\n } else {\n tail = useCapturedOutput ? result.output : log.tail;\n tailLines = useCapturedOutput ? countLines(tail) : log.tailLines;\n outputLines = Math.max(log.lines, tailLines);\n outputBytes = useCapturedOutput ? Buffer.byteLength(tail, 'utf8') : log.bytes;\n truncated = !useCapturedOutput && (outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8'));\n }\n const plainTail = stripAnsi(tail).replace(/\\r?\\n$/, '');\n const failed = completionFailed(result);\n const status = completionStatus(result);\n const textLines: string[] = [];\n\n if (plainTail.length === 0) {\n textLines.push(status === undefined ? 'Completed with no output.' : 'No output.');\n } else if (truncated) {\n const label = result.rtkOutput ? `RTK ${result.rtkOutput.filter} excerpt` : 'Log excerpt';\n textLines.push(\n `${label} (${tailLines.toLocaleString('en-US')} of ${outputLines.toLocaleString('en-US')} lines):\\n${plainTail}`,\n );\n } else {\n textLines.push(plainTail);\n }\n if (status !== undefined) textLines.push(status);\n if (result.rtkWarning) textLines.push(result.rtkWarning);\n\n if (truncated) {\n const label = result.rtkOutput ? 'Complete raw log' : 'Full log';\n textLines.push(\n `${label}: ${result.logPath} (${formatSize(log.bytes)}, ${log.lines.toLocaleString('en-US')} lines); inspect with doom-runner logs ${result.id}`,\n );\n } else if (failed && plainTail.length === 0) {\n textLines.push(\n `Log: ${result.logPath}`,\n 'Next: run one read-only diagnostic; retry only after correcting the cause.',\n );\n }\n\n const text = boundResultText(textLines.join('\\n'), maxBytes);\n const details = {\n id: result.id,\n runner: result.name,\n exitCode: result.exitCode,\n logPath: result.logPath,\n backend: result.backend,\n fileSize: log.bytes,\n lines: useCapturedOutput ? tailLines : log.lines,\n tail,\n tailLines,\n ...(result.rtkOutput\n ? {\n rtkFilter: result.rtkOutput.filter,\n rtkOutputBytes: result.rtkOutput.bytes,\n rtkOutputLines: result.rtkOutput.lines,\n }\n : {}),\n ...(result.rtkWarning ? { rtkWarning: result.rtkWarning } : {}),\n ...(result.timedOut ? { timedOut: true } : {}),\n };\n\n if (failed) throw new Error(text);\n return textResult(text, details);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.d.cts","names":[],"sources":["../../../../src/commands/bash/bashTool.ts"],"mappings":";;;;cAgCa;;iBAIG,qBAAqB;UAUpB;EACf,gBAAgB;EAChB,yBAAyB;;EAEzB,gBAAgB;;;;;;;;iBASF,iBAAiB,IAAI,cAAc,cAAc;iBA2EjD,gBAAgB,QAAQ,eAAe,SAAQ,eAAoB"}
1
+ {"version":3,"file":"bashTool.d.cts","names":[],"sources":["../../../../src/commands/bash/bashTool.ts"],"mappings":";;;;cAkCa;;iBAIG,qBAAqB;UAUpB;EACf,gBAAgB;EAChB,yBAAyB;;EAEzB,gBAAgB;;;;;;;;iBASF,iBAAiB,IAAI,cAAc,cAAc;iBA2EjD,gBAAgB,QAAQ,eAAe,SAAQ,eAAoB"}
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.d.mts","names":[],"sources":["../../../../src/commands/bash/bashTool.ts"],"mappings":";;;;cAgCa;;iBAIG,qBAAqB;UAUpB;EACf,gBAAgB;EAChB,yBAAyB;;EAEzB,gBAAgB;;;;;;;;iBASF,iBAAiB,IAAI,cAAc,cAAc;iBA2EjD,gBAAgB,QAAQ,eAAe,SAAQ,eAAoB"}
1
+ {"version":3,"file":"bashTool.d.mts","names":[],"sources":["../../../../src/commands/bash/bashTool.ts"],"mappings":";;;;cAkCa;;iBAIG,qBAAqB;UAUpB;EACf,gBAAgB;EAChB,yBAAyB;;EAEzB,gBAAgB;;;;;;;;iBASF,iBAAiB,IAAI,cAAc,cAAc;iBA2EjD,gBAAgB,QAAQ,eAAe,SAAQ,eAAoB"}
@@ -1,6 +1,6 @@
1
- import{getBackgroundThresholdMs as e,getResultMaxBytes as t,getResultMaxLines as n,getSuccessResultMaxBytes as r}from"../../types/config.mjs";import{stripAnsi as i}from"../../services/AnsiScrub/ansiScrub.mjs";import{boundExcerpt as a,boundResultText as o,composeExcerpt as s,countLines as c,formatSize as l,parseResultPragma as u,summarizeLog as d,textResult as f}from"./responseEnvelope.mjs";import{BASH_TOOL_LABEL as p,BASH_TOOL_NAME as m,BashParamsSchema as h}from"../../schemas/bashTool.mjs";import{renderBashCall as g,renderBashResult as _}from"../../tui/bashRender.mjs";const v=1e3,y={requested:`Started in the background`,threshold:`Still running after the background threshold`,interactive:`Started interactively`},b=`Execute shell commands with bounded foreground output and supervised background runners`;function x(t=e()){return[`A command still running after ${Math.round(t/v)} seconds remains active as a background runner with an id and streaming log path.`,`Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.`,`Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.`,`On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.`,`Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.`]}function S(e,t){e.registerTool({name:m,label:p,description:`Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.`,promptSnippet:b,promptGuidelines:x(),parameters:h,renderShell:`self`,async execute(e,n,r,i,a){let{command:o,timeout:s,background:c,interactive:l,name:d}=n,p;i&&i(f(`Starting ${l===!0?`interactive runner`:c===!0?`background runner`:`command`}...`)),c!==!0&&l!==!0&&i&&(p=e=>i(f(e)));let m;try{m=await t.bashRunService.run({command:o,timeoutMs:s===void 0?void 0:s*v,background:c,interactive:l,name:d,...p?{onOutput:p}:{},sessionId:await t.getSessionId()})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error([`Could not execute command: ${t}`,`Next: verify the command, runtime, and working directory. Retry only after correcting the cause.`].join(`
2
- `),{cause:e})}return m.kind===`promoted`&&t.onRunnerStarted(m.id),T(m,u(n.command))},renderCall(e,t,n){return g(e,t)},renderResult(e,t,n,r){return _(e,{...t,isError:r.isError},n)}})}function C(e){return e.timedOut===!0||e.signal!==null||e.exitCode!==null&&e.exitCode!==0}function w(e){if(e.timedOut===!0)return`Timed out: exceeded the requested timeout.`;if(e.signal!==null)return`Signal: ${e.signal}`;if(e.exitCode===null)return`Exit status unavailable.`;if(e.exitCode!==0)return`Exit: ${e.exitCode}`}function T(e,u={}){if(e.kind===`failed`)throw Error([`Could not start runner "${e.name}": ${e.error}`,`Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.`].join(`
3
- `));if(e.kind===`promoted`){let t=[`${y[e.reason]}: runner "${e.name}" (${e.id}).`,`Streaming log: ${e.logPath}`,`Inspect: doom-runner logs ${e.id}`].join(`
4
- `);return f(t,{id:e.id,runner:e.name,pid:e.pid,logPath:e.logPath,promoted:!0,reason:e.reason})}let p=!C(e),m=u.maxLines??n(),h=u.maxBytes??(p?r():t()),g=d(e.logPath,m,h),_=!e.rtkOutput&&g.tail.length===0&&e.output.length>0,v,b,x,S,T;if(e.rtkOutput){let t=Buffer.byteLength(e.rtkOutput.output,`utf8`)<e.rtkOutput.bytes?s(e.rtkOutput.head,e.rtkOutput.output,e.rtkOutput.lines,m,h):a(e.rtkOutput.output,m,h);v=t.text,b=t.lines,x=e.rtkOutput.lines,S=e.rtkOutput.bytes,T=x>b||S>Buffer.byteLength(v,`utf8`)}else v=_?e.output:g.tail,b=_?c(v):g.tailLines,x=Math.max(g.lines,b),S=_?Buffer.byteLength(v,`utf8`):g.bytes,T=!_&&(x>b||S>Buffer.byteLength(v,`utf8`));let E=i(v).replace(/\r?\n$/,``),D=C(e),O=w(e),k=[];if(E.length===0)k.push(O===void 0?`Completed with no output.`:`No output.`);else if(T){let t=e.rtkOutput?`RTK ${e.rtkOutput.filter} excerpt`:`Log excerpt`;k.push(`${t} (${b.toLocaleString(`en-US`)} of ${x.toLocaleString(`en-US`)} lines):\n${E}`)}else k.push(E);if(O!==void 0&&k.push(O),e.rtkWarning&&k.push(e.rtkWarning),T){let t=e.rtkOutput?`Complete raw log`:`Full log`;k.push(`${t}: ${e.logPath} (${l(g.bytes)}, ${g.lines.toLocaleString(`en-US`)} lines); inspect with doom-runner logs ${e.id}`)}else D&&E.length===0&&k.push(`Log: ${e.logPath}`,`Next: run one read-only diagnostic; retry only after correcting the cause.`);let A=o(k.join(`
5
- `),h),j={id:e.id,runner:e.name,exitCode:e.exitCode,logPath:e.logPath,backend:e.backend,fileSize:g.bytes,lines:_?b:g.lines,tail:v,tailLines:b,...e.rtkOutput?{rtkFilter:e.rtkOutput.filter,rtkOutputBytes:e.rtkOutput.bytes,rtkOutputLines:e.rtkOutput.lines}:{},...e.rtkWarning?{rtkWarning:e.rtkWarning}:{},...e.timedOut?{timedOut:!0}:{}};if(D)throw Error(A);return f(A,j)}export{b as BASH_PROMPT_SNIPPET,x as bashPromptGuidelines,T as formatRunResult,S as registerBashTool};
1
+ import{getBackgroundThresholdMs as e,getResultMaxBytes as t,getResultMaxLines as n,getResultMaxTokens as r,getSuccessResultMaxBytes as i,getSuccessResultMaxTokens as a}from"../../types/config.mjs";import{stripAnsi as o}from"../../services/AnsiScrub/ansiScrub.mjs";import{boundExcerpt as s,boundResultText as c,composeExcerpt as l,countLines as u,formatSize as d,parseResultPragma as f,summarizeLog as p,textResult as m}from"./responseEnvelope.mjs";import{BASH_TOOL_LABEL as h,BASH_TOOL_NAME as g,BashParamsSchema as _}from"../../schemas/bashTool.mjs";import{renderBashCall as v,renderBashResult as y}from"../../tui/bashRender.mjs";const b=1e3,x={requested:`Started in the background`,threshold:`Still running after the background threshold`,interactive:`Started interactively`},S=`Execute shell commands with bounded foreground output and supervised background runners`;function C(t=e()){return[`A command still running after ${Math.round(t/b)} seconds remains active as a background runner with an id and streaming log path.`,`Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.`,`Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.`,`On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.`,`Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.`]}function w(e,t){e.registerTool({name:g,label:h,description:`Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.`,promptSnippet:S,promptGuidelines:C(),parameters:_,renderShell:`self`,async execute(e,n,r,i,a){let{command:o,timeout:s,background:c,interactive:l,name:u}=n,d;i&&i(m(`Starting ${l===!0?`interactive runner`:c===!0?`background runner`:`command`}...`)),c!==!0&&l!==!0&&i&&(d=e=>i(m(e)));let p;try{p=await t.bashRunService.run({command:o,timeoutMs:s===void 0?void 0:s*b,background:c,interactive:l,name:u,...d?{onOutput:d}:{},sessionId:await t.getSessionId()})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error([`Could not execute command: ${t}`,`Next: verify the command, runtime, and working directory. Retry only after correcting the cause.`].join(`
2
+ `),{cause:e})}return p.kind===`promoted`&&t.onRunnerStarted(p.id),D(p,f(n.command))},renderCall(e,t,n){return v(e,t)},renderResult(e,t,n,r){return y(e,{...t,isError:r.isError},n)}})}function T(e){return e.timedOut===!0||e.signal!==null||e.exitCode!==null&&e.exitCode!==0}function E(e){if(e.timedOut===!0)return`Timed out: exceeded the requested timeout.`;if(e.signal!==null)return`Signal: ${e.signal}`;if(e.exitCode===null)return`Exit status unavailable.`;if(e.exitCode!==0)return`Exit: ${e.exitCode}`}function D(e,f={}){if(e.kind===`failed`)throw Error([`Could not start runner "${e.name}": ${e.error}`,`Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.`].join(`
3
+ `));if(e.kind===`promoted`){let t=[`${x[e.reason]}: runner "${e.name}" (${e.id}).`,`Streaming log: ${e.logPath}`,`Inspect: doom-runner logs ${e.id}`].join(`
4
+ `);return m(t,{id:e.id,runner:e.name,pid:e.pid,logPath:e.logPath,promoted:!0,reason:e.reason})}let h=!T(e),g=f.maxLines??n(),_=f.maxBytes??(h?i():t()),v=f.maxTokens??(h?a():r()),y=p(e.logPath,g,_,v),b=!e.rtkOutput&&y.tail.length===0&&e.output.length>0,S,C,w,D,O;if(e.rtkOutput){let t=Buffer.byteLength(e.rtkOutput.output,`utf8`)<e.rtkOutput.bytes?l(e.rtkOutput.head,e.rtkOutput.output,e.rtkOutput.lines,g,_,[],v):s(e.rtkOutput.output,g,_,v);S=t.text,C=t.lines,w=e.rtkOutput.lines,D=e.rtkOutput.bytes,O=w>C||D>Buffer.byteLength(S,`utf8`)}else S=b?e.output:y.tail,C=b?u(S):y.tailLines,w=Math.max(y.lines,C),D=b?Buffer.byteLength(S,`utf8`):y.bytes,O=!b&&(w>C||D>Buffer.byteLength(S,`utf8`));let k=o(S).replace(/\r?\n$/,``),A=T(e),j=E(e),M=[];if(k.length===0)M.push(j===void 0?`Completed with no output.`:`No output.`);else if(O){let t=e.rtkOutput?`RTK ${e.rtkOutput.filter} excerpt`:`Log excerpt`;M.push(`${t} (${C.toLocaleString(`en-US`)} of ${w.toLocaleString(`en-US`)} lines):\n${k}`)}else M.push(k);if(j!==void 0&&M.push(j),e.rtkWarning&&M.push(e.rtkWarning),O){let t=e.rtkOutput?`Complete raw log`:`Full log`;M.push(`${t}: ${e.logPath} (${d(y.bytes)}, ${y.lines.toLocaleString(`en-US`)} lines); inspect with doom-runner logs ${e.id}`)}else A&&k.length===0&&M.push(`Log: ${e.logPath}`,`Next: run one read-only diagnostic; retry only after correcting the cause.`);let N=c(M.join(`
5
+ `),_),P={id:e.id,runner:e.name,exitCode:e.exitCode,logPath:e.logPath,backend:e.backend,fileSize:y.bytes,lines:b?C:y.lines,tail:S,tailLines:C,...e.rtkOutput?{rtkFilter:e.rtkOutput.filter,rtkOutputBytes:e.rtkOutput.bytes,rtkOutputLines:e.rtkOutput.lines}:{},...e.rtkWarning?{rtkWarning:e.rtkWarning}:{},...e.timedOut?{timedOut:!0}:{}};if(A)throw Error(N);return m(N,P)}export{S as BASH_PROMPT_SNIPPET,C as bashPromptGuidelines,D as formatRunResult,w as registerBashTool};
6
6
  //# sourceMappingURL=bashTool.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.mjs","names":[],"sources":["../../../../src/commands/bash/bashTool.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME, type BashParams, BashParamsSchema } from '../../schemas/bashTool.ts';\nimport { stripAnsi } from '../../services/AnsiScrub/ansiScrub';\nimport type { BashRunResult, CompletedRun, IBashRunService } from '../../types/bashRunService';\nimport { renderBashCall, renderBashResult } from '../../tui/bashRender.ts';\nimport {\n getBackgroundThresholdMs,\n getResultMaxBytes,\n getResultMaxLines,\n getSuccessResultMaxBytes,\n} from '../../types/config.ts';\nimport {\n boundExcerpt,\n boundResultText,\n composeExcerpt,\n parseResultPragma,\n type ResultBudget,\n countLines,\n formatSize,\n summarizeLog,\n type ToolResult,\n textResult,\n} from './responseEnvelope.ts';\n\nconst MS_PER_SECOND = 1000;\n\nconst PROMOTION_REASONS: Record<'requested' | 'threshold' | 'interactive', string> = {\n requested: 'Started in the background',\n threshold: 'Still running after the background threshold',\n interactive: 'Started interactively',\n};\n\nexport const BASH_PROMPT_SNIPPET =\n 'Execute shell commands with bounded foreground output and supervised background runners';\n\n/** Written at registration time so the stated threshold matches the configured one. */\nexport function bashPromptGuidelines(thresholdMs = getBackgroundThresholdMs()): string[] {\n return [\n `A command still running after ${Math.round(thresholdMs / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log path.`,\n 'Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.',\n 'Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.',\n 'On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.',\n 'Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.',\n ];\n}\n\nexport interface BashToolDependencies {\n bashRunService: IBashRunService;\n getSessionId(): string | Promise<string>;\n /** Called after a runner is promoted, so UI state can refresh. */\n onRunnerStarted(id: string): void;\n}\n\n/**\n * Registers a tool named `bash`, replacing pi's built-in.\n *\n * The name is deliberate: hooks, guardrails and doom-pi's dispatcher all key on\n * `bash`, and they keep working only while the replacement keeps the name.\n */\nexport function registerBashTool(pi: ExtensionAPI, dependencies: BashToolDependencies): void {\n pi.registerTool({\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.',\n promptSnippet: BASH_PROMPT_SNIPPET,\n promptGuidelines: bashPromptGuidelines(),\n parameters: BashParamsSchema,\n // Bash output already carries its own status glyphs and ANSI colours. Owning\n // the shell keeps Pi from filling every successful command with the global\n // toolSuccessBg, which overwhelms long logs and diffs.\n renderShell: 'self',\n\n async execute(_toolCallId, params, _signal, onUpdate, _ctx): Promise<ToolResult> {\n const { command, timeout, background, interactive, name } = params as BashParams;\n let onOutput: ((output: string) => void) | undefined;\n if (onUpdate) {\n const mode =\n interactive === true ? 'interactive runner' : background === true ? 'background runner' : 'command';\n onUpdate(textResult(`Starting ${mode}...`));\n }\n if (background !== true && interactive !== true && onUpdate) {\n onOutput = (output) => onUpdate(textResult(output));\n }\n let result: BashRunResult;\n try {\n result = await dependencies.bashRunService.run({\n command,\n timeoutMs: timeout === undefined ? undefined : timeout * MS_PER_SECOND,\n background,\n interactive,\n name,\n ...(onOutput ? { onOutput } : {}),\n sessionId: await dependencies.getSessionId(),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n [\n `Could not execute command: ${message}`,\n 'Next: verify the command, runtime, and working directory. Retry only after correcting the cause.',\n ].join('\\n'),\n { cause: error },\n );\n }\n\n if (result.kind === 'promoted') dependencies.onRunnerStarted(result.id);\n return formatRunResult(result, parseResultPragma(params.command));\n },\n\n // Without these, pi falls back to echoing the raw command and the tail end of\n // the result text, which is the metadata footer rather than the log output.\n renderCall(args, theme, _context) {\n return renderBashCall(args as BashParams, theme);\n },\n\n renderResult(result, options, theme, context) {\n return renderBashResult(result, { ...options, isError: context.isError }, theme);\n },\n });\n}\n\nfunction completionFailed(result: CompletedRun): boolean {\n return result.timedOut === true || result.signal !== null || (result.exitCode !== null && result.exitCode !== 0);\n}\n\nfunction completionStatus(result: CompletedRun): string | undefined {\n if (result.timedOut === true) return 'Timed out: exceeded the requested timeout.';\n if (result.signal !== null) return `Signal: ${result.signal}`;\n if (result.exitCode === null) return 'Exit status unavailable.';\n if (result.exitCode !== 0) return `Exit: ${result.exitCode}`;\n return undefined;\n}\n\nexport function formatRunResult(result: BashRunResult, budget: ResultBudget = {}): ToolResult {\n if (result.kind === 'failed') {\n throw new Error(\n [\n `Could not start runner \"${result.name}\": ${result.error}`,\n 'Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.',\n ].join('\\n'),\n );\n }\n\n if (result.kind === 'promoted') {\n const reason = PROMOTION_REASONS[result.reason];\n const body = [\n `${reason}: runner \"${result.name}\" (${result.id}).`,\n `Streaming log: ${result.logPath}`,\n `Inspect: doom-runner logs ${result.id}`,\n ].join('\\n');\n\n return textResult(body, {\n id: result.id,\n runner: result.name,\n pid: result.pid,\n logPath: result.logPath,\n promoted: true,\n reason: result.reason,\n });\n }\n\n // Same shape either way; a success just buys less of it. Exiting 0 has already\n // reported the outcome, so its output is worth a fraction of a failure's.\n const succeeded = !completionFailed(result);\n const maxLines = budget.maxLines ?? getResultMaxLines();\n // An explicit pragma still wins: asking for a wider result is the point of it.\n const maxBytes = budget.maxBytes ?? (succeeded ? getSuccessResultMaxBytes() : getResultMaxBytes());\n const log = summarizeLog(result.logPath, maxLines, maxBytes);\n const useCapturedOutput = !result.rtkOutput && log.tail.length === 0 && result.output.length > 0;\n let tail: string;\n let tailLines: number;\n let outputLines: number;\n let outputBytes: number;\n let truncated: boolean;\n if (result.rtkOutput) {\n const clipped = Buffer.byteLength(result.rtkOutput.output, 'utf8') < result.rtkOutput.bytes;\n const bounded = clipped\n ? composeExcerpt(result.rtkOutput.head, result.rtkOutput.output, result.rtkOutput.lines, maxLines, maxBytes)\n : boundExcerpt(result.rtkOutput.output, maxLines, maxBytes);\n tail = bounded.text;\n tailLines = bounded.lines;\n outputLines = result.rtkOutput.lines;\n outputBytes = result.rtkOutput.bytes;\n truncated = outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8');\n } else {\n tail = useCapturedOutput ? result.output : log.tail;\n tailLines = useCapturedOutput ? countLines(tail) : log.tailLines;\n outputLines = Math.max(log.lines, tailLines);\n outputBytes = useCapturedOutput ? Buffer.byteLength(tail, 'utf8') : log.bytes;\n truncated = !useCapturedOutput && (outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8'));\n }\n const plainTail = stripAnsi(tail).replace(/\\r?\\n$/, '');\n const failed = completionFailed(result);\n const status = completionStatus(result);\n const textLines: string[] = [];\n\n if (plainTail.length === 0) {\n textLines.push(status === undefined ? 'Completed with no output.' : 'No output.');\n } else if (truncated) {\n const label = result.rtkOutput ? `RTK ${result.rtkOutput.filter} excerpt` : 'Log excerpt';\n textLines.push(\n `${label} (${tailLines.toLocaleString('en-US')} of ${outputLines.toLocaleString('en-US')} lines):\\n${plainTail}`,\n );\n } else {\n textLines.push(plainTail);\n }\n if (status !== undefined) textLines.push(status);\n if (result.rtkWarning) textLines.push(result.rtkWarning);\n\n if (truncated) {\n const label = result.rtkOutput ? 'Complete raw log' : 'Full log';\n textLines.push(\n `${label}: ${result.logPath} (${formatSize(log.bytes)}, ${log.lines.toLocaleString('en-US')} lines); inspect with doom-runner logs ${result.id}`,\n );\n } else if (failed && plainTail.length === 0) {\n textLines.push(\n `Log: ${result.logPath}`,\n 'Next: run one read-only diagnostic; retry only after correcting the cause.',\n );\n }\n\n const text = boundResultText(textLines.join('\\n'), maxBytes);\n const details = {\n id: result.id,\n runner: result.name,\n exitCode: result.exitCode,\n logPath: result.logPath,\n backend: result.backend,\n fileSize: log.bytes,\n lines: useCapturedOutput ? tailLines : log.lines,\n tail,\n tailLines,\n ...(result.rtkOutput\n ? {\n rtkFilter: result.rtkOutput.filter,\n rtkOutputBytes: result.rtkOutput.bytes,\n rtkOutputLines: result.rtkOutput.lines,\n }\n : {}),\n ...(result.rtkWarning ? { rtkWarning: result.rtkWarning } : {}),\n ...(result.timedOut ? { timedOut: true } : {}),\n };\n\n if (failed) throw new Error(text);\n return textResult(text, details);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bashTool.mjs","names":[],"sources":["../../../../src/commands/bash/bashTool.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME, type BashParams, BashParamsSchema } from '../../schemas/bashTool.ts';\nimport { stripAnsi } from '../../services/AnsiScrub/ansiScrub';\nimport type { BashRunResult, CompletedRun, IBashRunService } from '../../types/bashRunService';\nimport { renderBashCall, renderBashResult } from '../../tui/bashRender.ts';\nimport {\n getBackgroundThresholdMs,\n getResultMaxBytes,\n getResultMaxLines,\n getResultMaxTokens,\n getSuccessResultMaxBytes,\n getSuccessResultMaxTokens,\n} from '../../types/config.ts';\nimport {\n boundExcerpt,\n boundResultText,\n composeExcerpt,\n parseResultPragma,\n type ResultBudget,\n countLines,\n formatSize,\n summarizeLog,\n type ToolResult,\n textResult,\n} from './responseEnvelope.ts';\n\nconst MS_PER_SECOND = 1000;\n\nconst PROMOTION_REASONS: Record<'requested' | 'threshold' | 'interactive', string> = {\n requested: 'Started in the background',\n threshold: 'Still running after the background threshold',\n interactive: 'Started interactively',\n};\n\nexport const BASH_PROMPT_SNIPPET =\n 'Execute shell commands with bounded foreground output and supervised background runners';\n\n/** Written at registration time so the stated threshold matches the configured one. */\nexport function bashPromptGuidelines(thresholdMs = getBackgroundThresholdMs()): string[] {\n return [\n `A command still running after ${Math.round(thresholdMs / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log path.`,\n 'Pass background: true only for commands you know will remain active, such as dev servers, watchers, and tails.',\n 'Pass interactive: true only when the command will prompt for input. Use Runner Space for terminal input; avoid interactive mode otherwise because its logs are noisier.',\n 'On failure, use the returned output first. Inspect the saved log only when the result says output was truncated or no useful output was returned. Never retry an unchanged command merely to recover output.',\n 'Stop background runners when they are no longer needed. Every runner is stopped automatically when the session ends.',\n ];\n}\n\nexport interface BashToolDependencies {\n bashRunService: IBashRunService;\n getSessionId(): string | Promise<string>;\n /** Called after a runner is promoted, so UI state can refresh. */\n onRunnerStarted(id: string): void;\n}\n\n/**\n * Registers a tool named `bash`, replacing pi's built-in.\n *\n * The name is deliberate: hooks, guardrails and doom-pi's dispatcher all key on\n * `bash`, and they keep working only while the replacement keeps the name.\n */\nexport function registerBashTool(pi: ExtensionAPI, dependencies: BashToolDependencies): void {\n pi.registerTool({\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute one Bash command in the current working directory. Foreground commands return bounded output; commands that outlive the threshold return a supervised runner id and streaming log path. Do not rerun a command merely to recover output; inspect the saved log only when the result identifies missing context.',\n promptSnippet: BASH_PROMPT_SNIPPET,\n promptGuidelines: bashPromptGuidelines(),\n parameters: BashParamsSchema,\n // Bash output already carries its own status glyphs and ANSI colours. Owning\n // the shell keeps Pi from filling every successful command with the global\n // toolSuccessBg, which overwhelms long logs and diffs.\n renderShell: 'self',\n\n async execute(_toolCallId, params, _signal, onUpdate, _ctx): Promise<ToolResult> {\n const { command, timeout, background, interactive, name } = params as BashParams;\n let onOutput: ((output: string) => void) | undefined;\n if (onUpdate) {\n const mode =\n interactive === true ? 'interactive runner' : background === true ? 'background runner' : 'command';\n onUpdate(textResult(`Starting ${mode}...`));\n }\n if (background !== true && interactive !== true && onUpdate) {\n onOutput = (output) => onUpdate(textResult(output));\n }\n let result: BashRunResult;\n try {\n result = await dependencies.bashRunService.run({\n command,\n timeoutMs: timeout === undefined ? undefined : timeout * MS_PER_SECOND,\n background,\n interactive,\n name,\n ...(onOutput ? { onOutput } : {}),\n sessionId: await dependencies.getSessionId(),\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n [\n `Could not execute command: ${message}`,\n 'Next: verify the command, runtime, and working directory. Retry only after correcting the cause.',\n ].join('\\n'),\n { cause: error },\n );\n }\n\n if (result.kind === 'promoted') dependencies.onRunnerStarted(result.id);\n return formatRunResult(result, parseResultPragma(params.command));\n },\n\n // Without these, pi falls back to echoing the raw command and the tail end of\n // the result text, which is the metadata footer rather than the log output.\n renderCall(args, theme, _context) {\n return renderBashCall(args as BashParams, theme);\n },\n\n renderResult(result, options, theme, context) {\n return renderBashResult(result, { ...options, isError: context.isError }, theme);\n },\n });\n}\n\nfunction completionFailed(result: CompletedRun): boolean {\n return result.timedOut === true || result.signal !== null || (result.exitCode !== null && result.exitCode !== 0);\n}\n\nfunction completionStatus(result: CompletedRun): string | undefined {\n if (result.timedOut === true) return 'Timed out: exceeded the requested timeout.';\n if (result.signal !== null) return `Signal: ${result.signal}`;\n if (result.exitCode === null) return 'Exit status unavailable.';\n if (result.exitCode !== 0) return `Exit: ${result.exitCode}`;\n return undefined;\n}\n\nexport function formatRunResult(result: BashRunResult, budget: ResultBudget = {}): ToolResult {\n if (result.kind === 'failed') {\n throw new Error(\n [\n `Could not start runner \"${result.name}\": ${result.error}`,\n 'Next: correct the reported launch or supervision problem. Retry only after changing the command or environment.',\n ].join('\\n'),\n );\n }\n\n if (result.kind === 'promoted') {\n const reason = PROMOTION_REASONS[result.reason];\n const body = [\n `${reason}: runner \"${result.name}\" (${result.id}).`,\n `Streaming log: ${result.logPath}`,\n `Inspect: doom-runner logs ${result.id}`,\n ].join('\\n');\n\n return textResult(body, {\n id: result.id,\n runner: result.name,\n pid: result.pid,\n logPath: result.logPath,\n promoted: true,\n reason: result.reason,\n });\n }\n\n // Same shape either way; a success just buys less of it. Exiting 0 has already\n // reported the outcome, so its output is worth a fraction of a failure's.\n const succeeded = !completionFailed(result);\n const maxLines = budget.maxLines ?? getResultMaxLines();\n // An explicit pragma still wins: asking for a wider result is the point of it.\n const maxBytes = budget.maxBytes ?? (succeeded ? getSuccessResultMaxBytes() : getResultMaxBytes());\n const maxTokens = budget.maxTokens ?? (succeeded ? getSuccessResultMaxTokens() : getResultMaxTokens());\n const log = summarizeLog(result.logPath, maxLines, maxBytes, maxTokens);\n const useCapturedOutput = !result.rtkOutput && log.tail.length === 0 && result.output.length > 0;\n let tail: string;\n let tailLines: number;\n let outputLines: number;\n let outputBytes: number;\n let truncated: boolean;\n if (result.rtkOutput) {\n const clipped = Buffer.byteLength(result.rtkOutput.output, 'utf8') < result.rtkOutput.bytes;\n const bounded = clipped\n ? composeExcerpt(\n result.rtkOutput.head,\n result.rtkOutput.output,\n result.rtkOutput.lines,\n maxLines,\n maxBytes,\n [],\n maxTokens,\n )\n : boundExcerpt(result.rtkOutput.output, maxLines, maxBytes, maxTokens);\n tail = bounded.text;\n tailLines = bounded.lines;\n outputLines = result.rtkOutput.lines;\n outputBytes = result.rtkOutput.bytes;\n truncated = outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8');\n } else {\n tail = useCapturedOutput ? result.output : log.tail;\n tailLines = useCapturedOutput ? countLines(tail) : log.tailLines;\n outputLines = Math.max(log.lines, tailLines);\n outputBytes = useCapturedOutput ? Buffer.byteLength(tail, 'utf8') : log.bytes;\n truncated = !useCapturedOutput && (outputLines > tailLines || outputBytes > Buffer.byteLength(tail, 'utf8'));\n }\n const plainTail = stripAnsi(tail).replace(/\\r?\\n$/, '');\n const failed = completionFailed(result);\n const status = completionStatus(result);\n const textLines: string[] = [];\n\n if (plainTail.length === 0) {\n textLines.push(status === undefined ? 'Completed with no output.' : 'No output.');\n } else if (truncated) {\n const label = result.rtkOutput ? `RTK ${result.rtkOutput.filter} excerpt` : 'Log excerpt';\n textLines.push(\n `${label} (${tailLines.toLocaleString('en-US')} of ${outputLines.toLocaleString('en-US')} lines):\\n${plainTail}`,\n );\n } else {\n textLines.push(plainTail);\n }\n if (status !== undefined) textLines.push(status);\n if (result.rtkWarning) textLines.push(result.rtkWarning);\n\n if (truncated) {\n const label = result.rtkOutput ? 'Complete raw log' : 'Full log';\n textLines.push(\n `${label}: ${result.logPath} (${formatSize(log.bytes)}, ${log.lines.toLocaleString('en-US')} lines); inspect with doom-runner logs ${result.id}`,\n );\n } else if (failed && plainTail.length === 0) {\n textLines.push(\n `Log: ${result.logPath}`,\n 'Next: run one read-only diagnostic; retry only after correcting the cause.',\n );\n }\n\n const text = boundResultText(textLines.join('\\n'), maxBytes);\n const details = {\n id: result.id,\n runner: result.name,\n exitCode: result.exitCode,\n logPath: result.logPath,\n backend: result.backend,\n fileSize: log.bytes,\n lines: useCapturedOutput ? tailLines : log.lines,\n tail,\n tailLines,\n ...(result.rtkOutput\n ? {\n rtkFilter: result.rtkOutput.filter,\n rtkOutputBytes: result.rtkOutput.bytes,\n rtkOutputLines: result.rtkOutput.lines,\n }\n : {}),\n ...(result.rtkWarning ? { rtkWarning: result.rtkWarning } : {}),\n ...(result.timedOut ? { timedOut: true } : {}),\n };\n\n if (failed) throw new Error(text);\n return textResult(text, details);\n}\n"],"mappings":"unBA0BA,MAAM,EAAgB,IAEhB,EAA+E,CACnF,UAAW,4BACX,UAAW,+CACX,YAAa,uBACf,EAEa,EACX,0FAGF,SAAgB,EAAqB,EAAc,EAAyB,EAAa,CACvF,MAAO,CACL,iCAAiC,KAAK,MAAM,EAAc,CAAa,EAAE,mFACzE,iHACA,0KACA,+MACA,sHACF,CACF,CAeA,SAAgB,EAAiB,EAAkB,EAA0C,CAC3F,EAAG,aAAa,CACd,KAAM,EACN,MAAO,EACP,YACE,0TACF,cAAe,EACf,iBAAkB,EAAqB,EACvC,WAAY,EAIZ,YAAa,OAEb,MAAM,QAAQ,EAAa,EAAQ,EAAS,EAAU,EAA2B,CAC/E,GAAM,CAAE,UAAS,UAAS,aAAY,cAAa,QAAS,EACxD,EACA,GAGF,EAAS,EAAW,YADlB,IAAgB,GAAO,qBAAuB,IAAe,GAAO,oBAAsB,UACvD,IAAI,CAAC,EAExC,IAAe,IAAQ,IAAgB,IAAQ,IACjD,EAAY,GAAW,EAAS,EAAW,CAAM,CAAC,GAEpD,IAAI,EACJ,GAAI,CACF,EAAS,MAAM,EAAa,eAAe,IAAI,CAC7C,UACA,UAAW,IAAY,IAAA,GAAY,IAAA,GAAY,EAAU,EACzD,aACA,cACA,OACA,GAAI,EAAW,CAAE,UAAS,EAAI,CAAC,EAC/B,UAAW,MAAM,EAAa,aAAa,CAC7C,CAAC,CACH,OAAS,EAAO,CACd,IAAM,EAAU,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACrE,MAAU,MACR,CACE,8BAA8B,IAC9B,kGACF,CAAC,CAAC,KAAK;CAAI,EACX,CAAE,MAAO,CAAM,CACjB,CACF,CAGA,OADI,EAAO,OAAS,YAAY,EAAa,gBAAgB,EAAO,EAAE,EAC/D,EAAgB,EAAQ,EAAkB,EAAO,OAAO,CAAC,CAClE,EAIA,WAAW,EAAM,EAAO,EAAU,CAChC,OAAO,EAAe,EAAoB,CAAK,CACjD,EAEA,aAAa,EAAQ,EAAS,EAAO,EAAS,CAC5C,OAAO,EAAiB,EAAQ,CAAE,GAAG,EAAS,QAAS,EAAQ,OAAQ,EAAG,CAAK,CACjF,CACF,CAAC,CACH,CAEA,SAAS,EAAiB,EAA+B,CACvD,OAAO,EAAO,WAAa,IAAQ,EAAO,SAAW,MAAS,EAAO,WAAa,MAAQ,EAAO,WAAa,CAChH,CAEA,SAAS,EAAiB,EAA0C,CAClE,GAAI,EAAO,WAAa,GAAM,MAAO,6CACrC,GAAI,EAAO,SAAW,KAAM,MAAO,WAAW,EAAO,SACrD,GAAI,EAAO,WAAa,KAAM,MAAO,2BACrC,GAAI,EAAO,WAAa,EAAG,MAAO,SAAS,EAAO,UAEpD,CAEA,SAAgB,EAAgB,EAAuB,EAAuB,CAAC,EAAe,CAC5F,GAAI,EAAO,OAAS,SAClB,MAAU,MACR,CACE,2BAA2B,EAAO,KAAK,KAAK,EAAO,QACnD,iHACF,CAAC,CAAC,KAAK;CAAI,CACb,EAGF,GAAI,EAAO,OAAS,WAAY,CAE9B,IAAM,EAAO,CACX,GAFa,EAAkB,EAAO,QAE5B,YAAY,EAAO,KAAK,KAAK,EAAO,GAAG,IACjD,kBAAkB,EAAO,UACzB,6BAA6B,EAAO,IACtC,CAAC,CAAC,KAAK;CAAI,EAEX,OAAO,EAAW,EAAM,CACtB,GAAI,EAAO,GACX,OAAQ,EAAO,KACf,IAAK,EAAO,IACZ,QAAS,EAAO,QAChB,SAAU,GACV,OAAQ,EAAO,MACjB,CAAC,CACH,CAIA,IAAM,EAAY,CAAC,EAAiB,CAAM,EACpC,EAAW,EAAO,UAAY,EAAkB,EAEhD,EAAW,EAAO,WAAa,EAAY,EAAyB,EAAI,EAAkB,GAC1F,EAAY,EAAO,YAAc,EAAY,EAA0B,EAAI,EAAmB,GAC9F,EAAM,EAAa,EAAO,QAAS,EAAU,EAAU,CAAS,EAChE,EAAoB,CAAC,EAAO,WAAa,EAAI,KAAK,SAAW,GAAK,EAAO,OAAO,OAAS,EAC3F,EACA,EACA,EACA,EACA,EACJ,GAAI,EAAO,UAAW,CAEpB,IAAM,EADU,OAAO,WAAW,EAAO,UAAU,OAAQ,MAAM,EAAI,EAAO,UAAU,MAElF,EACE,EAAO,UAAU,KACjB,EAAO,UAAU,OACjB,EAAO,UAAU,MACjB,EACA,EACA,CAAC,EACD,CACF,EACA,EAAa,EAAO,UAAU,OAAQ,EAAU,EAAU,CAAS,EACvE,EAAO,EAAQ,KACf,EAAY,EAAQ,MACpB,EAAc,EAAO,UAAU,MAC/B,EAAc,EAAO,UAAU,MAC/B,EAAY,EAAc,GAAa,EAAc,OAAO,WAAW,EAAM,MAAM,CACrF,KACE,GAAO,EAAoB,EAAO,OAAS,EAAI,KAC/C,EAAY,EAAoB,EAAW,CAAI,EAAI,EAAI,UACvD,EAAc,KAAK,IAAI,EAAI,MAAO,CAAS,EAC3C,EAAc,EAAoB,OAAO,WAAW,EAAM,MAAM,EAAI,EAAI,MACxE,EAAY,CAAC,IAAsB,EAAc,GAAa,EAAc,OAAO,WAAW,EAAM,MAAM,GAE5G,IAAM,EAAY,EAAU,CAAI,CAAC,CAAC,QAAQ,SAAU,EAAE,EAChD,EAAS,EAAiB,CAAM,EAChC,EAAS,EAAiB,CAAM,EAChC,EAAsB,CAAC,EAE7B,GAAI,EAAU,SAAW,EACvB,EAAU,KAAK,IAAW,IAAA,GAAY,4BAA8B,YAAY,OAC3E,GAAI,EAAW,CACpB,IAAM,EAAQ,EAAO,UAAY,OAAO,EAAO,UAAU,OAAO,UAAY,cAC5E,EAAU,KACR,GAAG,EAAM,IAAI,EAAU,eAAe,OAAO,EAAE,MAAM,EAAY,eAAe,OAAO,EAAE,YAAY,GACvG,CACF,MACE,EAAU,KAAK,CAAS,EAK1B,GAHI,IAAW,IAAA,IAAW,EAAU,KAAK,CAAM,EAC3C,EAAO,YAAY,EAAU,KAAK,EAAO,UAAU,EAEnD,EAAW,CACb,IAAM,EAAQ,EAAO,UAAY,mBAAqB,WACtD,EAAU,KACR,GAAG,EAAM,IAAI,EAAO,QAAQ,IAAI,EAAW,EAAI,KAAK,EAAE,IAAI,EAAI,MAAM,eAAe,OAAO,EAAE,yCAAyC,EAAO,IAC9I,CACF,MAAW,GAAU,EAAU,SAAW,GACxC,EAAU,KACR,QAAQ,EAAO,UACf,4EACF,EAGF,IAAM,EAAO,EAAgB,EAAU,KAAK;CAAI,EAAG,CAAQ,EACrD,EAAU,CACd,GAAI,EAAO,GACX,OAAQ,EAAO,KACf,SAAU,EAAO,SACjB,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,SAAU,EAAI,MACd,MAAO,EAAoB,EAAY,EAAI,MAC3C,OACA,YACA,GAAI,EAAO,UACP,CACE,UAAW,EAAO,UAAU,OAC5B,eAAgB,EAAO,UAAU,MACjC,eAAgB,EAAO,UAAU,KACnC,EACA,CAAC,EACL,GAAI,EAAO,WAAa,CAAE,WAAY,EAAO,UAAW,EAAI,CAAC,EAC7D,GAAI,EAAO,SAAW,CAAE,SAAU,EAAK,EAAI,CAAC,CAC9C,EAEA,GAAI,EAAQ,MAAU,MAAM,CAAI,EAChC,OAAO,EAAW,EAAM,CAAO,CACjC"}
@@ -1,14 +1,14 @@
1
- const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs");let n=require("node:fs");n=e.__toESM(n,1);let r=require("node:string_decoder");const i=1024,a=i*i,o=65536,s=/(?<![\w\-/=.])(?:errors?|fatal|panic|exception|traceback|assertion|assert|fail(?:ed|ure|ures)?)\b/iu,c=/\b(?:no|not|without|zero|0)\s+$/iu;function l(e,t={}){return{content:[{type:`text`,text:e}],details:t}}function u(e){return l(`Error: ${e}`,{error:e})}function d(e){return e<i?`${e} B`:e<a?`${(e/i).toFixed(1)} KB`:`${(e/a).toFixed(1)} MB`}function f(e){return e.toLocaleString(`en-US`)}function p(e){return e.length===0?0:e.endsWith(`
1
+ const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../services/TokenEstimate/tokenEstimate.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:string_decoder");const a=1024,o=a*a,s=65536,c=/(?<![\w\-/=.])(?:errors?|fatal|panic|exception|traceback|assertion|assert|fail(?:ed|ure|ures)?)\b/iu,l=/\b(?:no|not|without|zero|0)\s+$/iu;function u(e,t={}){return{content:[{type:`text`,text:e}],details:t}}function d(e){return u(`Error: ${e}`,{error:e})}function f(e){return e<a?`${e} B`:e<o?`${(e/a).toFixed(1)} KB`:`${(e/o).toFixed(1)} MB`}function p(e){return e.toLocaleString(`en-US`)}function m(e){return e.length===0?0:e.endsWith(`
2
2
  `)?e.split(`
3
3
  `).length-1:e.split(`
4
- `).length}var m=class{maxEntries;byKey=new Map;constructor(e=t.getErrorMaxEntries()){this.maxEntries=e}push(e){if(!y(e))return;let t=e.trim(),n=S(e),r=this.byKey.get(n);if(r){r.count+=1,!r.variants.includes(t)&&r.variants.length<12&&r.variants.push(t);return}this.byKey.size>=this.maxEntries||this.byKey.set(n,{variants:[t],count:1})}entries(){return[...this.byKey.values()]}};function h(e,i=t.getResultMaxLines(),a=t.getResultMaxBytes()){let s;try{let t=n.default.statSync(e).size;s=n.default.openSync(e,`r`);let c=Buffer.alloc(o),l=Math.max(1,a+1),u=Buffer.alloc(0),d=Buffer.alloc(0),f=0,p,h=new r.StringDecoder(`utf8`),g=new m,_=``;for(;;){let e=n.default.readSync(s,c,0,c.byteLength,null);if(e===0)break;let t=c.subarray(0,e);for(let e of t)e===10&&(f+=1);p=t.at(-1),u.byteLength<l&&(u=Buffer.concat([u,t.subarray(0,l-u.byteLength)])),d=W(d,t,l),_+=h.write(t);let r=_.indexOf(`
5
- `);for(;r>=0;)g.push(_.slice(0,r)),_=_.slice(r+1),r=_.indexOf(`
6
- `)}_+=h.end(),_.length>0&&g.push(_);let v=f+ +(t>0&&p!==10),y=t<=d.byteLength?I(U(d,d.byteLength),i,a):L(u.toString(`utf8`),U(d,d.byteLength),v,i,a,g.entries());return{tail:y.text,bytes:t,lines:v,tailLines:y.lines}}catch{return{tail:``,bytes:0,lines:0,tailLines:0}}finally{s!==void 0&&n.default.closeSync(s)}}const g=/^\s*(?:#|\/\/)\s*@doom:\s*(\{.*\})\s*$/u;function _(e){return typeof e==`number`&&Number.isInteger(e)&&e>0?e:void 0}function v(e){let t=g.exec(e.split(`
7
- `,1)[0]??``);if(!t?.[1])return{};let n;try{n=JSON.parse(t[1])}catch{return{}}if(typeof n!=`object`||!n||Array.isArray(n))return{};let r=n,i=_(r.maxResultBytes),a=_(r.maxResultLines);return{...i===void 0?{}:{maxBytes:Math.min(i,262144)},...a===void 0?{}:{maxLines:Math.min(a,5e3)}}}function y(e){let n=e.slice(0,60),r=s.exec(n);return r&&!c.test(n.slice(0,r.index))?!0:t.getErrorPatterns().some(t=>x(t,e))}const b=new Map;function x(e,t){if(!b.has(e))try{b.set(e,new RegExp(e,`iu`))}catch{b.set(e,void 0)}return b.get(e)?.test(t)??!1}function S(e){return e.replace(/0x[0-9a-f]+/giu,`0x#`).replace(/\/[^\s:,)]+/gu,`/#`).replace(/\d+/gu,`#`).trim().toLowerCase()}function C(e,n=t.getErrorMaxEntries()){let r=new m(n);for(let t of e)r.push(t);return r.entries()}function w(e){let t=e.reduce((e,t)=>Math.min(e,t.length),1/0),n=0;for(;n<t&&e.every(t=>t[n]===e[0][n]);)n+=1;let r=0;for(;r<t-n&&e.every(t=>t[t.length-1-r]===e[0][e[0].length-1-r]);)r+=1;return{prefix:n,suffix:r}}function T(e,t){let n=e.lastIndexOf(` `,Math.max(0,t-1));return n>=0?n+1:0}function E(e,t){let n=e.length-t,r=e.indexOf(` `,n);return r>=0?e.length-r:0}function D(e){let[n]=e.variants;if(n===void 0)return``;if(e.variants.length===1)return e.count>1?`${n} (\u00d7${f(e.count)})`:n;let r=e.variants.slice(0,t.getErrorMaxVariantsJoined()),{prefix:i,suffix:a}=w(r),o=n.slice(0,T(n,i)),s=E(n,a),c=s>0?n.slice(n.length-s):``;if(o.length+c.length<8)return r.join(`
8
- `);let l=r.map(e=>e.slice(o.length,e.length-c.length)),u=e.variants.length-r.length;return`${o}[${u>0?`${l.join(`|`)}|+${f(u)}`:l.join(`|`)}]${c}`}function O(e,t){let n=[],r=0;for(let i of e){let e=D(i);if(e.length===0)continue;let a=Buffer.byteLength(e,`utf8`)+1;if(r+a>t)break;n.push(e),r+=a}return n}function k(e,t=0){let n=e===1?`line`:`lines`,r=t>0?`, ${f(t)} shown below`:``;return`\u2026 [${f(e)} ${n} elided${r}] \u2026`}function A(e){let t=e.endsWith(`
4
+ `).length}var h=class{maxEntries;byKey=new Map;constructor(e=t.getErrorMaxEntries()){this.maxEntries=e}push(e){if(!b(e))return;let t=e.trim(),n=C(e),r=this.byKey.get(n);if(r){r.count+=1,!r.variants.includes(t)&&r.variants.length<12&&r.variants.push(t);return}this.byKey.size>=this.maxEntries||this.byKey.set(n,{variants:[t],count:1})}entries(){return[...this.byKey.values()]}};function g(e,n=t.getResultMaxLines(),a=t.getResultMaxBytes(),o=t.getResultMaxTokens()){let c;try{let t=r.default.statSync(e).size;c=r.default.openSync(e,`r`);let l=Buffer.alloc(s),u=Math.max(1,a+1),d=Buffer.alloc(0),f=Buffer.alloc(0),p=0,m,g=new i.StringDecoder(`utf8`),_=new h,v=``;for(;;){let e=r.default.readSync(c,l,0,l.byteLength,null);if(e===0)break;let t=l.subarray(0,e);for(let e of t)e===10&&(p+=1);m=t.at(-1),d.byteLength<u&&(d=Buffer.concat([d,t.subarray(0,u-d.byteLength)])),f=G(f,t,u),v+=g.write(t);let n=v.indexOf(`
5
+ `);for(;n>=0;)_.push(v.slice(0,n)),v=v.slice(n+1),n=v.indexOf(`
6
+ `)}v+=g.end(),v.length>0&&_.push(v);let y=p+ +(t>0&&m!==10),b=t<=f.byteLength?L(W(f,f.byteLength),n,a,o):R(d.toString(`utf8`),W(f,f.byteLength),y,n,a,_.entries(),o);return{tail:b.text,bytes:t,lines:y,tailLines:b.lines}}catch{return{tail:``,bytes:0,lines:0,tailLines:0}}finally{c!==void 0&&r.default.closeSync(c)}}const _=/^\s*(?:#|\/\/)\s*@doom:\s*(\{.*\})\s*$/u;function v(e){return typeof e==`number`&&Number.isInteger(e)&&e>0?e:void 0}function y(e){let t=_.exec(e.split(`
7
+ `,1)[0]??``);if(!t?.[1])return{};let n;try{n=JSON.parse(t[1])}catch{return{}}if(typeof n!=`object`||!n||Array.isArray(n))return{};let r=n,i=v(r.maxResultBytes),a=v(r.maxResultLines),o=v(r.maxResultTokens);return{...i===void 0?{}:{maxBytes:Math.min(i,262144)},...a===void 0?{}:{maxLines:Math.min(a,5e3)},...o===void 0?{}:{maxTokens:Math.min(o,1e5)}}}function b(e){let n=e.slice(0,60),r=c.exec(n);return r&&!l.test(n.slice(0,r.index))?!0:t.getErrorPatterns().some(t=>S(t,e))}const x=new Map;function S(e,t){if(!x.has(e))try{x.set(e,new RegExp(e,`iu`))}catch{x.set(e,void 0)}return x.get(e)?.test(t)??!1}function C(e){return e.replace(/0x[0-9a-f]+/giu,`0x#`).replace(/\/[^\s:,)]+/gu,`/#`).replace(/\d+/gu,`#`).trim().toLowerCase()}function w(e,n=t.getErrorMaxEntries()){let r=new h(n);for(let t of e)r.push(t);return r.entries()}function T(e){let t=e.reduce((e,t)=>Math.min(e,t.length),1/0),n=0;for(;n<t&&e.every(t=>t[n]===e[0][n]);)n+=1;let r=0;for(;r<t-n&&e.every(t=>t[t.length-1-r]===e[0][e[0].length-1-r]);)r+=1;return{prefix:n,suffix:r}}function E(e,t){let n=e.lastIndexOf(` `,Math.max(0,t-1));return n>=0?n+1:0}function D(e,t){let n=e.length-t,r=e.indexOf(` `,n);return r>=0?e.length-r:0}function O(e){let[n]=e.variants;if(n===void 0)return``;if(e.variants.length===1)return e.count>1?`${n} (\u00d7${p(e.count)})`:n;let r=e.variants.slice(0,t.getErrorMaxVariantsJoined()),{prefix:i,suffix:a}=T(r),o=n.slice(0,E(n,i)),s=D(n,a),c=s>0?n.slice(n.length-s):``;if(o.length+c.length<8)return r.join(`
8
+ `);let l=r.map(e=>e.slice(o.length,e.length-c.length)),u=e.variants.length-r.length;return`${o}[${u>0?`${l.join(`|`)}|+${p(u)}`:l.join(`|`)}]${c}`}function k(e,t){let n=[],r=0;for(let i of e){let e=O(i);if(e.length===0)continue;let a=Buffer.byteLength(e,`utf8`)+1;if(r+a>t)break;n.push(e),r+=a}return n}function A(e,t=0){let n=e===1?`line`:`lines`,r=t>0?`, ${p(t)} shown below`:``;return`\u2026 [${p(e)} ${n} elided${r}] \u2026`}function j(e){let t=e.endsWith(`
9
9
  `),n=e.split(`
10
- `);return t&&n.pop(),{lines:n,trailingNewline:t}}function j(e,t,n,r){if(n<=0||t<=0)return{text:``,count:0};let i=r?[...e].reverse():[...e],a=[],o=0;for(let e of i){if(a.length>=t)break;let r=Buffer.byteLength(e,`utf8`)+ +(a.length>0);if(o+r>n)break;a.push(e),o+=r}if(a.length===0)return{text:``,count:0};let s=r?a.reverse():a;return{text:s.join(`
11
- `),count:s.length}}function M(e,t){let n=U(Buffer.from(e,`utf8`),t),r=n.indexOf(`
12
- `),i=r>=0?n.slice(r+1):n;return{text:i,count:+(i.length>0)}}function N(e){return Buffer.byteLength(k(e),`utf8`)+2}function P(e,t,n,r,i,a=[]){let o=n>0?[k(n,a.length)]:[],s=n>0&&a.length>0?[`…`]:[],c=[e.text,...o,...a,...s,t.text].filter(e=>e.length>0).join(`
13
- `),l=r&&c.length>0?`${c}\n`:c,u=Buffer.from(l,`utf8`);return{text:u.byteLength>i?U(u,i):l,lines:e.count+t.count,truncated:!0,elidedLines:Math.max(0,n)}}function F(e,n,r){let i=Math.max(1,Math.floor(e)),a=Math.max(0,n-N(r));return{limitLines:i,headLines:Math.max(1,Math.floor(i*t.getHeadRatio())),headBytes:Math.max(0,Math.floor(a*t.getHeadRatio())),usable:a}}function I(e,n=t.getResultMaxLines(),r=t.getResultMaxBytes()){if(e.length===0)return{text:``,lines:0,truncated:!1,elidedLines:0};let{lines:i,trailingNewline:a}=A(e),o=Buffer.byteLength(e,`utf8`),s=Number.isFinite(r)?Math.max(0,Math.floor(r)):o,c=Math.max(1,Math.floor(n));if(i.length<=c&&o<=s)return{text:e,lines:i.length,truncated:!1,elidedLines:0};let l=F(n,s,i.length),u=j(i,l.headLines,l.headBytes,!1),d=i.slice(u.count),f=d.some(y)?Math.floor(l.usable*t.getErrorBudgetRatio()):0,p=l.usable-Buffer.byteLength(u.text,`utf8`)-f,m=j(d,Math.max(1,l.limitLines-u.count),p,!0);if(u.count===0&&m.count===0){let t=M(e,s);return{text:t.text,lines:t.count,truncated:!0,elidedLines:i.length-t.count}}let h=i.slice(u.count,i.length-m.count),g=f>0?O(C(h),f):[];return P(u,m,i.length-u.count-m.count,a,s,g)}function L(e,n,r,i=t.getResultMaxLines(),a=t.getResultMaxBytes(),o=[]){let s=Number.isFinite(a)?Math.max(0,Math.floor(a)):2**53-1,c=F(i,s,r),l=A(e).lines,{lines:u,trailingNewline:d}=A(n),f=j(l.slice(0,Math.max(0,l.length-1)),c.headLines,c.headBytes,!1),p=o.length>0?Math.floor(c.usable*t.getErrorBudgetRatio()):0,m=c.usable-Buffer.byteLength(f.text,`utf8`)-p,h=j(u.slice(1),Math.max(1,c.limitLines-f.count),m,!0);if(f.count===0&&h.count===0){let e=M(n,s);return{text:e.text,lines:e.count,truncated:!0,elidedLines:Math.max(0,r-e.count)}}let g=p>0?O(o,p):[];return P(f,h,Math.max(0,r-f.count-h.count),d,s,g)}function R(e,n=t.getResultMaxLines(),r=t.getResultMaxBytes()){let i=I(e,n,r);return{tail:i.text,tailLines:i.lines}}function z(e,n=t.getResultMaxBytes()){return I(e,t.getResultMaxLines(),n).text}function B(e,n,r=t.getResultMaxBytes(),i=t.getResultMaxLines()){let a=I(e,i,r);return a.truncated?{text:`${a.text}\n${V(a,n,e)}`,truncated:!0,outputLines:a.lines}:{text:e,truncated:!1,outputLines:a.lines}}function V(e,t,n){let r=H(t),i=r?.lines??p(n),a=r?.bytes??Buffer.byteLength(n,`utf8`);return[`[output truncated: showing ${f(e.lines)} of ${f(i)} lines,`,`${f(e.elidedLines)} elided from the middle.`,`Full output is at ${t} (${d(a)}, ${f(i)} lines).`,`Inspect it with doom-runner logs, or read the file with an offset near line ${f(i)}.]`].join(` `)}function H(e){let t;try{let r=n.default.statSync(e).size;t=n.default.openSync(e,`r`);let i=Buffer.alloc(o),a=0,s;for(;;){let e=n.default.readSync(t,i,0,i.byteLength,null);if(e===0)break;let r=i.subarray(0,e);for(let e of r)e===10&&(a+=1);s=r.at(-1)}return{bytes:r,lines:a+ +(r>0&&s!==10)}}catch{return}finally{t!==void 0&&n.default.closeSync(t)}}function U(e,t){let n=Math.max(0,e.byteLength-t);for(;n<e.byteLength&&(e[n]&192)==128;)n+=1;return e.subarray(n).toString(`utf8`)}function W(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function G(e,t=Date.now()){let n=e.interactive?` interactive`:``;return`${e.name} pid ${e.pid} up ${K(e.startedAt,t)}${n} ${e.command}`}function K(e,t){let n=Date.parse(e);if(Number.isNaN(n))return`unknown`;let r=Math.max(0,Math.floor((t-n)/1e3));if(r<60)return`${r}s`;let i=Math.floor(r/60);if(i<60)return`${i}m`;let a=Math.floor(i/60);return a<24?`${a}h`:`${Math.floor(a/24)}d`}exports.boundExcerpt=I,exports.boundResultText=z,exports.collectErrorLines=C,exports.composeExcerpt=L,exports.countLines=p,exports.errorResult=u,exports.formatRunnerLine=G,exports.formatSize=d,exports.formatUptime=K,exports.parseResultPragma=v,exports.summarizeLog=h,exports.summarizeText=R,exports.textResult=l,exports.truncateForResult=B;
10
+ `);return t&&n.pop(),{lines:n,trailingNewline:t}}function M(e,t,r,i,a=1/0){if(r<=0||t<=0)return{text:``,count:0};let o=i?[...e].reverse():[...e],s=[],c=0,l=0;for(let e of o){if(s.length>=t)break;let i=Buffer.byteLength(e,`utf8`)+ +(s.length>0),o=n.estimateTokens(e)+ +(s.length>0);if(c+i>r||l+o>a)break;s.push(e),c+=i,l+=o}if(s.length===0)return{text:``,count:0};let u=i?s.reverse():s;return{text:u.join(`
11
+ `),count:u.length}}function N(e,t){let n=W(Buffer.from(e,`utf8`),t),r=n.indexOf(`
12
+ `),i=r>=0?n.slice(r+1):n;return{text:i,count:+(i.length>0)}}function P(e){return Buffer.byteLength(A(e),`utf8`)+2}function F(e,t,n,r,i,a=[]){let o=n>0?[A(n,a.length)]:[],s=n>0&&a.length>0?[`…`]:[],c=[e.text,...o,...a,...s,t.text].filter(e=>e.length>0).join(`
13
+ `),l=r&&c.length>0?`${c}\n`:c,u=Buffer.from(l,`utf8`);return{text:u.byteLength>i?W(u,i):l,lines:e.count+t.count,truncated:!0,elidedLines:Math.max(0,n)}}function I(e,r,i,a){let o=Math.max(1,Math.floor(e)),s=Math.max(0,r-P(i)),c=Math.max(1,Math.floor(a)-n.estimateTokens(A(i)));return{limitLines:o,headLines:Math.max(1,Math.floor(o*t.getHeadRatio())),headBytes:Math.max(0,Math.floor(s*t.getHeadRatio())),headTokens:Math.max(1,Math.floor(c*t.getHeadRatio())),usable:s,tokens:c}}function L(e,r=t.getResultMaxLines(),i=t.getResultMaxBytes(),a=t.getResultMaxTokens()){if(e.length===0)return{text:``,lines:0,truncated:!1,elidedLines:0};let{lines:o,trailingNewline:s}=j(e),c=Buffer.byteLength(e,`utf8`),l=Number.isFinite(i)?Math.max(0,Math.floor(i)):c,u=Math.max(1,Math.floor(r));if(o.length<=u&&c<=l&&n.estimateTokens(e)<=a)return{text:e,lines:o.length,truncated:!1,elidedLines:0};let d=I(r,l,o.length,a),f=M(o,d.headLines,d.headBytes,!1,d.headTokens),p=o.slice(f.count),m=p.some(b)?Math.floor(d.usable*t.getErrorBudgetRatio()):0,h=d.usable-Buffer.byteLength(f.text,`utf8`)-m,g=d.tokens-n.estimateTokens(f.text),_=M(p,Math.max(1,d.limitLines-f.count),h,!0,g);if(f.count===0&&_.count===0){let t=N(e,l);return{text:t.text,lines:t.count,truncated:!0,elidedLines:o.length-t.count}}let v=o.slice(f.count,o.length-_.count),y=m>0?k(w(v),m):[];return F(f,_,o.length-f.count-_.count,s,l,y)}function R(e,r,i,a=t.getResultMaxLines(),o=t.getResultMaxBytes(),s=[],c=t.getResultMaxTokens()){let l=Number.isFinite(o)?Math.max(0,Math.floor(o)):2**53-1,u=I(a,l,i,c),d=j(e).lines,{lines:f,trailingNewline:p}=j(r),m=M(d.slice(0,Math.max(0,d.length-1)),u.headLines,u.headBytes,!1,u.headTokens),h=s.length>0?Math.floor(u.usable*t.getErrorBudgetRatio()):0,g=u.usable-Buffer.byteLength(m.text,`utf8`)-h,_=u.tokens-n.estimateTokens(m.text),v=M(f.slice(1),Math.max(1,u.limitLines-m.count),g,!0,_);if(m.count===0&&v.count===0){let e=N(r,l);return{text:e.text,lines:e.count,truncated:!0,elidedLines:Math.max(0,i-e.count)}}let y=h>0?k(s,h):[];return F(m,v,Math.max(0,i-m.count-v.count),p,l,y)}function z(e,n=t.getResultMaxLines(),r=t.getResultMaxBytes()){let i=L(e,n,r);return{tail:i.text,tailLines:i.lines}}function B(e,n=t.getResultMaxBytes()){return L(e,t.getResultMaxLines(),n).text}function V(e,n,r=t.getResultMaxBytes(),i=t.getResultMaxLines()){let a=L(e,i,r);return a.truncated?{text:`${a.text}\n${H(a,n,e)}`,truncated:!0,outputLines:a.lines}:{text:e,truncated:!1,outputLines:a.lines}}function H(e,t,n){let r=U(t),i=r?.lines??m(n),a=r?.bytes??Buffer.byteLength(n,`utf8`);return[`[output truncated: showing ${p(e.lines)} of ${p(i)} lines,`,`${p(e.elidedLines)} elided from the middle.`,`Full output is at ${t} (${f(a)}, ${p(i)} lines).`,`Inspect it with doom-runner logs, or read the file with an offset near line ${p(i)}.]`].join(` `)}function U(e){let t;try{let n=r.default.statSync(e).size;t=r.default.openSync(e,`r`);let i=Buffer.alloc(s),a=0,o;for(;;){let e=r.default.readSync(t,i,0,i.byteLength,null);if(e===0)break;let n=i.subarray(0,e);for(let e of n)e===10&&(a+=1);o=n.at(-1)}return{bytes:n,lines:a+ +(n>0&&o!==10)}}catch{return}finally{t!==void 0&&r.default.closeSync(t)}}function W(e,t){let n=Math.max(0,e.byteLength-t);for(;n<e.byteLength&&(e[n]&192)==128;)n+=1;return e.subarray(n).toString(`utf8`)}function G(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function K(e,t=Date.now()){let n=e.interactive?` interactive`:``;return`${e.name} pid ${e.pid} up ${q(e.startedAt,t)}${n} ${e.command}`}function q(e,t){let n=Date.parse(e);if(Number.isNaN(n))return`unknown`;let r=Math.max(0,Math.floor((t-n)/1e3));if(r<60)return`${r}s`;let i=Math.floor(r/60);if(i<60)return`${i}m`;let a=Math.floor(i/60);return a<24?`${a}h`:`${Math.floor(a/24)}d`}exports.boundExcerpt=L,exports.boundResultText=B,exports.collectErrorLines=w,exports.composeExcerpt=R,exports.countLines=m,exports.errorResult=d,exports.formatRunnerLine=K,exports.formatSize=f,exports.formatUptime=q,exports.parseResultPragma=y,exports.summarizeLog=g,exports.summarizeText=z,exports.textResult=u,exports.truncateForResult=V;
14
14
  //# sourceMappingURL=responseEnvelope.cjs.map