@agimon-ai/doompi-runner 0.0.1-alpha.49 → 0.0.1-alpha.50

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.
@@ -1,2 +1,2 @@
1
- const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../schemas/runnerSpec.cjs");let r=require("node:crypto"),i=require("node:fs");i=e.__toESM(i,1);let a=require("node:path");a=e.__toESM(a,1);let o=require("node:module"),s=require("@rmux/sdk");const c=1e3,l=`doom-runner-`,u=/^doom-runner-[A-Za-z0-9_-]+$/,d={NO_COLOR:`1`,CI:`1`},f={"darwin-arm64":`@agimon-ai/doompi-runner-rmux-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rmux-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rmux-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rmux-linux-x64`};var p=class{paths;clientPromise;runs=new Map;completions=new Map;constructor(e){this.paths=e}async launch(e){let r=await this.client();if(!r)return;this.paths.ensureDirectories();let a=`${l}${e.id}`,o=this.paths.logPathFor(e.id),s=this.auxPaths(e.id,e.sessionId),c=!1,u=!1;try{this.prepareFiles(e,o,s),await r.cmd(`new-session`,`-d`,`-s`,a,`-c`,e.cwd,process.execPath,n.runtimeEntry(`runnerHost`),s.spec,s.gate,s.exit,{check:!0}),c=!0,await r.cmd(`pipe-pane`,`-t`,a,this.logPipeCommand(o,s.logDone),{check:!0});let l=r.session(a).pane(0,0),d=await v(r,a);i.default.writeFileSync(s.gate,``,{mode:384}),u=!0;let f=!0,p=this.monitor(r,l,a,s);this.trackCompletion(a,p);let m={id:e.id,name:e.name,pid:d,logPath:o,backend:`rmux`,backendTarget:a,output:()=>f?E(o,t.getResultMaxBytes()*2):``,completion:()=>p,detach:()=>{f=!1},stop:()=>this.stop(a,d)};return e.interactive&&this.runs.set(e.name,this.interactiveRun(m,l,p)),p.finally(()=>this.runs.delete(e.name)).catch(e=>process.emitWarning(`RMUX completion failed for ${a}: ${O(e)}`)),m}catch(t){if(u)throw t;c&&await r.session(a).kill().catch(e=>process.emitWarning(`Could not clean up failed RMUX launch ${a}: ${O(e)}`)),n.cleanupSupervisorFiles(s),process.emitWarning(e.interactive?`RMUX launch unavailable for interactive runner: ${O(t)}`:`RMUX launch unavailable, using supervised subprocess: ${O(t)}`);return}}async stop(e,t){if(!u.test(e)||!Number.isInteger(t)||t<=0)return!1;let n=await this.client();if(!n)return!1;try{if(await w(n,e))return!0;let r=await v(n,e);if(r!==t)return!1;let i=T(r,e);return i===`gone`?!0:i===`failed`?!1:await S(n,e,3e3)||await w(n,e)?!0:await v(n,e)===t&&x(n,e)}catch(t){return process.emitWarning(`Could not stop RMUX target ${e}: ${O(t)}`),!1}}async watch(e,t,n){let r=await this.client();if(!r)return;let i=r.session(t).pane(0,0);try{await r.displayMessage(`#{pane_id}`,{target:t})}catch(e){process.emitWarning(`Could not watch RMUX target ${t}: ${O(e)}`);return}let a=this.monitor(r,i,t,this.auxPaths(e,n));return this.trackCompletion(t,a),a}async input(e,t){let n=await this.client();return n?await n.session(e).pane(0,0).sendText(t).catch(t=>{process.emitWarning(`Could not send input to RMUX target ${e}: ${O(t)}`)})!==void 0:!1}get(e){return this.runs.get(e)}readOutcome(e,t){return n.readExitMetadata(this.auxPaths(e,t).exit)}trackCompletion(e,t){this.completions.set(e,t),t.finally(()=>this.completions.delete(e)).catch(t=>process.emitWarning(`RMUX monitor failed for ${e}: ${O(t)}`))}client(){return this.clientPromise??=this.resolveClient(),this.clientPromise}async resolveClient(){let e=[h(),`rmux`].filter(e=>e!==void 0);for(let t of e)try{t!==`rmux`&&i.default.chmodSync(t,493);let e=new s.RMUX({binary:t,socketName:g(this.paths.repositoryPath())});return await e.capabilities(),e}catch(e){process.emitWarning(`RMUX candidate ${t} is unavailable: ${O(e)}`)}}prepareFiles(e,t,r){n.cleanupSupervisorFiles(r),i.default.writeFileSync(t,``,{mode:384});let a=_(e.sessionId,e.interactive),o={command:e.command,cwd:e.cwd,env:a};n.writeCommandSpec(r.spec,o)}auxPaths(e,t){return n.supervisorPaths(this.paths.stateDirectory(t),e)}logPipeCommand(e,r){return n.shellJoin([process.execPath,n.runtimeEntry(`logSink`),e,this.paths.rotatedLogPathFor(a.default.basename(e,`.log`)),String(t.getLogMaxBytes()),r])}async monitor(e,t,r,i){try{let t=await y(e,r,i.exit);return await b(e,r,i.logDone),n.readExitMetadata(i.exit)??{code:t,signal:null}}finally{await w(e,r).catch(()=>!1)||await t.server.session(r).kill().catch(e=>process.emitWarning(`Could not clean up completed RMUX target ${r}: ${O(e)}`)),n.cleanupSupervisorFiles(i)}}interactiveRun(e,t,n){let r=``,i=!0,a=new Set;return(async()=>{for(;i;){let n=await t.captureText().catch(t=>(process.emitWarning(`Could not capture RMUX screen for ${e.name}: ${O(t)}`),r));if(n!==r){r=n;for(let e of a)e(n)}await D(100)}})(),n.finally(()=>{i=!1}).catch(t=>process.emitWarning(`RMUX interactive completion failed for ${e.name}: ${O(t)}`)),{...e,write:e=>{t.sendText(e).catch(e=>process.emitWarning(O(e)))},screen:()=>r,onData:e=>(a.add(e),()=>a.delete(e)),resize:(e,n)=>{t.resize({width:e,height:n}).catch(e=>process.emitWarning(O(e)))}}}};function m(e,t){let n=`${e}-${t}`,r=f[n];return r||process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${n}`),r}function h(){let e=m(process.platform,process.arch);if(e)try{let t=(0,o.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${e}/package.json`);return a.default.join(a.default.dirname(t),`vendor`,`bin`,`rmux`)}catch(t){process.emitWarning(`Bundled RMUX binary is unavailable for ${e}: ${O(t)}`);return}}function g(e){let t=(0,r.createHash)(`sha256`).update(e).digest(`hex`).slice(0,12);return`${l}${t}`}function _(e,t){let r={...process.env,...t?{}:d,...n.NO_TERMINAL_INPUT_ENV,PI_SESSION_ID:e};return t||delete r.FORCE_COLOR,Object.fromEntries(Object.entries(r).filter(e=>e[1]!==void 0))}async function v(e,t){let n=await e.displayMessage(`#{pane_pid}`,{target:t}),r=Number(n.message);if(!Number.isInteger(r)||r<=0)throw Error(`RMUX did not report a pane pid for ${t}`);return r}async function y(e,t,n){for(;;){if(i.default.existsSync(n))return null;let r=await C(e,t);if(r.exited)return r.code;await D(100)}}async function b(e,t,n){let r=Date.now()+2e3;for(;Date.now()<r;){if(i.default.existsSync(n)||await w(e,t))return;await D(25)}}async function x(e,t){return new Promise(n=>{let r=!1,i=e=>{r||(r=!0,clearTimeout(a),n(e))},a=setTimeout(()=>{process.emitWarning(`Timed out closing RMUX target ${t} after ${c}ms`),i(!1)},c);e.session(t).kill().then(()=>i(!0),e=>{process.emitWarning(`Could not close RMUX target ${t}: ${O(e)}`),i(!1)})})}async function S(e,t,n){let r=Date.now()+n;for(;Date.now()<r;){try{if((await C(e,t)).exited)return!0}catch(e){return process.emitWarning(`Could not verify RMUX target ${t} stopped: ${O(e)}`),!1}await D(100)}return!1}async function C(e,t){let n;try{let r=await e.displayMessage(`#{pane_dead}:#{pane_dead_status}:#{session_name}`,{target:t});n=typeof r.message==`string`?r.message:``}catch{return{exited:!0,code:null}}let[r,i,...a]=n.trim().split(`:`);if(a.join(`:`)===``)return{exited:!0,code:null};if(r===`1`||r===`true`){let e=Number(i);return{exited:!0,code:Number.isInteger(e)?e:null}}return{exited:!1,code:null}}async function w(e,t){try{return(await e.cmd(`has-session`,`-t`,t)).returnCode!==0}catch{return!0}}function T(e,t){try{return process.kill(e,`SIGTERM`),`signalled`}catch(e){return e.code===`ESRCH`?`gone`:(process.emitWarning(`Could not signal RMUX target ${t}: ${O(e)}`),`failed`)}}function E(e,t){let n;try{let r=i.default.statSync(e).size,a=Math.min(r,t),o=Buffer.alloc(a);return n=i.default.openSync(e,`r`),i.default.readSync(n,o,0,a,r-a),o.toString(`utf8`)}catch(t){return t.code!==`ENOENT`&&process.emitWarning(`Could not read RMUX log ${e}: ${O(t)}`),``}finally{n!==void 0&&i.default.closeSync(n)}}function D(e){return new Promise(t=>{setTimeout(t,e).unref?.()})}function O(e){return e instanceof Error?e.message:String(e)}exports.RmuxBackend=p,exports.rmuxPackageForTarget=m;
1
+ const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../schemas/runnerSpec.cjs");let r=require("node:crypto"),i=require("node:fs");i=e.__toESM(i,1);let a=require("node:path");a=e.__toESM(a,1);let o=require("node:module"),s=require("@rmux/sdk");const c=1e3,l=`doom-runner-`,u=/^doom-runner-[A-Za-z0-9_-]+$/,d={NO_COLOR:`1`,CI:`1`},f={"darwin-arm64":`@agimon-ai/doompi-runner-rmux-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rmux-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rmux-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rmux-linux-x64`};var p=class{paths;clientPromise;runs=new Map;completions=new Map;constructor(e){this.paths=e}async launch(e){let r=await this.client();if(!r)return;this.paths.ensureDirectories();let a=`${l}${e.id}`,o=this.paths.logPathFor(e.id),s=this.auxPaths(e.id,e.sessionId),c=!1,u=!1;try{this.prepareFiles(e,o,s),await r.cmd(`new-session`,`-d`,`-s`,a,`-c`,e.cwd,process.execPath,n.runtimeEntry(`runnerHost`),s.spec,s.gate,s.exit,{check:!0}),c=!0,await r.cmd(`pipe-pane`,`-t`,a,this.logPipeCommand(o,s.logDone),{check:!0});let l=r.session(a).pane(0,0),d=await v(r,a);i.default.writeFileSync(s.gate,``,{mode:384}),u=!0;let f=!0,p=this.monitor(r,l,a,s);this.trackCompletion(a,p);let m={id:e.id,name:e.name,pid:d,logPath:o,backend:`rmux`,backendTarget:a,output:()=>f?E(o,t.getResultMaxBytes()*2):``,completion:()=>p,detach:()=>{f=!1},stop:()=>this.stop(a,d)};return e.interactive&&this.runs.set(e.name,this.interactiveRun(m,l,p)),p.finally(()=>this.runs.delete(e.name)).catch(e=>process.emitWarning(`RMUX completion failed for ${a}: ${O(e)}`)),m}catch(t){if(u)throw t;c&&await r.session(a).kill().catch(e=>process.emitWarning(`Could not clean up failed RMUX launch ${a}: ${O(e)}`)),n.cleanupSupervisorFiles(s),process.emitWarning(e.interactive?`RMUX launch unavailable for interactive runner: ${O(t)}`:`RMUX launch unavailable, using supervised subprocess: ${O(t)}`);return}}async stop(e,t){if(!u.test(e)||!Number.isInteger(t)||t<=0)return!1;let n=await this.client();if(!n)return!1;try{if(await w(n,e))return!0;let r=await v(n,e);if(r!==t)return!1;let i=T(r,e);return i===`gone`?!0:i===`failed`?!1:await S(n,e,3e3)||await w(n,e)?!0:await v(n,e)===t&&x(n,e)}catch(t){return process.emitWarning(`Could not stop RMUX target ${e}: ${O(t)}`),!1}}async watch(e,t,n){let r=await this.client();if(!r)return;let i=r.session(t).pane(0,0);try{await r.displayMessage(`#{pane_id}`,{target:t})}catch(e){process.emitWarning(`Could not watch RMUX target ${t}: ${O(e)}`);return}let a=this.monitor(r,i,t,this.auxPaths(e,n));return this.trackCompletion(t,a),a}async input(e,t){let n=await this.client();return n?await n.session(e).pane(0,0).sendText(t).catch(t=>{process.emitWarning(`Could not send input to RMUX target ${e}: ${O(t)}`)})!==void 0:!1}get(e){return this.runs.get(e)}readOutcome(e,t){return n.readExitMetadata(this.auxPaths(e,t).exit)}trackCompletion(e,t){this.completions.set(e,t),t.finally(()=>this.completions.delete(e)).catch(t=>process.emitWarning(`RMUX monitor failed for ${e}: ${O(t)}`))}client(){return this.clientPromise??=this.resolveClient(),this.clientPromise}async resolveClient(){let e=[h(),`rmux`].filter(e=>e!==void 0);for(let t of e)try{t!==`rmux`&&i.default.chmodSync(t,493);let e=new s.RMUX({binary:t,socketName:g(this.paths.repositoryPath())});return await e.capabilities(),e}catch(e){process.emitWarning(`RMUX candidate ${t} is unavailable: ${O(e)}`)}}prepareFiles(e,t,r){n.cleanupSupervisorFiles(r),i.default.writeFileSync(t,``,{mode:384});let a=_(e.sessionId,e.interactive),o={command:e.command,cwd:e.cwd,env:a};n.writeCommandSpec(r.spec,o)}auxPaths(e,t){return n.supervisorPaths(this.paths.stateDirectory(t),e)}logPipeCommand(e,r){return n.shellJoin([process.execPath,n.runtimeEntry(`logSink`),e,this.paths.rotatedLogPathFor(a.default.basename(e,`.log`)),String(t.getLogMaxBytes()),r])}async monitor(e,t,r,i){try{let t=await y(e,r,i.exit);return await b(e,r,i.logDone),n.readExitMetadata(i.exit)??{code:t,signal:null}}finally{await w(e,r).catch(()=>!1)||await t.server.session(r).kill().catch(e=>process.emitWarning(`Could not clean up completed RMUX target ${r}: ${O(e)}`)),n.cleanupSupervisorFiles(i)}}interactiveRun(e,t,n){let r=``,i=!0,a=new Set;return(async()=>{for(;i;){let n=await t.captureText().catch(t=>(process.emitWarning(`Could not capture RMUX screen for ${e.name}: ${O(t)}`),r));if(n!==r){r=n;for(let e of a)e(n)}await D(100)}})(),n.finally(()=>{i=!1}).catch(t=>process.emitWarning(`RMUX interactive completion failed for ${e.name}: ${O(t)}`)),{...e,write:e=>{t.sendText(e).catch(e=>process.emitWarning(O(e)))},screen:()=>r,onData:e=>(a.add(e),()=>a.delete(e)),resize:(e,n)=>{t.resize({width:e,height:n}).catch(e=>process.emitWarning(O(e)))}}}};function m(e,t){let n=`${e}-${t}`,r=f[n];return r||process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${n}`),r}function h(){let e=process.env.DOOMPI_RMUX_BINARY;if(e!==void 0&&e!==``)return e;let t=m(process.platform,process.arch);if(t)try{let e=(0,o.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${t}/package.json`);return a.default.join(a.default.dirname(e),`vendor`,`bin`,`rmux`)}catch(e){process.emitWarning(`Bundled RMUX binary is unavailable for ${t}: ${O(e)}`);return}}function g(e){let t=(0,r.createHash)(`sha256`).update(e).digest(`hex`).slice(0,12);return`${l}${t}`}function _(e,t){let r={...process.env,...t?{}:d,...n.NO_TERMINAL_INPUT_ENV,PI_SESSION_ID:e};return t||delete r.FORCE_COLOR,Object.fromEntries(Object.entries(r).filter(e=>e[1]!==void 0))}async function v(e,t){let n=await e.displayMessage(`#{pane_pid}`,{target:t}),r=Number(n.message);if(!Number.isInteger(r)||r<=0)throw Error(`RMUX did not report a pane pid for ${t}`);return r}async function y(e,t,n){for(;;){if(i.default.existsSync(n))return null;let r=await C(e,t);if(r.exited)return r.code;await D(100)}}async function b(e,t,n){let r=Date.now()+2e3;for(;Date.now()<r;){if(i.default.existsSync(n)||await w(e,t))return;await D(25)}}async function x(e,t){return new Promise(n=>{let r=!1,i=e=>{r||(r=!0,clearTimeout(a),n(e))},a=setTimeout(()=>{process.emitWarning(`Timed out closing RMUX target ${t} after ${c}ms`),i(!1)},c);e.session(t).kill().then(()=>i(!0),e=>{process.emitWarning(`Could not close RMUX target ${t}: ${O(e)}`),i(!1)})})}async function S(e,t,n){let r=Date.now()+n;for(;Date.now()<r;){try{if((await C(e,t)).exited)return!0}catch(e){return process.emitWarning(`Could not verify RMUX target ${t} stopped: ${O(e)}`),!1}await D(100)}return!1}async function C(e,t){let n;try{let r=await e.displayMessage(`#{pane_dead}:#{pane_dead_status}:#{session_name}`,{target:t});n=typeof r.message==`string`?r.message:``}catch{return{exited:!0,code:null}}let[r,i,...a]=n.trim().split(`:`);if(a.join(`:`)===``)return{exited:!0,code:null};if(r===`1`||r===`true`){let e=Number(i);return{exited:!0,code:Number.isInteger(e)?e:null}}return{exited:!1,code:null}}async function w(e,t){try{return(await e.cmd(`has-session`,`-t`,t)).returnCode!==0}catch{return!0}}function T(e,t){try{return process.kill(e,`SIGTERM`),`signalled`}catch(e){return e.code===`ESRCH`?`gone`:(process.emitWarning(`Could not signal RMUX target ${t}: ${O(e)}`),`failed`)}}function E(e,t){let n;try{let r=i.default.statSync(e).size,a=Math.min(r,t),o=Buffer.alloc(a);return n=i.default.openSync(e,`r`),i.default.readSync(n,o,0,a,r-a),o.toString(`utf8`)}catch(t){return t.code!==`ENOENT`&&process.emitWarning(`Could not read RMUX log ${e}: ${O(t)}`),``}finally{n!==void 0&&i.default.closeSync(n)}}function D(e){return new Promise(t=>{setTimeout(t,e).unref?.()})}function O(e){return e instanceof Error?e.message:String(e)}exports.RmuxBackend=p,exports.rmuxPackageForTarget=m;
2
2
  //# sourceMappingURL=RmuxBackend.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RmuxBackend.cjs","names":["runtimeEntry","getResultMaxBytes","readExitMetadata","RMUX","supervisorPaths","shellJoin","path","getLogMaxBytes","createRequire","createHash","NO_TERMINAL_INPUT_ENV","fs"],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { type Pane, RMUX, type Rmux } from '@rmux/sdk';\nimport {\n type CommandSpec,\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n runtimeEntry,\n type SupervisorPaths,\n shellJoin,\n supervisorPaths,\n writeCommandSpec,\n} from '../../schemas/runnerSpec.ts';\nimport { getLogMaxBytes, getResultMaxBytes } from '../../types/config.ts';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport type { ExitResult } from '../../types/spawner';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\n\nconst POLL_MS = 100;\nconst LOG_DRAIN_TIMEOUT_MS = 2_000;\nconst LOG_DRAIN_POLL_MS = 25;\n/** Pane liveness, its exit status, and whether the session still exists, in one read. */\nconst PANE_STATE_FORMAT = '#{pane_dead}:#{pane_dead_status}:#{session_name}';\nconst STOP_GRACE_MS = 3_000;\nconst STOP_CLOSE_TIMEOUT_MS = 1_000;\nconst EXECUTABLE_MODE = 0o755;\nconst SESSION_PREFIX = 'doom-runner-';\nconst OWNED_TARGET_PATTERN = /^doom-runner-[A-Za-z0-9_-]+$/;\nconst SOCKET_HASH_LENGTH = 12;\nconst NON_INTERACTIVE_ENV = { NO_COLOR: '1', CI: '1' };\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rmux-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rmux-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rmux-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rmux-linux-x64',\n};\n\nexport class RmuxBackend implements IRmuxBackend {\n private clientPromise: Promise<Rmux | undefined> | undefined;\n private readonly runs = new Map<string, PtyRun>();\n private readonly completions = new Map<string, Promise<ExitResult>>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n\n this.paths.ensureDirectories();\n const target = `${SESSION_PREFIX}${request.id}`;\n const logPath = this.paths.logPathFor(request.id);\n const aux = this.auxPaths(request.id, request.sessionId);\n let created = false;\n let released = false;\n\n try {\n this.prepareFiles(request, logPath, aux);\n await rmux.cmd(\n 'new-session',\n '-d',\n '-s',\n target,\n '-c',\n request.cwd,\n process.execPath,\n runtimeEntry('runnerHost'),\n aux.spec,\n aux.gate,\n aux.exit,\n { check: true },\n );\n created = true;\n // No remain-on-exit: a finished pane ends its session, and the last session\n // ending stops the server, so a completed run leaves no process behind.\n await rmux.cmd('pipe-pane', '-t', target, this.logPipeCommand(logPath, aux.logDone), { check: true });\n\n const pane = rmux.session(target).pane(0, 0);\n const pid = await panePid(rmux, target);\n fs.writeFileSync(aux.gate, '', { mode: 0o600 });\n released = true;\n\n let buffering = true;\n const completion = this.monitor(rmux, pane, target, aux);\n this.trackCompletion(target, completion);\n const stop = (): Promise<boolean> => this.stop(target, pid);\n const handle: RunHandle = {\n id: request.id,\n name: request.name,\n pid,\n logPath,\n backend: 'rmux',\n backendTarget: target,\n output: () => (buffering ? readTail(logPath, getResultMaxBytes() * 2) : ''),\n completion: () => completion,\n detach: () => {\n buffering = false;\n },\n stop,\n };\n\n if (request.interactive) this.runs.set(request.name, this.interactiveRun(handle, pane, completion));\n void completion\n .finally(() => this.runs.delete(request.name))\n .catch((error: unknown) => process.emitWarning(`RMUX completion failed for ${target}: ${errorMessage(error)}`));\n return handle;\n } catch (error) {\n if (released) throw error;\n if (created) {\n await rmux\n .session(target)\n .kill()\n .catch((cleanupError: unknown) =>\n process.emitWarning(`Could not clean up failed RMUX launch ${target}: ${errorMessage(cleanupError)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n process.emitWarning(\n request.interactive\n ? `RMUX launch unavailable for interactive runner: ${errorMessage(error)}`\n : `RMUX launch unavailable, using supervised subprocess: ${errorMessage(error)}`,\n );\n return undefined;\n }\n }\n\n async stop(target: string, expectedPid: number): Promise<boolean> {\n if (!OWNED_TARGET_PATTERN.test(target) || !Number.isInteger(expectedPid) || expectedPid <= 0) return false;\n\n const rmux = await this.client();\n if (!rmux) return false;\n try {\n // A session that already ended took its process group with it.\n if (await sessionMissing(rmux, target)) return true;\n\n const pid = await panePid(rmux, target);\n if (pid !== expectedPid) return false;\n const signalled = signalPane(pid, target);\n if (signalled === 'gone') return true;\n if (signalled === 'failed') return false;\n\n if (await waitForPaneStop(rmux, target, STOP_GRACE_MS)) return true;\n if (await sessionMissing(rmux, target)) return true;\n\n // Revalidate after the grace period so a replacement pane is never closed.\n const currentPid = await panePid(rmux, target);\n if (currentPid !== expectedPid) return false;\n\n return closeOwnedSession(rmux, target);\n } catch (error) {\n process.emitWarning(`Could not stop RMUX target ${target}: ${errorMessage(error)}`);\n return false;\n }\n }\n\n async watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n const pane = rmux.session(target).pane(0, 0);\n try {\n await rmux.displayMessage('#{pane_id}', { target });\n } catch (error) {\n process.emitWarning(`Could not watch RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n }\n const completion = this.monitor(rmux, pane, target, this.auxPaths(id, sessionId));\n this.trackCompletion(target, completion);\n return completion;\n }\n\n async input(target: string, text: string): Promise<boolean> {\n const rmux = await this.client();\n if (!rmux) return false;\n const result = await rmux\n .session(target)\n .pane(0, 0)\n .sendText(text)\n .catch((error: unknown) => {\n process.emitWarning(`Could not send input to RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result !== undefined;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return readExitMetadata(this.auxPaths(id, sessionId).exit);\n }\n\n private trackCompletion(target: string, completion: Promise<ExitResult>): void {\n this.completions.set(target, completion);\n void completion\n .finally(() => this.completions.delete(target))\n .catch((error: unknown) => process.emitWarning(`RMUX monitor failed for ${target}: ${errorMessage(error)}`));\n }\n\n private client(): Promise<Rmux | undefined> {\n this.clientPromise ??= this.resolveClient();\n return this.clientPromise;\n }\n\n private async resolveClient(): Promise<Rmux | undefined> {\n const binaries = [bundledBinary(), 'rmux'].filter((value): value is string => value !== undefined);\n for (const binary of binaries) {\n try {\n if (binary !== 'rmux') fs.chmodSync(binary, EXECUTABLE_MODE);\n const client = new RMUX({ binary, socketName: socketName(this.paths.repositoryPath()) });\n await client.capabilities();\n return client;\n } catch (error) {\n process.emitWarning(`RMUX candidate ${binary} is unavailable: ${errorMessage(error)}`);\n // Try the next compatible candidate. Non-interactive calls can use the subprocess launcher.\n }\n }\n return undefined;\n }\n\n private prepareFiles(request: RmuxLaunchRequest, logPath: string, aux: SupervisorPaths): void {\n cleanupSupervisorFiles(aux);\n fs.writeFileSync(logPath, '', { mode: 0o600 });\n const env = environment(request.sessionId, request.interactive);\n // runnerHost supervises the command and watches the lifeline itself, so an\n // owner that dies mid-run is handled inside the pane rather than around it.\n const spec: CommandSpec = { command: request.command, cwd: request.cwd, env };\n writeCommandSpec(aux.spec, spec);\n }\n\n private auxPaths(id: string, sessionId?: string): SupervisorPaths {\n return supervisorPaths(this.paths.stateDirectory(sessionId), id);\n }\n\n private logPipeCommand(logPath: string, donePath: string): string {\n return shellJoin([\n process.execPath,\n runtimeEntry('logSink'),\n logPath,\n this.paths.rotatedLogPathFor(path.basename(logPath, '.log')),\n String(getLogMaxBytes()),\n donePath,\n ]);\n }\n\n private async monitor(rmux: Rmux, pane: Pane, target: string, aux: SupervisorPaths): Promise<ExitResult> {\n try {\n const paneCode = await waitForRunEnd(rmux, target, aux.exit);\n await waitForLogDrain(rmux, target, aux.logDone);\n return readExitMetadata(aux.exit) ?? { code: paneCode, signal: null };\n } finally {\n // The pane ends its own session, so this only covers a session that somehow\n // outlived its pane. Killing unconditionally would warn on every clean run.\n if (!(await sessionMissing(rmux, target).catch(() => false))) {\n await pane.server\n .session(target)\n .kill()\n .catch((error: unknown) =>\n process.emitWarning(`Could not clean up completed RMUX target ${target}: ${errorMessage(error)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n }\n }\n\n private interactiveRun(handle: RunHandle, pane: Pane, completion: Promise<ExitResult>): PtyRun {\n let screen = '';\n let active = true;\n const handlers = new Set<(data: string) => void>();\n const refresh = async (): Promise<void> => {\n while (active) {\n const next = await pane.captureText().catch((error: unknown) => {\n process.emitWarning(`Could not capture RMUX screen for ${handle.name}: ${errorMessage(error)}`);\n return screen;\n });\n if (next !== screen) {\n screen = next;\n for (const handler of handlers) handler(next);\n }\n await delay(POLL_MS);\n }\n };\n void refresh();\n void completion\n .finally(() => {\n active = false;\n })\n .catch((error: unknown) =>\n process.emitWarning(`RMUX interactive completion failed for ${handle.name}: ${errorMessage(error)}`),\n );\n return {\n ...handle,\n write: (text) => {\n void pane.sendText(text).catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n screen: () => screen,\n onData: (handler) => {\n handlers.add(handler);\n return () => handlers.delete(handler);\n },\n resize: (cols, rows) => {\n void pane\n .resize({ width: cols, height: rows })\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n };\n }\n}\n\nexport function rmuxPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rmuxPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rmux');\n } catch (error) {\n process.emitWarning(`Bundled RMUX binary is unavailable for ${packageName}: ${errorMessage(error)}`);\n return undefined;\n }\n}\n\nfunction socketName(repositoryPath: string): string {\n const hash = createHash('sha256').update(repositoryPath).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return `${SESSION_PREFIX}${hash}`;\n}\n\nfunction environment(sessionId: string, interactive: boolean): Record<string, string> {\n const values: NodeJS.ProcessEnv = {\n ...process.env,\n ...(interactive ? {} : NON_INTERACTIVE_ENV),\n // Applied to interactive runs as well: a pane is a tty either way, and an\n // agent sending input to a prompt still has no way past a pager.\n ...NO_TERMINAL_INPUT_ENV,\n PI_SESSION_ID: sessionId,\n };\n if (!interactive) delete values['FORCE_COLOR'];\n return Object.fromEntries(\n Object.entries(values).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n}\n\nasync function panePid(rmux: Rmux, target: string): Promise<number> {\n const response = await rmux.displayMessage('#{pane_pid}', { target });\n const pid = Number(response['message']);\n if (!Number.isInteger(pid) || pid <= 0) throw new Error(`RMUX did not report a pane pid for ${target}`);\n return pid;\n}\n\n/**\n * Waits for the run to end, preferring the supervisor's own record of it.\n *\n * The sidecar is written before the command's pane can end, and it outlives the\n * server that the pane's exit takes down with it. Asking the server is the\n * fallback for a supervisor that died without recording anything.\n */\nasync function waitForRunEnd(rmux: Rmux, target: string, exitPath: string): Promise<number | null> {\n for (;;) {\n if (fs.existsSync(exitPath)) return null;\n const state = await readPaneExit(rmux, target);\n if (state.exited) return state.code;\n await delay(POLL_MS);\n }\n}\n\n/**\n * Gives the log sink a moment to mark the log complete.\n *\n * The sink dies with the session it was piped from, so once the session is gone\n * the marker is never coming and there is nothing left to wait for. Its writes\n * are synchronous, so the log itself is already on disk either way.\n */\nasync function waitForLogDrain(rmux: Rmux, target: string, donePath: string): Promise<void> {\n const deadline = Date.now() + LOG_DRAIN_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (fs.existsSync(donePath)) return;\n if (await sessionMissing(rmux, target)) return;\n await delay(LOG_DRAIN_POLL_MS);\n }\n}\n\nasync function closeOwnedSession(rmux: Rmux, target: string): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(result);\n };\n const timer = setTimeout(() => {\n process.emitWarning(`Timed out closing RMUX target ${target} after ${STOP_CLOSE_TIMEOUT_MS}ms`);\n finish(false);\n }, STOP_CLOSE_TIMEOUT_MS);\n void rmux\n .session(target)\n .kill()\n .then(\n () => finish(true),\n (error: unknown) => {\n process.emitWarning(`Could not close RMUX target ${target}: ${errorMessage(error)}`);\n finish(false);\n },\n );\n });\n}\n\nasync function waitForPaneStop(rmux: Rmux, target: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n if ((await readPaneExit(rmux, target)).exited) return true;\n } catch (error) {\n process.emitWarning(`Could not verify RMUX target ${target} stopped: ${errorMessage(error)}`);\n return false;\n }\n await delay(POLL_MS);\n }\n return false;\n}\n\n/**\n * One formatted read answers both questions this poll asks, so watching a run\n * costs the same single rmux call it always did.\n */\nasync function readPaneExit(rmux: Rmux, target: string): Promise<{ exited: boolean; code: number | null }> {\n let message: string;\n try {\n const response = await rmux.displayMessage(PANE_STATE_FORMAT, { target });\n message = typeof response['message'] === 'string' ? response['message'] : '';\n } catch {\n // The last session ending stops the server, so a server that cannot be\n // reached is the ordinary end of the last run rather than a fault.\n return { exited: true, code: null };\n }\n const [dead, status, ...rest] = message.trim().split(':');\n // A missing session formats as a live pane with no name rather than failing,\n // so the name is what says whether there is still anything to watch.\n if (rest.join(':') === '') return { exited: true, code: null };\n if (dead === '1' || dead === 'true') {\n const code = Number(status);\n return { exited: true, code: Number.isInteger(code) ? code : null };\n }\n return { exited: false, code: null };\n}\n\n/** A session that cannot be found, on a server that may itself be gone. */\nasync function sessionMissing(rmux: Rmux, target: string): Promise<boolean> {\n try {\n const probe = await rmux.cmd('has-session', '-t', target);\n return probe.returnCode !== 0;\n } catch {\n // No server to ask means no session to find.\n return true;\n }\n}\n\n/**\n * A pane that was already gone and a signal that could not be delivered are\n * opposite answers to \"is it stopped\", so they are reported apart.\n */\nfunction signalPane(pid: number, target: string): 'signalled' | 'gone' | 'failed' {\n try {\n process.kill(pid, 'SIGTERM');\n return 'signalled';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'gone';\n process.emitWarning(`Could not signal RMUX target ${target}: ${errorMessage(error)}`);\n return 'failed';\n }\n}\n\nfunction readTail(target: string, maxBytes: number): string {\n let handle: number | undefined;\n try {\n const size = fs.statSync(target).size;\n const bytes = Math.min(size, maxBytes);\n const buffer = Buffer.alloc(bytes);\n handle = fs.openSync(target, 'r');\n fs.readSync(handle, buffer, 0, bytes, size - bytes);\n return buffer.toString('utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n process.emitWarning(`Could not read RMUX log ${target}: ${errorMessage(error)}`);\n }\n return '';\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RmuxBackend.cjs","names":["runtimeEntry","getResultMaxBytes","readExitMetadata","RMUX","supervisorPaths","shellJoin","path","getLogMaxBytes","createRequire","createHash","NO_TERMINAL_INPUT_ENV","fs"],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { type Pane, RMUX, type Rmux } from '@rmux/sdk';\nimport {\n type CommandSpec,\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n runtimeEntry,\n type SupervisorPaths,\n shellJoin,\n supervisorPaths,\n writeCommandSpec,\n} from '../../schemas/runnerSpec.ts';\nimport { getLogMaxBytes, getResultMaxBytes } from '../../types/config.ts';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport type { ExitResult } from '../../types/spawner';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\n\nconst RMUX_BINARY_ENV = 'DOOMPI_RMUX_BINARY';\nconst POLL_MS = 100;\nconst LOG_DRAIN_TIMEOUT_MS = 2_000;\nconst LOG_DRAIN_POLL_MS = 25;\n/** Pane liveness, its exit status, and whether the session still exists, in one read. */\nconst PANE_STATE_FORMAT = '#{pane_dead}:#{pane_dead_status}:#{session_name}';\nconst STOP_GRACE_MS = 3_000;\nconst STOP_CLOSE_TIMEOUT_MS = 1_000;\nconst EXECUTABLE_MODE = 0o755;\nconst SESSION_PREFIX = 'doom-runner-';\nconst OWNED_TARGET_PATTERN = /^doom-runner-[A-Za-z0-9_-]+$/;\nconst SOCKET_HASH_LENGTH = 12;\nconst NON_INTERACTIVE_ENV = { NO_COLOR: '1', CI: '1' };\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rmux-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rmux-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rmux-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rmux-linux-x64',\n};\n\nexport class RmuxBackend implements IRmuxBackend {\n private clientPromise: Promise<Rmux | undefined> | undefined;\n private readonly runs = new Map<string, PtyRun>();\n private readonly completions = new Map<string, Promise<ExitResult>>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n\n this.paths.ensureDirectories();\n const target = `${SESSION_PREFIX}${request.id}`;\n const logPath = this.paths.logPathFor(request.id);\n const aux = this.auxPaths(request.id, request.sessionId);\n let created = false;\n let released = false;\n\n try {\n this.prepareFiles(request, logPath, aux);\n await rmux.cmd(\n 'new-session',\n '-d',\n '-s',\n target,\n '-c',\n request.cwd,\n process.execPath,\n runtimeEntry('runnerHost'),\n aux.spec,\n aux.gate,\n aux.exit,\n { check: true },\n );\n created = true;\n // No remain-on-exit: a finished pane ends its session, and the last session\n // ending stops the server, so a completed run leaves no process behind.\n await rmux.cmd('pipe-pane', '-t', target, this.logPipeCommand(logPath, aux.logDone), { check: true });\n\n const pane = rmux.session(target).pane(0, 0);\n const pid = await panePid(rmux, target);\n fs.writeFileSync(aux.gate, '', { mode: 0o600 });\n released = true;\n\n let buffering = true;\n const completion = this.monitor(rmux, pane, target, aux);\n this.trackCompletion(target, completion);\n const stop = (): Promise<boolean> => this.stop(target, pid);\n const handle: RunHandle = {\n id: request.id,\n name: request.name,\n pid,\n logPath,\n backend: 'rmux',\n backendTarget: target,\n output: () => (buffering ? readTail(logPath, getResultMaxBytes() * 2) : ''),\n completion: () => completion,\n detach: () => {\n buffering = false;\n },\n stop,\n };\n\n if (request.interactive) this.runs.set(request.name, this.interactiveRun(handle, pane, completion));\n void completion\n .finally(() => this.runs.delete(request.name))\n .catch((error: unknown) => process.emitWarning(`RMUX completion failed for ${target}: ${errorMessage(error)}`));\n return handle;\n } catch (error) {\n if (released) throw error;\n if (created) {\n await rmux\n .session(target)\n .kill()\n .catch((cleanupError: unknown) =>\n process.emitWarning(`Could not clean up failed RMUX launch ${target}: ${errorMessage(cleanupError)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n process.emitWarning(\n request.interactive\n ? `RMUX launch unavailable for interactive runner: ${errorMessage(error)}`\n : `RMUX launch unavailable, using supervised subprocess: ${errorMessage(error)}`,\n );\n return undefined;\n }\n }\n\n async stop(target: string, expectedPid: number): Promise<boolean> {\n if (!OWNED_TARGET_PATTERN.test(target) || !Number.isInteger(expectedPid) || expectedPid <= 0) return false;\n\n const rmux = await this.client();\n if (!rmux) return false;\n try {\n // A session that already ended took its process group with it.\n if (await sessionMissing(rmux, target)) return true;\n\n const pid = await panePid(rmux, target);\n if (pid !== expectedPid) return false;\n const signalled = signalPane(pid, target);\n if (signalled === 'gone') return true;\n if (signalled === 'failed') return false;\n\n if (await waitForPaneStop(rmux, target, STOP_GRACE_MS)) return true;\n if (await sessionMissing(rmux, target)) return true;\n\n // Revalidate after the grace period so a replacement pane is never closed.\n const currentPid = await panePid(rmux, target);\n if (currentPid !== expectedPid) return false;\n\n return closeOwnedSession(rmux, target);\n } catch (error) {\n process.emitWarning(`Could not stop RMUX target ${target}: ${errorMessage(error)}`);\n return false;\n }\n }\n\n async watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n const pane = rmux.session(target).pane(0, 0);\n try {\n await rmux.displayMessage('#{pane_id}', { target });\n } catch (error) {\n process.emitWarning(`Could not watch RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n }\n const completion = this.monitor(rmux, pane, target, this.auxPaths(id, sessionId));\n this.trackCompletion(target, completion);\n return completion;\n }\n\n async input(target: string, text: string): Promise<boolean> {\n const rmux = await this.client();\n if (!rmux) return false;\n const result = await rmux\n .session(target)\n .pane(0, 0)\n .sendText(text)\n .catch((error: unknown) => {\n process.emitWarning(`Could not send input to RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result !== undefined;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return readExitMetadata(this.auxPaths(id, sessionId).exit);\n }\n\n private trackCompletion(target: string, completion: Promise<ExitResult>): void {\n this.completions.set(target, completion);\n void completion\n .finally(() => this.completions.delete(target))\n .catch((error: unknown) => process.emitWarning(`RMUX monitor failed for ${target}: ${errorMessage(error)}`));\n }\n\n private client(): Promise<Rmux | undefined> {\n this.clientPromise ??= this.resolveClient();\n return this.clientPromise;\n }\n\n private async resolveClient(): Promise<Rmux | undefined> {\n const binaries = [bundledBinary(), 'rmux'].filter((value): value is string => value !== undefined);\n for (const binary of binaries) {\n try {\n if (binary !== 'rmux') fs.chmodSync(binary, EXECUTABLE_MODE);\n const client = new RMUX({ binary, socketName: socketName(this.paths.repositoryPath()) });\n await client.capabilities();\n return client;\n } catch (error) {\n process.emitWarning(`RMUX candidate ${binary} is unavailable: ${errorMessage(error)}`);\n // Try the next compatible candidate. Non-interactive calls can use the subprocess launcher.\n }\n }\n return undefined;\n }\n\n private prepareFiles(request: RmuxLaunchRequest, logPath: string, aux: SupervisorPaths): void {\n cleanupSupervisorFiles(aux);\n fs.writeFileSync(logPath, '', { mode: 0o600 });\n const env = environment(request.sessionId, request.interactive);\n // runnerHost supervises the command and watches the lifeline itself, so an\n // owner that dies mid-run is handled inside the pane rather than around it.\n const spec: CommandSpec = { command: request.command, cwd: request.cwd, env };\n writeCommandSpec(aux.spec, spec);\n }\n\n private auxPaths(id: string, sessionId?: string): SupervisorPaths {\n return supervisorPaths(this.paths.stateDirectory(sessionId), id);\n }\n\n private logPipeCommand(logPath: string, donePath: string): string {\n return shellJoin([\n process.execPath,\n runtimeEntry('logSink'),\n logPath,\n this.paths.rotatedLogPathFor(path.basename(logPath, '.log')),\n String(getLogMaxBytes()),\n donePath,\n ]);\n }\n\n private async monitor(rmux: Rmux, pane: Pane, target: string, aux: SupervisorPaths): Promise<ExitResult> {\n try {\n const paneCode = await waitForRunEnd(rmux, target, aux.exit);\n await waitForLogDrain(rmux, target, aux.logDone);\n return readExitMetadata(aux.exit) ?? { code: paneCode, signal: null };\n } finally {\n // The pane ends its own session, so this only covers a session that somehow\n // outlived its pane. Killing unconditionally would warn on every clean run.\n if (!(await sessionMissing(rmux, target).catch(() => false))) {\n await pane.server\n .session(target)\n .kill()\n .catch((error: unknown) =>\n process.emitWarning(`Could not clean up completed RMUX target ${target}: ${errorMessage(error)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n }\n }\n\n private interactiveRun(handle: RunHandle, pane: Pane, completion: Promise<ExitResult>): PtyRun {\n let screen = '';\n let active = true;\n const handlers = new Set<(data: string) => void>();\n const refresh = async (): Promise<void> => {\n while (active) {\n const next = await pane.captureText().catch((error: unknown) => {\n process.emitWarning(`Could not capture RMUX screen for ${handle.name}: ${errorMessage(error)}`);\n return screen;\n });\n if (next !== screen) {\n screen = next;\n for (const handler of handlers) handler(next);\n }\n await delay(POLL_MS);\n }\n };\n void refresh();\n void completion\n .finally(() => {\n active = false;\n })\n .catch((error: unknown) =>\n process.emitWarning(`RMUX interactive completion failed for ${handle.name}: ${errorMessage(error)}`),\n );\n return {\n ...handle,\n write: (text) => {\n void pane.sendText(text).catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n screen: () => screen,\n onData: (handler) => {\n handlers.add(handler);\n return () => handlers.delete(handler);\n },\n resize: (cols, rows) => {\n void pane\n .resize({ width: cols, height: rows })\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n };\n }\n}\n\nexport function rmuxPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const configured = process.env[RMUX_BINARY_ENV];\n if (configured !== undefined && configured !== '') return configured;\n const packageName = rmuxPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rmux');\n } catch (error) {\n process.emitWarning(`Bundled RMUX binary is unavailable for ${packageName}: ${errorMessage(error)}`);\n return undefined;\n }\n}\n\nfunction socketName(repositoryPath: string): string {\n const hash = createHash('sha256').update(repositoryPath).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return `${SESSION_PREFIX}${hash}`;\n}\n\nfunction environment(sessionId: string, interactive: boolean): Record<string, string> {\n const values: NodeJS.ProcessEnv = {\n ...process.env,\n ...(interactive ? {} : NON_INTERACTIVE_ENV),\n // Applied to interactive runs as well: a pane is a tty either way, and an\n // agent sending input to a prompt still has no way past a pager.\n ...NO_TERMINAL_INPUT_ENV,\n PI_SESSION_ID: sessionId,\n };\n if (!interactive) delete values['FORCE_COLOR'];\n return Object.fromEntries(\n Object.entries(values).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n}\n\nasync function panePid(rmux: Rmux, target: string): Promise<number> {\n const response = await rmux.displayMessage('#{pane_pid}', { target });\n const pid = Number(response['message']);\n if (!Number.isInteger(pid) || pid <= 0) throw new Error(`RMUX did not report a pane pid for ${target}`);\n return pid;\n}\n\n/**\n * Waits for the run to end, preferring the supervisor's own record of it.\n *\n * The sidecar is written before the command's pane can end, and it outlives the\n * server that the pane's exit takes down with it. Asking the server is the\n * fallback for a supervisor that died without recording anything.\n */\nasync function waitForRunEnd(rmux: Rmux, target: string, exitPath: string): Promise<number | null> {\n for (;;) {\n if (fs.existsSync(exitPath)) return null;\n const state = await readPaneExit(rmux, target);\n if (state.exited) return state.code;\n await delay(POLL_MS);\n }\n}\n\n/**\n * Gives the log sink a moment to mark the log complete.\n *\n * The sink dies with the session it was piped from, so once the session is gone\n * the marker is never coming and there is nothing left to wait for. Its writes\n * are synchronous, so the log itself is already on disk either way.\n */\nasync function waitForLogDrain(rmux: Rmux, target: string, donePath: string): Promise<void> {\n const deadline = Date.now() + LOG_DRAIN_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (fs.existsSync(donePath)) return;\n if (await sessionMissing(rmux, target)) return;\n await delay(LOG_DRAIN_POLL_MS);\n }\n}\n\nasync function closeOwnedSession(rmux: Rmux, target: string): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(result);\n };\n const timer = setTimeout(() => {\n process.emitWarning(`Timed out closing RMUX target ${target} after ${STOP_CLOSE_TIMEOUT_MS}ms`);\n finish(false);\n }, STOP_CLOSE_TIMEOUT_MS);\n void rmux\n .session(target)\n .kill()\n .then(\n () => finish(true),\n (error: unknown) => {\n process.emitWarning(`Could not close RMUX target ${target}: ${errorMessage(error)}`);\n finish(false);\n },\n );\n });\n}\n\nasync function waitForPaneStop(rmux: Rmux, target: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n if ((await readPaneExit(rmux, target)).exited) return true;\n } catch (error) {\n process.emitWarning(`Could not verify RMUX target ${target} stopped: ${errorMessage(error)}`);\n return false;\n }\n await delay(POLL_MS);\n }\n return false;\n}\n\n/**\n * One formatted read answers both questions this poll asks, so watching a run\n * costs the same single rmux call it always did.\n */\nasync function readPaneExit(rmux: Rmux, target: string): Promise<{ exited: boolean; code: number | null }> {\n let message: string;\n try {\n const response = await rmux.displayMessage(PANE_STATE_FORMAT, { target });\n message = typeof response['message'] === 'string' ? response['message'] : '';\n } catch {\n // The last session ending stops the server, so a server that cannot be\n // reached is the ordinary end of the last run rather than a fault.\n return { exited: true, code: null };\n }\n const [dead, status, ...rest] = message.trim().split(':');\n // A missing session formats as a live pane with no name rather than failing,\n // so the name is what says whether there is still anything to watch.\n if (rest.join(':') === '') return { exited: true, code: null };\n if (dead === '1' || dead === 'true') {\n const code = Number(status);\n return { exited: true, code: Number.isInteger(code) ? code : null };\n }\n return { exited: false, code: null };\n}\n\n/** A session that cannot be found, on a server that may itself be gone. */\nasync function sessionMissing(rmux: Rmux, target: string): Promise<boolean> {\n try {\n const probe = await rmux.cmd('has-session', '-t', target);\n return probe.returnCode !== 0;\n } catch {\n // No server to ask means no session to find.\n return true;\n }\n}\n\n/**\n * A pane that was already gone and a signal that could not be delivered are\n * opposite answers to \"is it stopped\", so they are reported apart.\n */\nfunction signalPane(pid: number, target: string): 'signalled' | 'gone' | 'failed' {\n try {\n process.kill(pid, 'SIGTERM');\n return 'signalled';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'gone';\n process.emitWarning(`Could not signal RMUX target ${target}: ${errorMessage(error)}`);\n return 'failed';\n }\n}\n\nfunction readTail(target: string, maxBytes: number): string {\n let handle: number | undefined;\n try {\n const size = fs.statSync(target).size;\n const bytes = Math.min(size, maxBytes);\n const buffer = Buffer.alloc(bytes);\n handle = fs.openSync(target, 'r');\n fs.readSync(handle, buffer, 0, bytes, size - bytes);\n return buffer.toString('utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n process.emitWarning(`Could not read RMUX log ${target}: ${errorMessage(error)}`);\n }\n return '';\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RmuxBackend.d.cts","names":[],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"mappings":";;;;;;cA0Ca,uBAAuB;mBAKL;UAJrB;mBACS;mBACA;EAEjB,YAA6B,OAAO;EAE9B,OAAO,SAAS,oBAAoB,QAAQ;EAiF5C,KAAK,gBAAgB,sBAAsB;EA6B3C,MAAM,YAAY,gBAAgB,qBAAqB,QAAQ;EAe/D,MAAM,gBAAgB,eAAe;EAc3C,IAAI,eAAe;EAInB,YAAY,YAAY,oBAAoB;UAIpC;UAOA;UAKM;UAgBN;UAUA;UAIA;UAWM;UAoBN;;iBA4CM,qBAAqB,kBAAkB"}
1
+ {"version":3,"file":"RmuxBackend.d.cts","names":[],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"mappings":";;;;;;cA2Ca,uBAAuB;mBAKL;UAJrB;mBACS;mBACA;EAEjB,YAA6B,OAAO;EAE9B,OAAO,SAAS,oBAAoB,QAAQ;EAiF5C,KAAK,gBAAgB,sBAAsB;EA6B3C,MAAM,YAAY,gBAAgB,qBAAqB,QAAQ;EAe/D,MAAM,gBAAgB,eAAe;EAc3C,IAAI,eAAe;EAInB,YAAY,YAAY,oBAAoB;UAIpC;UAOA;UAKM;UAgBN;UAUA;UAIA;UAWM;UAoBN;;iBA4CM,qBAAqB,kBAAkB"}
@@ -1 +1 @@
1
- {"version":3,"file":"RmuxBackend.d.mts","names":[],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"mappings":";;;;;;cA0Ca,uBAAuB;mBAKL;UAJrB;mBACS;mBACA;EAEjB,YAA6B,OAAO;EAE9B,OAAO,SAAS,oBAAoB,QAAQ;EAiF5C,KAAK,gBAAgB,sBAAsB;EA6B3C,MAAM,YAAY,gBAAgB,qBAAqB,QAAQ;EAe/D,MAAM,gBAAgB,eAAe;EAc3C,IAAI,eAAe;EAInB,YAAY,YAAY,oBAAoB;UAIpC;UAOA;UAKM;UAgBN;UAUA;UAIA;UAWM;UAoBN;;iBA4CM,qBAAqB,kBAAkB"}
1
+ {"version":3,"file":"RmuxBackend.d.mts","names":[],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"mappings":";;;;;;cA2Ca,uBAAuB;mBAKL;UAJrB;mBACS;mBACA;EAEjB,YAA6B,OAAO;EAE9B,OAAO,SAAS,oBAAoB,QAAQ;EAiF5C,KAAK,gBAAgB,sBAAsB;EA6B3C,MAAM,YAAY,gBAAgB,qBAAqB,QAAQ;EAe/D,MAAM,gBAAgB,eAAe;EAc3C,IAAI,eAAe;EAInB,YAAY,YAAY,oBAAoB;UAIpC;UAOA;UAKM;UAgBN;UAUA;UAIA;UAWM;UAoBN;;iBA4CM,qBAAqB,kBAAkB"}
@@ -1,2 +1,2 @@
1
- import{getLogMaxBytes as e,getResultMaxBytes as t}from"../../types/config.mjs";import{NO_TERMINAL_INPUT_ENV as n,cleanupSupervisorFiles as r,readExitMetadata as i,runtimeEntry as a,shellJoin as o,supervisorPaths as s,writeCommandSpec as c}from"../../schemas/runnerSpec.mjs";import{createRequire as l}from"node:module";import{createHash as u}from"node:crypto";import d from"node:fs";import f from"node:path";import{RMUX as p}from"@rmux/sdk";const m=1e3,h=`doom-runner-`,g=/^doom-runner-[A-Za-z0-9_-]+$/,_={NO_COLOR:`1`,CI:`1`},v={"darwin-arm64":`@agimon-ai/doompi-runner-rmux-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rmux-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rmux-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rmux-linux-x64`};var y=class{paths;clientPromise;runs=new Map;completions=new Map;constructor(e){this.paths=e}async launch(e){let n=await this.client();if(!n)return;this.paths.ensureDirectories();let i=`${h}${e.id}`,o=this.paths.logPathFor(e.id),s=this.auxPaths(e.id,e.sessionId),c=!1,l=!1;try{this.prepareFiles(e,o,s),await n.cmd(`new-session`,`-d`,`-s`,i,`-c`,e.cwd,process.execPath,a(`runnerHost`),s.spec,s.gate,s.exit,{check:!0}),c=!0,await n.cmd(`pipe-pane`,`-t`,i,this.logPipeCommand(o,s.logDone),{check:!0});let r=n.session(i).pane(0,0),u=await w(n,i);d.writeFileSync(s.gate,``,{mode:384}),l=!0;let f=!0,p=this.monitor(n,r,i,s);this.trackCompletion(i,p);let m={id:e.id,name:e.name,pid:u,logPath:o,backend:`rmux`,backendTarget:i,output:()=>f?M(o,t()*2):``,completion:()=>p,detach:()=>{f=!1},stop:()=>this.stop(i,u)};return e.interactive&&this.runs.set(e.name,this.interactiveRun(m,r,p)),p.finally(()=>this.runs.delete(e.name)).catch(e=>process.emitWarning(`RMUX completion failed for ${i}: ${P(e)}`)),m}catch(t){if(l)throw t;c&&await n.session(i).kill().catch(e=>process.emitWarning(`Could not clean up failed RMUX launch ${i}: ${P(e)}`)),r(s),process.emitWarning(e.interactive?`RMUX launch unavailable for interactive runner: ${P(t)}`:`RMUX launch unavailable, using supervised subprocess: ${P(t)}`);return}}async stop(e,t){if(!g.test(e)||!Number.isInteger(t)||t<=0)return!1;let n=await this.client();if(!n)return!1;try{if(await A(n,e))return!0;let r=await w(n,e);if(r!==t)return!1;let i=j(r,e);return i===`gone`?!0:i===`failed`?!1:await O(n,e,3e3)||await A(n,e)?!0:await w(n,e)===t&&D(n,e)}catch(t){return process.emitWarning(`Could not stop RMUX target ${e}: ${P(t)}`),!1}}async watch(e,t,n){let r=await this.client();if(!r)return;let i=r.session(t).pane(0,0);try{await r.displayMessage(`#{pane_id}`,{target:t})}catch(e){process.emitWarning(`Could not watch RMUX target ${t}: ${P(e)}`);return}let a=this.monitor(r,i,t,this.auxPaths(e,n));return this.trackCompletion(t,a),a}async input(e,t){let n=await this.client();return n?await n.session(e).pane(0,0).sendText(t).catch(t=>{process.emitWarning(`Could not send input to RMUX target ${e}: ${P(t)}`)})!==void 0:!1}get(e){return this.runs.get(e)}readOutcome(e,t){return i(this.auxPaths(e,t).exit)}trackCompletion(e,t){this.completions.set(e,t),t.finally(()=>this.completions.delete(e)).catch(t=>process.emitWarning(`RMUX monitor failed for ${e}: ${P(t)}`))}client(){return this.clientPromise??=this.resolveClient(),this.clientPromise}async resolveClient(){let e=[x(),`rmux`].filter(e=>e!==void 0);for(let t of e)try{t!==`rmux`&&d.chmodSync(t,493);let e=new p({binary:t,socketName:S(this.paths.repositoryPath())});return await e.capabilities(),e}catch(e){process.emitWarning(`RMUX candidate ${t} is unavailable: ${P(e)}`)}}prepareFiles(e,t,n){r(n),d.writeFileSync(t,``,{mode:384});let i=C(e.sessionId,e.interactive),a={command:e.command,cwd:e.cwd,env:i};c(n.spec,a)}auxPaths(e,t){return s(this.paths.stateDirectory(t),e)}logPipeCommand(t,n){return o([process.execPath,a(`logSink`),t,this.paths.rotatedLogPathFor(f.basename(t,`.log`)),String(e()),n])}async monitor(e,t,n,a){try{let t=await T(e,n,a.exit);return await E(e,n,a.logDone),i(a.exit)??{code:t,signal:null}}finally{await A(e,n).catch(()=>!1)||await t.server.session(n).kill().catch(e=>process.emitWarning(`Could not clean up completed RMUX target ${n}: ${P(e)}`)),r(a)}}interactiveRun(e,t,n){let r=``,i=!0,a=new Set;return(async()=>{for(;i;){let n=await t.captureText().catch(t=>(process.emitWarning(`Could not capture RMUX screen for ${e.name}: ${P(t)}`),r));if(n!==r){r=n;for(let e of a)e(n)}await N(100)}})(),n.finally(()=>{i=!1}).catch(t=>process.emitWarning(`RMUX interactive completion failed for ${e.name}: ${P(t)}`)),{...e,write:e=>{t.sendText(e).catch(e=>process.emitWarning(P(e)))},screen:()=>r,onData:e=>(a.add(e),()=>a.delete(e)),resize:(e,n)=>{t.resize({width:e,height:n}).catch(e=>process.emitWarning(P(e)))}}}};function b(e,t){let n=`${e}-${t}`,r=v[n];return r||process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${n}`),r}function x(){let e=b(process.platform,process.arch);if(e)try{let t=l(import.meta.url).resolve(`${e}/package.json`);return f.join(f.dirname(t),`vendor`,`bin`,`rmux`)}catch(t){process.emitWarning(`Bundled RMUX binary is unavailable for ${e}: ${P(t)}`);return}}function S(e){let t=u(`sha256`).update(e).digest(`hex`).slice(0,12);return`${h}${t}`}function C(e,t){let r={...process.env,...t?{}:_,...n,PI_SESSION_ID:e};return t||delete r.FORCE_COLOR,Object.fromEntries(Object.entries(r).filter(e=>e[1]!==void 0))}async function w(e,t){let n=await e.displayMessage(`#{pane_pid}`,{target:t}),r=Number(n.message);if(!Number.isInteger(r)||r<=0)throw Error(`RMUX did not report a pane pid for ${t}`);return r}async function T(e,t,n){for(;;){if(d.existsSync(n))return null;let r=await k(e,t);if(r.exited)return r.code;await N(100)}}async function E(e,t,n){let r=Date.now()+2e3;for(;Date.now()<r;){if(d.existsSync(n)||await A(e,t))return;await N(25)}}async function D(e,t){return new Promise(n=>{let r=!1,i=e=>{r||(r=!0,clearTimeout(a),n(e))},a=setTimeout(()=>{process.emitWarning(`Timed out closing RMUX target ${t} after ${m}ms`),i(!1)},m);e.session(t).kill().then(()=>i(!0),e=>{process.emitWarning(`Could not close RMUX target ${t}: ${P(e)}`),i(!1)})})}async function O(e,t,n){let r=Date.now()+n;for(;Date.now()<r;){try{if((await k(e,t)).exited)return!0}catch(e){return process.emitWarning(`Could not verify RMUX target ${t} stopped: ${P(e)}`),!1}await N(100)}return!1}async function k(e,t){let n;try{let r=await e.displayMessage(`#{pane_dead}:#{pane_dead_status}:#{session_name}`,{target:t});n=typeof r.message==`string`?r.message:``}catch{return{exited:!0,code:null}}let[r,i,...a]=n.trim().split(`:`);if(a.join(`:`)===``)return{exited:!0,code:null};if(r===`1`||r===`true`){let e=Number(i);return{exited:!0,code:Number.isInteger(e)?e:null}}return{exited:!1,code:null}}async function A(e,t){try{return(await e.cmd(`has-session`,`-t`,t)).returnCode!==0}catch{return!0}}function j(e,t){try{return process.kill(e,`SIGTERM`),`signalled`}catch(e){return e.code===`ESRCH`?`gone`:(process.emitWarning(`Could not signal RMUX target ${t}: ${P(e)}`),`failed`)}}function M(e,t){let n;try{let r=d.statSync(e).size,i=Math.min(r,t),a=Buffer.alloc(i);return n=d.openSync(e,`r`),d.readSync(n,a,0,i,r-i),a.toString(`utf8`)}catch(t){return t.code!==`ENOENT`&&process.emitWarning(`Could not read RMUX log ${e}: ${P(t)}`),``}finally{n!==void 0&&d.closeSync(n)}}function N(e){return new Promise(t=>{setTimeout(t,e).unref?.()})}function P(e){return e instanceof Error?e.message:String(e)}export{y as RmuxBackend,b as rmuxPackageForTarget};
1
+ import{getLogMaxBytes as e,getResultMaxBytes as t}from"../../types/config.mjs";import{NO_TERMINAL_INPUT_ENV as n,cleanupSupervisorFiles as r,readExitMetadata as i,runtimeEntry as a,shellJoin as o,supervisorPaths as s,writeCommandSpec as c}from"../../schemas/runnerSpec.mjs";import{createRequire as l}from"node:module";import{createHash as u}from"node:crypto";import d from"node:fs";import f from"node:path";import{RMUX as p}from"@rmux/sdk";const m=1e3,h=`doom-runner-`,g=/^doom-runner-[A-Za-z0-9_-]+$/,_={NO_COLOR:`1`,CI:`1`},v={"darwin-arm64":`@agimon-ai/doompi-runner-rmux-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rmux-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rmux-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rmux-linux-x64`};var y=class{paths;clientPromise;runs=new Map;completions=new Map;constructor(e){this.paths=e}async launch(e){let n=await this.client();if(!n)return;this.paths.ensureDirectories();let i=`${h}${e.id}`,o=this.paths.logPathFor(e.id),s=this.auxPaths(e.id,e.sessionId),c=!1,l=!1;try{this.prepareFiles(e,o,s),await n.cmd(`new-session`,`-d`,`-s`,i,`-c`,e.cwd,process.execPath,a(`runnerHost`),s.spec,s.gate,s.exit,{check:!0}),c=!0,await n.cmd(`pipe-pane`,`-t`,i,this.logPipeCommand(o,s.logDone),{check:!0});let r=n.session(i).pane(0,0),u=await w(n,i);d.writeFileSync(s.gate,``,{mode:384}),l=!0;let f=!0,p=this.monitor(n,r,i,s);this.trackCompletion(i,p);let m={id:e.id,name:e.name,pid:u,logPath:o,backend:`rmux`,backendTarget:i,output:()=>f?M(o,t()*2):``,completion:()=>p,detach:()=>{f=!1},stop:()=>this.stop(i,u)};return e.interactive&&this.runs.set(e.name,this.interactiveRun(m,r,p)),p.finally(()=>this.runs.delete(e.name)).catch(e=>process.emitWarning(`RMUX completion failed for ${i}: ${P(e)}`)),m}catch(t){if(l)throw t;c&&await n.session(i).kill().catch(e=>process.emitWarning(`Could not clean up failed RMUX launch ${i}: ${P(e)}`)),r(s),process.emitWarning(e.interactive?`RMUX launch unavailable for interactive runner: ${P(t)}`:`RMUX launch unavailable, using supervised subprocess: ${P(t)}`);return}}async stop(e,t){if(!g.test(e)||!Number.isInteger(t)||t<=0)return!1;let n=await this.client();if(!n)return!1;try{if(await A(n,e))return!0;let r=await w(n,e);if(r!==t)return!1;let i=j(r,e);return i===`gone`?!0:i===`failed`?!1:await O(n,e,3e3)||await A(n,e)?!0:await w(n,e)===t&&D(n,e)}catch(t){return process.emitWarning(`Could not stop RMUX target ${e}: ${P(t)}`),!1}}async watch(e,t,n){let r=await this.client();if(!r)return;let i=r.session(t).pane(0,0);try{await r.displayMessage(`#{pane_id}`,{target:t})}catch(e){process.emitWarning(`Could not watch RMUX target ${t}: ${P(e)}`);return}let a=this.monitor(r,i,t,this.auxPaths(e,n));return this.trackCompletion(t,a),a}async input(e,t){let n=await this.client();return n?await n.session(e).pane(0,0).sendText(t).catch(t=>{process.emitWarning(`Could not send input to RMUX target ${e}: ${P(t)}`)})!==void 0:!1}get(e){return this.runs.get(e)}readOutcome(e,t){return i(this.auxPaths(e,t).exit)}trackCompletion(e,t){this.completions.set(e,t),t.finally(()=>this.completions.delete(e)).catch(t=>process.emitWarning(`RMUX monitor failed for ${e}: ${P(t)}`))}client(){return this.clientPromise??=this.resolveClient(),this.clientPromise}async resolveClient(){let e=[x(),`rmux`].filter(e=>e!==void 0);for(let t of e)try{t!==`rmux`&&d.chmodSync(t,493);let e=new p({binary:t,socketName:S(this.paths.repositoryPath())});return await e.capabilities(),e}catch(e){process.emitWarning(`RMUX candidate ${t} is unavailable: ${P(e)}`)}}prepareFiles(e,t,n){r(n),d.writeFileSync(t,``,{mode:384});let i=C(e.sessionId,e.interactive),a={command:e.command,cwd:e.cwd,env:i};c(n.spec,a)}auxPaths(e,t){return s(this.paths.stateDirectory(t),e)}logPipeCommand(t,n){return o([process.execPath,a(`logSink`),t,this.paths.rotatedLogPathFor(f.basename(t,`.log`)),String(e()),n])}async monitor(e,t,n,a){try{let t=await T(e,n,a.exit);return await E(e,n,a.logDone),i(a.exit)??{code:t,signal:null}}finally{await A(e,n).catch(()=>!1)||await t.server.session(n).kill().catch(e=>process.emitWarning(`Could not clean up completed RMUX target ${n}: ${P(e)}`)),r(a)}}interactiveRun(e,t,n){let r=``,i=!0,a=new Set;return(async()=>{for(;i;){let n=await t.captureText().catch(t=>(process.emitWarning(`Could not capture RMUX screen for ${e.name}: ${P(t)}`),r));if(n!==r){r=n;for(let e of a)e(n)}await N(100)}})(),n.finally(()=>{i=!1}).catch(t=>process.emitWarning(`RMUX interactive completion failed for ${e.name}: ${P(t)}`)),{...e,write:e=>{t.sendText(e).catch(e=>process.emitWarning(P(e)))},screen:()=>r,onData:e=>(a.add(e),()=>a.delete(e)),resize:(e,n)=>{t.resize({width:e,height:n}).catch(e=>process.emitWarning(P(e)))}}}};function b(e,t){let n=`${e}-${t}`,r=v[n];return r||process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${n}`),r}function x(){let e=process.env.DOOMPI_RMUX_BINARY;if(e!==void 0&&e!==``)return e;let t=b(process.platform,process.arch);if(t)try{let e=l(import.meta.url).resolve(`${t}/package.json`);return f.join(f.dirname(e),`vendor`,`bin`,`rmux`)}catch(e){process.emitWarning(`Bundled RMUX binary is unavailable for ${t}: ${P(e)}`);return}}function S(e){let t=u(`sha256`).update(e).digest(`hex`).slice(0,12);return`${h}${t}`}function C(e,t){let r={...process.env,...t?{}:_,...n,PI_SESSION_ID:e};return t||delete r.FORCE_COLOR,Object.fromEntries(Object.entries(r).filter(e=>e[1]!==void 0))}async function w(e,t){let n=await e.displayMessage(`#{pane_pid}`,{target:t}),r=Number(n.message);if(!Number.isInteger(r)||r<=0)throw Error(`RMUX did not report a pane pid for ${t}`);return r}async function T(e,t,n){for(;;){if(d.existsSync(n))return null;let r=await k(e,t);if(r.exited)return r.code;await N(100)}}async function E(e,t,n){let r=Date.now()+2e3;for(;Date.now()<r;){if(d.existsSync(n)||await A(e,t))return;await N(25)}}async function D(e,t){return new Promise(n=>{let r=!1,i=e=>{r||(r=!0,clearTimeout(a),n(e))},a=setTimeout(()=>{process.emitWarning(`Timed out closing RMUX target ${t} after ${m}ms`),i(!1)},m);e.session(t).kill().then(()=>i(!0),e=>{process.emitWarning(`Could not close RMUX target ${t}: ${P(e)}`),i(!1)})})}async function O(e,t,n){let r=Date.now()+n;for(;Date.now()<r;){try{if((await k(e,t)).exited)return!0}catch(e){return process.emitWarning(`Could not verify RMUX target ${t} stopped: ${P(e)}`),!1}await N(100)}return!1}async function k(e,t){let n;try{let r=await e.displayMessage(`#{pane_dead}:#{pane_dead_status}:#{session_name}`,{target:t});n=typeof r.message==`string`?r.message:``}catch{return{exited:!0,code:null}}let[r,i,...a]=n.trim().split(`:`);if(a.join(`:`)===``)return{exited:!0,code:null};if(r===`1`||r===`true`){let e=Number(i);return{exited:!0,code:Number.isInteger(e)?e:null}}return{exited:!1,code:null}}async function A(e,t){try{return(await e.cmd(`has-session`,`-t`,t)).returnCode!==0}catch{return!0}}function j(e,t){try{return process.kill(e,`SIGTERM`),`signalled`}catch(e){return e.code===`ESRCH`?`gone`:(process.emitWarning(`Could not signal RMUX target ${t}: ${P(e)}`),`failed`)}}function M(e,t){let n;try{let r=d.statSync(e).size,i=Math.min(r,t),a=Buffer.alloc(i);return n=d.openSync(e,`r`),d.readSync(n,a,0,i,r-i),a.toString(`utf8`)}catch(t){return t.code!==`ENOENT`&&process.emitWarning(`Could not read RMUX log ${e}: ${P(t)}`),``}finally{n!==void 0&&d.closeSync(n)}}function N(e){return new Promise(t=>{setTimeout(t,e).unref?.()})}function P(e){return e instanceof Error?e.message:String(e)}export{y as RmuxBackend,b as rmuxPackageForTarget};
2
2
  //# sourceMappingURL=RmuxBackend.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RmuxBackend.mjs","names":[],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { type Pane, RMUX, type Rmux } from '@rmux/sdk';\nimport {\n type CommandSpec,\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n runtimeEntry,\n type SupervisorPaths,\n shellJoin,\n supervisorPaths,\n writeCommandSpec,\n} from '../../schemas/runnerSpec.ts';\nimport { getLogMaxBytes, getResultMaxBytes } from '../../types/config.ts';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport type { ExitResult } from '../../types/spawner';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\n\nconst POLL_MS = 100;\nconst LOG_DRAIN_TIMEOUT_MS = 2_000;\nconst LOG_DRAIN_POLL_MS = 25;\n/** Pane liveness, its exit status, and whether the session still exists, in one read. */\nconst PANE_STATE_FORMAT = '#{pane_dead}:#{pane_dead_status}:#{session_name}';\nconst STOP_GRACE_MS = 3_000;\nconst STOP_CLOSE_TIMEOUT_MS = 1_000;\nconst EXECUTABLE_MODE = 0o755;\nconst SESSION_PREFIX = 'doom-runner-';\nconst OWNED_TARGET_PATTERN = /^doom-runner-[A-Za-z0-9_-]+$/;\nconst SOCKET_HASH_LENGTH = 12;\nconst NON_INTERACTIVE_ENV = { NO_COLOR: '1', CI: '1' };\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rmux-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rmux-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rmux-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rmux-linux-x64',\n};\n\nexport class RmuxBackend implements IRmuxBackend {\n private clientPromise: Promise<Rmux | undefined> | undefined;\n private readonly runs = new Map<string, PtyRun>();\n private readonly completions = new Map<string, Promise<ExitResult>>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n\n this.paths.ensureDirectories();\n const target = `${SESSION_PREFIX}${request.id}`;\n const logPath = this.paths.logPathFor(request.id);\n const aux = this.auxPaths(request.id, request.sessionId);\n let created = false;\n let released = false;\n\n try {\n this.prepareFiles(request, logPath, aux);\n await rmux.cmd(\n 'new-session',\n '-d',\n '-s',\n target,\n '-c',\n request.cwd,\n process.execPath,\n runtimeEntry('runnerHost'),\n aux.spec,\n aux.gate,\n aux.exit,\n { check: true },\n );\n created = true;\n // No remain-on-exit: a finished pane ends its session, and the last session\n // ending stops the server, so a completed run leaves no process behind.\n await rmux.cmd('pipe-pane', '-t', target, this.logPipeCommand(logPath, aux.logDone), { check: true });\n\n const pane = rmux.session(target).pane(0, 0);\n const pid = await panePid(rmux, target);\n fs.writeFileSync(aux.gate, '', { mode: 0o600 });\n released = true;\n\n let buffering = true;\n const completion = this.monitor(rmux, pane, target, aux);\n this.trackCompletion(target, completion);\n const stop = (): Promise<boolean> => this.stop(target, pid);\n const handle: RunHandle = {\n id: request.id,\n name: request.name,\n pid,\n logPath,\n backend: 'rmux',\n backendTarget: target,\n output: () => (buffering ? readTail(logPath, getResultMaxBytes() * 2) : ''),\n completion: () => completion,\n detach: () => {\n buffering = false;\n },\n stop,\n };\n\n if (request.interactive) this.runs.set(request.name, this.interactiveRun(handle, pane, completion));\n void completion\n .finally(() => this.runs.delete(request.name))\n .catch((error: unknown) => process.emitWarning(`RMUX completion failed for ${target}: ${errorMessage(error)}`));\n return handle;\n } catch (error) {\n if (released) throw error;\n if (created) {\n await rmux\n .session(target)\n .kill()\n .catch((cleanupError: unknown) =>\n process.emitWarning(`Could not clean up failed RMUX launch ${target}: ${errorMessage(cleanupError)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n process.emitWarning(\n request.interactive\n ? `RMUX launch unavailable for interactive runner: ${errorMessage(error)}`\n : `RMUX launch unavailable, using supervised subprocess: ${errorMessage(error)}`,\n );\n return undefined;\n }\n }\n\n async stop(target: string, expectedPid: number): Promise<boolean> {\n if (!OWNED_TARGET_PATTERN.test(target) || !Number.isInteger(expectedPid) || expectedPid <= 0) return false;\n\n const rmux = await this.client();\n if (!rmux) return false;\n try {\n // A session that already ended took its process group with it.\n if (await sessionMissing(rmux, target)) return true;\n\n const pid = await panePid(rmux, target);\n if (pid !== expectedPid) return false;\n const signalled = signalPane(pid, target);\n if (signalled === 'gone') return true;\n if (signalled === 'failed') return false;\n\n if (await waitForPaneStop(rmux, target, STOP_GRACE_MS)) return true;\n if (await sessionMissing(rmux, target)) return true;\n\n // Revalidate after the grace period so a replacement pane is never closed.\n const currentPid = await panePid(rmux, target);\n if (currentPid !== expectedPid) return false;\n\n return closeOwnedSession(rmux, target);\n } catch (error) {\n process.emitWarning(`Could not stop RMUX target ${target}: ${errorMessage(error)}`);\n return false;\n }\n }\n\n async watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n const pane = rmux.session(target).pane(0, 0);\n try {\n await rmux.displayMessage('#{pane_id}', { target });\n } catch (error) {\n process.emitWarning(`Could not watch RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n }\n const completion = this.monitor(rmux, pane, target, this.auxPaths(id, sessionId));\n this.trackCompletion(target, completion);\n return completion;\n }\n\n async input(target: string, text: string): Promise<boolean> {\n const rmux = await this.client();\n if (!rmux) return false;\n const result = await rmux\n .session(target)\n .pane(0, 0)\n .sendText(text)\n .catch((error: unknown) => {\n process.emitWarning(`Could not send input to RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result !== undefined;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return readExitMetadata(this.auxPaths(id, sessionId).exit);\n }\n\n private trackCompletion(target: string, completion: Promise<ExitResult>): void {\n this.completions.set(target, completion);\n void completion\n .finally(() => this.completions.delete(target))\n .catch((error: unknown) => process.emitWarning(`RMUX monitor failed for ${target}: ${errorMessage(error)}`));\n }\n\n private client(): Promise<Rmux | undefined> {\n this.clientPromise ??= this.resolveClient();\n return this.clientPromise;\n }\n\n private async resolveClient(): Promise<Rmux | undefined> {\n const binaries = [bundledBinary(), 'rmux'].filter((value): value is string => value !== undefined);\n for (const binary of binaries) {\n try {\n if (binary !== 'rmux') fs.chmodSync(binary, EXECUTABLE_MODE);\n const client = new RMUX({ binary, socketName: socketName(this.paths.repositoryPath()) });\n await client.capabilities();\n return client;\n } catch (error) {\n process.emitWarning(`RMUX candidate ${binary} is unavailable: ${errorMessage(error)}`);\n // Try the next compatible candidate. Non-interactive calls can use the subprocess launcher.\n }\n }\n return undefined;\n }\n\n private prepareFiles(request: RmuxLaunchRequest, logPath: string, aux: SupervisorPaths): void {\n cleanupSupervisorFiles(aux);\n fs.writeFileSync(logPath, '', { mode: 0o600 });\n const env = environment(request.sessionId, request.interactive);\n // runnerHost supervises the command and watches the lifeline itself, so an\n // owner that dies mid-run is handled inside the pane rather than around it.\n const spec: CommandSpec = { command: request.command, cwd: request.cwd, env };\n writeCommandSpec(aux.spec, spec);\n }\n\n private auxPaths(id: string, sessionId?: string): SupervisorPaths {\n return supervisorPaths(this.paths.stateDirectory(sessionId), id);\n }\n\n private logPipeCommand(logPath: string, donePath: string): string {\n return shellJoin([\n process.execPath,\n runtimeEntry('logSink'),\n logPath,\n this.paths.rotatedLogPathFor(path.basename(logPath, '.log')),\n String(getLogMaxBytes()),\n donePath,\n ]);\n }\n\n private async monitor(rmux: Rmux, pane: Pane, target: string, aux: SupervisorPaths): Promise<ExitResult> {\n try {\n const paneCode = await waitForRunEnd(rmux, target, aux.exit);\n await waitForLogDrain(rmux, target, aux.logDone);\n return readExitMetadata(aux.exit) ?? { code: paneCode, signal: null };\n } finally {\n // The pane ends its own session, so this only covers a session that somehow\n // outlived its pane. Killing unconditionally would warn on every clean run.\n if (!(await sessionMissing(rmux, target).catch(() => false))) {\n await pane.server\n .session(target)\n .kill()\n .catch((error: unknown) =>\n process.emitWarning(`Could not clean up completed RMUX target ${target}: ${errorMessage(error)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n }\n }\n\n private interactiveRun(handle: RunHandle, pane: Pane, completion: Promise<ExitResult>): PtyRun {\n let screen = '';\n let active = true;\n const handlers = new Set<(data: string) => void>();\n const refresh = async (): Promise<void> => {\n while (active) {\n const next = await pane.captureText().catch((error: unknown) => {\n process.emitWarning(`Could not capture RMUX screen for ${handle.name}: ${errorMessage(error)}`);\n return screen;\n });\n if (next !== screen) {\n screen = next;\n for (const handler of handlers) handler(next);\n }\n await delay(POLL_MS);\n }\n };\n void refresh();\n void completion\n .finally(() => {\n active = false;\n })\n .catch((error: unknown) =>\n process.emitWarning(`RMUX interactive completion failed for ${handle.name}: ${errorMessage(error)}`),\n );\n return {\n ...handle,\n write: (text) => {\n void pane.sendText(text).catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n screen: () => screen,\n onData: (handler) => {\n handlers.add(handler);\n return () => handlers.delete(handler);\n },\n resize: (cols, rows) => {\n void pane\n .resize({ width: cols, height: rows })\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n };\n }\n}\n\nexport function rmuxPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rmuxPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rmux');\n } catch (error) {\n process.emitWarning(`Bundled RMUX binary is unavailable for ${packageName}: ${errorMessage(error)}`);\n return undefined;\n }\n}\n\nfunction socketName(repositoryPath: string): string {\n const hash = createHash('sha256').update(repositoryPath).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return `${SESSION_PREFIX}${hash}`;\n}\n\nfunction environment(sessionId: string, interactive: boolean): Record<string, string> {\n const values: NodeJS.ProcessEnv = {\n ...process.env,\n ...(interactive ? {} : NON_INTERACTIVE_ENV),\n // Applied to interactive runs as well: a pane is a tty either way, and an\n // agent sending input to a prompt still has no way past a pager.\n ...NO_TERMINAL_INPUT_ENV,\n PI_SESSION_ID: sessionId,\n };\n if (!interactive) delete values['FORCE_COLOR'];\n return Object.fromEntries(\n Object.entries(values).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n}\n\nasync function panePid(rmux: Rmux, target: string): Promise<number> {\n const response = await rmux.displayMessage('#{pane_pid}', { target });\n const pid = Number(response['message']);\n if (!Number.isInteger(pid) || pid <= 0) throw new Error(`RMUX did not report a pane pid for ${target}`);\n return pid;\n}\n\n/**\n * Waits for the run to end, preferring the supervisor's own record of it.\n *\n * The sidecar is written before the command's pane can end, and it outlives the\n * server that the pane's exit takes down with it. Asking the server is the\n * fallback for a supervisor that died without recording anything.\n */\nasync function waitForRunEnd(rmux: Rmux, target: string, exitPath: string): Promise<number | null> {\n for (;;) {\n if (fs.existsSync(exitPath)) return null;\n const state = await readPaneExit(rmux, target);\n if (state.exited) return state.code;\n await delay(POLL_MS);\n }\n}\n\n/**\n * Gives the log sink a moment to mark the log complete.\n *\n * The sink dies with the session it was piped from, so once the session is gone\n * the marker is never coming and there is nothing left to wait for. Its writes\n * are synchronous, so the log itself is already on disk either way.\n */\nasync function waitForLogDrain(rmux: Rmux, target: string, donePath: string): Promise<void> {\n const deadline = Date.now() + LOG_DRAIN_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (fs.existsSync(donePath)) return;\n if (await sessionMissing(rmux, target)) return;\n await delay(LOG_DRAIN_POLL_MS);\n }\n}\n\nasync function closeOwnedSession(rmux: Rmux, target: string): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(result);\n };\n const timer = setTimeout(() => {\n process.emitWarning(`Timed out closing RMUX target ${target} after ${STOP_CLOSE_TIMEOUT_MS}ms`);\n finish(false);\n }, STOP_CLOSE_TIMEOUT_MS);\n void rmux\n .session(target)\n .kill()\n .then(\n () => finish(true),\n (error: unknown) => {\n process.emitWarning(`Could not close RMUX target ${target}: ${errorMessage(error)}`);\n finish(false);\n },\n );\n });\n}\n\nasync function waitForPaneStop(rmux: Rmux, target: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n if ((await readPaneExit(rmux, target)).exited) return true;\n } catch (error) {\n process.emitWarning(`Could not verify RMUX target ${target} stopped: ${errorMessage(error)}`);\n return false;\n }\n await delay(POLL_MS);\n }\n return false;\n}\n\n/**\n * One formatted read answers both questions this poll asks, so watching a run\n * costs the same single rmux call it always did.\n */\nasync function readPaneExit(rmux: Rmux, target: string): Promise<{ exited: boolean; code: number | null }> {\n let message: string;\n try {\n const response = await rmux.displayMessage(PANE_STATE_FORMAT, { target });\n message = typeof response['message'] === 'string' ? response['message'] : '';\n } catch {\n // The last session ending stops the server, so a server that cannot be\n // reached is the ordinary end of the last run rather than a fault.\n return { exited: true, code: null };\n }\n const [dead, status, ...rest] = message.trim().split(':');\n // A missing session formats as a live pane with no name rather than failing,\n // so the name is what says whether there is still anything to watch.\n if (rest.join(':') === '') return { exited: true, code: null };\n if (dead === '1' || dead === 'true') {\n const code = Number(status);\n return { exited: true, code: Number.isInteger(code) ? code : null };\n }\n return { exited: false, code: null };\n}\n\n/** A session that cannot be found, on a server that may itself be gone. */\nasync function sessionMissing(rmux: Rmux, target: string): Promise<boolean> {\n try {\n const probe = await rmux.cmd('has-session', '-t', target);\n return probe.returnCode !== 0;\n } catch {\n // No server to ask means no session to find.\n return true;\n }\n}\n\n/**\n * A pane that was already gone and a signal that could not be delivered are\n * opposite answers to \"is it stopped\", so they are reported apart.\n */\nfunction signalPane(pid: number, target: string): 'signalled' | 'gone' | 'failed' {\n try {\n process.kill(pid, 'SIGTERM');\n return 'signalled';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'gone';\n process.emitWarning(`Could not signal RMUX target ${target}: ${errorMessage(error)}`);\n return 'failed';\n }\n}\n\nfunction readTail(target: string, maxBytes: number): string {\n let handle: number | undefined;\n try {\n const size = fs.statSync(target).size;\n const bytes = Math.min(size, maxBytes);\n const buffer = Buffer.alloc(bytes);\n handle = fs.openSync(target, 'r');\n fs.readSync(handle, buffer, 0, bytes, size - bytes);\n return buffer.toString('utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n process.emitWarning(`Could not read RMUX log ${target}: ${errorMessage(error)}`);\n }\n return '';\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RmuxBackend.mjs","names":[],"sources":["../../../../src/adapters/RmuxBackend/RmuxBackend.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { type Pane, RMUX, type Rmux } from '@rmux/sdk';\nimport {\n type CommandSpec,\n cleanupSupervisorFiles,\n NO_TERMINAL_INPUT_ENV,\n readExitMetadata,\n runtimeEntry,\n type SupervisorPaths,\n shellJoin,\n supervisorPaths,\n writeCommandSpec,\n} from '../../schemas/runnerSpec.ts';\nimport { getLogMaxBytes, getResultMaxBytes } from '../../types/config.ts';\nimport type { RunHandle } from '../../types/launcher';\nimport type { PtyRun } from '../../types/ptyHost';\nimport type { IRunnerPaths } from '../../services/RunnerPaths/types';\nimport type { ExitResult } from '../../types/spawner';\nimport type { IRmuxBackend, RmuxLaunchRequest } from '../../types/rmuxBackend';\n\nconst RMUX_BINARY_ENV = 'DOOMPI_RMUX_BINARY';\nconst POLL_MS = 100;\nconst LOG_DRAIN_TIMEOUT_MS = 2_000;\nconst LOG_DRAIN_POLL_MS = 25;\n/** Pane liveness, its exit status, and whether the session still exists, in one read. */\nconst PANE_STATE_FORMAT = '#{pane_dead}:#{pane_dead_status}:#{session_name}';\nconst STOP_GRACE_MS = 3_000;\nconst STOP_CLOSE_TIMEOUT_MS = 1_000;\nconst EXECUTABLE_MODE = 0o755;\nconst SESSION_PREFIX = 'doom-runner-';\nconst OWNED_TARGET_PATTERN = /^doom-runner-[A-Za-z0-9_-]+$/;\nconst SOCKET_HASH_LENGTH = 12;\nconst NON_INTERACTIVE_ENV = { NO_COLOR: '1', CI: '1' };\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rmux-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rmux-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rmux-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rmux-linux-x64',\n};\n\nexport class RmuxBackend implements IRmuxBackend {\n private clientPromise: Promise<Rmux | undefined> | undefined;\n private readonly runs = new Map<string, PtyRun>();\n private readonly completions = new Map<string, Promise<ExitResult>>();\n\n constructor(private readonly paths: IRunnerPaths) {}\n\n async launch(request: RmuxLaunchRequest): Promise<RunHandle | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n\n this.paths.ensureDirectories();\n const target = `${SESSION_PREFIX}${request.id}`;\n const logPath = this.paths.logPathFor(request.id);\n const aux = this.auxPaths(request.id, request.sessionId);\n let created = false;\n let released = false;\n\n try {\n this.prepareFiles(request, logPath, aux);\n await rmux.cmd(\n 'new-session',\n '-d',\n '-s',\n target,\n '-c',\n request.cwd,\n process.execPath,\n runtimeEntry('runnerHost'),\n aux.spec,\n aux.gate,\n aux.exit,\n { check: true },\n );\n created = true;\n // No remain-on-exit: a finished pane ends its session, and the last session\n // ending stops the server, so a completed run leaves no process behind.\n await rmux.cmd('pipe-pane', '-t', target, this.logPipeCommand(logPath, aux.logDone), { check: true });\n\n const pane = rmux.session(target).pane(0, 0);\n const pid = await panePid(rmux, target);\n fs.writeFileSync(aux.gate, '', { mode: 0o600 });\n released = true;\n\n let buffering = true;\n const completion = this.monitor(rmux, pane, target, aux);\n this.trackCompletion(target, completion);\n const stop = (): Promise<boolean> => this.stop(target, pid);\n const handle: RunHandle = {\n id: request.id,\n name: request.name,\n pid,\n logPath,\n backend: 'rmux',\n backendTarget: target,\n output: () => (buffering ? readTail(logPath, getResultMaxBytes() * 2) : ''),\n completion: () => completion,\n detach: () => {\n buffering = false;\n },\n stop,\n };\n\n if (request.interactive) this.runs.set(request.name, this.interactiveRun(handle, pane, completion));\n void completion\n .finally(() => this.runs.delete(request.name))\n .catch((error: unknown) => process.emitWarning(`RMUX completion failed for ${target}: ${errorMessage(error)}`));\n return handle;\n } catch (error) {\n if (released) throw error;\n if (created) {\n await rmux\n .session(target)\n .kill()\n .catch((cleanupError: unknown) =>\n process.emitWarning(`Could not clean up failed RMUX launch ${target}: ${errorMessage(cleanupError)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n process.emitWarning(\n request.interactive\n ? `RMUX launch unavailable for interactive runner: ${errorMessage(error)}`\n : `RMUX launch unavailable, using supervised subprocess: ${errorMessage(error)}`,\n );\n return undefined;\n }\n }\n\n async stop(target: string, expectedPid: number): Promise<boolean> {\n if (!OWNED_TARGET_PATTERN.test(target) || !Number.isInteger(expectedPid) || expectedPid <= 0) return false;\n\n const rmux = await this.client();\n if (!rmux) return false;\n try {\n // A session that already ended took its process group with it.\n if (await sessionMissing(rmux, target)) return true;\n\n const pid = await panePid(rmux, target);\n if (pid !== expectedPid) return false;\n const signalled = signalPane(pid, target);\n if (signalled === 'gone') return true;\n if (signalled === 'failed') return false;\n\n if (await waitForPaneStop(rmux, target, STOP_GRACE_MS)) return true;\n if (await sessionMissing(rmux, target)) return true;\n\n // Revalidate after the grace period so a replacement pane is never closed.\n const currentPid = await panePid(rmux, target);\n if (currentPid !== expectedPid) return false;\n\n return closeOwnedSession(rmux, target);\n } catch (error) {\n process.emitWarning(`Could not stop RMUX target ${target}: ${errorMessage(error)}`);\n return false;\n }\n }\n\n async watch(id: string, target: string, sessionId?: string): Promise<ExitResult | undefined> {\n const rmux = await this.client();\n if (!rmux) return undefined;\n const pane = rmux.session(target).pane(0, 0);\n try {\n await rmux.displayMessage('#{pane_id}', { target });\n } catch (error) {\n process.emitWarning(`Could not watch RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n }\n const completion = this.monitor(rmux, pane, target, this.auxPaths(id, sessionId));\n this.trackCompletion(target, completion);\n return completion;\n }\n\n async input(target: string, text: string): Promise<boolean> {\n const rmux = await this.client();\n if (!rmux) return false;\n const result = await rmux\n .session(target)\n .pane(0, 0)\n .sendText(text)\n .catch((error: unknown) => {\n process.emitWarning(`Could not send input to RMUX target ${target}: ${errorMessage(error)}`);\n return undefined;\n });\n return result !== undefined;\n }\n\n get(name: string): PtyRun | undefined {\n return this.runs.get(name);\n }\n\n readOutcome(id: string, sessionId: string): ExitResult | undefined {\n return readExitMetadata(this.auxPaths(id, sessionId).exit);\n }\n\n private trackCompletion(target: string, completion: Promise<ExitResult>): void {\n this.completions.set(target, completion);\n void completion\n .finally(() => this.completions.delete(target))\n .catch((error: unknown) => process.emitWarning(`RMUX monitor failed for ${target}: ${errorMessage(error)}`));\n }\n\n private client(): Promise<Rmux | undefined> {\n this.clientPromise ??= this.resolveClient();\n return this.clientPromise;\n }\n\n private async resolveClient(): Promise<Rmux | undefined> {\n const binaries = [bundledBinary(), 'rmux'].filter((value): value is string => value !== undefined);\n for (const binary of binaries) {\n try {\n if (binary !== 'rmux') fs.chmodSync(binary, EXECUTABLE_MODE);\n const client = new RMUX({ binary, socketName: socketName(this.paths.repositoryPath()) });\n await client.capabilities();\n return client;\n } catch (error) {\n process.emitWarning(`RMUX candidate ${binary} is unavailable: ${errorMessage(error)}`);\n // Try the next compatible candidate. Non-interactive calls can use the subprocess launcher.\n }\n }\n return undefined;\n }\n\n private prepareFiles(request: RmuxLaunchRequest, logPath: string, aux: SupervisorPaths): void {\n cleanupSupervisorFiles(aux);\n fs.writeFileSync(logPath, '', { mode: 0o600 });\n const env = environment(request.sessionId, request.interactive);\n // runnerHost supervises the command and watches the lifeline itself, so an\n // owner that dies mid-run is handled inside the pane rather than around it.\n const spec: CommandSpec = { command: request.command, cwd: request.cwd, env };\n writeCommandSpec(aux.spec, spec);\n }\n\n private auxPaths(id: string, sessionId?: string): SupervisorPaths {\n return supervisorPaths(this.paths.stateDirectory(sessionId), id);\n }\n\n private logPipeCommand(logPath: string, donePath: string): string {\n return shellJoin([\n process.execPath,\n runtimeEntry('logSink'),\n logPath,\n this.paths.rotatedLogPathFor(path.basename(logPath, '.log')),\n String(getLogMaxBytes()),\n donePath,\n ]);\n }\n\n private async monitor(rmux: Rmux, pane: Pane, target: string, aux: SupervisorPaths): Promise<ExitResult> {\n try {\n const paneCode = await waitForRunEnd(rmux, target, aux.exit);\n await waitForLogDrain(rmux, target, aux.logDone);\n return readExitMetadata(aux.exit) ?? { code: paneCode, signal: null };\n } finally {\n // The pane ends its own session, so this only covers a session that somehow\n // outlived its pane. Killing unconditionally would warn on every clean run.\n if (!(await sessionMissing(rmux, target).catch(() => false))) {\n await pane.server\n .session(target)\n .kill()\n .catch((error: unknown) =>\n process.emitWarning(`Could not clean up completed RMUX target ${target}: ${errorMessage(error)}`),\n );\n }\n cleanupSupervisorFiles(aux);\n }\n }\n\n private interactiveRun(handle: RunHandle, pane: Pane, completion: Promise<ExitResult>): PtyRun {\n let screen = '';\n let active = true;\n const handlers = new Set<(data: string) => void>();\n const refresh = async (): Promise<void> => {\n while (active) {\n const next = await pane.captureText().catch((error: unknown) => {\n process.emitWarning(`Could not capture RMUX screen for ${handle.name}: ${errorMessage(error)}`);\n return screen;\n });\n if (next !== screen) {\n screen = next;\n for (const handler of handlers) handler(next);\n }\n await delay(POLL_MS);\n }\n };\n void refresh();\n void completion\n .finally(() => {\n active = false;\n })\n .catch((error: unknown) =>\n process.emitWarning(`RMUX interactive completion failed for ${handle.name}: ${errorMessage(error)}`),\n );\n return {\n ...handle,\n write: (text) => {\n void pane.sendText(text).catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n screen: () => screen,\n onData: (handler) => {\n handlers.add(handler);\n return () => handlers.delete(handler);\n },\n resize: (cols, rows) => {\n void pane\n .resize({ width: cols, height: rows })\n .catch((error: unknown) => process.emitWarning(errorMessage(error)));\n },\n };\n }\n}\n\nexport function rmuxPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RMUX binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const configured = process.env[RMUX_BINARY_ENV];\n if (configured !== undefined && configured !== '') return configured;\n const packageName = rmuxPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rmux');\n } catch (error) {\n process.emitWarning(`Bundled RMUX binary is unavailable for ${packageName}: ${errorMessage(error)}`);\n return undefined;\n }\n}\n\nfunction socketName(repositoryPath: string): string {\n const hash = createHash('sha256').update(repositoryPath).digest('hex').slice(0, SOCKET_HASH_LENGTH);\n return `${SESSION_PREFIX}${hash}`;\n}\n\nfunction environment(sessionId: string, interactive: boolean): Record<string, string> {\n const values: NodeJS.ProcessEnv = {\n ...process.env,\n ...(interactive ? {} : NON_INTERACTIVE_ENV),\n // Applied to interactive runs as well: a pane is a tty either way, and an\n // agent sending input to a prompt still has no way past a pager.\n ...NO_TERMINAL_INPUT_ENV,\n PI_SESSION_ID: sessionId,\n };\n if (!interactive) delete values['FORCE_COLOR'];\n return Object.fromEntries(\n Object.entries(values).filter((entry): entry is [string, string] => entry[1] !== undefined),\n );\n}\n\nasync function panePid(rmux: Rmux, target: string): Promise<number> {\n const response = await rmux.displayMessage('#{pane_pid}', { target });\n const pid = Number(response['message']);\n if (!Number.isInteger(pid) || pid <= 0) throw new Error(`RMUX did not report a pane pid for ${target}`);\n return pid;\n}\n\n/**\n * Waits for the run to end, preferring the supervisor's own record of it.\n *\n * The sidecar is written before the command's pane can end, and it outlives the\n * server that the pane's exit takes down with it. Asking the server is the\n * fallback for a supervisor that died without recording anything.\n */\nasync function waitForRunEnd(rmux: Rmux, target: string, exitPath: string): Promise<number | null> {\n for (;;) {\n if (fs.existsSync(exitPath)) return null;\n const state = await readPaneExit(rmux, target);\n if (state.exited) return state.code;\n await delay(POLL_MS);\n }\n}\n\n/**\n * Gives the log sink a moment to mark the log complete.\n *\n * The sink dies with the session it was piped from, so once the session is gone\n * the marker is never coming and there is nothing left to wait for. Its writes\n * are synchronous, so the log itself is already on disk either way.\n */\nasync function waitForLogDrain(rmux: Rmux, target: string, donePath: string): Promise<void> {\n const deadline = Date.now() + LOG_DRAIN_TIMEOUT_MS;\n while (Date.now() < deadline) {\n if (fs.existsSync(donePath)) return;\n if (await sessionMissing(rmux, target)) return;\n await delay(LOG_DRAIN_POLL_MS);\n }\n}\n\nasync function closeOwnedSession(rmux: Rmux, target: string): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (result: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(result);\n };\n const timer = setTimeout(() => {\n process.emitWarning(`Timed out closing RMUX target ${target} after ${STOP_CLOSE_TIMEOUT_MS}ms`);\n finish(false);\n }, STOP_CLOSE_TIMEOUT_MS);\n void rmux\n .session(target)\n .kill()\n .then(\n () => finish(true),\n (error: unknown) => {\n process.emitWarning(`Could not close RMUX target ${target}: ${errorMessage(error)}`);\n finish(false);\n },\n );\n });\n}\n\nasync function waitForPaneStop(rmux: Rmux, target: string, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n if ((await readPaneExit(rmux, target)).exited) return true;\n } catch (error) {\n process.emitWarning(`Could not verify RMUX target ${target} stopped: ${errorMessage(error)}`);\n return false;\n }\n await delay(POLL_MS);\n }\n return false;\n}\n\n/**\n * One formatted read answers both questions this poll asks, so watching a run\n * costs the same single rmux call it always did.\n */\nasync function readPaneExit(rmux: Rmux, target: string): Promise<{ exited: boolean; code: number | null }> {\n let message: string;\n try {\n const response = await rmux.displayMessage(PANE_STATE_FORMAT, { target });\n message = typeof response['message'] === 'string' ? response['message'] : '';\n } catch {\n // The last session ending stops the server, so a server that cannot be\n // reached is the ordinary end of the last run rather than a fault.\n return { exited: true, code: null };\n }\n const [dead, status, ...rest] = message.trim().split(':');\n // A missing session formats as a live pane with no name rather than failing,\n // so the name is what says whether there is still anything to watch.\n if (rest.join(':') === '') return { exited: true, code: null };\n if (dead === '1' || dead === 'true') {\n const code = Number(status);\n return { exited: true, code: Number.isInteger(code) ? code : null };\n }\n return { exited: false, code: null };\n}\n\n/** A session that cannot be found, on a server that may itself be gone. */\nasync function sessionMissing(rmux: Rmux, target: string): Promise<boolean> {\n try {\n const probe = await rmux.cmd('has-session', '-t', target);\n return probe.returnCode !== 0;\n } catch {\n // No server to ask means no session to find.\n return true;\n }\n}\n\n/**\n * A pane that was already gone and a signal that could not be delivered are\n * opposite answers to \"is it stopped\", so they are reported apart.\n */\nfunction signalPane(pid: number, target: string): 'signalled' | 'gone' | 'failed' {\n try {\n process.kill(pid, 'SIGTERM');\n return 'signalled';\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'gone';\n process.emitWarning(`Could not signal RMUX target ${target}: ${errorMessage(error)}`);\n return 'failed';\n }\n}\n\nfunction readTail(target: string, maxBytes: number): string {\n let handle: number | undefined;\n try {\n const size = fs.statSync(target).size;\n const bytes = Math.min(size, maxBytes);\n const buffer = Buffer.alloc(bytes);\n handle = fs.openSync(target, 'r');\n fs.readSync(handle, buffer, 0, bytes, size - bytes);\n return buffer.toString('utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n process.emitWarning(`Could not read RMUX log ${target}: ${errorMessage(error)}`);\n }\n return '';\n } finally {\n if (handle !== undefined) fs.closeSync(handle);\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"wbAuBA,MAOM,EAAwB,IAExB,EAAiB,eACjB,EAAuB,+BAEvB,EAAsB,CAAE,SAAU,IAAK,GAAI,GAAI,EAC/C,EAAwD,CAC5D,eAAgB,6CAChB,aAAc,2CACd,cAAe,4CACf,YAAa,yCACf,EAEA,IAAa,EAAb,KAAiD,CAKlB,MAJ7B,cACA,KAAwB,IAAI,IAC5B,YAA+B,IAAI,IAEnC,YAAY,EAAsC,CAArB,KAAA,MAAA,CAAsB,CAEnD,MAAM,OAAO,EAA4D,CACvE,IAAM,EAAO,MAAM,KAAK,OAAO,EAC/B,GAAI,CAAC,EAAM,OAEX,KAAK,MAAM,kBAAkB,EAC7B,IAAM,EAAS,GAAG,IAAiB,EAAQ,KACrC,EAAU,KAAK,MAAM,WAAW,EAAQ,EAAE,EAC1C,EAAM,KAAK,SAAS,EAAQ,GAAI,EAAQ,SAAS,EACnD,EAAU,GACV,EAAW,GAEf,GAAI,CACF,KAAK,aAAa,EAAS,EAAS,CAAG,EACvC,MAAM,EAAK,IACT,cACA,KACA,KACA,EACA,KACA,EAAQ,IACR,QAAQ,SACR,EAAa,YAAY,EACzB,EAAI,KACJ,EAAI,KACJ,EAAI,KACJ,CAAE,MAAO,EAAK,CAChB,EACA,EAAU,GAGV,MAAM,EAAK,IAAI,YAAa,KAAM,EAAQ,KAAK,eAAe,EAAS,EAAI,OAAO,EAAG,CAAE,MAAO,EAAK,CAAC,EAEpG,IAAM,EAAO,EAAK,QAAQ,CAAM,CAAC,CAAC,KAAK,EAAG,CAAC,EACrC,EAAM,MAAM,EAAQ,EAAM,CAAM,EACtC,EAAG,cAAc,EAAI,KAAM,GAAI,CAAE,KAAM,GAAM,CAAC,EAC9C,EAAW,GAEX,IAAI,EAAY,GACV,EAAa,KAAK,QAAQ,EAAM,EAAM,EAAQ,CAAG,EACvD,KAAK,gBAAgB,EAAQ,CAAU,EAEvC,IAAM,EAAoB,CACxB,GAAI,EAAQ,GACZ,KAAM,EAAQ,KACd,MACA,UACA,QAAS,OACT,cAAe,EACf,WAAe,EAAY,EAAS,EAAS,EAAkB,EAAI,CAAC,EAAI,GACxE,eAAkB,EAClB,WAAc,CACZ,EAAY,EACd,EACA,SAbmC,KAAK,KAAK,EAAQ,CAAG,CAc1D,EAMA,OAJI,EAAQ,aAAa,KAAK,KAAK,IAAI,EAAQ,KAAM,KAAK,eAAe,EAAQ,EAAM,CAAU,CAAC,EAClG,EACG,YAAc,KAAK,KAAK,OAAO,EAAQ,IAAI,CAAC,CAAC,CAC7C,MAAO,GAAmB,QAAQ,YAAY,8BAA8B,EAAO,IAAI,EAAa,CAAK,GAAG,CAAC,EACzG,CACT,OAAS,EAAO,CACd,GAAI,EAAU,MAAM,EAChB,GACF,MAAM,EACH,QAAQ,CAAM,CAAC,CACf,KAAK,CAAC,CACN,MAAO,GACN,QAAQ,YAAY,yCAAyC,EAAO,IAAI,EAAa,CAAY,GAAG,CACtG,EAEJ,EAAuB,CAAG,EAC1B,QAAQ,YACN,EAAQ,YACJ,mDAAmD,EAAa,CAAK,IACrE,yDAAyD,EAAa,CAAK,GACjF,EACA,MACF,CACF,CAEA,MAAM,KAAK,EAAgB,EAAuC,CAChE,GAAI,CAAC,EAAqB,KAAK,CAAM,GAAK,CAAC,OAAO,UAAU,CAAW,GAAK,GAAe,EAAG,MAAO,GAErG,IAAM,EAAO,MAAM,KAAK,OAAO,EAC/B,GAAI,CAAC,EAAM,MAAO,GAClB,GAAI,CAEF,GAAI,MAAM,EAAe,EAAM,CAAM,EAAG,MAAO,GAE/C,IAAM,EAAM,MAAM,EAAQ,EAAM,CAAM,EACtC,GAAI,IAAQ,EAAa,MAAO,GAChC,IAAM,EAAY,EAAW,EAAK,CAAM,EAWxC,OAVI,IAAc,OAAe,GAC7B,IAAc,SAAiB,GAE/B,MAAM,EAAgB,EAAM,EAAQ,GAAa,GACjD,MAAM,EAAe,EAAM,CAAM,EAAU,GAI3C,MADqB,EAAQ,EAAM,CAAM,IAC1B,GAEZ,EAAkB,EAAM,CAAM,CACvC,OAAS,EAAO,CAEd,OADA,QAAQ,YAAY,8BAA8B,EAAO,IAAI,EAAa,CAAK,GAAG,EAC3E,EACT,CACF,CAEA,MAAM,MAAM,EAAY,EAAgB,EAAqD,CAC3F,IAAM,EAAO,MAAM,KAAK,OAAO,EAC/B,GAAI,CAAC,EAAM,OACX,IAAM,EAAO,EAAK,QAAQ,CAAM,CAAC,CAAC,KAAK,EAAG,CAAC,EAC3C,GAAI,CACF,MAAM,EAAK,eAAe,aAAc,CAAE,QAAO,CAAC,CACpD,OAAS,EAAO,CACd,QAAQ,YAAY,+BAA+B,EAAO,IAAI,EAAa,CAAK,GAAG,EACnF,MACF,CACA,IAAM,EAAa,KAAK,QAAQ,EAAM,EAAM,EAAQ,KAAK,SAAS,EAAI,CAAS,CAAC,EAEhF,OADA,KAAK,gBAAgB,EAAQ,CAAU,EAChC,CACT,CAEA,MAAM,MAAM,EAAgB,EAAgC,CAC1D,IAAM,EAAO,MAAM,KAAK,OAAO,EAU/B,OATK,EASE,MARc,EAClB,QAAQ,CAAM,CAAC,CACf,KAAK,EAAG,CAAC,CAAC,CACV,SAAS,CAAI,CAAC,CACd,MAAO,GAAmB,CACzB,QAAQ,YAAY,uCAAuC,EAAO,IAAI,EAAa,CAAK,GAAG,CAE7F,CAAC,IACe,IAAA,GATA,EAUpB,CAEA,IAAI,EAAkC,CACpC,OAAO,KAAK,KAAK,IAAI,CAAI,CAC3B,CAEA,YAAY,EAAY,EAA2C,CACjE,OAAO,EAAiB,KAAK,SAAS,EAAI,CAAS,CAAC,CAAC,IAAI,CAC3D,CAEA,gBAAwB,EAAgB,EAAuC,CAC7E,KAAK,YAAY,IAAI,EAAQ,CAAU,EACvC,EACG,YAAc,KAAK,YAAY,OAAO,CAAM,CAAC,CAAC,CAC9C,MAAO,GAAmB,QAAQ,YAAY,2BAA2B,EAAO,IAAI,EAAa,CAAK,GAAG,CAAC,CAC/G,CAEA,QAA4C,CAE1C,MADA,MAAK,gBAAkB,KAAK,cAAc,EACnC,KAAK,aACd,CAEA,MAAc,eAA2C,CACvD,IAAM,EAAW,CAAC,EAAc,EAAG,MAAM,CAAC,CAAC,OAAQ,GAA2B,IAAU,IAAA,EAAS,EACjG,IAAK,IAAM,KAAU,EACnB,GAAI,CACE,IAAW,QAAQ,EAAG,UAAU,EAAQ,GAAe,EAC3D,IAAM,EAAS,IAAI,EAAK,CAAE,SAAQ,WAAY,EAAW,KAAK,MAAM,eAAe,CAAC,CAAE,CAAC,EAEvF,OADA,MAAM,EAAO,aAAa,EACnB,CACT,OAAS,EAAO,CACd,QAAQ,YAAY,kBAAkB,EAAO,mBAAmB,EAAa,CAAK,GAAG,CAEvF,CAGJ,CAEA,aAAqB,EAA4B,EAAiB,EAA4B,CAC5F,EAAuB,CAAG,EAC1B,EAAG,cAAc,EAAS,GAAI,CAAE,KAAM,GAAM,CAAC,EAC7C,IAAM,EAAM,EAAY,EAAQ,UAAW,EAAQ,WAAW,EAGxD,EAAoB,CAAE,QAAS,EAAQ,QAAS,IAAK,EAAQ,IAAK,KAAI,EAC5E,EAAiB,EAAI,KAAM,CAAI,CACjC,CAEA,SAAiB,EAAY,EAAqC,CAChE,OAAO,EAAgB,KAAK,MAAM,eAAe,CAAS,EAAG,CAAE,CACjE,CAEA,eAAuB,EAAiB,EAA0B,CAChE,OAAO,EAAU,CACf,QAAQ,SACR,EAAa,SAAS,EACtB,EACA,KAAK,MAAM,kBAAkB,EAAK,SAAS,EAAS,MAAM,CAAC,EAC3D,OAAO,EAAe,CAAC,EACvB,CACF,CAAC,CACH,CAEA,MAAc,QAAQ,EAAY,EAAY,EAAgB,EAA2C,CACvG,GAAI,CACF,IAAM,EAAW,MAAM,EAAc,EAAM,EAAQ,EAAI,IAAI,EAE3D,OADA,MAAM,EAAgB,EAAM,EAAQ,EAAI,OAAO,EACxC,EAAiB,EAAI,IAAI,GAAK,CAAE,KAAM,EAAU,OAAQ,IAAK,CACtE,QAAU,CAGF,MAAM,EAAe,EAAM,CAAM,CAAC,CAAC,UAAY,EAAK,GACxD,MAAM,EAAK,OACR,QAAQ,CAAM,CAAC,CACf,KAAK,CAAC,CACN,MAAO,GACN,QAAQ,YAAY,4CAA4C,EAAO,IAAI,EAAa,CAAK,GAAG,CAClG,EAEJ,EAAuB,CAAG,CAC5B,CACF,CAEA,eAAuB,EAAmB,EAAY,EAAyC,CAC7F,IAAI,EAAS,GACT,EAAS,GACP,EAAW,IAAI,IAsBrB,OARA,SAb2C,CACzC,KAAO,GAAQ,CACb,IAAM,EAAO,MAAM,EAAK,YAAY,CAAC,CAAC,MAAO,IAC3C,QAAQ,YAAY,qCAAqC,EAAO,KAAK,IAAI,EAAa,CAAK,GAAG,EACvF,EACR,EACD,GAAI,IAAS,EAAQ,CACnB,EAAS,EACT,IAAK,IAAM,KAAW,EAAU,EAAQ,CAAI,CAC9C,CACA,MAAM,EAAM,GAAO,CACrB,CACF,EACK,CAAQ,EACb,EACG,YAAc,CACb,EAAS,EACX,CAAC,CAAC,CACD,MAAO,GACN,QAAQ,YAAY,0CAA0C,EAAO,KAAK,IAAI,EAAa,CAAK,GAAG,CACrG,EACK,CACL,GAAG,EACH,MAAQ,GAAS,CACf,EAAU,SAAS,CAAI,CAAC,CAAC,MAAO,GAAmB,QAAQ,YAAY,EAAa,CAAK,CAAC,CAAC,CAC7F,EACA,WAAc,EACd,OAAS,IACP,EAAS,IAAI,CAAO,MACP,EAAS,OAAO,CAAO,GAEtC,QAAS,EAAM,IAAS,CACtB,EACG,OAAO,CAAE,MAAO,EAAM,OAAQ,CAAK,CAAC,CAAC,CACrC,MAAO,GAAmB,QAAQ,YAAY,EAAa,CAAK,CAAC,CAAC,CACvE,CACF,CACF,CACF,EAEA,SAAgB,EAAqB,EAAkB,EAA0C,CAC/F,IAAM,EAAS,GAAG,EAAS,GAAG,IACxB,EAAc,EAAoB,GAExC,OADK,GAAa,QAAQ,YAAY,6DAA6D,GAAQ,EACpG,CACT,CAEA,SAAS,GAAoC,CAC3C,IAAM,EAAa,QAAQ,IAAI,mBAC/B,GAAI,IAAe,IAAA,IAAa,IAAe,GAAI,OAAO,EAC1D,IAAM,EAAc,EAAqB,QAAQ,SAAU,QAAQ,IAAI,EAClE,KACL,GAAI,CAEF,IAAM,EADU,EAAc,YAAY,GACnB,CAAC,CAAC,QAAQ,GAAG,EAAY,cAAc,EAC9D,OAAO,EAAK,KAAK,EAAK,QAAQ,CAAQ,EAAG,SAAU,MAAO,MAAM,CAClE,OAAS,EAAO,CACd,QAAQ,YAAY,0CAA0C,EAAY,IAAI,EAAa,CAAK,GAAG,EACnG,MACF,CACF,CAEA,SAAS,EAAW,EAAgC,CAClD,IAAM,EAAO,EAAW,QAAQ,CAAC,CAAC,OAAO,CAAc,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,EAAkB,EAClG,MAAO,GAAG,IAAiB,GAC7B,CAEA,SAAS,EAAY,EAAmB,EAA8C,CACpF,IAAM,EAA4B,CAChC,GAAG,QAAQ,IACX,GAAI,EAAc,CAAC,EAAI,EAGvB,GAAG,EACH,cAAe,CACjB,EAEA,OADK,GAAa,OAAO,EAAO,YACzB,OAAO,YACZ,OAAO,QAAQ,CAAM,CAAC,CAAC,OAAQ,GAAqC,EAAM,KAAO,IAAA,EAAS,CAC5F,CACF,CAEA,eAAe,EAAQ,EAAY,EAAiC,CAClE,IAAM,EAAW,MAAM,EAAK,eAAe,cAAe,CAAE,QAAO,CAAC,EAC9D,EAAM,OAAO,EAAS,OAAU,EACtC,GAAI,CAAC,OAAO,UAAU,CAAG,GAAK,GAAO,EAAG,MAAU,MAAM,sCAAsC,GAAQ,EACtG,OAAO,CACT,CASA,eAAe,EAAc,EAAY,EAAgB,EAA0C,CACjG,OAAS,CACP,GAAI,EAAG,WAAW,CAAQ,EAAG,OAAO,KACpC,IAAM,EAAQ,MAAM,EAAa,EAAM,CAAM,EAC7C,GAAI,EAAM,OAAQ,OAAO,EAAM,KAC/B,MAAM,EAAM,GAAO,CACrB,CACF,CASA,eAAe,EAAgB,EAAY,EAAgB,EAAiC,CAC1F,IAAM,EAAW,KAAK,IAAI,EAAI,IAC9B,KAAO,KAAK,IAAI,EAAI,GAAU,CAE5B,GADI,EAAG,WAAW,CAAQ,GACtB,MAAM,EAAe,EAAM,CAAM,EAAG,OACxC,MAAM,EAAM,EAAiB,CAC/B,CACF,CAEA,eAAe,EAAkB,EAAY,EAAkC,CAC7E,OAAO,IAAI,QAAS,GAAY,CAC9B,IAAI,EAAU,GACR,EAAU,GAA0B,CACpC,IACJ,EAAU,GACV,aAAa,CAAK,EAClB,EAAQ,CAAM,EAChB,EACM,EAAQ,eAAiB,CAC7B,QAAQ,YAAY,iCAAiC,EAAO,SAAS,EAAsB,GAAG,EAC9F,EAAO,EAAK,CACd,EAAG,CAAqB,EACxB,EACG,QAAQ,CAAM,CAAC,CACf,KAAK,CAAC,CACN,SACO,EAAO,EAAI,EAChB,GAAmB,CAClB,QAAQ,YAAY,+BAA+B,EAAO,IAAI,EAAa,CAAK,GAAG,EACnF,EAAO,EAAK,CACd,CACF,CACJ,CAAC,CACH,CAEA,eAAe,EAAgB,EAAY,EAAgB,EAAqC,CAC9F,IAAM,EAAW,KAAK,IAAI,EAAI,EAC9B,KAAO,KAAK,IAAI,EAAI,GAAU,CAC5B,GAAI,CACF,IAAK,MAAM,EAAa,EAAM,CAAM,EAAA,CAAG,OAAQ,MAAO,EACxD,OAAS,EAAO,CAEd,OADA,QAAQ,YAAY,gCAAgC,EAAO,YAAY,EAAa,CAAK,GAAG,EACrF,EACT,CACA,MAAM,EAAM,GAAO,CACrB,CACA,MAAO,EACT,CAMA,eAAe,EAAa,EAAY,EAAmE,CACzG,IAAI,EACJ,GAAI,CACF,IAAM,EAAW,MAAM,EAAK,eAAe,mDAAmB,CAAE,QAAO,CAAC,EACxE,EAAU,OAAO,EAAS,SAAe,SAAW,EAAS,QAAa,EAC5E,MAAQ,CAGN,MAAO,CAAE,OAAQ,GAAM,KAAM,IAAK,CACpC,CACA,GAAM,CAAC,EAAM,EAAQ,GAAG,GAAQ,EAAQ,KAAK,CAAC,CAAC,MAAM,GAAG,EAGxD,GAAI,EAAK,KAAK,GAAG,IAAM,GAAI,MAAO,CAAE,OAAQ,GAAM,KAAM,IAAK,EAC7D,GAAI,IAAS,KAAO,IAAS,OAAQ,CACnC,IAAM,EAAO,OAAO,CAAM,EAC1B,MAAO,CAAE,OAAQ,GAAM,KAAM,OAAO,UAAU,CAAI,EAAI,EAAO,IAAK,CACpE,CACA,MAAO,CAAE,OAAQ,GAAO,KAAM,IAAK,CACrC,CAGA,eAAe,EAAe,EAAY,EAAkC,CAC1E,GAAI,CAEF,OAAO,MADa,EAAK,IAAI,cAAe,KAAM,CAAM,EAAA,CAC3C,aAAe,CAC9B,MAAQ,CAEN,MAAO,EACT,CACF,CAMA,SAAS,EAAW,EAAa,EAAiD,CAChF,GAAI,CAEF,OADA,QAAQ,KAAK,EAAK,SAAS,EACpB,WACT,OAAS,EAAO,CAGd,OAFK,EAAgC,OAAS,QAAgB,QAC9D,QAAQ,YAAY,gCAAgC,EAAO,IAAI,EAAa,CAAK,GAAG,EAC7E,SACT,CACF,CAEA,SAAS,EAAS,EAAgB,EAA0B,CAC1D,IAAI,EACJ,GAAI,CACF,IAAM,EAAO,EAAG,SAAS,CAAM,CAAC,CAAC,KAC3B,EAAQ,KAAK,IAAI,EAAM,CAAQ,EAC/B,EAAS,OAAO,MAAM,CAAK,EAGjC,MAFA,GAAS,EAAG,SAAS,EAAQ,GAAG,EAChC,EAAG,SAAS,EAAQ,EAAQ,EAAG,EAAO,EAAO,CAAK,EAC3C,EAAO,SAAS,MAAM,CAC/B,OAAS,EAAO,CAId,OAHK,EAAgC,OAAS,UAC5C,QAAQ,YAAY,2BAA2B,EAAO,IAAI,EAAa,CAAK,GAAG,EAE1E,EACT,QAAU,CACJ,IAAW,IAAA,IAAW,EAAG,UAAU,CAAM,CAC/C,CACF,CAEA,SAAS,EAAM,EAA2B,CACxC,OAAO,IAAI,QAAS,GAAY,CAE9B,WADyB,EAAS,CAC9B,CAAC,CAAC,QAAQ,CAChB,CAAC,CACH,CAEA,SAAS,EAAa,EAAwB,CAC5C,OAAO,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,CAC9D"}
@@ -1,2 +1,2 @@
1
- const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../types/rtkProcessor.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:module"),o=require("node:child_process");const s=/^[A-Za-z0-9_./:@%+=,-]+$/u,c=/[\r\n;&|<>`$\\'"(){}*?!]/u,l=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),u=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],d={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var f=class{resolveBinary;timeoutMs;unrunnable=!1;constructor(e=h,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let t=p(e.command);if(!t)return{kind:`skipped`};let i;try{i=r.default.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:n.RTK_FAILED_WARNING}}if(i>10485760)return{kind:`fallback`,warning:n.RTK_OVERSIZED_WARNING};if(this.unrunnable)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};let a;try{if(a=this.resolveBinary(),!a)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};r.default.statSync(a).mode&73||r.default.chmodSync(a,493)}catch{return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING}}return this.pipe(a,t,e.logPath,i)}pipe(e,i,a,s){return new Promise(c=>{let l;try{l=(0,o.spawn)(e,[`pipe`,`-f`,i],{stdio:`pipe`})}catch{this.unrunnable=!0,c({kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING});return}let u=r.default.createReadStream(a),d=t.getResultMaxBytes()*2,f=Buffer.alloc(0),p=Buffer.alloc(0),m=0,h=0,v,y=!1,b,x,S=e=>{y||(y=!0,clearTimeout(w),x&&clearTimeout(x),u.destroy(),c(e))},C=e=>{if(y)return;b=e;let t=!1;try{t=l.kill(`SIGKILL`)}catch{S({kind:`fallback`,warning:e});return}if(!t){S({kind:`fallback`,warning:e});return}x??=setTimeout(()=>S({kind:`fallback`,warning:e}),1e3)},w=setTimeout(()=>C(n.RTK_TIMEOUT_WARNING),this.timeoutMs);l.stdout.on(`data`,e=>{m+=e.byteLength;for(let t of e)t===10&&(h+=1);v=e.at(-1),f.byteLength<d&&(f=Buffer.concat([f,e.subarray(0,d-f.byteLength)])),p=g(p,e,d)}),l.stderr.resume(),l.once(`error`,e=>{if(e.code===`ENOENT`||e.code===`EACCES`||e.code===`ENOEXEC`){this.unrunnable=!0,C(n.RTK_UNAVAILABLE_WARNING);return}C(n.RTK_FAILED_WARNING)}),l.stdin.once(`error`,()=>C(n.RTK_FAILED_WARNING)),u.once(`error`,()=>C(n.RTK_FAILED_WARNING)),l.once(`close`,e=>{if(b){S({kind:`fallback`,warning:b});return}let t=_(p);if(e!==0||s>0&&t.trim().length===0){S({kind:`fallback`,warning:n.RTK_FAILED_WARNING});return}let r=h+ +(m>0&&v!==10);S({kind:`processed`,result:{filter:i,head:m>p.byteLength?f.toString(`utf8`):``,output:t,bytes:m,lines:r}})}),u.pipe(l.stdin)})}};function p(e){if(c.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!s.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>l.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||y(n)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||y(n)||!n.includes(`--no-heading`)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function m(e,t){let n=`${e}-${t}`,r=d[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function h(){let e=m(process.platform,process.arch);if(e)try{let t=(0,a.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${e}/package.json`);return i.default.join(i.default.dirname(t),`vendor`,`bin`,`rtk`)}catch{return}}function g(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function _(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function v(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function y(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||u.some(t=>e===t||e.startsWith(`${t}=`)))}exports.RTK_STDIN_MAX_BYTES=10485760,exports.RtkProcessor=f,exports.classifyRtkFilter=p,exports.rtkPackageForTarget=m;
1
+ const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/config.cjs"),n=require("../../types/rtkProcessor.cjs");let r=require("node:fs");r=e.__toESM(r,1);let i=require("node:path");i=e.__toESM(i,1);let a=require("node:module"),o=require("node:child_process");const s=/^[A-Za-z0-9_./:@%+=,-]+$/u,c=/[\r\n;&|<>`$\\'"(){}*?!]/u,l=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),u=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],d={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var f=class{resolveBinary;timeoutMs;unrunnable=!1;constructor(e=h,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let t=p(e.command);if(!t)return{kind:`skipped`};let i;try{i=r.default.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:n.RTK_FAILED_WARNING}}if(i>10485760)return{kind:`fallback`,warning:n.RTK_OVERSIZED_WARNING};if(this.unrunnable)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};let a;try{if(a=this.resolveBinary(),!a)return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING};r.default.statSync(a).mode&73||r.default.chmodSync(a,493)}catch{return{kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING}}return this.pipe(a,t,e.logPath,i)}pipe(e,i,a,s){return new Promise(c=>{let l;try{l=(0,o.spawn)(e,[`pipe`,`-f`,i],{stdio:`pipe`})}catch{this.unrunnable=!0,c({kind:`fallback`,warning:n.RTK_UNAVAILABLE_WARNING});return}let u=r.default.createReadStream(a),d=t.getResultMaxBytes()*2,f=Buffer.alloc(0),p=Buffer.alloc(0),m=0,h=0,v,y=!1,b,x,S=e=>{y||(y=!0,clearTimeout(w),x&&clearTimeout(x),u.destroy(),c(e))},C=e=>{if(y)return;b=e;let t=!1;try{t=l.kill(`SIGKILL`)}catch{S({kind:`fallback`,warning:e});return}if(!t){S({kind:`fallback`,warning:e});return}x??=setTimeout(()=>S({kind:`fallback`,warning:e}),1e3)},w=setTimeout(()=>C(n.RTK_TIMEOUT_WARNING),this.timeoutMs);l.stdout.on(`data`,e=>{m+=e.byteLength;for(let t of e)t===10&&(h+=1);v=e.at(-1),f.byteLength<d&&(f=Buffer.concat([f,e.subarray(0,d-f.byteLength)])),p=g(p,e,d)}),l.stderr.resume(),l.once(`error`,e=>{if(e.code===`ENOENT`||e.code===`EACCES`||e.code===`ENOEXEC`){this.unrunnable=!0,C(n.RTK_UNAVAILABLE_WARNING);return}C(n.RTK_FAILED_WARNING)}),l.stdin.once(`error`,()=>C(n.RTK_FAILED_WARNING)),u.once(`error`,()=>C(n.RTK_FAILED_WARNING)),l.once(`close`,e=>{if(b){S({kind:`fallback`,warning:b});return}let t=_(p);if(e!==0||s>0&&t.trim().length===0){S({kind:`fallback`,warning:n.RTK_FAILED_WARNING});return}let r=h+ +(m>0&&v!==10);S({kind:`processed`,result:{filter:i,head:m>p.byteLength?f.toString(`utf8`):``,output:t,bytes:m,lines:r}})}),u.pipe(l.stdin)})}};function p(e){if(c.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!s.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>l.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||y(n)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||y(n)||!n.includes(`--no-heading`)?void 0:v(n,`n`,`--line-number`)&&v(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function m(e,t){let n=`${e}-${t}`,r=d[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function h(){let e=process.env.DOOMPI_RTK_BINARY;if(e!==void 0&&e!==``)return e;let t=m(process.platform,process.arch);if(t)try{let e=(0,a.createRequire)(require("url").pathToFileURL(__filename).href).resolve(`${t}/package.json`);return i.default.join(i.default.dirname(e),`vendor`,`bin`,`rtk`)}catch{return}}function g(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function _(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function v(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function y(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||u.some(t=>e===t||e.startsWith(`${t}=`)))}exports.RTK_STDIN_MAX_BYTES=10485760,exports.RtkProcessor=f,exports.classifyRtkFilter=p,exports.rtkPackageForTarget=m;
2
2
  //# sourceMappingURL=RtkProcessor.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.cjs","names":["fs","RTK_FAILED_WARNING","RTK_OVERSIZED_WARNING","RTK_UNAVAILABLE_WARNING","spawn","getResultMaxBytes","RTK_TIMEOUT_WARNING","createRequire","path"],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RtkProcessor.cjs","names":["fs","RTK_FAILED_WARNING","RTK_OVERSIZED_WARNING","RTK_UNAVAILABLE_WARNING","spawn","getResultMaxBytes","RTK_TIMEOUT_WARNING","createRequire","path"],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst RTK_BINARY_ENV = 'DOOMPI_RTK_BINARY';\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const configured = process.env[RTK_BINARY_ENV];\n if (configured !== undefined && configured !== '') return configured;\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.d.cts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KAyDR;cAEQ,wBAAwB;mBAYhB;mBACA;;;;;;;;;UAJX;EAER,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UA0B3C;;;;;;iBAwGM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
1
+ {"version":3,"file":"RtkProcessor.d.cts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KA0DR;cAEQ,wBAAwB;mBAYhB;mBACA;;;;;;;;;UAJX;EAER,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UA0B3C;;;;;;iBAwGM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.d.mts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KAyDR;cAEQ,wBAAwB;mBAYhB;mBACA;;;;;;;;;UAJX;EAER,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UA0B3C;;;;;;iBAwGM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
1
+ {"version":3,"file":"RtkProcessor.d.mts","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"mappings":";;cAiBa;KA0DR;cAEQ,wBAAwB;mBAYhB;mBACA;;;;;;;;;UAJX;EAER,YACmB,gBAAe,gBACf;EAGb,QAAQ,SAAS,oBAAoB,QAAQ;UA0B3C;;;;;;iBAwGM,kBAAkB,kBAAkB;iBAyDpC,oBAAoB,kBAAkB"}
@@ -1,2 +1,2 @@
1
- import{getResultMaxBytes as e}from"../../types/config.mjs";import{RTK_FAILED_WARNING as t,RTK_OVERSIZED_WARNING as n,RTK_TIMEOUT_WARNING as r,RTK_UNAVAILABLE_WARNING as i}from"../../types/rtkProcessor.mjs";import{createRequire as a}from"node:module";import o from"node:fs";import s from"node:path";import{spawn as c}from"node:child_process";const l=10485760,u=/^[A-Za-z0-9_./:@%+=,-]+$/u,d=/[\r\n;&|<>`$\\'"(){}*?!]/u,f=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),p=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],m={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var h=class{resolveBinary;timeoutMs;unrunnable=!1;constructor(e=v,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let r=g(e.command);if(!r)return{kind:`skipped`};let a;try{a=o.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:t}}if(a>10485760)return{kind:`fallback`,warning:n};if(this.unrunnable)return{kind:`fallback`,warning:i};let s;try{if(s=this.resolveBinary(),!s)return{kind:`fallback`,warning:i};o.statSync(s).mode&73||o.chmodSync(s,493)}catch{return{kind:`fallback`,warning:i}}return this.pipe(s,r,e.logPath,a)}pipe(n,a,s,l){return new Promise(u=>{let d;try{d=c(n,[`pipe`,`-f`,a],{stdio:`pipe`})}catch{this.unrunnable=!0,u({kind:`fallback`,warning:i});return}let f=o.createReadStream(s),p=e()*2,m=Buffer.alloc(0),h=Buffer.alloc(0),g=0,_=0,v,x=!1,S,C,w=e=>{x||(x=!0,clearTimeout(E),C&&clearTimeout(C),f.destroy(),u(e))},T=e=>{if(x)return;S=e;let t=!1;try{t=d.kill(`SIGKILL`)}catch{w({kind:`fallback`,warning:e});return}if(!t){w({kind:`fallback`,warning:e});return}C??=setTimeout(()=>w({kind:`fallback`,warning:e}),1e3)},E=setTimeout(()=>T(r),this.timeoutMs);d.stdout.on(`data`,e=>{g+=e.byteLength;for(let t of e)t===10&&(_+=1);v=e.at(-1),m.byteLength<p&&(m=Buffer.concat([m,e.subarray(0,p-m.byteLength)])),h=y(h,e,p)}),d.stderr.resume(),d.once(`error`,e=>{if(e.code===`ENOENT`||e.code===`EACCES`||e.code===`ENOEXEC`){this.unrunnable=!0,T(i);return}T(t)}),d.stdin.once(`error`,()=>T(t)),f.once(`error`,()=>T(t)),d.once(`close`,e=>{if(S){w({kind:`fallback`,warning:S});return}let n=b(h);if(e!==0||l>0&&n.trim().length===0){w({kind:`fallback`,warning:t});return}let r=_+ +(g>0&&v!==10);w({kind:`processed`,result:{filter:a,head:g>h.byteLength?m.toString(`utf8`):``,output:n,bytes:g,lines:r}})}),f.pipe(d.stdin)})}};function g(e){if(d.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!u.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>f.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||S(n)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||S(n)||!n.includes(`--no-heading`)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function _(e,t){let n=`${e}-${t}`,r=m[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function v(){let e=_(process.platform,process.arch);if(e)try{let t=a(import.meta.url).resolve(`${e}/package.json`);return s.join(s.dirname(t),`vendor`,`bin`,`rtk`)}catch{return}}function y(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function b(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function x(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function S(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||p.some(t=>e===t||e.startsWith(`${t}=`)))}export{l as RTK_STDIN_MAX_BYTES,h as RtkProcessor,g as classifyRtkFilter,_ as rtkPackageForTarget};
1
+ import{getResultMaxBytes as e}from"../../types/config.mjs";import{RTK_FAILED_WARNING as t,RTK_OVERSIZED_WARNING as n,RTK_TIMEOUT_WARNING as r,RTK_UNAVAILABLE_WARNING as i}from"../../types/rtkProcessor.mjs";import{createRequire as a}from"node:module";import o from"node:fs";import s from"node:path";import{spawn as c}from"node:child_process";const l=10485760,u=/^[A-Za-z0-9_./:@%+=,-]+$/u,d=/[\r\n;&|<>`$\\'"(){}*?!]/u,f=`-s.-z.--raw.--numstat.--shortstat.--stat.--dirstat.--dirstat-by-file.--summary.--compact-summary.--name-only.--name-status.--check.--binary.--word-diff.--word-diff-regex.--color.--color-words.--color-moved.--color-moved-ws.--line-prefix.--no-patch.--patch-with-raw.--patch-with-stat.--ext-diff.--textconv.--quiet.--submodule`.split(`.`),p=[`--json`,`--null`,`--null-data`,`--color`,`--colour`,`--after-context`,`--before-context`,`--context`,`--no-filename`,`--no-line-number`,`--heading`],m={"darwin-arm64":`@agimon-ai/doompi-runner-rtk-darwin-arm64`,"darwin-x64":`@agimon-ai/doompi-runner-rtk-darwin-x64`,"linux-arm64":`@agimon-ai/doompi-runner-rtk-linux-arm64`,"linux-x64":`@agimon-ai/doompi-runner-rtk-linux-x64`};var h=class{resolveBinary;timeoutMs;unrunnable=!1;constructor(e=v,t=1e4){this.resolveBinary=e,this.timeoutMs=t}async process(e){let r=g(e.command);if(!r)return{kind:`skipped`};let a;try{a=o.statSync(e.logPath).size}catch{return{kind:`fallback`,warning:t}}if(a>10485760)return{kind:`fallback`,warning:n};if(this.unrunnable)return{kind:`fallback`,warning:i};let s;try{if(s=this.resolveBinary(),!s)return{kind:`fallback`,warning:i};o.statSync(s).mode&73||o.chmodSync(s,493)}catch{return{kind:`fallback`,warning:i}}return this.pipe(s,r,e.logPath,a)}pipe(n,a,s,l){return new Promise(u=>{let d;try{d=c(n,[`pipe`,`-f`,a],{stdio:`pipe`})}catch{this.unrunnable=!0,u({kind:`fallback`,warning:i});return}let f=o.createReadStream(s),p=e()*2,m=Buffer.alloc(0),h=Buffer.alloc(0),g=0,_=0,v,x=!1,S,C,w=e=>{x||(x=!0,clearTimeout(E),C&&clearTimeout(C),f.destroy(),u(e))},T=e=>{if(x)return;S=e;let t=!1;try{t=d.kill(`SIGKILL`)}catch{w({kind:`fallback`,warning:e});return}if(!t){w({kind:`fallback`,warning:e});return}C??=setTimeout(()=>w({kind:`fallback`,warning:e}),1e3)},E=setTimeout(()=>T(r),this.timeoutMs);d.stdout.on(`data`,e=>{g+=e.byteLength;for(let t of e)t===10&&(_+=1);v=e.at(-1),m.byteLength<p&&(m=Buffer.concat([m,e.subarray(0,p-m.byteLength)])),h=y(h,e,p)}),d.stderr.resume(),d.once(`error`,e=>{if(e.code===`ENOENT`||e.code===`EACCES`||e.code===`ENOEXEC`){this.unrunnable=!0,T(i);return}T(t)}),d.stdin.once(`error`,()=>T(t)),f.once(`error`,()=>T(t)),d.once(`close`,e=>{if(S){w({kind:`fallback`,warning:S});return}let n=b(h);if(e!==0||l>0&&n.trim().length===0){w({kind:`fallback`,warning:t});return}let r=_+ +(g>0&&v!==10);w({kind:`processed`,result:{filter:a,head:g>h.byteLength?m.toString(`utf8`):``,output:n,bytes:g,lines:r}})}),f.pipe(d.stdin)})}};function g(e){if(d.test(e))return;let t=e.trim();if(!t)return;let n=t.split(/[ \t]+/u);if(!n.some(e=>!u.test(e))){if(n[0]===`cargo`&&n[1]===`test`)return n.includes(`--`)||n.some(e=>e.startsWith(`--message-format`)||e===`--json`)?void 0:`cargo-test`;if(n[0]===`pytest`)return n.some(e=>e.startsWith(`--json-report`))?void 0:`pytest`;if(n[0]===`git`&&n[1]===`diff`)return n.some(e=>f.some(t=>e===t||e.startsWith(`${t}=`)))?void 0:`git-diff`;if(n[0]===`grep`)return n.length<4||S(n)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`rg`)return n.length<4||S(n)||!n.includes(`--no-heading`)?void 0:x(n,`n`,`--line-number`)&&x(n,`H`,`--with-filename`)?`grep`:void 0;if(n[0]===`go`&&n[1]===`test`){let e=!1;for(let t of n.slice(2))(t===`-json`||t===`-json=true`)&&(e=!0),t===`-json=false`&&(e=!1);return e?`go-test`:void 0}if(n[0]===`ruff`&&n[1]===`check`)return n.some((e,t)=>e===`--output-format=json`||e===`--output-format`&&n[t+1]===`json`)?`ruff-check`:void 0}}function _(e,t){let n=`${e}-${t}`,r=m[n];return r||process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${n}`),r}function v(){let e=process.env.DOOMPI_RTK_BINARY;if(e!==void 0&&e!==``)return e;let t=_(process.platform,process.arch);if(t)try{let e=a(import.meta.url).resolve(`${t}/package.json`);return s.join(s.dirname(e),`vendor`,`bin`,`rtk`)}catch{return}}function y(e,t,n){if(t.byteLength>=n)return Buffer.from(t.subarray(t.byteLength-n));let r=Buffer.concat([e,t]);return r.byteLength>n?Buffer.from(r.subarray(r.byteLength-n)):r}function b(e){let t=0;for(;t<e.byteLength&&(e[t]&192)==128;)t+=1;return e.subarray(t).toString(`utf8`)}function x(e,t,n){return e.some(e=>e===n||e.startsWith(`-`)&&!e.startsWith(`--`)&&e.slice(1).includes(t))}function S(e){return e.some(e=>e.startsWith(`-`)&&!e.startsWith(`--`)&&/[0ABCILNchzZ]/u.test(e.slice(1))||p.some(t=>e===t||e.startsWith(`${t}=`)))}export{l as RTK_STDIN_MAX_BYTES,h as RtkProcessor,g as classifyRtkFilter,_ as rtkPackageForTarget};
2
2
  //# sourceMappingURL=RtkProcessor.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RtkProcessor.mjs","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RtkProcessor.mjs","names":[],"sources":["../../../../src/adapters/RtkProcessor/RtkProcessor.ts"],"sourcesContent":["import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { getResultMaxBytes } from '../../types/config.ts';\nimport {\n type IRtkProcessor,\n RTK_FAILED_WARNING,\n RTK_OVERSIZED_WARNING,\n RTK_TIMEOUT_WARNING,\n RTK_UNAVAILABLE_WARNING,\n type RtkFilter,\n type RtkProcessRequest,\n type RtkProcessResult,\n} from '../../types/rtkProcessor';\n\nconst BYTES_PER_MIB = 1024 * 1024;\nexport const RTK_STDIN_MAX_BYTES = 10 * BYTES_PER_MIB;\nconst DEFAULT_RTK_TIMEOUT_MS = 10_000;\nconst RTK_KILL_WAIT_MS = 1_000;\nconst OUTPUT_BUFFER_FACTOR = 2;\nconst EXECUTABLE_MODE = 0o755;\nconst SIMPLE_TOKEN_PATTERN = /^[A-Za-z0-9_./:@%+=,-]+$/u;\nconst SHELL_CONTROL_PATTERN = /[\\r\\n;&|<>`$\\\\'\"(){}*?!]/u;\nconst GIT_DIFF_INCOMPATIBLE_OPTIONS = [\n '-s',\n '-z',\n '--raw',\n '--numstat',\n '--shortstat',\n '--stat',\n '--dirstat',\n '--dirstat-by-file',\n '--summary',\n '--compact-summary',\n '--name-only',\n '--name-status',\n '--check',\n '--binary',\n '--word-diff',\n '--word-diff-regex',\n '--color',\n '--color-words',\n '--color-moved',\n '--color-moved-ws',\n '--line-prefix',\n '--no-patch',\n '--patch-with-raw',\n '--patch-with-stat',\n '--ext-diff',\n '--textconv',\n '--quiet',\n '--submodule',\n] as const;\nconst STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS = [\n '--json',\n '--null',\n '--null-data',\n '--color',\n '--colour',\n '--after-context',\n '--before-context',\n '--context',\n '--no-filename',\n '--no-line-number',\n '--heading',\n] as const;\nconst RTK_BINARY_ENV = 'DOOMPI_RTK_BINARY';\nconst PACKAGE_BY_PLATFORM: Readonly<Record<string, string>> = {\n 'darwin-arm64': '@agimon-ai/doompi-runner-rtk-darwin-arm64',\n 'darwin-x64': '@agimon-ai/doompi-runner-rtk-darwin-x64',\n 'linux-arm64': '@agimon-ai/doompi-runner-rtk-linux-arm64',\n 'linux-x64': '@agimon-ai/doompi-runner-rtk-linux-x64',\n};\n\ntype BinaryResolver = () => string | undefined;\n\nexport class RtkProcessor implements IRtkProcessor {\n /**\n * Latched once a binary proves unrunnable on this host.\n *\n * A glibc build installed on a musl host satisfies os and cpu, so it\n * resolves and then fails to exec. Without this the same doomed spawn is\n * paid on every command, and each one is reported as a processing failure\n * rather than the absence it actually is.\n */\n private unrunnable = false;\n\n constructor(\n private readonly resolveBinary: BinaryResolver = bundledBinary,\n private readonly timeoutMs = DEFAULT_RTK_TIMEOUT_MS,\n ) {}\n\n async process(request: RtkProcessRequest): Promise<RtkProcessResult> {\n const filter = classifyRtkFilter(request.command);\n if (!filter) return { kind: 'skipped' };\n\n let inputBytes: number;\n try {\n inputBytes = fs.statSync(request.logPath).size;\n } catch {\n return { kind: 'fallback', warning: RTK_FAILED_WARNING };\n }\n if (inputBytes > RTK_STDIN_MAX_BYTES) return { kind: 'fallback', warning: RTK_OVERSIZED_WARNING };\n\n if (this.unrunnable) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n\n let binary: string | undefined;\n try {\n binary = this.resolveBinary();\n if (!binary) return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n if ((fs.statSync(binary).mode & 0o111) === 0) fs.chmodSync(binary, EXECUTABLE_MODE);\n } catch {\n return { kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING };\n }\n\n return this.pipe(binary, filter, request.logPath, inputBytes);\n }\n\n private pipe(binary: string, filter: RtkFilter, logPath: string, inputBytes: number): Promise<RtkProcessResult> {\n return new Promise((resolve) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawn(binary, ['pipe', '-f', filter], { stdio: 'pipe' });\n } catch {\n this.unrunnable = true;\n resolve({ kind: 'fallback', warning: RTK_UNAVAILABLE_WARNING });\n return;\n }\n\n const input = fs.createReadStream(logPath);\n const outputLimit = getResultMaxBytes() * OUTPUT_BUFFER_FACTOR;\n let head: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let output: Buffer<ArrayBufferLike> = Buffer.alloc(0);\n let outputBytes = 0;\n let newlineCount = 0;\n let lastByte: number | undefined;\n let settled = false;\n let fallbackWarning: string | undefined;\n let killWaitTimer: ReturnType<typeof setTimeout> | undefined;\n\n const finish = (result: RtkProcessResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (killWaitTimer) clearTimeout(killWaitTimer);\n input.destroy();\n resolve(result);\n };\n const stop = (warning: string): void => {\n if (settled) return;\n fallbackWarning = warning;\n let killed = false;\n try {\n killed = child.kill('SIGKILL');\n } catch {\n finish({ kind: 'fallback', warning });\n return;\n }\n if (!killed) {\n finish({ kind: 'fallback', warning });\n return;\n }\n killWaitTimer ??= setTimeout(() => finish({ kind: 'fallback', warning }), RTK_KILL_WAIT_MS);\n };\n const timer = setTimeout(() => stop(RTK_TIMEOUT_WARNING), this.timeoutMs);\n\n child.stdout.on('data', (chunk: Buffer) => {\n outputBytes += chunk.byteLength;\n for (const byte of chunk) if (byte === 0x0a) newlineCount += 1;\n lastByte = chunk.at(-1);\n if (head.byteLength < outputLimit) {\n head = Buffer.concat([head, chunk.subarray(0, outputLimit - head.byteLength)]);\n }\n output = appendTail(output, chunk, outputLimit);\n });\n child.stderr.resume();\n child.once('error', (error: NodeJS.ErrnoException) => {\n // Reaching the process is a precondition, not part of processing, so a\n // failure to start reports absence and is not attempted again.\n if (error.code === 'ENOENT' || error.code === 'EACCES' || error.code === 'ENOEXEC') {\n this.unrunnable = true;\n stop(RTK_UNAVAILABLE_WARNING);\n return;\n }\n stop(RTK_FAILED_WARNING);\n });\n child.stdin.once('error', () => stop(RTK_FAILED_WARNING));\n input.once('error', () => stop(RTK_FAILED_WARNING));\n child.once('close', (code) => {\n if (fallbackWarning) {\n finish({ kind: 'fallback', warning: fallbackWarning });\n return;\n }\n const processedOutput = decodeUtf8Tail(output);\n if (code !== 0 || (inputBytes > 0 && processedOutput.trim().length === 0)) {\n finish({ kind: 'fallback', warning: RTK_FAILED_WARNING });\n return;\n }\n const lines = newlineCount + (outputBytes > 0 && lastByte !== 0x0a ? 1 : 0);\n finish({\n kind: 'processed',\n result: {\n filter,\n // Only meaningful once the stream outgrew the buffer; below that\n // `output` already starts at byte zero.\n head: outputBytes > output.byteLength ? head.toString('utf8') : '',\n output: processedOutput,\n bytes: outputBytes,\n lines,\n },\n });\n });\n\n input.pipe(child.stdin);\n });\n }\n}\n\n/**\n * Classifies only whitespace-delimited single commands. Shell syntax, expansion,\n * quoting, and globbing are rejected instead of being partially interpreted.\n */\nexport function classifyRtkFilter(command: string): RtkFilter | undefined {\n if (SHELL_CONTROL_PATTERN.test(command)) return undefined;\n const trimmed = command.trim();\n if (!trimmed) return undefined;\n const tokens = trimmed.split(/[ \\t]+/u);\n if (tokens.some((token) => !SIMPLE_TOKEN_PATTERN.test(token))) return undefined;\n\n if (tokens[0] === 'cargo' && tokens[1] === 'test') {\n if (tokens.includes('--') || tokens.some((token) => token.startsWith('--message-format') || token === '--json')) {\n return undefined;\n }\n return 'cargo-test';\n }\n if (tokens[0] === 'pytest') {\n if (tokens.some((token) => token.startsWith('--json-report'))) return undefined;\n return 'pytest';\n }\n if (tokens[0] === 'git' && tokens[1] === 'diff') {\n if (\n tokens.some((token) =>\n GIT_DIFF_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n )\n ) {\n return undefined;\n }\n return 'git-diff';\n }\n if (tokens[0] === 'grep') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens)) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'rg') {\n if (tokens.length < 4 || incompatibleStructuredSearch(tokens) || !tokens.includes('--no-heading')) return undefined;\n return hasShortOrLongOption(tokens, 'n', '--line-number') && hasShortOrLongOption(tokens, 'H', '--with-filename')\n ? 'grep'\n : undefined;\n }\n if (tokens[0] === 'go' && tokens[1] === 'test') {\n let jsonOutput = false;\n for (const token of tokens.slice(2)) {\n if (token === '-json' || token === '-json=true') jsonOutput = true;\n if (token === '-json=false') jsonOutput = false;\n }\n return jsonOutput ? 'go-test' : undefined;\n }\n if (tokens[0] === 'ruff' && tokens[1] === 'check') {\n const jsonOutput = tokens.some(\n (token, index) =>\n token === '--output-format=json' || (token === '--output-format' && tokens[index + 1] === 'json'),\n );\n return jsonOutput ? 'ruff-check' : undefined;\n }\n return undefined;\n}\n\nexport function rtkPackageForTarget(platform: string, architecture: string): string | undefined {\n const target = `${platform}-${architecture}`;\n const packageName = PACKAGE_BY_PLATFORM[target];\n if (!packageName) process.emitWarning(`Bundled RTK binary is unavailable for unsupported target ${target}`);\n return packageName;\n}\n\nfunction bundledBinary(): string | undefined {\n const configured = process.env[RTK_BINARY_ENV];\n if (configured !== undefined && configured !== '') return configured;\n const packageName = rtkPackageForTarget(process.platform, process.arch);\n if (!packageName) return undefined;\n try {\n const require = createRequire(import.meta.url);\n const manifest = require.resolve(`${packageName}/package.json`);\n return path.join(path.dirname(manifest), 'vendor', 'bin', 'rtk');\n } catch {\n return undefined;\n }\n}\n\nfunction appendTail(\n current: Buffer<ArrayBufferLike>,\n chunk: Buffer<ArrayBufferLike>,\n limit: number,\n): Buffer<ArrayBufferLike> {\n if (chunk.byteLength >= limit) return Buffer.from(chunk.subarray(chunk.byteLength - limit));\n const combined = Buffer.concat([current, chunk]);\n return combined.byteLength > limit ? Buffer.from(combined.subarray(combined.byteLength - limit)) : combined;\n}\n\nfunction decodeUtf8Tail(output: Buffer<ArrayBufferLike>): string {\n let start = 0;\n while (start < output.byteLength && (output[start]! & 0xc0) === 0x80) start += 1;\n return output.subarray(start).toString('utf8');\n}\nfunction hasShortOrLongOption(tokens: readonly string[], short: string, long: string): boolean {\n return tokens.some(\n (token) => token === long || (token.startsWith('-') && !token.startsWith('--') && token.slice(1).includes(short)),\n );\n}\n\nfunction incompatibleStructuredSearch(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) =>\n (token.startsWith('-') && !token.startsWith('--') && /[0ABCILNchzZ]/u.test(token.slice(1))) ||\n STRUCTURED_SEARCH_INCOMPATIBLE_OPTIONS.some((option) => token === option || token.startsWith(`${option}=`)),\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-runner",
3
- "version": "0.0.1-alpha.49",
3
+ "version": "0.0.1-alpha.50",
4
4
  "description": "Supervised shell execution, background process control, and run logs for Pi coding agents.",
5
5
  "keywords": [
6
6
  "ai",
@@ -324,12 +324,12 @@
324
324
  "@xterm/headless": "6.0.0",
325
325
  "hono": "4.13.5",
326
326
  "typebox": "1.3.19",
327
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.48",
328
- "@agimon-ai/doompi-ui": "0.0.1-alpha.49",
329
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.9",
330
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.48",
331
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.12",
332
- "@agimon-ai/doompi-web-security": "0.0.1-alpha.10"
327
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.49",
328
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.49",
329
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.50",
330
+ "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.13",
331
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.10",
332
+ "@agimon-ai/doompi-web-security": "0.0.1-alpha.11"
333
333
  },
334
334
  "devDependencies": {
335
335
  "@earendil-works/pi-coding-agent": "0.84.4",
@@ -357,14 +357,14 @@
357
357
  }
358
358
  },
359
359
  "optionalDependencies": {
360
- "@agimon-ai/doompi-runner-rmux-darwin-arm64": "0.0.1-alpha.46",
361
- "@agimon-ai/doompi-runner-rmux-darwin-x64": "0.0.1-alpha.46",
362
- "@agimon-ai/doompi-runner-rmux-linux-x64": "0.0.1-alpha.46",
363
- "@agimon-ai/doompi-runner-rtk-darwin-arm64": "0.0.1-alpha.46",
364
- "@agimon-ai/doompi-runner-rtk-darwin-x64": "0.0.1-alpha.46",
365
- "@agimon-ai/doompi-runner-rtk-linux-arm64": "0.0.1-alpha.46",
366
- "@agimon-ai/doompi-runner-rtk-linux-x64": "0.0.1-alpha.46",
367
- "@agimon-ai/doompi-runner-rmux-linux-arm64": "0.0.1-alpha.46"
360
+ "@agimon-ai/doompi-runner-rmux-darwin-arm64": "0.0.1-alpha.47",
361
+ "@agimon-ai/doompi-runner-rmux-darwin-x64": "0.0.1-alpha.47",
362
+ "@agimon-ai/doompi-runner-rmux-linux-arm64": "0.0.1-alpha.47",
363
+ "@agimon-ai/doompi-runner-rtk-darwin-arm64": "0.0.1-alpha.47",
364
+ "@agimon-ai/doompi-runner-rmux-linux-x64": "0.0.1-alpha.47",
365
+ "@agimon-ai/doompi-runner-rtk-darwin-x64": "0.0.1-alpha.47",
366
+ "@agimon-ai/doompi-runner-rtk-linux-arm64": "0.0.1-alpha.47",
367
+ "@agimon-ai/doompi-runner-rtk-linux-x64": "0.0.1-alpha.47"
368
368
  },
369
369
  "engines": {
370
370
  "node": ">=22.19.0"