@agimon-ai/workflow-mcp 0.2.9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli.cjs +4 -4
- package/dist/cli.mjs +4 -4
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/stdio-CMbfCrs1.cjs +55 -0
- package/dist/stdio-k5DKbtyw.mjs +55 -0
- package/package.json +5 -5
- package/dist/stdio-BPkG6mPS.cjs +0 -55
- package/dist/stdio-CAi-NNxh.mjs +0 -55
package/README.md
CHANGED
|
@@ -39,6 +39,12 @@ Stop a running workflow through the registry:
|
|
|
39
39
|
workflow-mcp stop-workflow <run-key> --workspace default
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
Recover a failed workflow from its recorded failed job:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
workflow-mcp recover-workflow <run-key> --workspace default
|
|
46
|
+
```
|
|
47
|
+
|
|
42
48
|
## Documentation
|
|
43
49
|
|
|
44
50
|
- [CLI reference](https://github.com/AgiFlow/public-packages-internals/blob/main/packages/mcp/workflow-mcp/docs/cli.md) — all commands, flags, and examples
|
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require(`./stdio-
|
|
3
|
-
`,`utf8`)},workflowStatusFile:process.env[u]};async function te(e=ee){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=
|
|
4
|
-
`,`utf8`)},workflowStatusFile:process.env[
|
|
5
|
-
`,`utf8`)},workflowStatusFile:process.env[y]},S={readStdin:N,readStatusFile:P,writeSessionBinding:async(e,t)=>{await(0,n.writeFile)(e,`${JSON.stringify({sessionId:t})}\n`,`utf8`)},workflowStatusFile:process.env[y]};async function C(e=S){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=D(n);if(!r?.session_id)return;let i=await e.readStatusFile(t);i===null||i.trim().length>0||await e.writeSessionBinding(t,r.session_id)}async function w(e=x){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=O(n);if(!r||r.hook_event_name&&r.hook_event_name!==`Stop`||r.stop_hook_active&&!r.hasTaskRegistry)return;let i=A(await e.readStatusFile(t));if(!r.session_id||r.session_id!==i)return;let a=k(r.transcript_path);if(!a)return;let o=await E(e,a),s=o.sessionMeta;if(!s||r.session_id&&s.id&&r.session_id!==s.id||j(s.source))return;let c=r.hasTaskRegistry&&(r.background_tasks?.length??0)>0,l=r.hasTaskRegistry&&(r.session_crons?.length??0)>0;if(c||!r.hasTaskRegistry&&o.hasPendingWork){e.writeStdout?.(T(`In-progress work is still running. Wait for spawned agents and running tool calls to finish before ending the turn.`));return}l||await e.writeStatusFile(t)}function T(e){return`${JSON.stringify({decision:`block`,reason:e,suppressOutput:!0})}\n`}async function E(e,t){return e.readTranscriptState?e.readTranscriptState(t):e.readTranscriptHead===F?I(t):{sessionMeta:await e.readTranscriptHead(t),hasPendingWork:!1}}function D(e){try{let t=JSON.parse(e);return M(t)?{session_id:typeof t.session_id==`string`?t.session_id:void 0}:null}catch{return null}}function O(e){try{let t=JSON.parse(e);if(!M(t))return null;let n=Array.isArray(t.background_tasks)?t.background_tasks:void 0,r=Array.isArray(t.session_crons)?t.session_crons:void 0;return{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0,transcript_path:k(t.transcript_path),stop_hook_active:t.stop_hook_active===!0,background_tasks:n,session_crons:r,hasTaskRegistry:n!==void 0||r!==void 0}}catch{return null}}function k(e){return typeof e==`string`&&e.trim().length>0?e:null}function A(e){if(!e)return null;let t=e.trim();if(!t||t===`YES`)return null;try{let e=JSON.parse(t);return typeof e.sessionId==`string`&&e.sessionId.length>0?e.sessionId:null}catch{return null}}function j(e){return typeof e==`string`?e.startsWith(`subagent_`)||e.startsWith(`internal_`):M(e)?be.some(t=>t in e):!1}function M(e){return typeof e==`object`&&!!e}async function N(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function P(e){try{return await(0,n.readFile)(e,`utf8`)}catch{return null}}async function F(e){return(await I(e)).sessionMeta}async function I(e){let t=(0,i.createReadStream)(e,{encoding:`utf8`}),n=(0,o.createInterface)({input:t,crlfDelay:1/0}),r=new Set,a=new Map,s=null;try{for await(let e of n){let t=e.trim();if(t.length===0)continue;let n=Ce(t);n&&(s||=we(n),Te(n,r),Ee(n,a))}}finally{n.close(),t.destroy()}return{sessionMeta:s,hasPendingWork:r.size>0||Array.from(a.values()).some(z)}}function Ce(e){try{let t=JSON.parse(e);return M(t)?t:null}catch{return null}}function we(e){return e.type===b?e.payload??null:e.item?.type===b?e.item.payload??null:null}function Te(e,t){let n=B(e,[`payload`,`item`])??B(e,[`item`,`payload`,`item`]);if(!n)return;let r=V(n.id),i=V(n.type);!r||!i||!Se.has(i)||(z(n.status)?t.add(r):t.delete(r))}function Ee(e,t){let n=B(e,[`payload`])??B(e,[`item`,`payload`]);if(!n||!V(n.type)?.startsWith(`collab_`))return;L(t,V(n.new_thread_id),n.status),L(t,V(n.receiver_thread_id),n.status);let r=B(n,[`statuses`]);if(r)for(let[e,n]of Object.entries(r))L(t,e,n)}function L(e,t,n){let r=R(n);!t||!r||e.set(t,r)}function R(e){if(typeof e==`string`)return e;if(!M(e))return null;let[t]=Object.keys(e);return t??null}function z(e){let t=typeof e==`string`?e:R(e);return t!==null&&xe.has(t)}function B(e,t){let n=e;for(let e of t){if(!M(n))return null;n=n[e]}return M(n)?n:null}function V(e){return typeof e==`string`&&e.length>0?e:void 0}const De=new s.Command(`codex-session-start-hook`).description(`Codex session-start hook: binds ${y} to the first workflow Codex session id`).action(async()=>{await C()}),Oe=new s.Command(`codex-stop-hook`).description(`Codex stop hook: reads stop-hook JSON on stdin and marks ${y} when the root session ends`).action(async()=>{await w()}),H=`list-crons`;function ke(t={}){let n=t.createService??(()=>new e.o),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command(H).description(`List cron jobs scheduled via workflow-mcp`).action(async()=>{try{let e=await n().list();a(`${JSON.stringify(e,null,2)}\n`),r(0)}catch(e){let t=new p(`Error listing cron jobs.`,`LIST_CRONS_COMMAND_FAILED`,{command:H},{cause:e});i(`${t.message} [${t.code}]`,t),r(1)}})}const Ae=ke(),U=`list-workflow-statuses`;function W(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw new s.InvalidArgumentError(`Expected a positive integer.`);return t}function je(t={}){let n=t.createRegistry??(()=>new e.a),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command(U).description(`List tracked workflow runs and their current stages`).option(`--page <number>`,`Page number to return`,W,1).option(`--page-size <number>`,`Number of workflow runs per page`,W,20).option(`-w, --workspace <name>`,`Filter workflow runs by workspace`,`all`).action(async e=>{try{let t=n(),i=e.workspace===`all`?void 0:e.workspace,o=await t.listRunsPage({page:e.page,pageSize:e.pageSize,workspace:i});a(`${JSON.stringify(o,null,2)}\n`),r(0)}catch(t){let n=new p(`Error listing workflow statuses.`,`LIST_WORKFLOW_STATUSES_FAILED`,{command:U,workspace:e.workspace??`all`},{cause:t});i(`${n.message} [${n.code}]`,n),r(1)}})}const Me=je(),Ne=()=>{try{return(0,c.createRequire)(require(`url`).pathToFileURL(__filename).href)(`@agimon-ai/foundation-process-registry`)}catch{return null}};function G(e,t){for(let n of e){if(n==null||typeof n!=`object`&&typeof n!=`function`)continue;let e=n;for(let n of t){let t=e[n];if(typeof t==`function`)return t}}return null}function Pe(e){let t=[e];if(e&&typeof e==`object`){let n=e.default;if(n&&(t.push(n),typeof n==`function`))try{t.push(new n)}catch{}}return t}async function Fe(e){let t=Ne();if(!t)return async()=>{};let n=Pe(t),r=G(n,[`registerProcess`,`register`,`registerProcessInstance`]),i=G(n,[`unregisterProcess`,`unregister`,`releaseProcess`]);if(!r)return async()=>{};try{await Promise.resolve(r({name:e,pid:process.pid,command:process.argv.join(` `)}))}catch{return async()=>{}}return i?async()=>{await Promise.resolve(i({name:e,pid:process.pid}))}:async()=>{}}async function Ie(e,t){await e.start();let n=async n=>{console.error(`\\nReceived ${n}, shutting down gracefully...`);let r=0;try{await e.stop()}catch(e){console.error(`Error during shutdown:`,e),r=1}try{await t()}catch(e){console.error(`Error during resource cleanup:`,e),r=1}process.exit(r)};process.on(`SIGINT`,()=>{n(`SIGINT`)}),process.on(`SIGTERM`,()=>{n(`SIGTERM`)})}async function Le(e){try{await e()}catch{}}const Re=new s.Command(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio`,`stdio`).option(`--service-name <name>`,`Service name for registry tracking`,`workflow-mcp`).action(async t=>{let n=await Fe(t.serviceName);try{let r=t.type.toLowerCase();r===`stdio`?await Ie(new e.t(e.n()),n):(console.error(`Unknown transport type: ${r}. Use: stdio`),process.exit(1))}catch(e){await Le(n),console.error(`Failed to start MCP server:`,e),process.exit(1)}}),K=`run-workflow`,q=`RUN_WORKFLOW_COMMAND_FAILED`,J=`WORKFLOW_MCP_BACKGROUND_CHILD`,Y=`workflow-mcp-background-run`;function X(e){if(!e||e.length===0)return;let t={};for(let n of e){let[e,...r]=n.split(`=`);t[e]=r.join(`=`)}return Object.keys(t).length>0?t:void 0}function Z(e){if(e.runner&&e.cliAgent&&e.runner!==e.cliAgent)throw new p(`Conflicting runner selectors.`,q,{cliAgent:e.cliAgent,command:K,runner:e.runner});return e.runner??e.cliAgent}function ze(e,n){let r=process.argv[1];if(!r)throw new p(`Unable to determine the workflow-mcp CLI entry point for background execution.`,`RUN_WORKFLOW_BACKGROUND_LAUNCH_FAILED`,{command:K,workflow:e,workspace:n.workspace});let i=[r,`run-workflow`,e];n.job&&i.push(`--job`,n.job);for(let e of n.input??[])i.push(`--input`,e);for(let e of n.env??[])i.push(`--env`,e);n.secretFile&&i.push(`--secret-file`,n.secretFile),n.dryRun&&i.push(`--dry-run`),n.continueOnError&&i.push(`--continue-on-error`),n.keepWorktree&&i.push(`--keep-worktree`);let a=Z(n);a&&i.push(`--runner`,a),n.prompt&&i.push(`--prompt`,n.prompt),n.name&&i.push(`--name`,n.name),n.workspace&&i.push(`--workspace`,n.workspace);let o=(0,t.spawn)(process.execPath,i,{detached:!0,env:{...process.env,[J]:`1`},stdio:`ignore`});return o.unref(),o.pid}async function Be(e,t){if(process.env[J]!==`1`)return async()=>{};let n=new a.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH),i=(0,r.resolve)(process.cwd()),o=process.env.NODE_ENV??`development`;return(await n.registerProcess({repositoryPath:i,serviceName:Y,serviceType:`tool`,environment:o,pid:process.pid,command:process.argv.join(` `),args:process.argv.slice(2),metadata:{workflow:e,workspace:t.workspace,job:t.job,name:t.name,runner:t.runner??t.cliAgent},force:!0})).success?async()=>{let e=await n.releaseProcess({repositoryPath:i,serviceName:Y,serviceType:`tool`,environment:o,pid:process.pid,kill:!1,releasePort:!1,force:!0});if(!e.success&&!e.error?.includes(`No matching process entry`))throw Error(e.error??`Failed to release workflow background process`)}:async()=>{}}function Ve(t={}){let n=t.createService??(()=>new e.r),r=t.exit??(e=>process.exit(e)),i=t.launchBackgroundRun??ze,a=t.registerBackgroundChild??Be,o=t.logError??((e,t)=>console.error(e,t)),c=t.logInfo??(e=>process.stdout.write(`${e}\n`));return new s.Command(K).description(`Run a GitHub Actions workflow file locally on macOS`).argument(`<workflow>`,`Path to the workflow YAML file`).option(`-j, --job <name>`,`Run only this job (and its dependencies)`).option(`-i, --input <key=value...>`,`Set workflow_dispatch input (repeatable)`).option(`-e, --env <key=value...>`,`Set extra environment variable (repeatable)`).option(`--secret-file <path>`,`Load secrets from a dotenv-style file`).option(`--dry-run`,`Print steps without executing`).option(`--continue-on-error`,`Continue past step failures`).option(`--runner <runner>`,`Preferred runner key for step command maps`).option(`--cli-agent <agent>`,`Deprecated alias for --runner`).option(`-p, --prompt <text>`,`User prompt for user_prompt trigger workflows`).option(`-n, --name <name>`,`Name for the workflow run context directory`).option(`-w, --workspace <name>`,`Workspace for workflow registry storage`).option(`--keep-worktree`,`Keep worktree on completion (skip merge and cleanup for retry)`).option(`--skip-launch`,`Skip launch-command delegation (used by inner invocations)`).option(`-b, --background`,`Run the workflow in a detached background process`).action(async(e,t)=>{try{if(t.background){let n=i(e,t);c(`Started workflow in background${n?` (PID: ${n})`:``}`),r(0);return}let o=await a(e,t),s=n();try{let n=Z(t),i=await s.run({cliAgent:t.cliAgent,runner:n,workflowPath:e,job:t.job,inputs:X(t.input),env:X(t.env),secretFile:t.secretFile,dryRun:t.dryRun,continueOnError:t.continueOnError,keepWorktree:t.keepWorktree,prompt:t.prompt,name:t.name,workspace:t.workspace,skipLaunch:t.skipLaunch});await o(),r(i.exitCode)}catch(e){throw await o(),e}}catch(n){let i=n instanceof p?n:new p(`Error executing run-workflow.`,q,{background:!!t.background,command:K,workflow:e,workspace:t.workspace},{cause:n});o(`${i.message} [${i.code}]`,i),r(1)}})}const He=Ve(),Q=`schedule-cron`;function Ue(t={}){let n=t.createService??(()=>new e.o),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(`${e}\n`));return new s.Command(Q).description(`Schedule a headless Claude Code or Codex CLI run via system crontab`).argument(`<name>`,`Unique name for this cron job`).option(`-d, --cwd <path>`,`Working directory for the CLI run`,process.cwd()).option(`-c, --cli <cli>`,`CLI to use: claude or codex`,e.s).option(`-p, --prompt <text>`,`Prompt to pass to the CLI`).option(`-f, --prompt-file <path>`,`Path to a file whose content is used as the prompt (read at cron execution time)`).option(`-s, --schedule <cron>`,`Cron expression (e.g., "*/10 * * * *")`).option(`-i, --interval-minutes <minutes>`,`Run every N minutes (alternative to --schedule)`).action(async(t,o)=>{try{let i=e.c.parse({name:t,cwd:o.cwd??process.cwd(),cli:o.cli??`claude`,prompt:o.prompt,promptFile:o.promptFile,schedule:o.schedule,intervalMinutes:o.intervalMinutes?Number.parseInt(o.intervalMinutes,10):void 0}),s=await n().schedule(i);a(`Scheduled cron job "${s.name}" with schedule: ${s.schedule}`),a(`CLI: ${s.cli} | CWD: ${s.cwd}`),s.prompt&&a(`Prompt: ${s.prompt}`),r(0)}catch(e){let n=e instanceof p?e:new p(`Error scheduling cron job.`,`SCHEDULE_CRON_COMMAND_FAILED`,{command:Q,name:t},{cause:e});i(`${n.message} [${n.code}]`,n),r(1)}})}const We=Ue(),$=`stop-workflow`;function Ge(t={}){let n=t.createRegistry??(()=>new e.a),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command($).description(`Request a running workflow to stop gracefully`).argument(`<run-key>`,`Running workflow run key`).option(`-w, --workspace <name>`,`Workspace containing the running workflow`,`default`).option(`-r, --reason <text>`,`Optional stop reason to record`).action(async(e,t)=>{try{let i=n(),o=await i.requestStop(t.workspace,e,t.reason);a(`${JSON.stringify({reason:o.reason,requestedAt:o.requestedAt,runKey:e,workspace:i.resolveWorkspace(t.workspace)},null,2)}\n`),r(0)}catch(n){let a=new p(`Error requesting workflow stop.`,`STOP_WORKFLOW_FAILED`,{command:$,runKey:e,workspace:t.workspace},{cause:n});i(`${a.message} [${a.code}]`,a),r(1)}})}const Ke=Ge();async function qe(){let e=new s.Command;e.name(`workflow-mcp`).description(`MCP server for running GitHub Actions workflows locally`).version(l),e.addCommand(Ae),e.addCommand(Me),e.addCommand(le),e.addCommand(se),e.addCommand(ve),e.addCommand(ye),e.addCommand(De),e.addCommand(Oe),e.addCommand(Re),e.addCommand(He),e.addCommand(We),e.addCommand(Ke),await e.parseAsync(process.argv)}qe().catch(e=>{console.error(`[CLI_STARTUP_ERROR] workflow-mcp startup failed:`,e),process.exit(1)});
|
|
2
|
+
const e=require(`./stdio-CMbfCrs1.cjs`);let t=require(`node:child_process`),n=require(`node:fs/promises`),r=require(`node:path`),i=require(`node:fs`),a=require(`@agimon-ai/foundation-process-registry`),o=require(`node:readline`),s=require(`commander`),c=require(`node:module`);var l=`0.3.0`;const u=`WORKFLOW_STATUS_FILE`,ee={readStdin:oe,readStatusFile:se,writeStdout:e=>{process.stdout.write(e)},writeStatusFile:async e=>{await(0,n.writeFile)(e,`YES
|
|
3
|
+
`,`utf8`)},workflowStatusFile:process.env[u]};async function te(e=ee){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=ne(n);if(!r?.conversationId)return;let i=await e.readStatusFile(t);if(!(i===null||i.trim()===`YES`)){if(!r.fullyIdle){e.writeStdout?.(re(`In-progress work is still running. Continue the execution loop until background tasks finish.`));return}ie(r.error)||r.terminationReason!==`model_stop`||await e.writeStatusFile(t)}}function ne(e){try{let t=JSON.parse(e);return ae(t)?{conversationId:typeof t.conversationId==`string`?t.conversationId:void 0,transcriptPath:d(t.transcriptPath),terminationReason:typeof t.terminationReason==`string`?t.terminationReason:void 0,error:typeof t.error==`string`?t.error:void 0,fullyIdle:t.fullyIdle===!0}:null}catch{return null}}function re(e){return`${JSON.stringify({decision:`continue`,reason:e})}\n`}function d(e){return typeof e==`string`&&e.trim().length>0?e:null}function ie(e){return typeof e==`string`&&e.trim().length>0}function ae(e){return typeof e==`object`&&!!e}async function oe(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function se(e){try{return await(0,n.readFile)(e,`utf8`)}catch{return null}}const ce=new s.Command(`antigravity-stop-hook`).description(`Antigravity stop hook: reads Stop hook JSON on stdin and marks ${u} when the execution is fully idle`).action(async()=>{await te()});var f=class extends Error{constructor(e,t,n={},r){super(e,r),this.code=t,this.context=n,this.name=`WorkflowCommandError`}};const p=`check-codex-quota`;function le(t={}){let n=t.createService??(()=>new e.a),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command(p).description(`Check whether Codex quota is currently blocking new work`).action(async()=>{try{let e=await n().getQuotaStatus();if(a(`${JSON.stringify({blockingLimit:e?.blockingLimit??null,planType:e?.planType??null},null,2)}\n`),e?.blockingLimit){let t=new f(`Codex quota is blocking work at ${e.blockingLimit.limitId}/${e.blockingLimit.window}.`,`CODEX_QUOTA_BLOCKED`,{command:p,limitId:e.blockingLimit.limitId,limitName:e.blockingLimit.limitName,window:e.blockingLimit.window});i(`${t.message} [${t.code}]`,t),r(2);return}r(0)}catch(e){let t=e instanceof f?e:new f(`Error checking Codex quota.`,`CHECK_CODEX_QUOTA_COMMAND_FAILED`,{command:p},{cause:e});i(`${t.message} [${t.code}]`,t),r(1)}})}const ue=le(),m=`WORKFLOW_STATUS_FILE`,de={readStdin:_,readStatusFile:v,writeStdout:e=>{process.stdout.write(e)},writeStatusFile:async e=>{await(0,n.writeFile)(e,`YES
|
|
4
|
+
`,`utf8`)},workflowStatusFile:process.env[m]},h={readStdin:_,readStatusFile:v,writeSessionBinding:async(e,t)=>{await(0,n.writeFile)(e,`${JSON.stringify({sessionId:t})}\n`,`utf8`)},workflowStatusFile:process.env[m]};async function fe(e=h){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=he(n);if(!r?.session_id||r.hook_event_name&&r.hook_event_name!==`SessionStart`)return;let i=await e.readStatusFile(t);i===null||i.trim().length>0||await e.writeSessionBinding(t,r.session_id)}async function pe(e=de){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=ge(n);if(!r||r.hook_event_name&&r.hook_event_name!==`Stop`)return;let i=_e(await e.readStatusFile(t));if(!r.session_id||r.session_id!==i)return;let a=r.background_tasks.length>0,o=r.session_crons.length>0;if(a){e.writeStdout?.(me(`In-progress work is still running. Wait for spawned agents and running tool calls to finish before ending the turn.`));return}o||await e.writeStatusFile(t)}function me(e){return`${JSON.stringify({decision:`block`,reason:e,suppressOutput:!0})}\n`}function he(e){try{let t=JSON.parse(e);return g(t)?{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0}:null}catch{return null}}function ge(e){try{let t=JSON.parse(e);if(!g(t))return null;let n=Array.isArray(t.background_tasks)?t.background_tasks:[],r=Array.isArray(t.session_crons)?t.session_crons:[];return{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0,stop_hook_active:t.stop_hook_active===!0,background_tasks:n,session_crons:r}}catch{return null}}function _e(e){if(!e)return null;let t=e.trim();if(!t||t===`YES`)return null;try{let e=JSON.parse(t);return typeof e.sessionId==`string`&&e.sessionId.length>0?e.sessionId:null}catch{return null}}function g(e){return typeof e==`object`&&!!e}async function _(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function v(e){try{return await(0,n.readFile)(e,`utf8`)}catch{return null}}const ve=new s.Command(`claude-session-start-hook`).description(`Claude Code SessionStart hook: binds ${m} to the root Claude session id`).action(async()=>{await fe()}),ye=new s.Command(`claude-stop-hook`).description(`Claude Code stop hook: reads Stop hook JSON on stdin and marks ${m} when the root session is complete`).action(async()=>{await pe()}),y=`WORKFLOW_STATUS_FILE`,b=`session_meta`,be=[`sub_agent`,`subagent`],xe=new Set([`in_progress`,`inProgress`,`running`,`pending_init`,`pendingInit`]),Se=new Set([`collabAgentToolCall`,`commandExecution`,`dynamicToolCall`,`imageGeneration`,`local_shell_call`,`mcpToolCall`]),Ce={readStdin:M,readStatusFile:N,readTranscriptHead:P,readTranscriptState:F,writeStdout:e=>{process.stdout.write(e)},writeStatusFile:async e=>{await(0,n.writeFile)(e,`YES
|
|
5
|
+
`,`utf8`)},workflowStatusFile:process.env[y]},x={readStdin:M,readStatusFile:N,writeSessionBinding:async(e,t)=>{await(0,n.writeFile)(e,`${JSON.stringify({sessionId:t})}\n`,`utf8`)},workflowStatusFile:process.env[y]};async function S(e=x){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=E(n);if(!r?.session_id)return;let i=await e.readStatusFile(t);i===null||i.trim().length>0||await e.writeSessionBinding(t,r.session_id)}async function C(e=Ce){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=D(n);if(!r||r.hook_event_name&&r.hook_event_name!==`Stop`||r.stop_hook_active&&!r.hasTaskRegistry)return;let i=k(await e.readStatusFile(t));if(!r.session_id||r.session_id!==i)return;let a=O(r.transcript_path);if(!a)return;let o=await T(e,a),s=o.sessionMeta;if(!s||r.session_id&&s.id&&r.session_id!==s.id||A(s.source))return;let c=r.hasTaskRegistry&&(r.background_tasks?.length??0)>0,l=r.hasTaskRegistry&&(r.session_crons?.length??0)>0;if(c||!r.hasTaskRegistry&&o.hasPendingWork){e.writeStdout?.(w(`In-progress work is still running. Wait for spawned agents and running tool calls to finish before ending the turn.`));return}l||await e.writeStatusFile(t)}function w(e){return`${JSON.stringify({decision:`block`,reason:e,suppressOutput:!0})}\n`}async function T(e,t){return e.readTranscriptState?e.readTranscriptState(t):e.readTranscriptHead===P?F(t):{sessionMeta:await e.readTranscriptHead(t),hasPendingWork:!1}}function E(e){try{let t=JSON.parse(e);return j(t)?{session_id:typeof t.session_id==`string`?t.session_id:void 0}:null}catch{return null}}function D(e){try{let t=JSON.parse(e);if(!j(t))return null;let n=Array.isArray(t.background_tasks)?t.background_tasks:void 0,r=Array.isArray(t.session_crons)?t.session_crons:void 0;return{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0,transcript_path:O(t.transcript_path),stop_hook_active:t.stop_hook_active===!0,background_tasks:n,session_crons:r,hasTaskRegistry:n!==void 0||r!==void 0}}catch{return null}}function O(e){return typeof e==`string`&&e.trim().length>0?e:null}function k(e){if(!e)return null;let t=e.trim();if(!t||t===`YES`)return null;try{let e=JSON.parse(t);return typeof e.sessionId==`string`&&e.sessionId.length>0?e.sessionId:null}catch{return null}}function A(e){return typeof e==`string`?e.startsWith(`subagent_`)||e.startsWith(`internal_`):j(e)?be.some(t=>t in e):!1}function j(e){return typeof e==`object`&&!!e}async function M(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function N(e){try{return await(0,n.readFile)(e,`utf8`)}catch{return null}}async function P(e){return(await F(e)).sessionMeta}async function F(e){let t=(0,i.createReadStream)(e,{encoding:`utf8`}),n=(0,o.createInterface)({input:t,crlfDelay:1/0}),r=new Set,a=new Map,s=null;try{for await(let e of n){let t=e.trim();if(t.length===0)continue;let n=we(t);n&&(s||=Te(n),Ee(n,r),De(n,a))}}finally{n.close(),t.destroy()}return{sessionMeta:s,hasPendingWork:r.size>0||Array.from(a.values()).some(R)}}function we(e){try{let t=JSON.parse(e);return j(t)?t:null}catch{return null}}function Te(e){return e.type===b?e.payload??null:e.item?.type===b?e.item.payload??null:null}function Ee(e,t){let n=z(e,[`payload`,`item`])??z(e,[`item`,`payload`,`item`]);if(!n)return;let r=B(n.id),i=B(n.type);!r||!i||!Se.has(i)||(R(n.status)?t.add(r):t.delete(r))}function De(e,t){let n=z(e,[`payload`])??z(e,[`item`,`payload`]);if(!n||!B(n.type)?.startsWith(`collab_`))return;I(t,B(n.new_thread_id),n.status),I(t,B(n.receiver_thread_id),n.status);let r=z(n,[`statuses`]);if(r)for(let[e,n]of Object.entries(r))I(t,e,n)}function I(e,t,n){let r=L(n);!t||!r||e.set(t,r)}function L(e){if(typeof e==`string`)return e;if(!j(e))return null;let[t]=Object.keys(e);return t??null}function R(e){let t=typeof e==`string`?e:L(e);return t!==null&&xe.has(t)}function z(e,t){let n=e;for(let e of t){if(!j(n))return null;n=n[e]}return j(n)?n:null}function B(e){return typeof e==`string`&&e.length>0?e:void 0}const Oe=new s.Command(`codex-session-start-hook`).description(`Codex session-start hook: binds ${y} to the first workflow Codex session id`).action(async()=>{await S()}),ke=new s.Command(`codex-stop-hook`).description(`Codex stop hook: reads stop-hook JSON on stdin and marks ${y} when the root session ends`).action(async()=>{await C()}),V=`list-crons`;function Ae(t={}){let n=t.createService??(()=>new e.s),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command(V).description(`List cron jobs scheduled via workflow-mcp`).action(async()=>{try{let e=await n().list();a(`${JSON.stringify(e,null,2)}\n`),r(0)}catch(e){let t=new f(`Error listing cron jobs.`,`LIST_CRONS_COMMAND_FAILED`,{command:V},{cause:e});i(`${t.message} [${t.code}]`,t),r(1)}})}const je=Ae(),H=`list-workflow-statuses`;function U(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw new s.InvalidArgumentError(`Expected a positive integer.`);return t}function Me(t={}){let n=t.createRegistry??(()=>new e.o),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command(H).description(`List tracked workflow runs and their current stages`).option(`--page <number>`,`Page number to return`,U,1).option(`--page-size <number>`,`Number of workflow runs per page`,U,20).option(`-w, --workspace <name>`,`Filter workflow runs by workspace`,`all`).action(async e=>{try{let t=n(),i=e.workspace===`all`?void 0:e.workspace,o=await t.listRunsPage({page:e.page,pageSize:e.pageSize,workspace:i});a(`${JSON.stringify(o,null,2)}\n`),r(0)}catch(t){let n=new f(`Error listing workflow statuses.`,`LIST_WORKFLOW_STATUSES_FAILED`,{command:H,workspace:e.workspace??`all`},{cause:t});i(`${n.message} [${n.code}]`,n),r(1)}})}const Ne=Me(),Pe=()=>{try{return(0,c.createRequire)(require(`url`).pathToFileURL(__filename).href)(`@agimon-ai/foundation-process-registry`)}catch{return null}};function W(e,t){for(let n of e){if(n==null||typeof n!=`object`&&typeof n!=`function`)continue;let e=n;for(let n of t){let t=e[n];if(typeof t==`function`)return t}}return null}function Fe(e){let t=[e];if(e&&typeof e==`object`){let n=e.default;if(n&&(t.push(n),typeof n==`function`))try{t.push(new n)}catch{}}return t}async function Ie(e){let t=Pe();if(!t)return async()=>{};let n=Fe(t),r=W(n,[`registerProcess`,`register`,`registerProcessInstance`]),i=W(n,[`unregisterProcess`,`unregister`,`releaseProcess`]);if(!r)return async()=>{};try{await Promise.resolve(r({name:e,pid:process.pid,command:process.argv.join(` `)}))}catch{return async()=>{}}return i?async()=>{await Promise.resolve(i({name:e,pid:process.pid}))}:async()=>{}}async function Le(e,t){await e.start();let n=async n=>{console.error(`\\nReceived ${n}, shutting down gracefully...`);let r=0;try{await e.stop()}catch(e){console.error(`Error during shutdown:`,e),r=1}try{await t()}catch(e){console.error(`Error during resource cleanup:`,e),r=1}process.exit(r)};process.on(`SIGINT`,()=>{n(`SIGINT`)}),process.on(`SIGTERM`,()=>{n(`SIGTERM`)})}async function Re(e){try{await e()}catch{}}const ze=new s.Command(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio`,`stdio`).option(`--service-name <name>`,`Service name for registry tracking`,`workflow-mcp`).action(async t=>{let n=await Ie(t.serviceName);try{let r=t.type.toLowerCase();r===`stdio`?await Le(new e.t(e.n()),n):(console.error(`Unknown transport type: ${r}. Use: stdio`),process.exit(1))}catch(e){await Re(n),console.error(`Failed to start MCP server:`,e),process.exit(1)}}),G=`recover-workflow`;function Be(t={}){let n=t.createService??(()=>new e.r),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t));return new s.Command(G).description(`Recover a failed workflow run from the local workflow registry`).argument(`<run-key>`,`Failed workflow run key`).option(`-w, --workspace <name>`,`Workspace containing the failed workflow run`,`default`).option(`-j, --job <name>`,`Override the job to recover from`).option(`--runner <runner>`,`Override the runner key for step command maps`).option(`--dry-run`,`Print recovery steps without executing`).action(async(e,t)=>{try{r((await n().recover({dryRun:t.dryRun,job:t.job,runKey:e,runner:t.runner,workspace:t.workspace})).exitCode)}catch(n){let a=new f(`Error recovering workflow.`,`RECOVER_WORKFLOW_FAILED`,{command:G,runKey:e,workspace:t.workspace},{cause:n});i(`${a.message} [${a.code}]`,a),r(1)}})}const Ve=Be(),K=`run-workflow`,q=`RUN_WORKFLOW_COMMAND_FAILED`,J=`WORKFLOW_MCP_BACKGROUND_CHILD`,Y=`workflow-mcp-background-run`;function X(e){if(!e||e.length===0)return;let t={};for(let n of e){let[e,...r]=n.split(`=`);t[e]=r.join(`=`)}return Object.keys(t).length>0?t:void 0}function Z(e){if(e.runner&&e.cliAgent&&e.runner!==e.cliAgent)throw new f(`Conflicting runner selectors.`,q,{cliAgent:e.cliAgent,command:K,runner:e.runner});return e.runner??e.cliAgent}function He(e,n){let r=process.argv[1];if(!r)throw new f(`Unable to determine the workflow-mcp CLI entry point for background execution.`,`RUN_WORKFLOW_BACKGROUND_LAUNCH_FAILED`,{command:K,workflow:e,workspace:n.workspace});let i=[r,`run-workflow`,e];n.job&&i.push(`--job`,n.job);for(let e of n.input??[])i.push(`--input`,e);for(let e of n.env??[])i.push(`--env`,e);n.secretFile&&i.push(`--secret-file`,n.secretFile),n.dryRun&&i.push(`--dry-run`),n.continueOnError&&i.push(`--continue-on-error`),n.keepWorktree&&i.push(`--keep-worktree`);let a=Z(n);a&&i.push(`--runner`,a),n.prompt&&i.push(`--prompt`,n.prompt),n.name&&i.push(`--name`,n.name),n.workspace&&i.push(`--workspace`,n.workspace);let o=(0,t.spawn)(process.execPath,i,{detached:!0,env:{...process.env,[J]:`1`},stdio:`ignore`});return o.unref(),o.pid}async function Ue(e,t){if(process.env[J]!==`1`)return async()=>{};let n=new a.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH),i=(0,r.resolve)(process.cwd()),o=process.env.NODE_ENV??`development`;return(await n.registerProcess({repositoryPath:i,serviceName:Y,serviceType:`tool`,environment:o,pid:process.pid,command:process.argv.join(` `),args:process.argv.slice(2),metadata:{workflow:e,workspace:t.workspace,job:t.job,name:t.name,runner:t.runner??t.cliAgent},force:!0})).success?async()=>{let e=await n.releaseProcess({repositoryPath:i,serviceName:Y,serviceType:`tool`,environment:o,pid:process.pid,kill:!1,releasePort:!1,force:!0});if(!e.success&&!e.error?.includes(`No matching process entry`))throw Error(e.error??`Failed to release workflow background process`)}:async()=>{}}function We(t={}){let n=t.createService??(()=>new e.i),r=t.exit??(e=>process.exit(e)),i=t.launchBackgroundRun??He,a=t.registerBackgroundChild??Ue,o=t.logError??((e,t)=>console.error(e,t)),c=t.logInfo??(e=>process.stdout.write(`${e}\n`));return new s.Command(K).description(`Run a GitHub Actions workflow file locally on macOS`).argument(`<workflow>`,`Path to the workflow YAML file`).option(`-j, --job <name>`,`Run only this job (and its dependencies)`).option(`-i, --input <key=value...>`,`Set workflow_dispatch input (repeatable)`).option(`-e, --env <key=value...>`,`Set extra environment variable (repeatable)`).option(`--secret-file <path>`,`Load secrets from a dotenv-style file`).option(`--dry-run`,`Print steps without executing`).option(`--continue-on-error`,`Continue past step failures`).option(`--runner <runner>`,`Preferred runner key for step command maps`).option(`--cli-agent <agent>`,`Deprecated alias for --runner`).option(`-p, --prompt <text>`,`User prompt for user_prompt trigger workflows`).option(`-n, --name <name>`,`Name for the workflow run context directory`).option(`-w, --workspace <name>`,`Workspace for workflow registry storage`).option(`--keep-worktree`,`Keep worktree on completion (skip merge and cleanup for retry)`).option(`--skip-launch`,`Skip launch-command delegation (used by inner invocations)`).option(`-b, --background`,`Run the workflow in a detached background process`).action(async(e,t)=>{try{if(t.background){let n=i(e,t);c(`Started workflow in background${n?` (PID: ${n})`:``}`),r(0);return}let o=await a(e,t),s=n();try{let n=Z(t),i=await s.run({cliAgent:t.cliAgent,runner:n,workflowPath:e,job:t.job,inputs:X(t.input),env:X(t.env),secretFile:t.secretFile,dryRun:t.dryRun,continueOnError:t.continueOnError,keepWorktree:t.keepWorktree,prompt:t.prompt,name:t.name,workspace:t.workspace,skipLaunch:t.skipLaunch});await o(),r(i.exitCode)}catch(e){throw await o(),e}}catch(n){let i=n instanceof f?n:new f(`Error executing run-workflow.`,q,{background:!!t.background,command:K,workflow:e,workspace:t.workspace},{cause:n});o(`${i.message} [${i.code}]`,i),r(1)}})}const Ge=We(),Q=`schedule-cron`;function Ke(t={}){let n=t.createService??(()=>new e.s),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(`${e}\n`));return new s.Command(Q).description(`Schedule a headless Claude Code or Codex CLI run via system crontab`).argument(`<name>`,`Unique name for this cron job`).option(`-d, --cwd <path>`,`Working directory for the CLI run`,process.cwd()).option(`-c, --cli <cli>`,`CLI to use: claude or codex`,e.c).option(`-p, --prompt <text>`,`Prompt to pass to the CLI`).option(`-f, --prompt-file <path>`,`Path to a file whose content is used as the prompt (read at cron execution time)`).option(`-s, --schedule <cron>`,`Cron expression (e.g., "*/10 * * * *")`).option(`-i, --interval-minutes <minutes>`,`Run every N minutes (alternative to --schedule)`).action(async(t,o)=>{try{let i=e.l.parse({name:t,cwd:o.cwd??process.cwd(),cli:o.cli??`claude`,prompt:o.prompt,promptFile:o.promptFile,schedule:o.schedule,intervalMinutes:o.intervalMinutes?Number.parseInt(o.intervalMinutes,10):void 0}),s=await n().schedule(i);a(`Scheduled cron job "${s.name}" with schedule: ${s.schedule}`),a(`CLI: ${s.cli} | CWD: ${s.cwd}`),s.prompt&&a(`Prompt: ${s.prompt}`),r(0)}catch(e){let n=e instanceof f?e:new f(`Error scheduling cron job.`,`SCHEDULE_CRON_COMMAND_FAILED`,{command:Q,name:t},{cause:e});i(`${n.message} [${n.code}]`,n),r(1)}})}const qe=Ke(),$=`stop-workflow`;function Je(t={}){let n=t.createRegistry??(()=>new e.o),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new s.Command($).description(`Request a running workflow to stop gracefully`).argument(`<run-key>`,`Running workflow run key`).option(`-w, --workspace <name>`,`Workspace containing the running workflow`,`default`).option(`-r, --reason <text>`,`Optional stop reason to record`).action(async(e,t)=>{try{let i=n(),o=await i.requestStop(t.workspace,e,t.reason);a(`${JSON.stringify({reason:o.reason,requestedAt:o.requestedAt,runKey:e,workspace:i.resolveWorkspace(t.workspace)},null,2)}\n`),r(0)}catch(n){let a=new f(`Error requesting workflow stop.`,`STOP_WORKFLOW_FAILED`,{command:$,runKey:e,workspace:t.workspace},{cause:n});i(`${a.message} [${a.code}]`,a),r(1)}})}const Ye=Je();async function Xe(){let e=new s.Command;e.name(`workflow-mcp`).description(`MCP server for running GitHub Actions workflows locally`).version(l),e.addCommand(je),e.addCommand(Ne),e.addCommand(ue),e.addCommand(ce),e.addCommand(ve),e.addCommand(ye),e.addCommand(Oe),e.addCommand(ke),e.addCommand(ze),e.addCommand(Ve),e.addCommand(Ge),e.addCommand(qe),e.addCommand(Ye),await e.parseAsync(process.argv)}Xe().catch(e=>{console.error(`[CLI_STARTUP_ERROR] workflow-mcp startup failed:`,e),process.exit(1)});
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,c as t,i as n,
|
|
3
|
-
`,`utf8`)},workflowStatusFile:process.env[
|
|
4
|
-
`,`utf8`)},workflowStatusFile:process.env[
|
|
5
|
-
`,`utf8`)},workflowStatusFile:process.env[C]},k={readStdin:M,readStatusFile:N,writeSessionBinding:async(e,t)=>{await d(e,`${JSON.stringify({sessionId:t})}\n`,`utf8`)},workflowStatusFile:process.env[C]};async function Te(e=k){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=ke(n);if(!r?.session_id)return;let i=await e.readStatusFile(t);i===null||i.trim().length>0||await e.writeSessionBinding(t,r.session_id)}async function Ee(e=O){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=Ae(n);if(!r||r.hook_event_name&&r.hook_event_name!==`Stop`||r.stop_hook_active&&!r.hasTaskRegistry)return;let i=je(await e.readStatusFile(t));if(!r.session_id||r.session_id!==i)return;let a=A(r.transcript_path);if(!a)return;let o=await Oe(e,a),s=o.sessionMeta;if(!s||r.session_id&&s.id&&r.session_id!==s.id||Me(s.source))return;let c=r.hasTaskRegistry&&(r.background_tasks?.length??0)>0,l=r.hasTaskRegistry&&(r.session_crons?.length??0)>0;if(c||!r.hasTaskRegistry&&o.hasPendingWork){e.writeStdout?.(De(`In-progress work is still running. Wait for spawned agents and running tool calls to finish before ending the turn.`));return}l||await e.writeStatusFile(t)}function De(e){return`${JSON.stringify({decision:`block`,reason:e,suppressOutput:!0})}\n`}async function Oe(e,t){return e.readTranscriptState?e.readTranscriptState(t):e.readTranscriptHead===P?F(t):{sessionMeta:await e.readTranscriptHead(t),hasPendingWork:!1}}function ke(e){try{let t=JSON.parse(e);return j(t)?{session_id:typeof t.session_id==`string`?t.session_id:void 0}:null}catch{return null}}function Ae(e){try{let t=JSON.parse(e);if(!j(t))return null;let n=Array.isArray(t.background_tasks)?t.background_tasks:void 0,r=Array.isArray(t.session_crons)?t.session_crons:void 0;return{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0,transcript_path:A(t.transcript_path),stop_hook_active:t.stop_hook_active===!0,background_tasks:n,session_crons:r,hasTaskRegistry:n!==void 0||r!==void 0}}catch{return null}}function A(e){return typeof e==`string`&&e.trim().length>0?e:null}function je(e){if(!e)return null;let t=e.trim();if(!t||t===`YES`)return null;try{let e=JSON.parse(t);return typeof e.sessionId==`string`&&e.sessionId.length>0?e.sessionId:null}catch{return null}}function Me(e){return typeof e==`string`?e.startsWith(`subagent_`)||e.startsWith(`internal_`):j(e)?T.some(t=>t in e):!1}function j(e){return typeof e==`object`&&!!e}async function M(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function N(e){try{return await u(e,`utf8`)}catch{return null}}async function P(e){return(await F(e)).sessionMeta}async function F(e){let t=te(e,{encoding:`utf8`}),n=f({input:t,crlfDelay:1/0}),r=new Set,i=new Map,a=null;try{for await(let e of n){let t=e.trim();if(t.length===0)continue;let n=Ne(t);n&&(a||=Pe(n),Fe(n,r),Ie(n,i))}}finally{n.close(),t.destroy()}return{sessionMeta:a,hasPendingWork:r.size>0||Array.from(i.values()).some(R)}}function Ne(e){try{let t=JSON.parse(e);return j(t)?t:null}catch{return null}}function Pe(e){return e.type===w?e.payload??null:e.item?.type===w?e.item.payload??null:null}function Fe(e,t){let n=z(e,[`payload`,`item`])??z(e,[`item`,`payload`,`item`]);if(!n)return;let r=B(n.id),i=B(n.type);!r||!i||!D.has(i)||(R(n.status)?t.add(r):t.delete(r))}function Ie(e,t){let n=z(e,[`payload`])??z(e,[`item`,`payload`]);if(!n||!B(n.type)?.startsWith(`collab_`))return;I(t,B(n.new_thread_id),n.status),I(t,B(n.receiver_thread_id),n.status);let r=z(n,[`statuses`]);if(r)for(let[e,n]of Object.entries(r))I(t,e,n)}function I(e,t,n){let r=L(n);!t||!r||e.set(t,r)}function L(e){if(typeof e==`string`)return e;if(!j(e))return null;let[t]=Object.keys(e);return t??null}function R(e){let t=typeof e==`string`?e:L(e);return t!==null&&E.has(t)}function z(e,t){let n=e;for(let e of t){if(!j(n))return null;n=n[e]}return j(n)?n:null}function B(e){return typeof e==`string`&&e.length>0?e:void 0}const Le=new p(`codex-session-start-hook`).description(`Codex session-start hook: binds ${C} to the first workflow Codex session id`).action(async()=>{await Te()}),Re=new p(`codex-stop-hook`).description(`Codex stop hook: reads stop-hook JSON on stdin and marks ${C} when the root session ends`).action(async()=>{await Ee()}),V=`list-crons`;function H(e={}){let t=e.createService??(()=>new i),n=e.exit??(e=>process.exit(e)),r=e.logError??((e,t)=>console.error(e,t)),a=e.writeStdout??(e=>process.stdout.write(e));return new p(V).description(`List cron jobs scheduled via workflow-mcp`).action(async()=>{try{let e=await t().list();a(`${JSON.stringify(e,null,2)}\n`),n(0)}catch(e){let t=new g(`Error listing cron jobs.`,`LIST_CRONS_COMMAND_FAILED`,{command:V},{cause:e});r(`${t.message} [${t.code}]`,t),n(1)}})}const ze=H(),U=`list-workflow-statuses`;function W(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw new re(`Expected a positive integer.`);return t}function Be(t={}){let n=t.createRegistry??(()=>new e),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new p(U).description(`List tracked workflow runs and their current stages`).option(`--page <number>`,`Page number to return`,W,1).option(`--page-size <number>`,`Number of workflow runs per page`,W,20).option(`-w, --workspace <name>`,`Filter workflow runs by workspace`,`all`).action(async e=>{try{let t=n(),i=e.workspace===`all`?void 0:e.workspace,o=await t.listRunsPage({page:e.page,pageSize:e.pageSize,workspace:i});a(`${JSON.stringify(o,null,2)}\n`),r(0)}catch(t){let n=new g(`Error listing workflow statuses.`,`LIST_WORKFLOW_STATUSES_FAILED`,{command:U,workspace:e.workspace??`all`},{cause:t});i(`${n.message} [${n.code}]`,n),r(1)}})}const Ve=Be(),He=()=>{try{return c(import.meta.url)(`@agimon-ai/foundation-process-registry`)}catch{return null}};function G(e,t){for(let n of e){if(n==null||typeof n!=`object`&&typeof n!=`function`)continue;let e=n;for(let n of t){let t=e[n];if(typeof t==`function`)return t}}return null}function Ue(e){let t=[e];if(e&&typeof e==`object`){let n=e.default;if(n&&(t.push(n),typeof n==`function`))try{t.push(new n)}catch{}}return t}async function We(e){let t=He();if(!t)return async()=>{};let n=Ue(t),r=G(n,[`registerProcess`,`register`,`registerProcessInstance`]),i=G(n,[`unregisterProcess`,`unregister`,`releaseProcess`]);if(!r)return async()=>{};try{await Promise.resolve(r({name:e,pid:process.pid,command:process.argv.join(` `)}))}catch{return async()=>{}}return i?async()=>{await Promise.resolve(i({name:e,pid:process.pid}))}:async()=>{}}async function Ge(e,t){await e.start();let n=async n=>{console.error(`\\nReceived ${n}, shutting down gracefully...`);let r=0;try{await e.stop()}catch(e){console.error(`Error during shutdown:`,e),r=1}try{await t()}catch(e){console.error(`Error during resource cleanup:`,e),r=1}process.exit(r)};process.on(`SIGINT`,()=>{n(`SIGINT`)}),process.on(`SIGTERM`,()=>{n(`SIGTERM`)})}async function Ke(e){try{await e()}catch{}}const qe=new p(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio`,`stdio`).option(`--service-name <name>`,`Service name for registry tracking`,`workflow-mcp`).action(async e=>{let t=await We(e.serviceName);try{let n=e.type.toLowerCase();n===`stdio`?await Ge(new s(r()),t):(console.error(`Unknown transport type: ${n}. Use: stdio`),process.exit(1))}catch(e){await Ke(t),console.error(`Failed to start MCP server:`,e),process.exit(1)}}),K=`run-workflow`,q=`RUN_WORKFLOW_COMMAND_FAILED`,J=`WORKFLOW_MCP_BACKGROUND_CHILD`,Y=`workflow-mcp-background-run`;function X(e){if(!e||e.length===0)return;let t={};for(let n of e){let[e,...r]=n.split(`=`);t[e]=r.join(`=`)}return Object.keys(t).length>0?t:void 0}function Z(e){if(e.runner&&e.cliAgent&&e.runner!==e.cliAgent)throw new g(`Conflicting runner selectors.`,q,{cliAgent:e.cliAgent,command:K,runner:e.runner});return e.runner??e.cliAgent}function Je(e,t){let n=process.argv[1];if(!n)throw new g(`Unable to determine the workflow-mcp CLI entry point for background execution.`,`RUN_WORKFLOW_BACKGROUND_LAUNCH_FAILED`,{command:K,workflow:e,workspace:t.workspace});let r=[n,`run-workflow`,e];t.job&&r.push(`--job`,t.job);for(let e of t.input??[])r.push(`--input`,e);for(let e of t.env??[])r.push(`--env`,e);t.secretFile&&r.push(`--secret-file`,t.secretFile),t.dryRun&&r.push(`--dry-run`),t.continueOnError&&r.push(`--continue-on-error`),t.keepWorktree&&r.push(`--keep-worktree`);let i=Z(t);i&&r.push(`--runner`,i),t.prompt&&r.push(`--prompt`,t.prompt),t.name&&r.push(`--name`,t.name),t.workspace&&r.push(`--workspace`,t.workspace);let a=l(process.execPath,r,{detached:!0,env:{...process.env,[J]:`1`},stdio:`ignore`});return a.unref(),a.pid}async function Ye(e,t){if(process.env[J]!==`1`)return async()=>{};let n=new ne(process.env.PROCESS_REGISTRY_PATH),r=ee(process.cwd()),i=process.env.NODE_ENV??`development`;return(await n.registerProcess({repositoryPath:r,serviceName:Y,serviceType:`tool`,environment:i,pid:process.pid,command:process.argv.join(` `),args:process.argv.slice(2),metadata:{workflow:e,workspace:t.workspace,job:t.job,name:t.name,runner:t.runner??t.cliAgent},force:!0})).success?async()=>{let e=await n.releaseProcess({repositoryPath:r,serviceName:Y,serviceType:`tool`,environment:i,pid:process.pid,kill:!1,releasePort:!1,force:!0});if(!e.success&&!e.error?.includes(`No matching process entry`))throw Error(e.error??`Failed to release workflow background process`)}:async()=>{}}function Xe(e={}){let t=e.createService??(()=>new a),n=e.exit??(e=>process.exit(e)),r=e.launchBackgroundRun??Je,i=e.registerBackgroundChild??Ye,o=e.logError??((e,t)=>console.error(e,t)),s=e.logInfo??(e=>process.stdout.write(`${e}\n`));return new p(K).description(`Run a GitHub Actions workflow file locally on macOS`).argument(`<workflow>`,`Path to the workflow YAML file`).option(`-j, --job <name>`,`Run only this job (and its dependencies)`).option(`-i, --input <key=value...>`,`Set workflow_dispatch input (repeatable)`).option(`-e, --env <key=value...>`,`Set extra environment variable (repeatable)`).option(`--secret-file <path>`,`Load secrets from a dotenv-style file`).option(`--dry-run`,`Print steps without executing`).option(`--continue-on-error`,`Continue past step failures`).option(`--runner <runner>`,`Preferred runner key for step command maps`).option(`--cli-agent <agent>`,`Deprecated alias for --runner`).option(`-p, --prompt <text>`,`User prompt for user_prompt trigger workflows`).option(`-n, --name <name>`,`Name for the workflow run context directory`).option(`-w, --workspace <name>`,`Workspace for workflow registry storage`).option(`--keep-worktree`,`Keep worktree on completion (skip merge and cleanup for retry)`).option(`--skip-launch`,`Skip launch-command delegation (used by inner invocations)`).option(`-b, --background`,`Run the workflow in a detached background process`).action(async(e,a)=>{try{if(a.background){let t=r(e,a);s(`Started workflow in background${t?` (PID: ${t})`:``}`),n(0);return}let o=await i(e,a),c=t();try{let t=Z(a),r=await c.run({cliAgent:a.cliAgent,runner:t,workflowPath:e,job:a.job,inputs:X(a.input),env:X(a.env),secretFile:a.secretFile,dryRun:a.dryRun,continueOnError:a.continueOnError,keepWorktree:a.keepWorktree,prompt:a.prompt,name:a.name,workspace:a.workspace,skipLaunch:a.skipLaunch});await o(),n(r.exitCode)}catch(e){throw await o(),e}}catch(t){let r=t instanceof g?t:new g(`Error executing run-workflow.`,q,{background:!!a.background,command:K,workflow:e,workspace:a.workspace},{cause:t});o(`${r.message} [${r.code}]`,r),n(1)}})}const Ze=Xe(),Q=`schedule-cron`;function Qe(e={}){let n=e.createService??(()=>new i),r=e.exit??(e=>process.exit(e)),a=e.logError??((e,t)=>console.error(e,t)),s=e.writeStdout??(e=>process.stdout.write(`${e}\n`));return new p(Q).description(`Schedule a headless Claude Code or Codex CLI run via system crontab`).argument(`<name>`,`Unique name for this cron job`).option(`-d, --cwd <path>`,`Working directory for the CLI run`,process.cwd()).option(`-c, --cli <cli>`,`CLI to use: claude or codex`,o).option(`-p, --prompt <text>`,`Prompt to pass to the CLI`).option(`-f, --prompt-file <path>`,`Path to a file whose content is used as the prompt (read at cron execution time)`).option(`-s, --schedule <cron>`,`Cron expression (e.g., "*/10 * * * *")`).option(`-i, --interval-minutes <minutes>`,`Run every N minutes (alternative to --schedule)`).action(async(e,i)=>{try{let a=t.parse({name:e,cwd:i.cwd??process.cwd(),cli:i.cli??`claude`,prompt:i.prompt,promptFile:i.promptFile,schedule:i.schedule,intervalMinutes:i.intervalMinutes?Number.parseInt(i.intervalMinutes,10):void 0}),o=await n().schedule(a);s(`Scheduled cron job "${o.name}" with schedule: ${o.schedule}`),s(`CLI: ${o.cli} | CWD: ${o.cwd}`),o.prompt&&s(`Prompt: ${o.prompt}`),r(0)}catch(t){let n=t instanceof g?t:new g(`Error scheduling cron job.`,`SCHEDULE_CRON_COMMAND_FAILED`,{command:Q,name:e},{cause:t});a(`${n.message} [${n.code}]`,n),r(1)}})}const $e=Qe(),$=`stop-workflow`;function et(t={}){let n=t.createRegistry??(()=>new e),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new p($).description(`Request a running workflow to stop gracefully`).argument(`<run-key>`,`Running workflow run key`).option(`-w, --workspace <name>`,`Workspace containing the running workflow`,`default`).option(`-r, --reason <text>`,`Optional stop reason to record`).action(async(e,t)=>{try{let i=n(),o=await i.requestStop(t.workspace,e,t.reason);a(`${JSON.stringify({reason:o.reason,requestedAt:o.requestedAt,runKey:e,workspace:i.resolveWorkspace(t.workspace)},null,2)}\n`),r(0)}catch(n){let a=new g(`Error requesting workflow stop.`,`STOP_WORKFLOW_FAILED`,{command:$,runKey:e,workspace:t.workspace},{cause:n});i(`${a.message} [${a.code}]`,a),r(1)}})}const tt=et();async function nt(){let e=new p;e.name(`workflow-mcp`).description(`MCP server for running GitHub Actions workflows locally`).version(ie),e.addCommand(ze),e.addCommand(Ve),e.addCommand(he),e.addCommand(pe),e.addCommand(we),e.addCommand(S),e.addCommand(Le),e.addCommand(Re),e.addCommand(qe),e.addCommand(Ze),e.addCommand($e),e.addCommand(tt),await e.parseAsync(process.argv)}nt().catch(e=>{console.error(`[CLI_STARTUP_ERROR] workflow-mcp startup failed:`,e),process.exit(1)});export{};
|
|
2
|
+
import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c}from"./stdio-k5DKbtyw.mjs";import{createRequire as l}from"node:module";import{spawn as ee}from"node:child_process";import{readFile as u,writeFile as d}from"node:fs/promises";import{resolve as te}from"node:path";import{createReadStream as ne}from"node:fs";import{ProcessRegistryService as f}from"@agimon-ai/foundation-process-registry";import{createInterface as re}from"node:readline";import{Command as p,InvalidArgumentError as m}from"commander";var ie=`0.3.0`;const h=`WORKFLOW_STATUS_FILE`,ae={readStdin:g,readStatusFile:fe,writeStdout:e=>{process.stdout.write(e)},writeStatusFile:async e=>{await d(e,`YES
|
|
3
|
+
`,`utf8`)},workflowStatusFile:process.env[h]};async function oe(e=ae){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=se(n);if(!r?.conversationId)return;let i=await e.readStatusFile(t);if(!(i===null||i.trim()===`YES`)){if(!r.fullyIdle){e.writeStdout?.(ce(`In-progress work is still running. Continue the execution loop until background tasks finish.`));return}ue(r.error)||r.terminationReason!==`model_stop`||await e.writeStatusFile(t)}}function se(e){try{let t=JSON.parse(e);return de(t)?{conversationId:typeof t.conversationId==`string`?t.conversationId:void 0,transcriptPath:le(t.transcriptPath),terminationReason:typeof t.terminationReason==`string`?t.terminationReason:void 0,error:typeof t.error==`string`?t.error:void 0,fullyIdle:t.fullyIdle===!0}:null}catch{return null}}function ce(e){return`${JSON.stringify({decision:`continue`,reason:e})}\n`}function le(e){return typeof e==`string`&&e.trim().length>0?e:null}function ue(e){return typeof e==`string`&&e.trim().length>0}function de(e){return typeof e==`object`&&!!e}async function g(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function fe(e){try{return await u(e,`utf8`)}catch{return null}}const pe=new p(`antigravity-stop-hook`).description(`Antigravity stop hook: reads Stop hook JSON on stdin and marks ${h} when the execution is fully idle`).action(async()=>{await oe()});var _=class extends Error{constructor(e,t,n={},r){super(e,r),this.code=t,this.context=n,this.name=`WorkflowCommandError`}};const v=`check-codex-quota`;function me(t={}){let n=t.createService??(()=>new e),r=t.exit??(e=>process.exit(e)),i=t.logError??((e,t)=>console.error(e,t)),a=t.writeStdout??(e=>process.stdout.write(e));return new p(v).description(`Check whether Codex quota is currently blocking new work`).action(async()=>{try{let e=await n().getQuotaStatus();if(a(`${JSON.stringify({blockingLimit:e?.blockingLimit??null,planType:e?.planType??null},null,2)}\n`),e?.blockingLimit){let t=new _(`Codex quota is blocking work at ${e.blockingLimit.limitId}/${e.blockingLimit.window}.`,`CODEX_QUOTA_BLOCKED`,{command:v,limitId:e.blockingLimit.limitId,limitName:e.blockingLimit.limitName,window:e.blockingLimit.window});i(`${t.message} [${t.code}]`,t),r(2);return}r(0)}catch(e){let t=e instanceof _?e:new _(`Error checking Codex quota.`,`CHECK_CODEX_QUOTA_COMMAND_FAILED`,{command:v},{cause:e});i(`${t.message} [${t.code}]`,t),r(1)}})}const he=me(),y=`WORKFLOW_STATUS_FILE`,ge={readStdin:x,readStatusFile:S,writeStdout:e=>{process.stdout.write(e)},writeStatusFile:async e=>{await d(e,`YES
|
|
4
|
+
`,`utf8`)},workflowStatusFile:process.env[y]},_e={readStdin:x,readStatusFile:S,writeSessionBinding:async(e,t)=>{await d(e,`${JSON.stringify({sessionId:t})}\n`,`utf8`)},workflowStatusFile:process.env[y]};async function ve(e=_e){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=xe(n);if(!r?.session_id||r.hook_event_name&&r.hook_event_name!==`SessionStart`)return;let i=await e.readStatusFile(t);i===null||i.trim().length>0||await e.writeSessionBinding(t,r.session_id)}async function ye(e=ge){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=Se(n);if(!r||r.hook_event_name&&r.hook_event_name!==`Stop`)return;let i=Ce(await e.readStatusFile(t));if(!r.session_id||r.session_id!==i)return;let a=r.background_tasks.length>0,o=r.session_crons.length>0;if(a){e.writeStdout?.(be(`In-progress work is still running. Wait for spawned agents and running tool calls to finish before ending the turn.`));return}o||await e.writeStatusFile(t)}function be(e){return`${JSON.stringify({decision:`block`,reason:e,suppressOutput:!0})}\n`}function xe(e){try{let t=JSON.parse(e);return b(t)?{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0}:null}catch{return null}}function Se(e){try{let t=JSON.parse(e);if(!b(t))return null;let n=Array.isArray(t.background_tasks)?t.background_tasks:[],r=Array.isArray(t.session_crons)?t.session_crons:[];return{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0,stop_hook_active:t.stop_hook_active===!0,background_tasks:n,session_crons:r}}catch{return null}}function Ce(e){if(!e)return null;let t=e.trim();if(!t||t===`YES`)return null;try{let e=JSON.parse(t);return typeof e.sessionId==`string`&&e.sessionId.length>0?e.sessionId:null}catch{return null}}function b(e){return typeof e==`object`&&!!e}async function x(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function S(e){try{return await u(e,`utf8`)}catch{return null}}const we=new p(`claude-session-start-hook`).description(`Claude Code SessionStart hook: binds ${y} to the root Claude session id`).action(async()=>{await ve()}),C=new p(`claude-stop-hook`).description(`Claude Code stop hook: reads Stop hook JSON on stdin and marks ${y} when the root session is complete`).action(async()=>{await ye()}),w=`WORKFLOW_STATUS_FILE`,T=`session_meta`,E=[`sub_agent`,`subagent`],D=new Set([`in_progress`,`inProgress`,`running`,`pending_init`,`pendingInit`]),O=new Set([`collabAgentToolCall`,`commandExecution`,`dynamicToolCall`,`imageGeneration`,`local_shell_call`,`mcpToolCall`]),k={readStdin:M,readStatusFile:N,readTranscriptHead:P,readTranscriptState:F,writeStdout:e=>{process.stdout.write(e)},writeStatusFile:async e=>{await d(e,`YES
|
|
5
|
+
`,`utf8`)},workflowStatusFile:process.env[w]},Te={readStdin:M,readStatusFile:N,writeSessionBinding:async(e,t)=>{await d(e,`${JSON.stringify({sessionId:t})}\n`,`utf8`)},workflowStatusFile:process.env[w]};async function Ee(e=Te){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=Ae(n);if(!r?.session_id)return;let i=await e.readStatusFile(t);i===null||i.trim().length>0||await e.writeSessionBinding(t,r.session_id)}async function De(e=k){let t=e.workflowStatusFile;if(!t)return;let n=await e.readStdin();if(n.trim().length===0)return;let r=je(n);if(!r||r.hook_event_name&&r.hook_event_name!==`Stop`||r.stop_hook_active&&!r.hasTaskRegistry)return;let i=Me(await e.readStatusFile(t));if(!r.session_id||r.session_id!==i)return;let a=A(r.transcript_path);if(!a)return;let o=await ke(e,a),s=o.sessionMeta;if(!s||r.session_id&&s.id&&r.session_id!==s.id||Ne(s.source))return;let c=r.hasTaskRegistry&&(r.background_tasks?.length??0)>0,l=r.hasTaskRegistry&&(r.session_crons?.length??0)>0;if(c||!r.hasTaskRegistry&&o.hasPendingWork){e.writeStdout?.(Oe(`In-progress work is still running. Wait for spawned agents and running tool calls to finish before ending the turn.`));return}l||await e.writeStatusFile(t)}function Oe(e){return`${JSON.stringify({decision:`block`,reason:e,suppressOutput:!0})}\n`}async function ke(e,t){return e.readTranscriptState?e.readTranscriptState(t):e.readTranscriptHead===P?F(t):{sessionMeta:await e.readTranscriptHead(t),hasPendingWork:!1}}function Ae(e){try{let t=JSON.parse(e);return j(t)?{session_id:typeof t.session_id==`string`?t.session_id:void 0}:null}catch{return null}}function je(e){try{let t=JSON.parse(e);if(!j(t))return null;let n=Array.isArray(t.background_tasks)?t.background_tasks:void 0,r=Array.isArray(t.session_crons)?t.session_crons:void 0;return{hook_event_name:typeof t.hook_event_name==`string`?t.hook_event_name:void 0,session_id:typeof t.session_id==`string`?t.session_id:void 0,transcript_path:A(t.transcript_path),stop_hook_active:t.stop_hook_active===!0,background_tasks:n,session_crons:r,hasTaskRegistry:n!==void 0||r!==void 0}}catch{return null}}function A(e){return typeof e==`string`&&e.trim().length>0?e:null}function Me(e){if(!e)return null;let t=e.trim();if(!t||t===`YES`)return null;try{let e=JSON.parse(t);return typeof e.sessionId==`string`&&e.sessionId.length>0?e.sessionId:null}catch{return null}}function Ne(e){return typeof e==`string`?e.startsWith(`subagent_`)||e.startsWith(`internal_`):j(e)?E.some(t=>t in e):!1}function j(e){return typeof e==`object`&&!!e}async function M(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(String(t)));return Buffer.concat(e).toString(`utf8`)}async function N(e){try{return await u(e,`utf8`)}catch{return null}}async function P(e){return(await F(e)).sessionMeta}async function F(e){let t=ne(e,{encoding:`utf8`}),n=re({input:t,crlfDelay:1/0}),r=new Set,i=new Map,a=null;try{for await(let e of n){let t=e.trim();if(t.length===0)continue;let n=Pe(t);n&&(a||=Fe(n),Ie(n,r),Le(n,i))}}finally{n.close(),t.destroy()}return{sessionMeta:a,hasPendingWork:r.size>0||Array.from(i.values()).some(R)}}function Pe(e){try{let t=JSON.parse(e);return j(t)?t:null}catch{return null}}function Fe(e){return e.type===T?e.payload??null:e.item?.type===T?e.item.payload??null:null}function Ie(e,t){let n=z(e,[`payload`,`item`])??z(e,[`item`,`payload`,`item`]);if(!n)return;let r=B(n.id),i=B(n.type);!r||!i||!O.has(i)||(R(n.status)?t.add(r):t.delete(r))}function Le(e,t){let n=z(e,[`payload`])??z(e,[`item`,`payload`]);if(!n||!B(n.type)?.startsWith(`collab_`))return;I(t,B(n.new_thread_id),n.status),I(t,B(n.receiver_thread_id),n.status);let r=z(n,[`statuses`]);if(r)for(let[e,n]of Object.entries(r))I(t,e,n)}function I(e,t,n){let r=L(n);!t||!r||e.set(t,r)}function L(e){if(typeof e==`string`)return e;if(!j(e))return null;let[t]=Object.keys(e);return t??null}function R(e){let t=typeof e==`string`?e:L(e);return t!==null&&D.has(t)}function z(e,t){let n=e;for(let e of t){if(!j(n))return null;n=n[e]}return j(n)?n:null}function B(e){return typeof e==`string`&&e.length>0?e:void 0}const Re=new p(`codex-session-start-hook`).description(`Codex session-start hook: binds ${w} to the first workflow Codex session id`).action(async()=>{await Ee()}),ze=new p(`codex-stop-hook`).description(`Codex stop hook: reads stop-hook JSON on stdin and marks ${w} when the root session ends`).action(async()=>{await De()}),V=`list-crons`;function Be(e={}){let t=e.createService??(()=>new s),n=e.exit??(e=>process.exit(e)),r=e.logError??((e,t)=>console.error(e,t)),i=e.writeStdout??(e=>process.stdout.write(e));return new p(V).description(`List cron jobs scheduled via workflow-mcp`).action(async()=>{try{let e=await t().list();i(`${JSON.stringify(e,null,2)}\n`),n(0)}catch(e){let t=new _(`Error listing cron jobs.`,`LIST_CRONS_COMMAND_FAILED`,{command:V},{cause:e});r(`${t.message} [${t.code}]`,t),n(1)}})}const Ve=Be(),H=`list-workflow-statuses`;function U(e){let t=Number(e);if(!Number.isInteger(t)||t<1)throw new m(`Expected a positive integer.`);return t}function He(e={}){let t=e.createRegistry??(()=>new a),n=e.exit??(e=>process.exit(e)),r=e.logError??((e,t)=>console.error(e,t)),i=e.writeStdout??(e=>process.stdout.write(e));return new p(H).description(`List tracked workflow runs and their current stages`).option(`--page <number>`,`Page number to return`,U,1).option(`--page-size <number>`,`Number of workflow runs per page`,U,20).option(`-w, --workspace <name>`,`Filter workflow runs by workspace`,`all`).action(async e=>{try{let r=t(),a=e.workspace===`all`?void 0:e.workspace,o=await r.listRunsPage({page:e.page,pageSize:e.pageSize,workspace:a});i(`${JSON.stringify(o,null,2)}\n`),n(0)}catch(t){let i=new _(`Error listing workflow statuses.`,`LIST_WORKFLOW_STATUSES_FAILED`,{command:H,workspace:e.workspace??`all`},{cause:t});r(`${i.message} [${i.code}]`,i),n(1)}})}const Ue=He(),We=()=>{try{return l(import.meta.url)(`@agimon-ai/foundation-process-registry`)}catch{return null}};function W(e,t){for(let n of e){if(n==null||typeof n!=`object`&&typeof n!=`function`)continue;let e=n;for(let n of t){let t=e[n];if(typeof t==`function`)return t}}return null}function Ge(e){let t=[e];if(e&&typeof e==`object`){let n=e.default;if(n&&(t.push(n),typeof n==`function`))try{t.push(new n)}catch{}}return t}async function Ke(e){let t=We();if(!t)return async()=>{};let n=Ge(t),r=W(n,[`registerProcess`,`register`,`registerProcessInstance`]),i=W(n,[`unregisterProcess`,`unregister`,`releaseProcess`]);if(!r)return async()=>{};try{await Promise.resolve(r({name:e,pid:process.pid,command:process.argv.join(` `)}))}catch{return async()=>{}}return i?async()=>{await Promise.resolve(i({name:e,pid:process.pid}))}:async()=>{}}async function qe(e,t){await e.start();let n=async n=>{console.error(`\\nReceived ${n}, shutting down gracefully...`);let r=0;try{await e.stop()}catch(e){console.error(`Error during shutdown:`,e),r=1}try{await t()}catch(e){console.error(`Error during resource cleanup:`,e),r=1}process.exit(r)};process.on(`SIGINT`,()=>{n(`SIGINT`)}),process.on(`SIGTERM`,()=>{n(`SIGTERM`)})}async function Je(e){try{await e()}catch{}}const Ye=new p(`mcp-serve`).description(`Start MCP server with specified transport`).option(`-t, --type <type>`,`Transport type: stdio`,`stdio`).option(`--service-name <name>`,`Service name for registry tracking`,`workflow-mcp`).action(async e=>{let t=await Ke(e.serviceName);try{let n=e.type.toLowerCase();n===`stdio`?await qe(new c(i()),t):(console.error(`Unknown transport type: ${n}. Use: stdio`),process.exit(1))}catch(e){await Je(t),console.error(`Failed to start MCP server:`,e),process.exit(1)}}),G=`recover-workflow`;function Xe(e={}){let t=e.createService??(()=>new o),n=e.exit??(e=>process.exit(e)),r=e.logError??((e,t)=>console.error(e,t));return new p(G).description(`Recover a failed workflow run from the local workflow registry`).argument(`<run-key>`,`Failed workflow run key`).option(`-w, --workspace <name>`,`Workspace containing the failed workflow run`,`default`).option(`-j, --job <name>`,`Override the job to recover from`).option(`--runner <runner>`,`Override the runner key for step command maps`).option(`--dry-run`,`Print recovery steps without executing`).action(async(e,i)=>{try{n((await t().recover({dryRun:i.dryRun,job:i.job,runKey:e,runner:i.runner,workspace:i.workspace})).exitCode)}catch(t){let a=new _(`Error recovering workflow.`,`RECOVER_WORKFLOW_FAILED`,{command:G,runKey:e,workspace:i.workspace},{cause:t});r(`${a.message} [${a.code}]`,a),n(1)}})}const Ze=Xe(),K=`run-workflow`,q=`RUN_WORKFLOW_COMMAND_FAILED`,J=`WORKFLOW_MCP_BACKGROUND_CHILD`,Y=`workflow-mcp-background-run`;function X(e){if(!e||e.length===0)return;let t={};for(let n of e){let[e,...r]=n.split(`=`);t[e]=r.join(`=`)}return Object.keys(t).length>0?t:void 0}function Z(e){if(e.runner&&e.cliAgent&&e.runner!==e.cliAgent)throw new _(`Conflicting runner selectors.`,q,{cliAgent:e.cliAgent,command:K,runner:e.runner});return e.runner??e.cliAgent}function Qe(e,t){let n=process.argv[1];if(!n)throw new _(`Unable to determine the workflow-mcp CLI entry point for background execution.`,`RUN_WORKFLOW_BACKGROUND_LAUNCH_FAILED`,{command:K,workflow:e,workspace:t.workspace});let r=[n,`run-workflow`,e];t.job&&r.push(`--job`,t.job);for(let e of t.input??[])r.push(`--input`,e);for(let e of t.env??[])r.push(`--env`,e);t.secretFile&&r.push(`--secret-file`,t.secretFile),t.dryRun&&r.push(`--dry-run`),t.continueOnError&&r.push(`--continue-on-error`),t.keepWorktree&&r.push(`--keep-worktree`);let i=Z(t);i&&r.push(`--runner`,i),t.prompt&&r.push(`--prompt`,t.prompt),t.name&&r.push(`--name`,t.name),t.workspace&&r.push(`--workspace`,t.workspace);let a=ee(process.execPath,r,{detached:!0,env:{...process.env,[J]:`1`},stdio:`ignore`});return a.unref(),a.pid}async function $e(e,t){if(process.env[J]!==`1`)return async()=>{};let n=new f(process.env.PROCESS_REGISTRY_PATH),r=te(process.cwd()),i=process.env.NODE_ENV??`development`;return(await n.registerProcess({repositoryPath:r,serviceName:Y,serviceType:`tool`,environment:i,pid:process.pid,command:process.argv.join(` `),args:process.argv.slice(2),metadata:{workflow:e,workspace:t.workspace,job:t.job,name:t.name,runner:t.runner??t.cliAgent},force:!0})).success?async()=>{let e=await n.releaseProcess({repositoryPath:r,serviceName:Y,serviceType:`tool`,environment:i,pid:process.pid,kill:!1,releasePort:!1,force:!0});if(!e.success&&!e.error?.includes(`No matching process entry`))throw Error(e.error??`Failed to release workflow background process`)}:async()=>{}}function et(e={}){let t=e.createService??(()=>new n),r=e.exit??(e=>process.exit(e)),i=e.launchBackgroundRun??Qe,a=e.registerBackgroundChild??$e,o=e.logError??((e,t)=>console.error(e,t)),s=e.logInfo??(e=>process.stdout.write(`${e}\n`));return new p(K).description(`Run a GitHub Actions workflow file locally on macOS`).argument(`<workflow>`,`Path to the workflow YAML file`).option(`-j, --job <name>`,`Run only this job (and its dependencies)`).option(`-i, --input <key=value...>`,`Set workflow_dispatch input (repeatable)`).option(`-e, --env <key=value...>`,`Set extra environment variable (repeatable)`).option(`--secret-file <path>`,`Load secrets from a dotenv-style file`).option(`--dry-run`,`Print steps without executing`).option(`--continue-on-error`,`Continue past step failures`).option(`--runner <runner>`,`Preferred runner key for step command maps`).option(`--cli-agent <agent>`,`Deprecated alias for --runner`).option(`-p, --prompt <text>`,`User prompt for user_prompt trigger workflows`).option(`-n, --name <name>`,`Name for the workflow run context directory`).option(`-w, --workspace <name>`,`Workspace for workflow registry storage`).option(`--keep-worktree`,`Keep worktree on completion (skip merge and cleanup for retry)`).option(`--skip-launch`,`Skip launch-command delegation (used by inner invocations)`).option(`-b, --background`,`Run the workflow in a detached background process`).action(async(e,n)=>{try{if(n.background){let t=i(e,n);s(`Started workflow in background${t?` (PID: ${t})`:``}`),r(0);return}let o=await a(e,n),c=t();try{let t=Z(n),i=await c.run({cliAgent:n.cliAgent,runner:t,workflowPath:e,job:n.job,inputs:X(n.input),env:X(n.env),secretFile:n.secretFile,dryRun:n.dryRun,continueOnError:n.continueOnError,keepWorktree:n.keepWorktree,prompt:n.prompt,name:n.name,workspace:n.workspace,skipLaunch:n.skipLaunch});await o(),r(i.exitCode)}catch(e){throw await o(),e}}catch(t){let i=t instanceof _?t:new _(`Error executing run-workflow.`,q,{background:!!n.background,command:K,workflow:e,workspace:n.workspace},{cause:t});o(`${i.message} [${i.code}]`,i),r(1)}})}const tt=et(),Q=`schedule-cron`;function nt(e={}){let n=e.createService??(()=>new s),i=e.exit??(e=>process.exit(e)),a=e.logError??((e,t)=>console.error(e,t)),o=e.writeStdout??(e=>process.stdout.write(`${e}\n`));return new p(Q).description(`Schedule a headless Claude Code or Codex CLI run via system crontab`).argument(`<name>`,`Unique name for this cron job`).option(`-d, --cwd <path>`,`Working directory for the CLI run`,process.cwd()).option(`-c, --cli <cli>`,`CLI to use: claude or codex`,t).option(`-p, --prompt <text>`,`Prompt to pass to the CLI`).option(`-f, --prompt-file <path>`,`Path to a file whose content is used as the prompt (read at cron execution time)`).option(`-s, --schedule <cron>`,`Cron expression (e.g., "*/10 * * * *")`).option(`-i, --interval-minutes <minutes>`,`Run every N minutes (alternative to --schedule)`).action(async(e,t)=>{try{let a=r.parse({name:e,cwd:t.cwd??process.cwd(),cli:t.cli??`claude`,prompt:t.prompt,promptFile:t.promptFile,schedule:t.schedule,intervalMinutes:t.intervalMinutes?Number.parseInt(t.intervalMinutes,10):void 0}),s=await n().schedule(a);o(`Scheduled cron job "${s.name}" with schedule: ${s.schedule}`),o(`CLI: ${s.cli} | CWD: ${s.cwd}`),s.prompt&&o(`Prompt: ${s.prompt}`),i(0)}catch(t){let n=t instanceof _?t:new _(`Error scheduling cron job.`,`SCHEDULE_CRON_COMMAND_FAILED`,{command:Q,name:e},{cause:t});a(`${n.message} [${n.code}]`,n),i(1)}})}const rt=nt(),$=`stop-workflow`;function it(e={}){let t=e.createRegistry??(()=>new a),n=e.exit??(e=>process.exit(e)),r=e.logError??((e,t)=>console.error(e,t)),i=e.writeStdout??(e=>process.stdout.write(e));return new p($).description(`Request a running workflow to stop gracefully`).argument(`<run-key>`,`Running workflow run key`).option(`-w, --workspace <name>`,`Workspace containing the running workflow`,`default`).option(`-r, --reason <text>`,`Optional stop reason to record`).action(async(e,a)=>{try{let r=t(),o=await r.requestStop(a.workspace,e,a.reason);i(`${JSON.stringify({reason:o.reason,requestedAt:o.requestedAt,runKey:e,workspace:r.resolveWorkspace(a.workspace)},null,2)}\n`),n(0)}catch(t){let i=new _(`Error requesting workflow stop.`,`STOP_WORKFLOW_FAILED`,{command:$,runKey:e,workspace:a.workspace},{cause:t});r(`${i.message} [${i.code}]`,i),n(1)}})}const at=it();async function ot(){let e=new p;e.name(`workflow-mcp`).description(`MCP server for running GitHub Actions workflows locally`).version(ie),e.addCommand(Ve),e.addCommand(Ue),e.addCommand(he),e.addCommand(pe),e.addCommand(we),e.addCommand(C),e.addCommand(Re),e.addCommand(ze),e.addCommand(Ye),e.addCommand(Ze),e.addCommand(tt),e.addCommand(rt),e.addCommand(at),await e.parseAsync(process.argv)}ot().catch(e=>{console.error(`[CLI_STARTUP_ERROR] workflow-mcp startup failed:`,e),process.exit(1)});export{};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./stdio-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./stdio-CMbfCrs1.cjs`);exports.StdioTransportHandler=e.t,exports.createServer=e.n;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./stdio-
|
|
1
|
+
import{n as e,t}from"./stdio-k5DKbtyw.mjs";export{t as StdioTransportHandler,e as createServer};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
let e=require(`@modelcontextprotocol/sdk/server/index.js`),t=require(`@modelcontextprotocol/sdk/types.js`),n=require(`zod`),r=require(`node:child_process`),i=require(`node:util`),a=require(`node:fs/promises`),o=require(`node:os`),s=require(`node:path`),c=require(`node:fs`),l=require(`@agimon-ai/foundation-port-registry`),u=require(`@agimon-ai/foundation-process-registry`),d=require(`node:crypto`),f=require(`node:readline`),p=require(`js-yaml`),m=require(`@modelcontextprotocol/sdk/server/stdio.js`);var h=class extends Error{code=`WORKFLOW_TOOL_NOT_FOUND`;constructor(e,t){super(`Unknown tool "${e}". Available tools: ${t.join(`, `)}`),this.toolName=e,this.availableTools=t,this.name=`WorkflowToolNotFoundError`}},g=class extends Error{constructor(e,t,n={},r){super(e,r),this.code=t,this.context=n,this.name=`WorkflowToolError`}},_=class extends Error{constructor(e,t,n={},r){super(e,r),this.code=t,this.context=n,this.name=`CronError`}};const v=`# workflow-mcp:cron:`,y=`crontab`,b=`claude`,x=`codex`,ee=`--cwd`,S=`CRON_WRITE_FAILED`,C=n.z.enum([b,x]),w=n.z.object({name:n.z.string().min(1),cwd:n.z.string().min(1),cli:C,schedule:n.z.string().min(1),prompt:n.z.string().optional(),promptFile:n.z.string().optional(),createdAt:n.z.string().min(1)}),T=n.z.object({name:n.z.string().min(1),cwd:n.z.string().min(1),cli:C.optional(),prompt:n.z.string().optional(),promptFile:n.z.string().optional(),schedule:n.z.string().optional(),intervalMinutes:n.z.number().positive().optional()});function E(e){return`'${e.replace(/'/g,`'\\''`)}'`}function te(e,t,n,r){let i=n?E(n):r?`"$(cat ${E(r)})"`:void 0;if(e===`codex`){let e=[x,`--approval-mode`,`full-auto`];return i&&e.push(`-q`,i),e.push(ee,E(t)),e.join(` `)}let a=[b,`--dangerously-skip-permissions`];return i&&a.push(`-p`,i),a.push(ee,E(t)),a.join(` `)}function ne(e){if(e<=0)throw Error(`Interval must be a positive number of minutes`);if(e<60)return`*/${e} * * * *`;let t=Math.floor(e/60);return t<24?`0 */${t} * * *`:`0 0 */${Math.floor(t/24)} * *`}const re=(0,i.promisify)(r.execFile);var ie=class{logger;constructor(e){this.logger=e??{info:e=>process.stdout.write(`${e}\n`),error:e=>console.error(e),warn:e=>console.error(e)}}async readCrontab(){try{let{stdout:e}=await re(y,[`-l`]);return e}catch(e){let t=e.code;if(t===`ENOENT`||(e.stderr??``).includes(`no crontab for`))return``;throw this.logger.error(`[CronService.readCrontab] failed exitCode=${t}`),new _(`Failed to read current crontab.`,`CRON_READ_FAILED`,{exitCode:t},{cause:e})}}async writeCrontab(e){let t=``,n=(0,r.execFile)(y,[`-`]);n.stderr?.on(`data`,e=>{t+=e}),n.stdin?.write(e),n.stdin?.end(),await new Promise((r,i)=>{n.on(`close`,n=>{n===0?r():(this.logger.error(`[CronService.writeCrontab] failed exitCode=${n} stderr=${t}`),i(new _(`crontab rejected input (exit ${n}).`,S,{exitCode:n,stderr:t,contentLength:e.length})))}),n.on(`error`,e=>{this.logger.error(`[CronService.writeCrontab] spawn failed: ${e.message}`),i(new _(`Failed to spawn crontab process.`,S,{},{cause:e}))})})}async schedule(e){let t=e.cli??`claude`;if(!e.schedule&&!e.intervalMinutes)throw new _(`Either schedule (cron expression) or intervalMinutes must be provided.`,`CRON_INVALID_INPUT`,{name:e.name});let n=e.schedule??ne(e.intervalMinutes),r=te(t,e.cwd,e.prompt,e.promptFile),i=new Date().toISOString(),a=w.parse({name:e.name,cwd:e.cwd,cli:t,schedule:n,prompt:e.prompt,promptFile:e.promptFile,createdAt:i}),o=await this.readCrontab(),s=this.removeCronBlock(o,e.name),c=`${`${v}${e.name}`}\n${`# workflow-mcp:meta:${JSON.stringify(a)}`}\n${`${n} ${r}`}`,l=s.trimEnd()?`${s.trimEnd()}\n${c}\n`:`${c}\n`;return await this.writeCrontab(l),this.logger.info(`Scheduled cron "${e.name}" [${n}]`),a}async list(){let e=await this.readCrontab(),t=[],n=e.split(`
|
|
2
|
+
`);for(let e=0;e<n.length;e++){let r=n[e];if(r.startsWith(`# workflow-mcp:meta:`))try{let e=r.slice(20),n=w.parse(JSON.parse(e));t.push(n)}catch(t){this.logger.warn(`[CronService.list] skipping malformed metadata at line ${e+1}: ${t.message}`)}}return t}async remove(e){let t=await this.readCrontab(),n=this.removeCronBlock(t,e);return n===t?!1:(await this.writeCrontab(n),this.logger.info(`Removed cron "${e}"`),!0)}removeCronBlock(e,t){let n=e.split(`
|
|
3
|
+
`),r=[],i=`${v}${t}`,a=!1;for(let e of n){if(e===i){a=!0;continue}if(a){if(e.startsWith(`# workflow-mcp:meta:`))continue;a=!1;continue}r.push(e)}return r.join(`
|
|
4
|
+
`)}},ae=class e{static TOOL_NAME=`list-crons`;constructor(e=new ie){this.service=e}getInputSchema(){return n.z.object({})}getDefinition(){return{name:e.TOOL_NAME,description:`List all cron jobs scheduled via workflow-mcp, showing name, schedule, CLI, cwd, and prompt.`,inputSchema:n.z.toJSONSchema(n.z.object({}))}}async execute(){try{let e=await this.service.list();return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}catch(t){let n=new g(`Failed to list cron jobs.`,`LIST_CRONS_TOOL_FAILED`,{tool:e.TOOL_NAME},{cause:t});return console.error(`[${e.TOOL_NAME}] ${n.message}`,n.context),{content:[{type:`text`,text:JSON.stringify({code:n.code,context:n.context,message:n.message},null,2)}],isError:!0}}}},D=class extends Error{code=`INVALID_WORKFLOW_RUN_RECORD`;constructor(e,t,n){super(`Invalid workflow run record at "${e}": ${t}`,n),this.recordPath=e,this.name=`InvalidWorkflowRunRecordError`}},O=class extends Error{code=`WORKFLOW_RUN_CONFLICT`;constructor(e,t){super(`Workflow "${t}" is already running in workspace "${e}"`),this.workspace=e,this.runKey=t,this.name=`WorkflowRunConflictError`}};const k=n.z.record(n.z.string(),n.z.coerce.string()),oe=n.z.object({description:n.z.string().optional(),required:n.z.boolean().optional(),default:n.z.string().optional(),type:n.z.string().optional(),options:n.z.array(n.z.string()).optional()}),se=n.z.record(n.z.string(),n.z.string()),A=n.z.record(n.z.string(),n.z.string()),j=n.z.union([n.z.string(),A]),ce=n.z.object({"fail-fast":n.z.boolean().optional(),matrix:n.z.looseObject({include:n.z.array(se).optional()}).optional()}),le=n.z.object({name:n.z.string().optional(),uses:n.z.string().optional(),run:j.optional(),interactiveRun:j.optional(),"timeout-minutes":n.z.number().optional(),env:k.optional(),with:n.z.record(n.z.string(),n.z.string()).optional(),"working-directory":n.z.string().optional(),if:n.z.string().optional(),"continue-on-error":n.z.boolean().optional(),"timeout-retries":n.z.number().optional(),id:n.z.string().optional()}),ue=n.z.object({name:n.z.string(),run:n.z.string(),env:k.optional(),"working-directory":n.z.string().optional(),"ready-check":n.z.union([n.z.string(),n.z.boolean()]).optional(),"ready-timeout":n.z.number().optional(),host:n.z.string().optional(),port:n.z.number().int().min(1).max(65535).optional(),"port-range":n.z.object({min:n.z.number().int().min(1).max(65535),max:n.z.number().int().min(1).max(65535)}).optional(),"service-type":n.z.enum([`tool`,`service`]).optional()}),de=n.z.object({services:n.z.array(ue).optional(),steps:n.z.array(le).optional()}),fe=n.z.object({steps:n.z.array(le).optional()}),pe=n.z.object({"runs-on":n.z.string().optional(),needs:n.z.union([n.z.string(),n.z.array(n.z.string())]).optional(),extends:n.z.union([n.z.string(),n.z.array(n.z.string())]).optional(),description:n.z.string().optional(),strategy:ce.optional(),steps:n.z.array(le),preJob:fe.optional(),postJob:fe.optional(),env:k.optional(),if:n.z.string().optional(),"system-prompt":n.z.string().optional(),context:n.z.string().optional()}),me=n.z.object({steps:n.z.array(le)}),he=n.z.object({steps:n.z.array(le)}),ge=n.z.object({beforeCreate:he.optional(),afterCreate:he.optional(),beforeCompleted:he.optional(),afterCompleted:he.optional()}),_e=n.z.object({STOP:n.z.string().optional()}),ve=n.z.object({name:n.z.string().optional(),workspace:n.z.string().optional(),imports:n.z.array(n.z.string()).optional(),"system-prompt":n.z.string().optional(),"max-workflows":n.z.number().int().positive().optional(),"launch-command":n.z.string().optional(),keybindings:_e.optional(),on:n.z.looseObject({workflow_dispatch:n.z.object({inputs:n.z.record(n.z.string(),oe).optional()}).nullable().optional(),agent_decision:me.optional()}).optional(),env:k.optional(),pre:de.optional(),post:de.optional(),worktree:ge.optional(),jobs:n.z.record(n.z.string(),pe)}),ye=n.z.enum([`running`,`completed`,`error`]),be=n.z.enum([`success`,`skipped`,`failed`,`interrupted`]),xe=n.z.object({cliAgent:n.z.string().optional(),continueOnError:n.z.boolean().optional(),displayName:n.z.string(),dryRun:n.z.boolean(),env:n.z.record(n.z.string(),n.z.string()).optional(),errorMessage:n.z.string().optional(),exitCode:n.z.number().optional(),failedJob:n.z.string().optional(),finishedAt:n.z.string().optional(),fixLoopCycle:n.z.number().int().positive().optional(),inputs:n.z.record(n.z.string(),n.z.string()).optional(),job:n.z.string().optional(),keepWorktree:n.z.boolean().optional(),originalRepoPath:n.z.string().optional(),outcome:be.optional(),pid:n.z.number().int().positive().optional(),prompt:n.z.string().optional(),runner:n.z.string().optional(),runKey:n.z.string(),secretFile:n.z.string().optional(),skipLaunch:n.z.boolean().optional(),stale:n.z.boolean().optional(),staleReason:n.z.string().optional(),stage:ye,startedAt:n.z.string(),failureReason:n.z.string().optional(),worktreeBranch:n.z.string().optional(),worktreePath:n.z.string().optional(),workflowPath:n.z.string(),workspace:n.z.string()});n.z.object({changelogPath:n.z.string(),contextPath:n.z.string(),displayName:n.z.string(),recordPath:n.z.string(),runDir:n.z.string(),runKey:n.z.string(),stopRequestPath:n.z.string(),workspace:n.z.string()});const Se=`default`,M=`running`,Ce=`completed`,we=`error`,Te=`workspaces`,N=`run.json`,Ee=`stop-request.json`,De=`workflow-run`,Oe=`Workflow process is no longer running`;var ke=class{constructor(e=(0,s.resolve)((0,o.homedir)(),`.workflow-mcp`)){this.homeDir=e}resolveWorkspace(e,t){return this.slugifySegment(e||t||Se,Se)}async createRun(e){let t=this.resolveWorkspace(e.workspace,e.workflowWorkspace),n=this.slugifySegment(e.displayName,De);await this.ensureWorkspaceStructure(t);let r=await this.findReusableRunStage(t,n);if(r===M)throw new O(t,n);let i=this.getRunDirectory(t,M,n);r?(await(0,a.rm)(i,{recursive:!0,force:!0}),await(0,a.rename)(this.getRunDirectory(t,r,n),i),await this.removeFileIfExists((0,s.resolve)(i,`fix.md`)),await this.removeFileIfExists((0,s.resolve)(i,Ee))):await(0,a.mkdir)(i,{recursive:!0});let o={cliAgent:e.cliAgent,continueOnError:e.continueOnError,displayName:e.displayName,dryRun:e.dryRun,env:e.env,inputs:e.inputs,job:e.job,keepWorktree:e.keepWorktree,runKey:n,prompt:e.prompt,runner:e.runner,secretFile:e.secretFile,skipLaunch:e.skipLaunch,stage:M,startedAt:new Date().toISOString(),pid:e.pid??process.pid,workflowPath:e.workflowPath,workspace:t},c=(0,s.resolve)(i,N);return await this.writeRunRecord(c,o),{changelogPath:(0,s.resolve)(i,`changelog.md`),contextPath:(0,s.resolve)(i,`context.md`),displayName:e.displayName,recordPath:c,runDir:i,runKey:n,stopRequestPath:(0,s.resolve)(i,Ee),workspace:t}}async finalizeRun(e,t){await this.ensureWorkspaceStructure(e.workspace);let n=this.getRunDirectory(e.workspace,t.stage,e.runKey),r=await this.readRunRecord(e.recordPath),i={...r,errorMessage:t.errorMessage,exitCode:t.exitCode,failedJob:t.failedJob,failureReason:t.failureReason,finishedAt:new Date().toISOString(),fixLoopCycle:t.fixLoopCycle,originalRepoPath:t.originalRepoPath,outcome:t.outcome,pid:r.pid,stage:t.stage,worktreeBranch:t.worktreeBranch,worktreePath:t.worktreePath};e.runDir!==n&&(await(0,a.rm)(n,{recursive:!0,force:!0}),await(0,a.rename)(e.runDir,n));let o=(0,s.resolve)(n,N);return await this.writeRunRecord(o,i),{...e,recordPath:o,runDir:n,stopRequestPath:(0,s.resolve)(n,Ee)}}async requestStop(e,t,n){let r=this.resolveWorkspace(e),i=this.slugifySegment(t,De),o=this.getRunDirectory(r,M,i),c=(0,s.resolve)(o,N);if(!await this.pathExists(c))throw Error(`Running workflow not found: ${r}/${t}`);let l=await this.inspectRunningRecord(r,i);if(l.stale)throw Error(l.staleReason??`Running workflow is stale: ${r}/${t}`);let u={reason:n?.trim()?n.trim():void 0,requestedAt:new Date().toISOString()};return await(0,a.writeFile)((0,s.resolve)(o,Ee),`${JSON.stringify(u,null,2)}\n`,`utf-8`),u}async hasStopRequest(e){return await this.pathExists(e.stopRequestPath)}async readRunRecord(e){try{return this.validateRunRecord(JSON.parse(await(0,a.readFile)(e,`utf-8`)),e)}catch(t){throw t instanceof D?t:new D(e,t instanceof Error?t.message:`Unknown parse failure`,{cause:t})}}async readRunByKey(e,t,n){let r=this.resolveWorkspace(e),i=this.slugifySegment(n,De),a=(0,s.resolve)(this.getRunDirectory(r,t,i),N);if(!await this.pathExists(a))throw Error(`Workflow run not found: ${r}/${t}/${n}`);return await this.readRunRecord(a)}async listRuns(e){let t=e?[this.resolveWorkspace(e)]:await this.listWorkspaceNames();return(await Promise.all(t.map(async e=>this.listWorkspaceRuns(e)))).flat().sort((e,t)=>this.compareRunRecordsByLatest(e,t))}async listRunsPage(e={}){let t=e.page??1,n=e.pageSize??20,r=await this.listRuns(e.workspace),i=r.length,a=Math.ceil(i/n),o=(t-1)*n,s=r.slice(o,o+n);return{hasNextPage:t<a,hasPreviousPage:t>1&&i>0,items:s,page:t,pageSize:n,total:i,totalPages:a}}async countRunningWorkflows(e){let t=this.getRunStageDirectory(e,M);if(!await this.pathExists(t))return 0;let n=await(0,a.readdir)(t,{withFileTypes:!0}),r=0;for(let e of n){if(!e.isDirectory())continue;let n=(0,s.resolve)(t,e.name,N);if(await this.pathExists(n))try{let e=await this.readRunRecord(n);e.pid&&this.isProcessAlive(e.pid)&&r++}catch{}}return r}async ensureWorkspaceStructure(e){await(0,a.mkdir)(this.getRunStageDirectory(e,M),{recursive:!0}),await(0,a.mkdir)(this.getRunStageDirectory(e,Ce),{recursive:!0}),await(0,a.mkdir)(this.getRunStageDirectory(e,we),{recursive:!0})}async findRunStage(e,t){for(let n of[M,Ce,we])if(await this.pathExists(this.getRunDirectory(e,n,t)))return n;return null}async listWorkspaceRuns(e){let t=new Map;for(let n of[M,Ce,we]){let r=this.getRunStageDirectory(e,n);if(!await this.pathExists(r))continue;let i=await(0,a.readdir)(r,{withFileTypes:!0});for(let a of i){if(!a.isDirectory())continue;let i=(0,s.resolve)(r,a.name,N);if(!await this.pathExists(i))continue;let o=await this.readListableRunRecord(e,n,a.name,i);o&&t.set(o.runKey,o)}}return[...t.values()]}async findReusableRunStage(e,t){let n=await this.findRunStage(e,t);return n===M?(await this.reconcileStaleRunningRecord(e,t),this.findRunStage(e,t)):n}async inspectRunningRecord(e,t){let n=(0,s.resolve)(this.getRunDirectory(e,M,t),N),r=await this.readRunRecord(n);return!r.pid||this.isProcessAlive(r.pid)?r:{...r,stale:!0,staleReason:`${Oe}: pid ${r.pid}`}}async readListableRunRecord(e,t,n,r){try{return t===M?await this.inspectRunningRecord(e,n):await this.readRunRecord(r)}catch(e){if(e instanceof D)return null;throw e}}async reconcileStaleRunningRecord(e,t){let n=this.getRunDirectory(e,M,t),r=(0,s.resolve)(n,N),i=await this.readRunRecord(r);if(!i.pid||this.isProcessAlive(i.pid))return i;let o=this.getRunDirectory(e,we,t),c={...i,errorMessage:`${Oe}: pid ${i.pid}`,exitCode:130,finishedAt:new Date().toISOString(),outcome:`interrupted`,stage:we};return await(0,a.rm)(o,{recursive:!0,force:!0}),await(0,a.rename)(n,o),await this.writeRunRecord((0,s.resolve)(o,N),c),c}async listWorkspaceNames(){let e=(0,s.resolve)(this.homeDir,Te);return await this.pathExists(e)?(await(0,a.readdir)(e,{withFileTypes:!0})).filter(e=>e.isDirectory()).map(e=>e.name).sort((e,t)=>e.localeCompare(t)):[]}compareRunRecordsByLatest(e,t){let n=e.finishedAt??e.startedAt,r=(t.finishedAt??t.startedAt).localeCompare(n);if(r!==0)return r;let i=e.workspace.localeCompare(t.workspace);return i===0?e.runKey.localeCompare(t.runKey):i}async writeRunRecord(e,t){await(0,a.writeFile)(e,`${JSON.stringify(t,null,2)}\n`,`utf-8`)}validateRunRecord(e,t){let n=xe.safeParse(e);if(!n.success)throw new D(t,n.error.message);return n.data}getRunStageDirectory(e,t){return(0,s.resolve)(this.homeDir,Te,e,t)}getRunDirectory(e,t,n){return(0,s.resolve)(this.getRunStageDirectory(e,t),n)}async pathExists(e){try{return await(0,a.access)(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async removeFileIfExists(e){try{await(0,a.unlink)(e)}catch(e){if(e.code!==`ENOENT`)throw e}}slugifySegment(e,t){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||t}isProcessAlive(e){try{return process.kill(e,0),!0}catch(e){let t=e.code;if(t===`ESRCH`)return!1;if(t===`EPERM`)return!0;throw e}}};const Ae=n.z.object({page:n.z.number().int().min(1).optional().describe(`Page number to return. Defaults to 1.`),pageSize:n.z.number().int().min(1).max(100).optional().describe(`Number of workflow runs per page. Defaults to 20 and is capped at 100.`),workspace:n.z.string().optional().describe(`Optional workspace filter. When omitted, runs from all workspaces are returned.`)});var je=class e{static TOOL_NAME=`list_workflow_statuses`;constructor(e=new ke){this.registry=e}getInputSchema(){return Ae}getDefinition(){return{name:e.TOOL_NAME,description:`List tracked workflow runs from the local workflow registry, including their workspace, stage, outcome, and timestamps.`,inputSchema:n.z.toJSONSchema(Ae)}}async execute(e={}){try{let t=Ae.parse(e),n=await this.registry.listRunsPage({page:t.page??1,pageSize:t.pageSize??20,workspace:t.workspace});return{content:[{type:`text`,text:JSON.stringify(n,null,2)}]}}catch(t){let n=new g(`Failed to list workflow statuses.`,`LIST_WORKFLOW_STATUSES_TOOL_FAILED`,{workspace:e.workspace??`all`},{cause:t});return{content:[{type:`text`,text:JSON.stringify({code:n.code,context:n.context,message:n.message},null,2)}],isError:!0}}}},Me=class extends Error{code=`WORKFLOW_CAPACITY_EXCEEDED`;constructor(e,t,n){super(`Workspace "${e}" is at capacity: ${t}/${n} workflows running.\n → Check running workflows: list-workflow-statuses --workspace ${e}\n → Wait for a running workflow to complete, or cancel one before dispatching again.`),this.workspace=e,this.running=t,this.max=n,this.name=`WorkflowCapacityError`}},P=class extends Error{code=`WORKFLOW_INTERRUPTED`;context;constructor(e,t={}){let n=t.context?` during ${t.context.phase}`:``;super(`Workflow interrupted by ${e}${n}`,{cause:t.cause??t.context}),this.signal=e,this.name=`WorkflowInterruptedError`,this.context=t.context}};const F=`\x1B[0m`,Ne=`\x1B[1m`,Pe=`\x1B[2m`,Fe=`\x1B[31m`,Ie=`\x1B[34m`,Le=[`pnpm-workspace.yaml`,`nx.json`,`.git`];function Re(e){let t=(0,s.resolve)(e);for(;;){for(let e of Le)if((0,c.existsSync)((0,s.join)(t,e)))return t;let n=(0,s.dirname)(t);if(n===t)return e;t=n}}var ze=class{runningServices=[];processRegistry=new u.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH);portRegistry=new l.PortRegistryService(process.env.PORT_REGISTRY_PATH);constructor(e){this.logger=e}resolveWorkingDirectory(e,t){return e[`working-directory`]?(0,s.resolve)(t,e[`working-directory`]):t}getRunningServices(){return this.runningServices}async startService(e,t,n,i){let a=e.run;if(i)return this.logger.info(` ${Ie}dry ${F} ${e.name}`),this.logger.info(` ${Pe}$ ${a}${F}`),null;this.logger.info(` ${Ie}start${F} ${e.name}`),this.logger.info(` ${Pe}$ ${a}${F}`);let o={...process.env,...t};if(e.env)for(let[t,n]of Object.entries(e.env))o[t]=String(n);let s=this.resolveWorkingDirectory(e,n),c=Re(s),l=o.NODE_ENV??process.env.NODE_ENV??`development`,u=e.host??`127.0.0.1`,d;if(e.port!==void 0||e[`port-range`]){let t=await this.portRegistry.reservePort({repositoryPath:c,serviceName:e.name,serviceType:e[`service-type`]??`tool`,environment:l,preferredPort:e.port,portRange:e[`port-range`],host:u,force:!0});if(!t.success||t.port===void 0)throw Error(t.error??`Failed to reserve port for background service ${e.name}`);d=t.port,o.PORT??=String(d),o.SERVICE_PORT??=String(d),o.HOST??=u,o.SERVICE_HOST??=u}let f=(0,r.spawn)(a,[],{stdio:[`ignore`,`pipe`,`pipe`],cwd:s,env:{...o,FORCE_COLOR:`1`},shell:`/bin/zsh`,detached:!0});if(f.pid===void 0)throw d!==void 0&&await this.portRegistry.releasePort({repositoryPath:c,serviceName:e.name,serviceType:e[`service-type`]??`tool`,environment:l,force:!0}),Error(`Failed to spawn background service ${e.name}`);let p=`${Ie}[${e.name}]${F}`;f.stdout?.on(`data`,e=>{for(let t of e.toString().split(`
|
|
5
|
+
`))t.trim()&&process.stdout.write(`${p} ${t}\n`)}),f.stderr?.on(`data`,e=>{for(let t of e.toString().split(`
|
|
6
|
+
`))t.trim()&&process.stderr.write(`${p} ${t}\n`)}),f.on(`error`,e=>{this.logger.error(`${p} Process error: ${e.message}`)});let m=async()=>{d!==void 0&&await this.portRegistry.releasePort({repositoryPath:c,serviceName:e.name,serviceType:e[`service-type`]??`tool`,environment:l,force:!0})};if(f.pid!==void 0){let t=await this.processRegistry.registerProcess({repositoryPath:c,serviceName:e.name,serviceType:e[`service-type`]??`tool`,environment:l,pid:f.pid,port:d,host:d===void 0?void 0:u,command:a,args:[],metadata:{workingDirectory:s,readyCheck:e[`ready-check`]},force:!0});if(!t.success){try{process.kill(-f.pid,`SIGTERM`)}catch{f.kill(`SIGTERM`)}throw d!==void 0&&await this.portRegistry.releasePort({repositoryPath:c,serviceName:e.name,serviceType:e[`service-type`]??`tool`,environment:l,force:!0}),Error(t.error??`Failed to register process for background service ${e.name}`)}m=async()=>{let t=await this.processRegistry.releaseProcess({repositoryPath:c,serviceName:e.name,serviceType:e[`service-type`]??`tool`,environment:l,pid:f.pid,kill:!0,releasePort:!0,force:!0});if(!t.success&&!t.error?.includes(`No matching process entry`))throw Error(t.error??`Failed to release ${e.name}`)}}let h={name:e.name,process:f,env:o,release:m};return this.runningServices.push(h),h}async waitForServiceReady(e,t,n,i){if(!e[`ready-check`]||typeof e[`ready-check`]!=`string`)return!0;let a=(e[`ready-timeout`]||30)*1e3,o=Date.now(),s=e[`ready-check`],c=this.resolveWorkingDirectory(e,t),l=this.runningServices.find(t=>t.name===e.name)?.env??{...process.env,...n};for(this.logger.info(` ${Pe}wait${F} Waiting for ${e.name} to be ready...`);Date.now()-o<a;){if(i?.())return!1;try{return(0,r.execSync)(s,{stdio:`ignore`,cwd:c,env:l,shell:`/bin/zsh`,timeout:5e3}),this.logger.info(` [32mready${F} ${e.name}`),!0}catch{if(i?.())return!1}await new Promise(e=>{setTimeout(e,1e3)})}return i?.()||this.logger.error(` ${Fe}timeout${F} ${e.name} not ready after ${e[`ready-timeout`]||30}s`),!1}async stopAll(){if(this.runningServices.length!==0){this.logger.info(`\n ${Ie}${Ne}pre${F} ${Ne}Stopping services${F}`),this.logger.info(` ${`─`.repeat(50)}`);for(let e of this.runningServices){if(!e.process.killed&&e.process.pid&&this.logger.info(` ${Pe}stop${F} ${e.name} (pid ${e.process.pid})`),e.release)try{await e.release();continue}catch(t){this.logger.warn(` ${Fe}warn${F} Failed registry cleanup for ${e.name}: ${t instanceof Error?t.message:String(t)}`)}if(!e.process.killed&&e.process.pid)try{process.kill(-e.process.pid,`SIGTERM`)}catch{e.process.kill(`SIGTERM`)}}this.runningServices=[]}}};const I=`\x1B[0m`,Be=`\x1B[1m`,Ve=`\x1B[2m`,He=`\x1B[33m`,Ue=`WORKFLOW_JOB_ID`,We=`WORKFLOW_JOB_NAME`,Ge=`PROCESS_REGISTRY_TAG`;var Ke=class{constructor(e,t,n){this.parser=e,this.stepRunner=t,this.logger=n}async runJob(e,t,n,r){let i=this.parser.expandMatrix(t),a=r.maxRetries;for(let o of i){let i=(0,d.randomUUID)(),l=Object.keys(o).length>0?` ${Ve}(${Object.entries(o).map(([e,t])=>`${e}=${t}`).join(`, `)})${I}`:``;this.logger.info(`\n [35m${Be}job${I} ${Be}${e}${I}${l}`),this.logger.info(` ${`─`.repeat(50)}`);let u={...n.workflowEnv,[Ue]:i,[We]:e,[Ge]:i};if(t.env)for(let[e,r]of Object.entries(t.env))u[e]=this.parser.interpolate(String(r),{inputs:n.inputs,matrix:o,secrets:n.secrets,env:u});u[Ue]=i,u[We]=e,u[Ge]=i,this.logger.info(` ${Ve}jobid${I} ${i}`);let f=[];if(u.WORKFLOW_RUN_DIR){let t=n.jobOrder.indexOf(e),r=n.jobOrder.slice(0,t+1).map(e=>{let t=n.allJobs[e]?.description;return t?` - ${e}: ${t}`:` - ${e}`}).join(`
|
|
7
|
+
`);f.push(`You are currently running job "${e}" in the following workflow:\n${r}\n\nWhen you have completed your task, you MUST either:\n1. If you find bugs, missing features, or issues that need fixes from a previous job, write to ${u.WORKFLOW_RUN_DIR}/fix.md with a YAML frontmatter restart-from field specifying which job should handle the fix, followed by a detailed description. Example:\n---\nrestart-from: development\n---\nDescription of what needs to be fixed...\n\nThe workflow runner will restart from the specified job. Only create fix.md for issues that require code changes — fix minor issues yourself.\n2. Otherwise, stop. Do not continue with unrelated work.`)}if(n.workflowSystemPrompt&&f.push(this.parser.interpolate(n.workflowSystemPrompt,{inputs:n.inputs,matrix:o,secrets:n.secrets,env:u})),t[`system-prompt`]&&f.push(this.parser.interpolate(t[`system-prompt`],{inputs:n.inputs,matrix:o,secrets:n.secrets,env:u})),f.length>0){let e=f.join(`
|
|
8
|
+
|
|
9
|
+
`);u.JOB_SYSTEM_PROMPT=e,this.logger.info(` ${Ve}prompt${I} ${e.split(`
|
|
10
|
+
`)[0].slice(0,80)}${e.includes(`
|
|
11
|
+
`)?`...`:``}`)}let p=t.context?this.parser.interpolate(t.context,{inputs:n.inputs,matrix:o,secrets:n.secrets,env:u}):u.CONTEXT_FILE;if(p){let e=(0,s.resolve)(r.workflowDir,p);(0,c.existsSync)(e)?(u.WORKFLOW_CONTEXT=e,u.WORKFLOW_CONTEXT_FILE=e,this.logger.info(` ${Ve}ctx ${I} ${p}`)):r.dryRun||this.logger.warn(` ${He}warn${I} context file not found: ${p}`)}if(u.CHANGELOG_FILE){let e=(0,s.resolve)(r.workflowDir,u.CHANGELOG_FILE),t=(0,s.dirname)(e);(0,c.existsSync)(t)||(0,c.mkdirSync)(t,{recursive:!0}),(0,c.existsSync)(e)||(0,c.writeFileSync)(e,`# Changelog
|
|
12
|
+
`,`utf-8`),u.WORKFLOW_CHANGELOG=e,this.logger.info(` ${Ve}log ${I} changelog at ${u.CHANGELOG_FILE}`)}let m={inputs:n.inputs,matrix:o,secrets:n.secrets,env:u},h=!1,g=t.preJob?.steps??[],_=t.steps??[],v=t.postJob?.steps??[];for(let t=1;t<=a;t++){t>1&&(this.logger.info(`\n ${He}retry${I} ${Be}${e}${I}${l} (attempt ${t}/${a})`),this.logger.info(` ${`─`.repeat(50)}`));let n=!0;if(g.length>0&&(n=await this.runStepSequence(g,m,r)),n&&=await this.runStepSequence(_,m,r),v.length>0){let e=await this.runStepSequence(v,m,r);n&&=e}if(n){h=!0;break}t<a&&this.logger.warn(` ${He}Job "${e}" failed, retrying...${I}`)}if(h)this.logger.info(` [32mJob "${e}" completed${I}${l}`);else if(this.logger.error(`\n [31mJob "${e}" failed after ${a} attempts${I}`),t.strategy?.[`fail-fast`]!==!1)return!1}return!0}async runStepSequence(e,t,n){for(let r of e)if(!await this.stepRunner.runStep(r,t,n))return!1;return!0}},qe=class extends Error{code=`WORKFLOW_STEP_SPAWN_FAILED`;constructor(e,t={}){super(`Failed to start workflow step "${e.stepName}"`,{cause:t.cause??e}),this.context=e,this.name=`WorkflowStepSpawnError`}},Je=class extends Error{code=`WORKFLOW_STEP_TIMEOUT_CONFIG_INVALID`;constructor(e,t={}){super(`Invalid ${e.configKey} for workflow step "${e.stepName}"`,{cause:t.cause??e}),this.context=e,this.name=`WorkflowStepTimeoutConfigError`}};const Ye=`/backend-api`;var Xe=class{codexHome;fetchFn;now;readTextFile;constructor(e={}){this.codexHome=e.codexHome??(0,s.join)((0,o.homedir)(),`.codex`),this.fetchFn=e.fetchFn??fetch,this.now=e.now??(()=>Date.now()),this.readTextFile=e.readTextFile??(e=>(0,a.readFile)(e,`utf8`))}isCodexCommand(e,t){let n=e.trim();return n===`codex`||n.startsWith(`codex `)}async getQuotaStatus(){let e=await this.readAuthFile(),t=e?.tokens?.access_token?.trim(),n=e?.tokens?.account_id?.trim();if(!t||!n)return null;let r=await this.readChatgptBaseUrl(),i=await this.fetchFn(this.buildUsageUrl(r),{headers:{Authorization:`Bearer ${t}`,"ChatGPT-Account-Id":n,"User-Agent":`codex-cli`}});if(!i.ok)throw Error(`codex quota request failed with HTTP ${i.status}`);let a=await i.json();return{blockingLimit:this.findBlockingLimit(a),planType:a.plan_type??null}}async readAuthFile(){let e=await this.readOptionalTextFile((0,s.join)(this.codexHome,`auth.json`));return e?JSON.parse(e):null}async readChatgptBaseUrl(){let e=(await this.readOptionalTextFile((0,s.join)(this.codexHome,`config.toml`)))?.match(/^\s*chatgpt_base_url\s*=\s*"([^"]+)"/m);return this.normalizeBaseUrl(e?.[1]??`https://chatgpt.com/backend-api/`)}buildUsageUrl(e){return e.includes(Ye)?`${e}/wham/usage`:`${e}/api/codex/usage`}normalizeBaseUrl(e){let t=e.trim().replace(/\/+$/,``);return(t.startsWith(`https://chatgpt.com`)||t.startsWith(`https://chat.openai.com`))&&!t.includes(Ye)&&(t=`${t}${Ye}`),t}async readOptionalTextFile(e){try{return await this.readTextFile(e)}catch(e){if(e.code===`ENOENT`)return null;throw e}}findBlockingLimit(e){let t=[];this.collectBlockingLimit(t,`codex`,`codex`,e.rate_limit);for(let n of e.additional_rate_limits??[])this.collectBlockingLimit(t,n.metered_feature??n.limit_name??`codex`,n.limit_name??n.metered_feature??`codex`,n.rate_limit);return t.sort((e,t)=>e.resetAt-t.resetAt||t.usedPercent-e.usedPercent),t[0]??null}collectBlockingLimit(e,t,n,r){if(!r)return;let i=[{payload:r.primary_window,window:`primary`},{payload:r.secondary_window,window:`secondary`}];for(let{payload:a,window:o}of i){let i=a?.reset_at,s=a?.used_percent??0;typeof i==`number`&&(s>=100||r.limit_reached===!0&&i*1e3>this.now()||r.allowed===!1&&i*1e3>this.now())&&e.push({limitId:t,limitName:n,resetAfterSeconds:a?.reset_after_seconds??null,resetAt:i,usedPercent:s,window:o,windowSeconds:a?.limit_window_seconds??null})}}};const L=`\x1B[0m`,R=`\x1B[2m`,Ze=`\x1B[36m`,Qe=`\x1B[32m`,z=`\x1B[33m`,B=`\x1B[31m`,$e=`SIGINT`,V=`SIGTERM`,et=`unnamed`,tt=`/bin/zsh`,nt=`FORCE_COLOR`,H=`inherit`,rt=`pipe`,it=` `,at=`
|
|
13
|
+
${it}\$ `,ot=`skip`,U=`warn`,W=`error`,G=`fail`,K=`interrupted`,st=`already stopping`,ct=`failed to signal`,q=`(continue-on-error)`,lt=`ESRCH`,ut=process.platform!==`win32`,dt=`interactiveRun`,ft=`timeout-minutes`,pt=`timeout-retries`,mt=`retry`,J=`SIGKILL`,ht=1e3,gt=1e3,_t=6e4,vt=Math.floor(2147483647/_t),yt=`WORKFLOW_STEP_DISPLAY`,bt=`workflow-mcp`,xt={status:`status_completed`},St=new Set([`ENOENT`,`EBUSY`,`EAGAIN`,`EPERM`]),Ct=1e3,wt=`done`,Tt=`stop`,Et=1e3,Dt=`stop_requested`,Ot=n.z.number().positive().finite().max(vt),kt=n.z.number().int().min(0).max(10).default(2);var At=class{activeStep=null;activeQuotaWait=null;constructor(e,t,n=new Xe,r=c.watch){this.parser=e,this.logger=t,this.quotaService=n,this.watchStatusFile=r}stopActiveStep(e){if(this.activeQuotaWait){if(this.activeQuotaWait.controller.signal.aborted){this.logger.info(` ${R}${ot}${L} ${this.activeQuotaWait.stepName} ${st}`);return}this.activeQuotaWait.signal=e,this.activeQuotaWait.controller.abort(),this.logger.info(` ${z}${K}${L} cancelling quota wait for ${this.activeQuotaWait.stepName}`);return}if(!this.activeStep){this.logger.info(` ${R}${ot}${L} no active step to stop`);return}if(this.activeStep.process.killed){this.logger.info(` ${R}${ot}${L} ${this.activeStep.stepName} ${st}`);return}this.logger.info(` ${z}${K}${L} sending ${e} to ${this.activeStep.stepName}`),this.killActiveStep(e)||this.logger.warn(` ${z}${U}${L} ${ct} ${this.activeStep.stepName}`)}async runStep(e,t,n){let r=this.resolveRunner(n),i=this.resolveStepCommand(e,t,r),a=e.name?this.parser.interpolate(e.name,t):i?.command.slice(0,60)||e.uses||et;if(e.uses){let n=this.parser.interpolate(e.uses,t);return this.parser.isActionSkipped(n)?(this.logger.info(` ${R}${ot}${L} ${n}`),!0):(this.logger.warn(` ${z}${U}${L} Unsupported action: ${n} (skipped)`),!0)}if(!i)return!0;if(i.missingRunner)return this.logger.error(` ${B}${W}${L} runner "${i.missingRunner.runner}" not found for step "${a}" (available runner keys: ${i.missingRunner.availableKeys.join(`, `)})`),e[`continue-on-error`]||n.continueOnError?(this.logger.warn(` ${z}${G}${L} ${a} ${q}\n`),!0):(this.logger.error(` ${B}${G}${L} ${a}\n`),!1);let{command:o,interactive:l}=i,u=this.quotaService.isCodexCommand(o,i.agentKey),d=e[`continue-on-error`]||n.continueOnError,f;try{f=this.resolveTimeoutMs(e,a)}catch(e){if(!(e instanceof Je))throw e;return this.logger.error(` ${B}${W}${L} invalid ${e.context.configKey} for "${a}": ${String(e.context.timeoutValue)}`),d?(this.logger.warn(` ${z}${G}${L} ${a} ${q}\n`),!0):(this.logger.error(` ${B}${G}${L} ${a}\n`),!1)}let p={...process.env,...t.env};if(e.env)for(let[n,r]of Object.entries(e.env))p[n]=this.parser.interpolate(String(r),t);p.WORKFLOW_STEP_NAME=a,p[yt]=this.formatStepDisplay(p.WORKFLOW_JOB_NAME,a);let m;l&&(m=this.createStatusFile(),p.WORKFLOW_STATUS_FILE=m,this.logger.info(` ${R}status-file${L} ${m}`));let h=e[`working-directory`]?(0,s.resolve)(n.workflowDir,this.parser.interpolate(e[`working-directory`],t)):n.workflowDir;if(n.dryRun)return this.logger.info(` ${Ze}dry ${L} ${a}`),this.logger.info(`${it}${R}\$ ${o.split(`
|
|
14
|
+
`).join(at)}${L}`),!0;let g;try{g=this.resolveTimeoutRetries(e,a)}catch(e){if(!(e instanceof Je))throw e;return this.logger.error(` ${B}${W}${L} invalid ${e.context.configKey} for "${a}": ${String(e.context.timeoutValue)}`),d?(this.logger.warn(` ${z}${G}${L} ${a} ${q}\n`),!0):(this.logger.error(` ${B}${G}${L} ${a}\n`),!1)}let _=!1;for(let e=0;e<=g;e++){if(await this.waitForCodexQuotaAvailability(a,o,u),e>0?this.logger.warn(` ${z}${mt}${L} retrying step after timeout "${a}" (attempt ${e+1}/${g+1})`):this.logger.info(` ${Ze}run ${L} ${a}`),this.logger.info(`${it}${R}\$ ${o.split(`
|
|
15
|
+
`)[0]}${o.includes(`
|
|
16
|
+
`)?` ...`:``}${L}`),n.stopRequestPath&&(0,c.existsSync)(n.stopRequestPath))throw this.logger.info(`\n ${z}${K}${L} ${a} (${Tt})`),new P(V,{cause:Error(`Workflow stop requested before "${a}"`),context:this.createInterruptContext(a,o)});let t=l?this.withInteractiveTerminalTitle(o,p):o,r=this.executeCommand(t,h,p,a,l,f),i=l&&m?await this.raceInteractiveCompletion(r,m,a,n.stopRequestPath):n.stopRequestPath?await this.raceStopRequest(r,a,n.stopRequestPath):await r;if(i.status===`stop_requested`)throw this.logger.info(`\n ${z}${K}${L} ${a} (${Tt})`),new P(V,{cause:Error(`Workflow stop requested during "${a}"`),context:this.createInterruptContext(a,o)});if(i.status===`signaled`)throw this.logger.info(`\n ${z}${K}${L} ${a}`),new P(i.signal,{cause:Error(`Step process ended with ${i.signal}`),context:this.createInterruptContext(a,o)});if(i.status===`completed`&&(i.exitCode===130||i.exitCode===143)){let e=i.exitCode===130?$e:V;throw this.logger.info(`\n ${z}${K}${L} ${a}`),new P(e,{cause:Error(`Step process exited with ${i.exitCode}`),context:this.createInterruptContext(a,o)})}if(i.status===`spawn_error`)return this.logger.error(` ${B}${W}${L} unable to start step "${a}" in ${i.error.context.workDir} (${this.formatSpawnError(i.error)})`),d?(this.logger.warn(` ${z}${G}${L} ${a} ${q}\n`),!0):(this.logger.error(` ${B}${G}${L} ${a}\n`),!1);if(i.status===`timed_out`){if(this.logger.error(` ${B}${W}${L} step timed out after ${this.formatTimeoutMs(i.timeoutMs)} "${a}" (${o.split(`
|
|
17
|
+
`)[0]})`),e<g){if(m)try{(0,c.writeFileSync)(m,``,`utf-8`)}catch(e){this.logger.warn(` ${z}${U}${L} status-file reset failed for ${a}: ${e instanceof Error?e.message:String(e)}`)}continue}return m&&this.cleanupStatusFile(m),d?(this.logger.warn(` ${z}${G}${L} ${a} ${q}\n`),!0):(this.logger.error(` ${B}${G}${L} ${a}\n`),!1)}if(m&&this.cleanupStatusFile(m),i.status===`status_completed`)return this.logger.info(`\n ${Qe}${wt}${L} ${a} (status-file signaled completion)\n`),!0;if(i.status===`completed`&&i.exitCode===0)return this.logger.info(`\n ${Qe}pass${L} ${a}\n`),!0;if(i.status===`completed`&&this.logger.error(` ${B}${W}${L} step "${a}" exited with code ${i.exitCode} (${o.split(`
|
|
18
|
+
`)[0]})`),!_&&await this.shouldRetryForCodexQuota(a,u)){_=!0,this.logger.warn(` ${z}${mt}${L} codex quota reached after failed step, retrying after reset`),--e;continue}return d?(this.logger.warn(` ${z}${G}${L} ${a} ${q}\n`),!0):(this.logger.error(` ${B}${G}${L} ${a}\n`),!1)}return!1}async executeCommand(e,t,n,i,a,o){return a&&this.logger.info(` ${R}(interactive: output renders directly to terminal)${L}`),await new Promise(s=>{let c=a?this.captureTerminalState():null,l=a?(0,r.spawn)(e,[],{stdio:[H,H,H],cwd:t,detached:!1,env:{...n,[nt]:`1`},shell:tt}):(0,r.spawn)(e,[],{stdio:[H,rt,rt],cwd:t,detached:ut,env:{...n,[nt]:`1`},shell:tt});this.activeStep={command:e,interactive:a,lastSignaledProcessIds:[],process:l,processGroupId:ut&&!a&&typeof l.pid==`number`?l.pid:null,rootProcessId:typeof l.pid==`number`?l.pid:null,stepName:i};let u=null,d=!1,f=!1,p=async e=>{f||(f=!0,m&&clearTimeout(m),u&&clearTimeout(u),a&&(await this.cleanupInteractiveProcessTree(V),this.restoreTerminalState(c)),this.activeStep=null,s(e))},m=o===null?null:setTimeout(()=>{if(d=!0,this.killActiveStep(V)){u=setTimeout(()=>{f||(this.logger.warn(` ${z}${U}${L} ${i} forcing SIGKILL`),this.killActiveStep(J))},ht);return}p({status:`timed_out`,timeoutMs:o})},o);l.stdout?.on(`data`,e=>{for(let t of e.toString().split(`
|
|
19
|
+
`))t&&this.logger.info(t)}),l.stderr?.on(`data`,e=>{for(let t of e.toString().split(`
|
|
20
|
+
`))t&&this.logger.error(t)}),l.on(`error`,n=>{p({status:`spawn_error`,error:new qe({commandPreview:e.split(`
|
|
21
|
+
`)[0],source:`StepRunnerService.executeCommand`,stepName:i,workDir:t},{cause:n})})}),l.on(`exit`,(e,t)=>{if(a||this.cleanupProcessGroup(),d&&o!==null){p({status:`timed_out`,timeoutMs:o});return}if(t===$e){p({status:`signaled`,signal:$e});return}if(t===V){p({status:`signaled`,signal:V});return}p({status:`completed`,exitCode:e??1})})})}createInterruptContext(e,t){return{phase:`step_execution`,stepName:e,commandPreview:t.split(`
|
|
22
|
+
`)[0],source:`StepRunnerService.runStep`}}formatSpawnError(e){let t=e.cause;return t instanceof Error?`${`code`in t&&typeof t.code==`string`?t.code:`unknown-spawn-error`}${`path`in t&&typeof t.path==`string`?` at ${t.path}`:``}; verify command, shell, and permissions`:`check shell availability, PATH, and permissions`}resolveStepCommand(e,t,n){let r=e[dt]!=null,i=r?{command:e[dt],interactive:!0}:{command:e.run,interactive:!1},a=r?{command:e.run,interactive:!1}:{command:e[dt],interactive:!0},o=this.resolveExactAgentCommand(i.command,t,n);if(o)return{...o,interactive:i.interactive};let s=this.resolveExactAgentCommand(a.command,t,n);if(s)return{...s,interactive:a.interactive};let c=this.resolveMissingExplicitRunner(e,n);if(c)return{command:``,interactive:!1,missingRunner:c};let l=this.resolveStringCommand(i.command,t);if(l)return{command:l,interactive:i.interactive};let u=this.resolveStringCommand(a.command,t);if(u)return{command:u,interactive:a.interactive};let d=this.resolveFirstMappedCommand(i.command,t);if(d)return{command:d,interactive:i.interactive};let f=this.resolveFirstMappedCommand(a.command,t);return f?{command:f,interactive:a.interactive}:null}resolveExactAgentCommand(e,t,n){if(!n||!e||typeof e==`string`)return null;let r=e[n];return r?{agentKey:n,command:this.parser.interpolate(r,t),interactive:!1}:null}resolveStringCommand(e,t){return!e||typeof e!=`string`?null:this.parser.interpolate(e,t)}resolveFirstMappedCommand(e,t){if(!e||typeof e==`string`)return null;let[n]=Object.values(e);return n?this.parser.interpolate(n,t):null}resolveMissingExplicitRunner(e,t){if(!t)return null;let n=this.collectMappedRunnerKeys(e);return n.length===0||n.includes(t)?null:{availableKeys:n,runner:t}}collectMappedRunnerKeys(e){let t=new Set;for(let n of[e[dt],e.run])if(n&&typeof n!=`string`)for(let e of Object.keys(n))t.add(e);return[...t]}resolveRunner(e){return e.runner??e.cliAgent}formatStepDisplay(e,t){return e?`${e} > ${t}`:t}withInteractiveTerminalTitle(e,t){let n=`${bt}: ${t[yt]??e.split(`
|
|
23
|
+
`)[0]??et}`,r=`[ -t 1 ] && printf '\\033]0;%s\\007' ${this.escapeShellArg(n)}`,i=`[ -t 1 ] && printf '\\033]0;%s\\007' ${this.escapeShellArg(bt)}`;return`trap ${this.escapeShellArg(i)} EXIT
|
|
24
|
+
${r}
|
|
25
|
+
${e}`}escapeShellArg(e){return`'${e.replace(/'/g,`'\\''`)}'`}async shouldRetryForCodexQuota(e,t){return t?(await this.readCodexQuotaStatus(e))?.blockingLimit!=null:!1}async waitForCodexQuotaAvailability(e,t,n){if(n)for(;;){let n=await this.readCodexQuotaStatus(e),r=n?.blockingLimit;if(!r)return;let i=r.resetAt*1e3+Et,a=Math.max(i-Date.now(),Et),o=new Date(i).toLocaleString(),s=n?.planType?` (${n.planType})`:``;this.logger.warn(` ${z}wait ${L} codex quota ${r.limitName}/${r.window} is at ${r.usedPercent}%${s}; waiting until ${o}`),await this.waitForQuotaDelay(e,t,a)}}async readCodexQuotaStatus(e){try{return await this.quotaService.getQuotaStatus()}catch(t){return this.logger.warn(` ${z}${U}${L} unable to read codex quota for "${e}" (${this.formatQuotaError(t)})`),null}}async waitForQuotaDelay(e,t,n){let r=new AbortController;this.activeQuotaWait={controller:r,signal:null,stepName:e};try{let e=n;for(;e>0;){let t=Math.min(e,3e4);await this.waitForDelay(t,r.signal),e-=t}}catch(n){throw r.signal.aborted?new P(this.activeQuotaWait?.signal??V,{cause:Error(`Interrupted while waiting for Codex quota reset`),context:{...this.createInterruptContext(e,t),phase:`StepRunnerService.waitForCodexQuota`}}):n}finally{this.activeQuotaWait=null}}async waitForDelay(e,t){await new Promise((n,r)=>{let i=setTimeout(()=>{t.removeEventListener(`abort`,a),n()},e),a=()=>{clearTimeout(i),t.removeEventListener(`abort`,a),r(Error(`aborted`))};t.addEventListener(`abort`,a,{once:!0})})}formatQuotaError(e){return e instanceof Error&&e.message.trim().length>0?e.message:`unknown quota error`}resolveTimeoutMs(e,t){let n=e[ft];if(n===void 0)return null;try{let e=Ot.parse(n);return Math.ceil(e*_t)}catch(e){throw new Je({stepName:t,configKey:ft,timeoutValue:n},{cause:e})}}resolveTimeoutRetries(e,t){let n=e[pt];try{return kt.parse(n)}catch(e){throw new Je({stepName:t,configKey:pt,timeoutValue:n},{cause:e})}}formatTimeoutMs(e){return e%_t===0?`${e/_t} minute(s)`:`${e}ms`}cleanupProcessGroup(){if(!this.activeStep)return;let{processGroupId:e,stepName:t}=this.activeStep;if(e!==null)try{process.kill(-e,V)}catch(n){let r=n.code;r===lt?this.logger.info(` ${R}cleanup${L} process group ${e} already exited (ESRCH)`):this.logger.warn(` ${z}${U}${L} failed to clean up process group for ${t} (pgid=${e}, error=${r})`)}}async cleanupInteractiveProcessTree(e){if(!this.activeStep||!this.activeStep.interactive)return!1;let t=this.collectInteractiveProcessIds();if(t.length===0)return!1;this.activeStep.lastSignaledProcessIds=t;let n=this.signalProcessIds(t,e);if(await this.waitForProcessIdsToExit(t,gt),e!==J){let e=this.uniqueLiveProcessIds(t);e.length>0&&(this.signalProcessIds(e,J),await this.waitForProcessIdsToExit(e,gt))}return n}collectInteractiveProcessIds(){if(!this.activeStep?.interactive||this.activeStep.rootProcessId===null)return[];let e=this.activeStep.rootProcessId,t=[...this.snapshotProcessTree(e).map(e=>e.pid),...this.activeStep.lastSignaledProcessIds,e];return this.uniqueLiveProcessIds(t)}snapshotProcessTree(e){let t;try{t=(0,r.execFileSync)(`ps`,[`-axo`,`pid=,ppid=`],{encoding:`utf-8`,stdio:[`ignore`,rt,`ignore`]})}catch(t){return this.logger.warn(` ${z}${U}${L} unable to inspect process tree for ${e} (${t instanceof Error?t.message:String(t)})`),this.isProcessAlive(e)?[{depth:0,pid:e}]:[]}let n=new Map;for(let e of t.split(`
|
|
26
|
+
`)){let t=e.trim();if(!t)continue;let[r,i]=t.split(/\s+/),a=Number(r),o=Number(i);if(!Number.isInteger(a)||!Number.isInteger(o))continue;let s=n.get(o)??[];s.push(a),n.set(o,s)}let i=[],a=new Set,o=(e,t)=>{if(!a.has(e)){a.add(e),e!==process.pid&&i.push({depth:t,pid:e});for(let r of n.get(e)??[])o(r,t+1)}};return o(e,0),i.sort((e,t)=>t.depth-e.depth)}signalProcessIds(e,t){let n=!1;for(let r of e)if(r!==process.pid)try{process.kill(r,t),n=!0}catch(e){let t=e.code;t!==lt&&this.logger.warn(` ${z}${U}${L} ${ct} pid ${r} (${t??e.message})`)}return n}uniqueLiveProcessIds(e){let t=new Set,n=[];for(let r of e)!Number.isInteger(r)||r<=0||r===process.pid||t.has(r)||(t.add(r),this.isProcessAlive(r)&&n.push(r));return n}isProcessAlive(e){try{return process.kill(e,0),!0}catch{return!1}}async waitForProcessIdsToExit(e,t){let n=Date.now()+t;for(;Date.now()<n;){if(e.every(e=>!this.isProcessAlive(e)))return;await new Promise(e=>setTimeout(e,50))}}killActiveStep(e){if(!this.activeStep)return!1;if(this.activeStep.interactive){let t=this.collectInteractiveProcessIds();if(t.length>0)return this.activeStep.lastSignaledProcessIds=t,this.signalProcessIds(t,e)}let{process:t,processGroupId:n}=this.activeStep;if(n!==null)try{return process.kill(-n,e),!0}catch(e){e.code===lt?this.logger.info(` ${R}signal${L} process group for ${this.activeStep.stepName} already exited (ESRCH)`):this.logger.warn(` ${z}${U}${L} ${ct} process group for ${this.activeStep.stepName}`)}try{return t.kill(e)}catch(e){return this.logger.warn(` ${z}${U}${L} ${ct} ${this.activeStep.stepName} (${e.message})`),!1}}createStatusFile(){let e=(0,s.join)((0,o.tmpdir)(),`workflow-step-${(0,d.randomUUID)()}.status`);return(0,c.writeFileSync)(e,``,`utf-8`),e}async raceInteractiveCompletion(e,t,n,r){return new Promise(i=>{let a=!1,o=!1,s=!1,l=null,u=null,d=null,f=null,p=null,m=e=>{a||(a=!0,u&&=(u.close(),null),d&&=(d.close(),null),f&&=(clearInterval(f),null),p&&=(clearInterval(p),null),l&&=(clearTimeout(l),null),i(e))},h=!1,g=!1,_=()=>{try{return(0,c.existsSync)(t)&&(0,c.readFileSync)(t,`utf-8`).trim()===`YES`}catch(e){let t=e.code;return!t||!St.has(t)?this.logger.warn(` ${z}${U}${L} status-file read failed for ${n}: ${e instanceof Error?e.message:String(e)}`):this.logger.info(` ${R}status-file${L} transient FS error (${t}) polling ${n}`),!1}},v=()=>{if(!(o||!_())){if(o=!0,h=this.killActiveStep(V),!h){this.logger.warn(` ${z}${U}${L} ${n} status-file: SIGTERM could not be delivered, process may have already exited`);return}l=setTimeout(()=>{a||(g=!0,this.logger.warn(` ${z}${U}${L} ${n} did not exit after status-file SIGTERM, forcing SIGKILL`),this.killActiveStep(J))},ht)}},y=()=>!!(r&&(0,c.existsSync)(r)),b=()=>{if(!(s||!y())){if(s=!0,h=this.killActiveStep(V),!h){this.logger.warn(` ${z}${U}${L} ${n} stop request: SIGTERM could not be delivered, process may have already exited`);return}l=setTimeout(()=>{a||(g=!0,this.logger.warn(` ${z}${U}${L} ${n} did not exit after stop-request SIGTERM, forcing SIGKILL`),this.killActiveStep(J))},ht)}};try{u=this.watchStatusFile(t,()=>v())}catch(e){this.logger.warn(` ${z}${U}${L} status-file watcher setup failed for ${n}: ${e instanceof Error?e.message:String(e)}`)}if(f=setInterval(v,1e3),r){try{d=this.watchStatusFile(r,()=>b())}catch(e){e.code!==`ENOENT`&&this.logger.warn(` ${z}${U}${L} stop-request watcher setup failed for ${n}: ${e instanceof Error?e.message:String(e)}`)}p=setInterval(b,Ct)}v(),b(),e.then(e=>{if(s){g&&this.logger.warn(` ${z}${U}${L} ${n} required SIGKILL escalation after stop request`),m({status:Dt});return}if(!(o||_())){m(e);return}switch(o||(o=!0,this.logger.info(` ${Qe}${wt}${L} ${n} status-file observed during process exit handling`)),e.status){case`spawn_error`:case`timed_out`:m(e);return;case`signaled`:g&&this.logger.warn(` ${z}${U}${L} ${n} required SIGKILL escalation (work completed via status-file)`),m(xt);return;case`completed`:e.exitCode===0||e.exitCode>128||g||!h?(g&&this.logger.warn(` ${z}${U}${L} ${n} required SIGKILL escalation (work completed via status-file, exit code ${e.exitCode})`),m(xt)):m(e);return;case`status_completed`:m(e);return;case Dt:m(e);return;default:m(e)}})})}async raceStopRequest(e,t,n){return new Promise(r=>{let i=!1,a=!1,o=null,s=null,l=null,u=e=>{i||(i=!0,l&&=(l.close(),null),s&&=(clearInterval(s),null),o&&=(clearTimeout(o),null),r(e))},d=()=>{if(!(a||!(0,c.existsSync)(n))){if(a=!0,!this.killActiveStep(V)){this.logger.warn(` ${z}${U}${L} ${t} stop request: SIGTERM could not be delivered, process may have already exited`);return}o=setTimeout(()=>{i||(this.logger.warn(` ${z}${U}${L} ${t} did not exit after stop-request SIGTERM, forcing SIGKILL`),this.killActiveStep(J))},ht)}};try{l=this.watchStatusFile(n,()=>d())}catch(e){e.code!==`ENOENT`&&this.logger.warn(` ${z}${U}${L} stop-request watcher setup failed for ${t}: ${e instanceof Error?e.message:String(e)}`)}s=setInterval(d,Ct),d(),e.then(e=>{u(a?{status:Dt}:e)})})}captureTerminalState(){if(!process.stdin.isTTY)return null;try{return(0,r.execFileSync)(`stty`,[`-g`],{encoding:`utf-8`,stdio:[H,rt,`ignore`]}).trim()}catch{return null}}restoreTerminalState(e){if(e&&process.stdin.isTTY)try{(0,r.execFileSync)(`stty`,[e],{stdio:[H,`ignore`,`ignore`]})}catch{}process.stdout.isTTY&&process.stdout.write(`\x1B[0m\x1B[?25h\x1B[?1049l`)}cleanupStatusFile(e){try{(0,c.existsSync)(e)&&(0,c.unlinkSync)(e)}catch(e){this.logger.warn(` ${z}${U}${L} status-file cleanup failed: ${e instanceof Error?e.message:String(e)}`)}}};const Y=`\x1B[0m`,jt=`\x1B[1m`,Mt=`\x1B[2m`,Nt=`\x1B[36m`,Pt=`\x1B[32m`,Ft=`\x1B[31m`,It=`\x1B[34m`;var Lt=class{activePrompt=null;activePromptSignal=null;constructor(e,t){this.stepRunner=e,this.logger=t}async pathExists(e){try{return await(0,a.access)(e),!0}catch{return!1}}abortActivePrompt(e){this.activePromptSignal=e,this.activePrompt?.close()}async handleUserPrompt(e,t,n,r,i){if(!e.on||!(`user_prompt`in e.on))return!0;this.logger.info(`\n ${It}${jt}trigger${Y} ${jt}user_prompt${Y}`),this.logger.info(` ${`─`.repeat(50)}`);let o=t,c=n.CONTEXT_FILE;if(!o&&c){let e=(0,s.resolve)(r,c);await this.pathExists(e)&&(o=(await(0,a.readFile)(e,`utf-8`)).trim(),o&&this.logger.info(` ${Pt}found${Y} Existing context.md -> ${c}`))}if(!o)if(i)this.logger.info(` ${Nt}dry ${Y} Would prompt user for input`),o=`(dry-run: no prompt collected)`;else{this.logger.info(` ${Nt}input${Y} Enter your prompt (press Enter twice to finish):\n`);let e=[],t=(0,f.createInterface)({input:process.stdin,output:process.stdout});this.activePrompt=t,this.activePromptSignal=null,o=await new Promise((n,r)=>{let i=!1,a=e=>{i||(i=!0,this.activePrompt=null,e())};t.on(`line`,n=>{n===``&&e.length>0&&e[e.length-1]===``?t.close():e.push(n)}),t.on(`SIGINT`,()=>{this.activePromptSignal=`SIGINT`,t.close()}),t.on(`close`,()=>a(()=>{let t=this.activePromptSignal;if(this.activePromptSignal=null,t){r(new P(t));return}e.length>0&&e[e.length-1]===``&&e.pop(),n(e.join(`
|
|
27
|
+
`))}))})}if(!o)return this.logger.error(` ${Ft}fail${Y} No prompt provided. Aborting.`),!1;if(c){let e=(0,s.resolve)(r,c);await(0,a.mkdir)((0,s.dirname)(e),{recursive:!0}),await(0,a.writeFile)(e,o,`utf-8`),this.logger.info(` ${Pt}saved${Y} ${o.split(`
|
|
28
|
+
`).length} line(s) -> ${c}`)}return n.USER_PROMPT=o,this.logger.info(` ${Mt}prompt${Y} ${o.split(`
|
|
29
|
+
`)[0].slice(0,80)}${o.includes(`
|
|
30
|
+
`)?`...`:``}`),!0}async handleAgentDecision(e,t,n,r,i,o){let c=e.on?.agent_decision;if(!c?.steps)return!0;this.logger.info(`\n ${It}${jt}trigger${Y} ${jt}agent_decision${Y}`),this.logger.info(` ${`─`.repeat(50)}`),o&&(t.HEARTBEAT_PROMPT=o,this.logger.info(` ${Mt}prompt${Y} ${o.split(`
|
|
31
|
+
`)[0].slice(0,80)}${o.includes(`
|
|
32
|
+
`)?`...`:``}`));let l=t.CONTEXT_FILE;l&&await(0,a.mkdir)((0,s.dirname)((0,s.resolve)(i.workflowDir,l)),{recursive:!0});let u={inputs:n,matrix:{},secrets:r,env:t};for(let e of c.steps)if(!await this.stepRunner.runStep(e,u,i))return this.logger.error(`\n ${Ft}Agent decision aborted workflow${Y}`),!1;if(l&&!i.dryRun){let e=(0,s.resolve)(i.workflowDir,l);if(!await this.pathExists(e))return this.logger.error(` ${Ft}fail${Y} Context file not created: ${l}`),!1;let t=(await(0,a.readFile)(e,`utf-8`)).trim();if(!t)return this.logger.error(` ${Ft}fail${Y} Context file is empty: ${l}`),!1;this.logger.info(` ${Pt}ready${Y} context has ${t.split(`
|
|
33
|
+
`).length} line(s)`)}return!0}};const Rt=[`actions/checkout`,`actions/setup-node`,`actions/cache`,`actions/upload-artifact`,`actions/download-artifact`,`pnpm/action-setup`],zt=`bold.calm.cool.dark.deep.fair.fast.free.gold.keen.kind.loud.neat.pure.rare.safe.slim.soft.tall.warm.wild.wise.blue.gray.jade.iron.zinc.ruby.opal.onyx`.split(`.`),Bt=`arch.beam.bird.bolt.cape.cove.dawn.dove.echo.fern.flux.gale.hawk.haze.iris.jade.kite.lake.lynx.mesa.moth.node.palm.peak.pine.raft.reef.sage.tide.vale`.split(`.`);var Vt=class{generateHumanReadableId(){return`${zt[Math.floor(Math.random()*zt.length)]}-${Bt[Math.floor(Math.random()*Bt.length)]}`}parseWorkflowFile(e){let t=(0,s.resolve)(e);if(!(0,c.existsSync)(t))throw Error(`Workflow file not found: ${t}`);let n=(0,c.readFileSync)(t,`utf-8`),r=ve.parse((0,p.load)(n));if(r.imports&&this.resolveImports(r,t),!r.jobs||Object.keys(r.jobs).length===0)throw Error(`No jobs found in workflow file`);return this.resolveExtends(r),r}resolveImports(e,t,n=new Set){let r=(0,s.resolve)(t);if(n.has(r))throw Error(`Circular import detected: ${r}`);n.add(r);let i=r.replace(/\/[^/]+$/,``);for(let t of e.imports??[]){let a=(0,s.resolve)(i,t);if(!(0,c.existsSync)(a))throw Error(`Imported workflow file not found: ${a} (from ${r})`);let o=(0,p.load)((0,c.readFileSync)(a,`utf-8`));if(o.imports&&this.resolveImports(o,a,n),o.env&&(e.env={...o.env,...e.env}),o.pre&&(e.pre||={},o.pre.services&&(e.pre.services=[...o.pre.services,...e.pre.services??[]]),o.pre.steps&&(e.pre.steps=[...o.pre.steps,...e.pre.steps??[]])),o.post&&(e.post||={},o.post.services&&(e.post.services=[...o.post.services,...e.post.services??[]]),o.post.steps&&(e.post.steps=[...o.post.steps,...e.post.steps??[]])),o.worktree){e.worktree||={};for(let t of[`beforeCreate`,`afterCreate`,`beforeCompleted`,`afterCompleted`])o.worktree[t]&&!e.worktree[t]&&(e.worktree[t]=o.worktree[t])}if(o[`max-workflows`]&&!e[`max-workflows`]&&(e[`max-workflows`]=o[`max-workflows`]),o[`launch-command`]&&!e[`launch-command`]&&(e[`launch-command`]=o[`launch-command`]),o.keybindings&&(e.keybindings={...o.keybindings,...e.keybindings}),o[`system-prompt`]&&(e[`system-prompt`]?e[`system-prompt`]=`${o[`system-prompt`]}\n\n${e[`system-prompt`]}`:e[`system-prompt`]=o[`system-prompt`]),o.jobs)for(let[t,n]of Object.entries(o.jobs))t in(e.jobs??{})||(e.jobs||={},e.jobs[t]=n)}delete e.imports}resolveExtends(e){let{jobs:t}=e,n=new Map;for(let[e,r]of Object.entries(t))e.startsWith(`.`)&&n.set(e,r);for(let[e,r]of Object.entries(t)){if(e.startsWith(`.`)||!r.extends)continue;let t=Array.isArray(r.extends)?r.extends:[r.extends],i=[...r.steps??[]],a=[...r.preJob?.steps??[]],o=[...r.postJob?.steps??[]],s=[],c=[],l=[];for(let i of t){let t=n.get(i);if(!t)throw Error(`Job "${e}" extends "${i}" but template not found`);s.push(...t.steps??[]),c.push(...t.preJob?.steps??[]),l.push(...t.postJob?.steps??[]),this.applyTemplate(r,t)}r.steps=[...s,...i],this.assignJobHookSteps(r,`preJob`,[...c,...a]),this.assignJobHookSteps(r,`postJob`,[...l,...o]),delete r.extends}for(let e of n.keys())delete t[e]}applyTemplate(e,t){e.steps=[...t.steps??[],...e.steps??[]],this.mergeJobHookSteps(e,t,`preJob`),this.mergeJobHookSteps(e,t,`postJob`),(t.env||e.env)&&(e.env={...t.env,...e.env}),t[`system-prompt`]&&e[`system-prompt`]?e[`system-prompt`]=`${t[`system-prompt`]}\n\n${e[`system-prompt`]}`:t[`system-prompt`]&&(e[`system-prompt`]=t[`system-prompt`]),t[`runs-on`]&&!e[`runs-on`]&&(e[`runs-on`]=t[`runs-on`]),t.strategy&&!e.strategy&&(e.strategy=t.strategy),t.context&&!e.context&&(e.context=t.context)}mergeJobHookSteps(e,t,n){let r=t[n]?.steps??[],i=e[n]?.steps??[];this.assignJobHookSteps(e,n,[...r,...i])}assignJobHookSteps(e,t,n){if(n.length>0){e[t]={steps:n};return}delete e[t]}loadSecrets(e){let t=new Map;if(!(0,c.existsSync)(e))return t;let n=(0,c.readFileSync)(e,`utf-8`);for(let e of n.split(`
|
|
34
|
+
`)){let n=e.trim();if(!n||n.startsWith(`#`))continue;let r=n.indexOf(`=`);r!==-1&&t.set(n.slice(0,r),n.slice(r+1))}return t}interpolate(e,t){return e.replace(/\$\{\{\s*(.+?)\s*\}\}/g,(e,n)=>{let i=n.trim().split(`.`);if(i[0]===`inputs`&&i[1])return t.inputs.get(i[1])||``;if(i[0]===`matrix`&&i[1])return t.matrix[i[1]]||``;if(i[0]===`secrets`&&i[1])return t.secrets.get(i[1])||process.env[i[1]]||``;if(i[0]===`env`&&i[1])return t.env[i[1]]||process.env[i[1]]||``;if(i[0]===`runner`&&i[1]===`os`)return`macOS`;if(i[0]===`github`&&i[1]===`sha`)try{return(0,r.execSync)(`git rev-parse HEAD`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return`local`}return n.includes(`hashFiles`)?`local`:`\${{ ${n} }}`})}isActionSkipped(e){return Rt.some(t=>e.startsWith(t))}resolveJobOrder(e,t){let n=new Set,r=[],i=t=>{if(n.has(t))return;n.add(t);let a=e[t];if(!a)throw Error(`Job not found: ${t}`);let o=a.needs?Array.isArray(a.needs)?a.needs:[a.needs]:[];for(let e of o)i(e);r.push(t)};if(t)i(t);else for(let t of Object.keys(e))i(t);return r}expandMatrix(e){if(!e.strategy?.matrix)return[{}];let{include:t,...n}=e.strategy.matrix;if(t&&t.length>0)return t;let r=Object.keys(n).filter(e=>Array.isArray(n[e]));if(r.length===0)return[{}];let i=[{}];for(let e of r){let t=n[e],r=[];for(let n of i)for(let i of t)r.push({...n,[e]:String(i)});i=r}return i}};const Ht=`\x1B[0m`,Ut=`\x1B[2m`;var Wt=class{constructor(e,t){this.stepRunner=e,this.logger=t}getWorktreeBranchName(e){return`worktree/${e.toLowerCase().replace(/[^a-z0-9-]+/g,`-`).replace(/^-|-$/g,``)}`}createWorktree(e,t){let n=this.getWorktreeBranchName(t),i=n.slice(9),a=(0,s.resolve)((0,s.dirname)(e),`${(0,s.basename)(e)}-worktree`),o=(0,s.resolve)(a,i);(0,c.existsSync)(a)||(0,c.mkdirSync)(a,{recursive:!0}),(0,c.existsSync)(o)&&(this.logger.info(` ${Ut}clean${Ht} Removing stale worktree at ${o}`),this.cleanupWorktreePath(e,o));try{(0,r.execSync)(`git branch -D "${n}"`,{cwd:e,stdio:[`ignore`,`pipe`,`ignore`]})}catch{}return this.logger.info(` [36mcreate${Ht} Worktree at ${o} (branch: ${n})`),(0,r.execSync)(`git worktree add -b "${n}" "${o}" HEAD`,{cwd:e,stdio:[`ignore`,`pipe`,`ignore`]}),o}removeWorktree(e,t){if((0,c.existsSync)(t)){this.logger.info(` ${Ut}remove${Ht} Worktree at ${t}`);try{this.cleanupWorktreePath(e,t)}catch{this.logger.warn(` [33mwarn${Ht} Failed to remove worktree (may need manual cleanup)`)}}}cleanupWorktreePath(e,t){if(this.isRegisteredWorktree(e,t)){(0,r.execSync)(`git worktree remove --force "${t}"`,{cwd:e,stdio:[`ignore`,`pipe`,`ignore`]});return}(0,c.rmSync)(t,{recursive:!0,force:!0})}isRegisteredWorktree(e,t){try{return(0,r.execSync)(`git worktree list --porcelain`,{cwd:e,encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).split(`
|
|
35
|
+
`).some(e=>e.startsWith(`worktree `)&&(0,s.resolve)(e.slice(9))===t)}catch{return!1}}async runHook(e,t,n,r,i,a){let o={inputs:r,matrix:{},secrets:i,env:n};for(let n of e.steps)if(!await this.stepRunner.runStep(n,o,{...a,workflowDir:t}))return!1;return!0}};const X=`\x1B[0m`,Z=`\x1B[1m`,Gt=`\x1B[2m`,Kt=`\x1B[32m`,qt=`\x1B[33m`,Q=`\x1B[31m`,$=`\x1B[34m`,Jt=`SIGINT`,Yt=`SIGTERM`,Xt=`workflow_execution`,Zt=`restart-from`,Qt=`user_prompt`,$t=`agent_decision`,en=`workflow_dispatch`,tn=`Workflow post-cleanup failed`,nn=`Workflow failed at job`;function rn(e,t){let n=Number(e);return Number.isInteger(n)&&n>0?n:t}var an=class{logger;parser;serviceManager;stepRunner;jobRunner;triggerService;worktreeService;registry;outputLines=[];interruptedSignal=null;constructor(e,t={}){let n=e||{info:e=>process.stdout.write(`${e}\n`),error:e=>console.error(e),warn:e=>console.error(e)},r=e=>{this.outputLines.push(e),this.outputLines.length>5e3&&this.outputLines.shift()};this.logger={info:e=>{r(e),n.info(e)},error:e=>{r(e),n.error(e)},warn:e=>{r(e),n.warn(e)}},this.parser=t.parser??new Vt,this.serviceManager=t.serviceManager??new ze(this.logger),this.stepRunner=t.stepRunner??new At(this.parser,this.logger),this.jobRunner=t.jobRunner??new Ke(this.parser,this.stepRunner,this.logger),this.triggerService=t.triggerService??new Lt(this.stepRunner,this.logger),this.worktreeService=t.worktreeService??new Wt(this.stepRunner,this.logger),this.registry=t.registry??new ke}async run(e){this.outputLines=[],this.interruptedSignal=null;let t=e.dryRun??!1,n=e.continueOnError??!1,r=!1,i=e=>{r||(r=!0,this.interrupt(e))},a=()=>i(Jt),o=()=>i(Yt);process.on(Jt,a),process.on(Yt,o);try{return await this.executeWorkflow(e,t,n,e.keepWorktree??!1)}catch(e){if(e instanceof P||this.isInterrupted())return await this.serviceManager.stopAll(),this.createInterruptedResult();throw e}finally{process.off(Jt,a),process.off(Yt,o),await this.serviceManager.stopAll()}}interrupt(e,t={phase:Xt}){if(this.interruptedSignal)return;this.interruptedSignal=e;let n=t.phase===Xt?``:` (${t.phase})`;this.logger.info(`\n\n${qt}${Z}Interrupted — shutting down...${X}${n}`),this.triggerService.abortActivePrompt(e),this.stepRunner.stopActiveStep(e),this.serviceManager.stopAll()}isInterrupted(){return this.interruptedSignal===Jt||this.interruptedSignal===Yt}createInterruptedResult(){return{exitCode:130,output:this.outputLines.join(`
|
|
36
|
+
`)}}parseFixRestartFrom(e,t){if(!e.startsWith(`---`))return{};let n=e.indexOf(`---`,3);if(n<0)return{};let r=e.slice(3,n);for(let e of r.split(`
|
|
37
|
+
`)){let n=e.trim();if(n.startsWith(`${Zt}:`)){let e=n.slice(`${Zt}:`.length).trim();return t.includes(e)?{restartFrom:e}:{invalidTarget:e}}}return{}}async pathExists(e){try{return await(0,a.access)(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async waitForServiceStartup(){await new Promise(e=>{setTimeout(e,2e3)})}resolveRunner(e){if(e.runner&&e.cliAgent&&e.runner!==e.cliAgent)throw Error(`Conflicting runner selectors: runner="${e.runner}" cliAgent="${e.cliAgent}"`);return e.runner??e.cliAgent}async executeWorkflow(e,t,n,i=!1){let o=(0,s.resolve)(e.workflowPath),l=this.parser.parseWorkflowFile(o),u=this.resolveRunner(e),d=null,f=null,p=null,m=(0,s.dirname)(o),h=m,g=null,_=null,v=!1,y=!1,b,x,ee,S=!!l.worktree,C=new Map,w=e.secretFile?this.parser.loadSecrets(e.secretFile):new Map,T={};try{let p=l.on?.[en]?.inputs||{};for(let[e,t]of Object.entries(p))t.default&&C.set(e,t.default);if(e.inputs)for(let[t,n]of Object.entries(e.inputs))C.set(t,n);if(l.env)for(let[e,t]of Object.entries(l.env))T[e]=String(t);if(e.env)for(let[t,n]of Object.entries(e.env))T[t]=n;u&&(T.WORKFLOW_RUNNER=u,T.WORKFLOW_CLI_AGENT=u);let E=m;for(;E!==`/`;){if(await this.pathExists((0,s.resolve)(E,`.git`))){m=E;break}E=(0,s.dirname)(E)}let te=l[`max-workflows`];if(te){let t=this.registry.resolveWorkspace(e.workspace,l.workspace),n=await this.registry.countRunningWorkflows(t);if(n>=te)throw new Me(t,n,te)}let ne=l[`launch-command`];if(ne&&!e.skipLaunch){let t=this.buildLaunchCliCommand(e,o),n=e.name||l.name||(0,s.basename)(o),i=e.name?n:`${n}-${this.parser.generateHumanReadableId()}`,a=ne.replace(`{name}`,i).replace(`{command}`,t);this.logger.info(`${Gt}Delegating via launch-command: ${a}${X}`);try{return(0,r.execSync)(a,{stdio:`inherit`,cwd:m,env:this.buildLaunchEnvironment()}),{exitCode:0,output:this.outputLines.join(`
|
|
38
|
+
`)}}catch(e){return{exitCode:e.status??1,output:this.outputLines.join(`
|
|
39
|
+
`)}}}let re=e.name||l.name||(0,s.basename)(o),ie=e.name?re:`${re}-${this.parser.generateHumanReadableId()}`;if(d=await this.registry.createRun({cliAgent:e.cliAgent,continueOnError:e.continueOnError,displayName:ie,dryRun:t,env:e.env,inputs:e.inputs,job:e.job,keepWorktree:e.keepWorktree,prompt:e.prompt,runner:u,secretFile:e.secretFile,skipLaunch:e.skipLaunch,workflowPath:o,workflowWorkspace:l.workspace,workspace:e.workspace}),T.WORKFLOW_RUN_DIR=d.runDir,T.WORKFLOW_WORKSPACE=d.workspace,T.CONTEXT_FILE&&(T.CONTEXT_FILE=d.contextPath,T.CHANGELOG_FILE=d.changelogPath),this.logger.info(`${Z}${`═`.repeat(60)}${X}`),this.logger.info(` ${Z}run-workflow${X} - ${d.displayName}`),this.logger.info(`${Z}${`═`.repeat(60)}${X}`),this.logger.info(` File: ${o}`),this.logger.info(` Workspace: ${d.workspace||`default`}`),this.logger.info(` Run Dir: ${d.runDir}`),this.logger.info(` CWD: ${m}`),C.size>0&&this.logger.info(` Inputs: ${[...C.entries()].map(([e,t])=>`${e}=${t}`).join(`, `)}`),w.size>0&&this.logger.info(` Secrets: ${[...w.keys()].join(`, `)}`),e.job&&this.logger.info(` Target: ${e.job}`),u&&this.logger.info(` Runner: ${u}`),l.pre){let e=l.pre.services?.length||0,t=l.pre.steps?.length||0;this.logger.info(` Pre: ${e} service(s), ${t} setup step(s)`)}if(l.post){let e=l.post.services?.length||0,t=l.post.steps?.length||0;this.logger.info(` Post: ${e} service(s), ${t} cleanup step(s)`)}S&&this.logger.info(` Worktree: enabled${i?` (keep on completion)`:``}`);let ae=l.on&&Qt in l.on?Qt:l.on?.[$t]?$t:en;if(this.logger.info(` Trigger: ${ae}`),this.logger.info(` Mode: ${t?`dry-run`:`execute`}`),this.logger.info(`${Z}${`═`.repeat(60)}${X}`),!await this.triggerService.handleUserPrompt(l,e.prompt||null,T,m,t))return this.isInterrupted()?(f=this.createInterruptedResult(),f):(this.logger.error(`\n${Q}${Z}Workflow aborted: no prompt provided${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
40
|
+
`)},f);if(!await this.triggerService.handleAgentDecision(l,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:d.stopRequestPath,workflowDir:m},e.prompt||null))return this.isInterrupted()?(f=this.createInterruptedResult(),f):(this.logger.info(`\n${qt}${Z}Workflow skipped: agent decided nothing to do${X}`),f={exitCode:2,output:this.outputLines.join(`
|
|
41
|
+
`)},f);if(h=m,S&&e.recovery){if(!e.recovery.worktreePath||!(0,c.existsSync)(e.recovery.worktreePath))throw Error(`Recoverable worktree not found: ${e.recovery.worktreePath??`(missing)`}`);h=e.recovery.originalRepoPath??h,g=e.recovery.worktreePath,_=e.recovery.worktreeBranch??this.worktreeService.getWorktreeBranchName(d.displayName),m=g,T.WORKTREE_PATH=g,T.WORKTREE_BRANCH=_,T.WORKFLOW_NAME=d.displayName,T.ORIGINAL_REPO_PATH=h,this.logger.info(`\n ${$}${Z}worktree${X} ${Z}Reusing worktree${X}`),this.logger.info(` ${`─`.repeat(50)}`),this.logger.info(` ${Gt}path ${X} ${g}`)}else if(S&&!t)l.worktree?.beforeCreate&&(this.logger.info(`\n ${$}${Z}worktree${X} ${Z}beforeCreate${X}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(l.worktree.beforeCreate,m,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:d.stopRequestPath})||(this.isInterrupted()?f=this.createInterruptedResult():(this.logger.error(`\n${Q}${Z}Workflow aborted: worktree beforeCreate hook failed${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
42
|
+
`)}))),f||(this.logger.info(`\n ${$}${Z}worktree${X} ${Z}Creating worktree${X}`),this.logger.info(` ${`─`.repeat(50)}`),_=this.worktreeService.getWorktreeBranchName(d.displayName),g=this.worktreeService.createWorktree(m,d.displayName),m=g,T.WORKTREE_PATH=g,T.WORKTREE_BRANCH=_,T.WORKFLOW_NAME=d.displayName,T.ORIGINAL_REPO_PATH=h,l.worktree?.afterCreate&&(this.logger.info(`\n ${$}${Z}worktree${X} ${Z}afterCreate${X}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(l.worktree.afterCreate,g,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:d.stopRequestPath})||(this.isInterrupted()?f=this.createInterruptedResult():(this.logger.error(`\n${Q}${Z}Workflow aborted: worktree afterCreate hook failed${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
43
|
+
`)}),y=!i)));else if(S&&t){let e=(0,s.dirname)(m),t=(0,s.basename)(m),n=d.displayName.toLowerCase().replace(/[^a-z0-9-]+/g,`-`).replace(/^-|-$/g,``),r=(0,s.resolve)(e,`${t}-worktree`,n);this.logger.info(`\n ${$}${Z}worktree${X} ${Z}(dry-run)${X} Would create at ${r}`)}!f&&l.pre&&(await this.runPreBlock(l.pre,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:d.stopRequestPath,workflowDir:m})||(this.isInterrupted()?f=this.createInterruptedResult():(this.logger.error(`\n${Q}${Z}Workflow aborted: pre-conditions failed${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
44
|
+
`)}),y=!!(g&&!i)));let D=rn(T.MAX_RETRIES,3),O=rn(T.FIX_LOOP_MAX_CYCLES,3),k=this.parser.resolveJobOrder(l.jobs,e.recovery?e.recovery.originalJob||null:e.job||null);this.logger.info(`\n ${Gt}Job order: ${k.join(` -> `)} (max retries: ${D})${X}`);let oe=new Set,se=T.WORKFLOW_RUN_DIR?(0,s.resolve)(T.WORKFLOW_RUN_DIR,`fix.md`):null,A=0,j=0;if(e.recovery?.startJob&&(j=k.indexOf(e.recovery.startJob),j<0))throw Error(`Recovery job not found in workflow order: ${e.recovery.startJob}`);for(;!f&&j<k.length;){let r=k[j],i=l.jobs[r],o=await this.jobRunner.runJob(r,i,{inputs:C,secrets:w,workflowEnv:T,workflowSystemPrompt:l[`system-prompt`],jobOrder:k,allJobs:l.jobs},{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:d.stopRequestPath,workflowDir:m,maxRetries:D});if(this.isInterrupted()&&(f=this.createInterruptedResult()),!f&&!o&&(b=r,x=`${nn} "${r}"`,this.logger.error(`\n${Q}${Z}${nn} "${r}"${X}`),await this.serviceManager.stopAll(),f={exitCode:1,output:this.outputLines.join(`
|
|
45
|
+
`)}),!f){if(oe.add(r),se&&await this.pathExists(se)&&!t){A++;let e=(await(0,a.readFile)(se,`utf-8`)).trim();if(await(0,a.unlink)(se),T.CONTEXT_FILE&&await this.pathExists(T.CONTEXT_FILE)&&await this.appendFixRequestToContext(T.CONTEXT_FILE,A,e),A>O){b=r,x=`Fix loop limit reached (${O} cycles).`,ee=A,this.logger.error(`\n${Q}${Z}Fix loop limit reached (${O} cycles). Aborting.${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
46
|
+
`)};continue}let{restartFrom:t,invalidTarget:n}=this.parseFixRestartFrom(e,k);this.logger.info(`\n${qt}${Z}fix.md detected after "${r}" (cycle ${A}/${O})${X}`),this.logger.info(`${Gt}${e.split(`
|
|
47
|
+
`)[0].slice(0,100)}${e.includes(`
|
|
48
|
+
`)?`...`:``}${X}`),n&&this.logger.warn(`${qt}fix.md restart-from "${n}" is not a valid job (available: ${k.join(`, `)}), falling back to default${X}`);let i=t?k.indexOf(t):-1,o=k.indexOf(`development`);j=i>=0?i:o>=0?o:1,this.logger.info(`${qt}Restarting from job "${k[j]}"${X}\n`);continue}j++}}!f&&this.isInterrupted()&&(f=this.createInterruptedResult()),f||=(v=!0,this.logger.info(`\n${Z}${`═`.repeat(60)}${X}`),this.logger.info(` ${Kt}${Z}Workflow completed successfully${X}`),this.logger.info(` Jobs run: ${[...oe].join(`, `)}`),g&&this.logger.info(` Worktree: ${g}`),this.logger.info(`${Z}${`═`.repeat(60)}${X}\n`),{exitCode:0,output:this.outputLines.join(`
|
|
49
|
+
`)})}catch(e){if(p=e,e instanceof P||this.isInterrupted())f=this.createInterruptedResult();else throw e}finally{if(v&&f?.exitCode===0&&S&&l.worktree?.beforeCompleted&&!t&&g&&(this.logger.info(`\n ${$}${Z}worktree${X} ${Z}beforeCompleted${X}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(l.worktree.beforeCompleted,g,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n})||(this.isInterrupted()?f=this.createInterruptedResult():(this.logger.error(`\n${Q}${Z}worktree beforeCompleted hook failed${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
50
|
+
`)}))),l.post)try{await this.runPostBlock(l.post,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,workflowDir:m})||(this.logger.error(`\n${Q}${Z}${tn}${X}`),(!f||f.exitCode===0||f.exitCode===2)&&(f={exitCode:1,output:this.outputLines.join(`
|
|
51
|
+
`)}))}catch(e){p??=e,this.logger.error(`\n${Q}${Z}${tn}${X}`),(!f||f.exitCode===0||f.exitCode===2)&&(f={exitCode:1,output:this.outputLines.join(`
|
|
52
|
+
`)})}v&&f?.exitCode===0&&S&&l.worktree?.afterCompleted&&!t&&!i&&(this.logger.info(`\n ${$}${Z}worktree${X} ${Z}afterCompleted${X}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(l.worktree.afterCompleted,h,T,C,w,{runner:u,cliAgent:e.cliAgent,dryRun:t,continueOnError:n})||(this.isInterrupted()?f=this.createInterruptedResult():(this.logger.error(`\n${Q}${Z}worktree afterCompleted hook failed${X}`),f={exitCode:1,output:this.outputLines.join(`
|
|
53
|
+
`)}))),y&&g&&this.worktreeService.removeWorktree(h,g),f&&={...f,output:this.outputLines.join(`
|
|
54
|
+
`)},d&&await this.finalizeRegistryRun(d,f,p,{failedJob:b,failureReason:x,fixLoopCycle:ee,originalRepoPath:g?h:void 0,worktreeBranch:_??void 0,worktreePath:g??void 0})}return f??{exitCode:1,output:this.outputLines.join(`
|
|
55
|
+
`)}}async finalizeRegistryRun(e,t,n,r={}){let i=t?.exitCode===130||n instanceof P,a=t?.exitCode??(i?130:1),o=a===0||a===2?`completed`:`error`,s=a===0?`success`:a===2?`skipped`:i||this.isInterrupted()?`interrupted`:`failed`,c=n instanceof Error?n.message:a===1?this.lastMeaningfulOutputLine():void 0;await this.registry.finalizeRun(e,{errorMessage:c,exitCode:a,...r,outcome:s,stage:o})}async appendFixRequestToContext(e,t,n){await(0,a.writeFile)(e,`${await(0,a.readFile)(e,`utf-8`)}\n\n---\n## Fix Request (cycle ${t})\n${n}`,`utf-8`)}resolveCliPath(){let e=__dirname,t=(0,s.resolve)(`/`);for(;e!==t;){let t=(0,s.resolve)(e,`cli.cjs`);if((0,c.existsSync)(t))return t;let n=(0,s.resolve)(e,`dist`,`cli.cjs`);if((0,c.existsSync)(n))return n;e=(0,s.dirname)(e)}throw Error(`Unable to locate workflow-mcp cli.cjs from `+__dirname)}buildLaunchEnvironment(){let e={...process.env};return delete e.CMUX_WORKSPACE_ID,e}buildLaunchCliCommand(e,t){let n=[`node`,this.resolveCliPath(),`run-workflow`,t,`--skip-launch`],r=this.resolveRunner(e);if(e.job&&n.push(`--job`,e.job),r&&n.push(`--runner`,r),e.dryRun&&n.push(`--dry-run`),e.continueOnError&&n.push(`--continue-on-error`),e.keepWorktree&&n.push(`--keep-worktree`),e.prompt&&n.push(`--prompt`,`'${e.prompt.replace(/'/g,`'\\''`)}'`),e.name&&n.push(`--name`,`'${e.name}'`),e.workspace&&n.push(`--workspace`,`'${e.workspace}'`),e.secretFile&&n.push(`--secret-file`,e.secretFile),e.inputs)for(let[t,r]of Object.entries(e.inputs))n.push(`--input`,`${t}=${r}`);if(e.env)for(let[t,r]of Object.entries(e.env))n.push(`--env`,`${t}=${r}`);return n.join(` `)}lastMeaningfulOutputLine(){return[...this.outputLines].reverse().find(e=>e.trim().length>0)}async runPreBlock(e,t,n,r,i){if(this.logger.info(`\n ${$}${Z}pre${X} ${Z}Pre-conditions${X}`),this.logger.info(` ${`─`.repeat(50)}`),t.CONTEXT_FILE&&await(0,a.mkdir)((0,s.dirname)((0,s.resolve)(i.workflowDir,t.CONTEXT_FILE)),{recursive:!0}),e.services&&e.services.length>0){this.logger.info(`\n ${$}${Z}services${X}`);for(let n of e.services)if(await this.serviceManager.startService(n,t,i.workflowDir,i.dryRun)&&n[`ready-check`]&&!i.dryRun){let e=await this.serviceManager.waitForServiceReady(n,i.workflowDir,t,()=>this.interruptedSignal!==null);if(this.isInterrupted())return!1;if(!e&&!i.continueOnError)return await this.serviceManager.stopAll(),!1}!i.dryRun&&this.serviceManager.getRunningServices().length>0&&await this.waitForServiceStartup()}if(e.steps&&e.steps.length>0){this.logger.info(`\n ${$}${Z}setup${X}`);let a={inputs:n,matrix:{},secrets:r,env:t};for(let t of e.steps)if(!await this.stepRunner.runStep(t,a,i))return this.logger.error(`\n ${Q}Pre-condition step failed${X}`),await this.serviceManager.stopAll(),!1}return this.logger.info(`\n ${Kt}Pre-conditions ready${X}`),!0}async runPostBlock(e,t,n,r,i){this.logger.info(`\n ${$}${Z}post${X} ${Z}Cleanup${X}`),this.logger.info(` ${`─`.repeat(50)}`),await this.serviceManager.stopAll();try{if(e.services&&e.services.length>0){this.logger.info(`\n ${$}${Z}services${X}`);for(let n of e.services)if(await this.serviceManager.startService(n,t,i.workflowDir,i.dryRun)&&n[`ready-check`]&&!i.dryRun&&!await this.serviceManager.waitForServiceReady(n,i.workflowDir,t,()=>!1)&&!i.continueOnError)return!1;!i.dryRun&&this.serviceManager.getRunningServices().length>0&&await this.waitForServiceStartup()}if(e.steps&&e.steps.length>0){this.logger.info(`\n ${$}${Z}cleanup${X}`);let a={inputs:n,matrix:{},secrets:r,env:t};for(let t of e.steps)if(!await this.stepRunner.runStep(t,a,i))return this.logger.error(`\n ${Q}Post-cleanup step failed${X}`),!1}return this.logger.info(`\n ${Kt}Cleanup complete${X}`),!0}finally{await this.serviceManager.stopAll()}}},on=class{constructor(e=new ke,t=new an(void 0,{registry:e})){this.registry=e,this.runner=t}async recover(e){let t=await this.registry.readRunByKey(e.workspace,`error`,e.runKey),n=e.job??t.failedJob;if(!n)throw Error(`Workflow run "${t.workspace}/${t.runKey}" does not include a recoverable failed job`);if(t.worktreePath&&!(0,c.existsSync)(t.worktreePath))throw Error(`Recoverable worktree not found: ${t.worktreePath}`);let r=e.runner??t.runner??t.cliAgent;return await this.runner.run({cliAgent:t.cliAgent,runner:r,workflowPath:t.workflowPath,job:n,inputs:t.inputs,env:t.env,secretFile:t.secretFile,dryRun:e.dryRun??t.dryRun,continueOnError:t.continueOnError,keepWorktree:t.keepWorktree,prompt:t.prompt,name:t.displayName,workspace:t.workspace,skipLaunch:!0,recovery:{originalJob:e.job?void 0:t.job,originalRepoPath:t.originalRepoPath,runKey:t.runKey,startJob:n,worktreeBranch:t.worktreeBranch,worktreePath:t.worktreePath}})}};const sn=n.z.object({dryRun:n.z.boolean().optional().describe(`Print recovery steps without executing.`),job:n.z.string().optional().describe(`Optional job override. Defaults to the failed job recorded on the run.`),runKey:n.z.string().min(1).describe(`Run key of the failed workflow to recover.`),runner:n.z.string().optional().describe(`Optional runner key override for step command maps.`),workspace:n.z.string().optional().describe(`Workspace containing the failed workflow. Defaults to default.`)});var cn=class e{static TOOL_NAME=`recover_workflow`;constructor(e=new on){this.service=e}getInputSchema(){return sn}getDefinition(){return{name:e.TOOL_NAME,description:`Recover a failed workflow run from the local workflow registry.`,inputSchema:n.z.toJSONSchema(sn)}}async execute(e){try{let t=sn.parse(e),n=await this.service.recover(t);return{content:[{type:`text`,text:n.output||`Workflow recovery finished with exit code ${n.exitCode}.`}],isError:n.exitCode===0?void 0:!0}}catch(e){return{content:[{type:`text`,text:`Failed to recover workflow. ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const ln=n.z.object({workflowPath:n.z.string().describe(`Path to the workflow YAML file (e.g., .github/workflows/deploy.yml)`),runner:n.z.string().optional().describe(`Preferred runner key for step command maps (e.g. ollama, claude, codex)`),cliAgent:n.z.string().optional().describe(`Deprecated alias for runner. Preferred runner command key for step command maps.`),job:n.z.string().optional().describe(`Run only this job (and its dependencies)`),inputs:n.z.record(n.z.string(),n.z.string()).optional().describe(`Workflow dispatch inputs as key-value pairs`),env:n.z.record(n.z.string(),n.z.string()).optional().describe(`Extra environment variables as key-value pairs`),secretFile:n.z.string().optional().describe(`Path to a dotenv-style secrets file`),dryRun:n.z.boolean().optional().describe(`Print steps without executing`),continueOnError:n.z.boolean().optional().describe(`Continue past step failures`),keepWorktree:n.z.boolean().optional().describe(`Keep worktree on completion (skip merge and cleanup for retry)`),prompt:n.z.string().optional().describe(`User prompt for user_prompt trigger workflows`),name:n.z.string().optional().describe(`Name for the workflow run context directory`),workspace:n.z.string().optional().describe(`Workspace for workflow registry storage`)});var un=class e{static TOOL_NAME=`run_workflow`;constructor(e=new an){this.service=e}getInputSchema(){return ln}getDefinition(){return{name:e.TOOL_NAME,description:`Run a GitHub Actions workflow file locally. Parses the workflow YAML and executes run steps on the host machine, respecting job dependencies, matrix strategies, environment variables, and workflow_dispatch inputs.`,inputSchema:n.z.toJSONSchema(ln)}}async execute(e){try{let t=ln.parse(e);if(t.runner&&t.cliAgent&&t.runner!==t.cliAgent)throw Error(`Conflicting runner selectors: runner="${t.runner}" cliAgent="${t.cliAgent}"`);let n={cliAgent:t.cliAgent,runner:t.runner??t.cliAgent,workflowPath:t.workflowPath,job:t.job,inputs:t.inputs,env:t.env,secretFile:t.secretFile,dryRun:t.dryRun,continueOnError:t.continueOnError,keepWorktree:t.keepWorktree,prompt:t.prompt,name:t.name,workspace:t.workspace},r=await this.service.run(n);return r.exitCode!==0&&r.exitCode!==2?{content:[{type:`text`,text:`Workflow failed (exit code ${r.exitCode}):\n\n${r.output}`}],isError:!0}:{content:[{type:`text`,text:r.output||`Workflow completed successfully.`}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}},dn=class e{static TOOL_NAME=`schedule-cron`;constructor(e=new ie){this.service=e}getInputSchema(){return T}getDefinition(){return{name:e.TOOL_NAME,description:`Schedule a headless Claude Code or Codex CLI run as a system cron job. Specify either a cron expression or an interval in minutes.`,inputSchema:n.z.toJSONSchema(T)}}async execute(t){try{let e=T.parse(t),n=await this.service.schedule(e);return{content:[{type:`text`,text:JSON.stringify(n,null,2)}]}}catch(n){let r=new g(`Failed to schedule cron job.`,`SCHEDULE_CRON_TOOL_FAILED`,{tool:e.TOOL_NAME,name:t.name,cwd:t.cwd},{cause:n});return console.error(`[${e.TOOL_NAME}] ${r.message}`,r.context),{content:[{type:`text`,text:JSON.stringify({code:r.code,context:r.context,message:r.message},null,2)}],isError:!0}}}};const fn=n.z.object({reason:n.z.string().optional().describe(`Optional stop reason to record.`),runKey:n.z.string().min(1).describe(`Run key of the running workflow to stop.`),workspace:n.z.string().optional().describe(`Workspace containing the running workflow. Defaults to default.`)});var pn=class e{static TOOL_NAME=`stop_workflow`;constructor(e=new ke){this.registry=e}getInputSchema(){return fn}getDefinition(){return{name:e.TOOL_NAME,description:`Request a running workflow to stop gracefully without taking over terminal stdin.`,inputSchema:n.z.toJSONSchema(fn)}}async execute(e){try{let t=fn.parse(e),n=await this.registry.requestStop(t.workspace,t.runKey,t.reason);return{content:[{type:`text`,text:JSON.stringify({reason:n.reason,requestedAt:n.requestedAt,runKey:t.runKey,workspace:this.registry.resolveWorkspace(t.workspace)},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Failed to request workflow stop. ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const mn=[un.TOOL_NAME,je.TOOL_NAME,cn.TOOL_NAME,dn.TOOL_NAME,ae.TOOL_NAME,pn.TOOL_NAME];function hn(){let n=new e.Server({name:`workflow-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),r=new un,i=new je,a=new cn,o=new dn,s=new ae,c=new pn;return n.setRequestHandler(t.ListToolsRequestSchema,async()=>({tools:[r.getDefinition(),i.getDefinition(),a.getDefinition(),o.getDefinition(),s.getDefinition(),c.getDefinition()]})),n.setRequestHandler(t.CallToolRequestSchema,async e=>{let{name:t,arguments:n}=e.params;if(t===un.TOOL_NAME)return await r.execute(n);if(t===je.TOOL_NAME)return await i.execute(n);if(t===cn.TOOL_NAME)return await a.execute(n);if(t===dn.TOOL_NAME)return await o.execute(n);if(t===ae.TOOL_NAME)return await s.execute();if(t===pn.TOOL_NAME)return await c.execute(n);throw new h(t,mn)}),n}var gn=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new m.StdioServerTransport,await this.server.connect(this.transport),console.error(`workflow-mcp MCP server started on stdio`)}async stop(){this.transport&&=(await this.transport.close(),null)}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return Xe}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return`claude`}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return an}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return hn}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return ke}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return on}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return ie}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return gn}});
|