@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/dist/stdio-CAi-NNxh.mjs
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import{Server as e}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as t,ListToolsRequestSchema as n}from"@modelcontextprotocol/sdk/types.js";import{z as r}from"zod";import{execFile as i,execFileSync as a,execSync as o,spawn as s}from"node:child_process";import{promisify as c}from"node:util";import{access as l,mkdir as u,readFile as d,readdir as f,rename as p,rm as m,unlink as h,writeFile as g}from"node:fs/promises";import{homedir as _,tmpdir as ee}from"node:os";import v,{basename as te,dirname as y,join as b,resolve as x}from"node:path";import{fileURLToPath as S}from"node:url";import{existsSync as C,mkdirSync as w,readFileSync as T,rmSync as ne,unlinkSync as re,watch as ie,writeFileSync as ae}from"node:fs";import{PortRegistryService as oe}from"@agimon-ai/foundation-port-registry";import{ProcessRegistryService as se}from"@agimon-ai/foundation-process-registry";import{randomUUID as E}from"node:crypto";import{createInterface as ce}from"node:readline";import{load as D}from"js-yaml";import{StdioServerTransport as le}from"@modelcontextprotocol/sdk/server/stdio.js";var O=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`}},ue=class extends Error{constructor(e,t,n={},r){super(e,r),this.code=t,this.context=n,this.name=`WorkflowToolError`}},de=class extends Error{constructor(e,t,n={},r){super(e,r),this.code=t,this.context=n,this.name=`CronError`}};const fe=`# workflow-mcp:cron:`,pe=`crontab`,me=`claude`,he=`codex`,ge=me,_e=`--cwd`,ve=`CRON_WRITE_FAILED`,ye=r.enum([me,he]),be=r.object({name:r.string().min(1),cwd:r.string().min(1),cli:ye,schedule:r.string().min(1),prompt:r.string().optional(),promptFile:r.string().optional(),createdAt:r.string().min(1)}),xe=r.object({name:r.string().min(1),cwd:r.string().min(1),cli:ye.optional(),prompt:r.string().optional(),promptFile:r.string().optional(),schedule:r.string().optional(),intervalMinutes:r.number().positive().optional()});function Se(e){return`'${e.replace(/'/g,`'\\''`)}'`}function Ce(e,t,n,r){let i=n?Se(n):r?`"$(cat ${Se(r)})"`:void 0;if(e===`codex`){let e=[he,`--approval-mode`,`full-auto`];return i&&e.push(`-q`,i),e.push(_e,Se(t)),e.join(` `)}let a=[me,`--dangerously-skip-permissions`];return i&&a.push(`-p`,i),a.push(_e,Se(t)),a.join(` `)}function we(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 Te=c(i);var Ee=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 Te(pe,[`-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 de(`Failed to read current crontab.`,`CRON_READ_FAILED`,{exitCode:t},{cause:e})}}async writeCrontab(e){let t=``,n=i(pe,[`-`]);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 de(`crontab rejected input (exit ${n}).`,ve,{exitCode:n,stderr:t,contentLength:e.length})))}),n.on(`error`,e=>{this.logger.error(`[CronService.writeCrontab] spawn failed: ${e.message}`),i(new de(`Failed to spawn crontab process.`,ve,{},{cause:e}))})})}async schedule(e){let t=e.cli??`claude`;if(!e.schedule&&!e.intervalMinutes)throw new de(`Either schedule (cron expression) or intervalMinutes must be provided.`,`CRON_INVALID_INPUT`,{name:e.name});let n=e.schedule??we(e.intervalMinutes),r=Ce(t,e.cwd,e.prompt,e.promptFile),i=new Date().toISOString(),a=be.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=`${`${fe}${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=be.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=`${fe}${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
|
-
`)}},De=class e{static TOOL_NAME=`list-crons`;constructor(e=new Ee){this.service=e}getInputSchema(){return r.object({})}getDefinition(){return{name:e.TOOL_NAME,description:`List all cron jobs scheduled via workflow-mcp, showing name, schedule, CLI, cwd, and prompt.`,inputSchema:r.toJSONSchema(r.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 ue(`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}}}},Oe=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`}},ke=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 Ae=r.record(r.string(),r.coerce.string()),je=r.object({description:r.string().optional(),required:r.boolean().optional(),default:r.string().optional(),type:r.string().optional(),options:r.array(r.string()).optional()}),Me=r.record(r.string(),r.string()),Ne=r.record(r.string(),r.string()),Pe=r.union([r.string(),Ne]),Fe=r.object({"fail-fast":r.boolean().optional(),matrix:r.looseObject({include:r.array(Me).optional()}).optional()}),Ie=r.object({name:r.string().optional(),uses:r.string().optional(),run:Pe.optional(),interactiveRun:Pe.optional(),"timeout-minutes":r.number().optional(),env:Ae.optional(),with:r.record(r.string(),r.string()).optional(),"working-directory":r.string().optional(),if:r.string().optional(),"continue-on-error":r.boolean().optional(),"timeout-retries":r.number().optional(),id:r.string().optional()}),Le=r.object({name:r.string(),run:r.string(),env:Ae.optional(),"working-directory":r.string().optional(),"ready-check":r.union([r.string(),r.boolean()]).optional(),"ready-timeout":r.number().optional(),host:r.string().optional(),port:r.number().int().min(1).max(65535).optional(),"port-range":r.object({min:r.number().int().min(1).max(65535),max:r.number().int().min(1).max(65535)}).optional(),"service-type":r.enum([`tool`,`service`]).optional()}),Re=r.object({services:r.array(Le).optional(),steps:r.array(Ie).optional()}),ze=r.object({steps:r.array(Ie).optional()}),Be=r.object({"runs-on":r.string().optional(),needs:r.union([r.string(),r.array(r.string())]).optional(),extends:r.union([r.string(),r.array(r.string())]).optional(),description:r.string().optional(),strategy:Fe.optional(),steps:r.array(Ie),preJob:ze.optional(),postJob:ze.optional(),env:Ae.optional(),if:r.string().optional(),"system-prompt":r.string().optional(),context:r.string().optional()}),Ve=r.object({steps:r.array(Ie)}),He=r.object({steps:r.array(Ie)}),Ue=r.object({beforeCreate:He.optional(),afterCreate:He.optional(),beforeCompleted:He.optional(),afterCompleted:He.optional()}),We=r.object({STOP:r.string().optional()}),Ge=r.object({name:r.string().optional(),workspace:r.string().optional(),imports:r.array(r.string()).optional(),"system-prompt":r.string().optional(),"max-workflows":r.number().int().positive().optional(),"launch-command":r.string().optional(),keybindings:We.optional(),on:r.looseObject({workflow_dispatch:r.object({inputs:r.record(r.string(),je).optional()}).nullable().optional(),agent_decision:Ve.optional()}).optional(),env:Ae.optional(),pre:Re.optional(),post:Re.optional(),worktree:Ue.optional(),jobs:r.record(r.string(),Be)}),Ke=r.enum([`running`,`completed`,`error`]),qe=r.enum([`success`,`skipped`,`failed`,`interrupted`]),Je=r.object({displayName:r.string(),dryRun:r.boolean(),errorMessage:r.string().optional(),exitCode:r.number().optional(),finishedAt:r.string().optional(),outcome:qe.optional(),pid:r.number().int().positive().optional(),runKey:r.string(),stale:r.boolean().optional(),staleReason:r.string().optional(),stage:Ke,startedAt:r.string(),workflowPath:r.string(),workspace:r.string()});r.object({changelogPath:r.string(),contextPath:r.string(),displayName:r.string(),recordPath:r.string(),runDir:r.string(),runKey:r.string(),stopRequestPath:r.string(),workspace:r.string()});const Ye=`default`,k=`running`,Xe=`completed`,A=`error`,Ze=`workspaces`,j=`run.json`,Qe=`stop-request.json`,$e=`workflow-run`,et=`Workflow process is no longer running`;var tt=class{constructor(e=x(_(),`.workflow-mcp`)){this.homeDir=e}resolveWorkspace(e,t){return this.slugifySegment(e||t||Ye,Ye)}async createRun(e){let t=this.resolveWorkspace(e.workspace,e.workflowWorkspace),n=this.slugifySegment(e.displayName,$e);await this.ensureWorkspaceStructure(t);let r=await this.findReusableRunStage(t,n);if(r===k)throw new ke(t,n);let i=this.getRunDirectory(t,k,n);r?(await m(i,{recursive:!0,force:!0}),await p(this.getRunDirectory(t,r,n),i),await this.removeFileIfExists(x(i,`fix.md`)),await this.removeFileIfExists(x(i,Qe))):await u(i,{recursive:!0});let a={displayName:e.displayName,dryRun:e.dryRun,runKey:n,stage:k,startedAt:new Date().toISOString(),pid:e.pid??process.pid,workflowPath:e.workflowPath,workspace:t},o=x(i,j);return await this.writeRunRecord(o,a),{changelogPath:x(i,`changelog.md`),contextPath:x(i,`context.md`),displayName:e.displayName,recordPath:o,runDir:i,runKey:n,stopRequestPath:x(i,Qe),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,finishedAt:new Date().toISOString(),outcome:t.outcome,pid:r.pid,stage:t.stage};e.runDir!==n&&(await m(n,{recursive:!0,force:!0}),await p(e.runDir,n));let a=x(n,j);return await this.writeRunRecord(a,i),{...e,recordPath:a,runDir:n,stopRequestPath:x(n,Qe)}}async requestStop(e,t,n){let r=this.resolveWorkspace(e),i=this.slugifySegment(t,$e),a=this.getRunDirectory(r,k,i),o=x(a,j);if(!await this.pathExists(o))throw Error(`Running workflow not found: ${r}/${t}`);let s=await this.inspectRunningRecord(r,i);if(s.stale)throw Error(s.staleReason??`Running workflow is stale: ${r}/${t}`);let c={reason:n?.trim()?n.trim():void 0,requestedAt:new Date().toISOString()};return await g(x(a,Qe),`${JSON.stringify(c,null,2)}\n`,`utf-8`),c}async hasStopRequest(e){return await this.pathExists(e.stopRequestPath)}async readRunRecord(e){try{return this.validateRunRecord(JSON.parse(await d(e,`utf-8`)),e)}catch(t){throw t instanceof Oe?t:new Oe(e,t instanceof Error?t.message:`Unknown parse failure`,{cause:t})}}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,k);if(!await this.pathExists(t))return 0;let n=await f(t,{withFileTypes:!0}),r=0;for(let e of n){if(!e.isDirectory())continue;let n=x(t,e.name,j);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 u(this.getRunStageDirectory(e,k),{recursive:!0}),await u(this.getRunStageDirectory(e,Xe),{recursive:!0}),await u(this.getRunStageDirectory(e,A),{recursive:!0})}async findRunStage(e,t){for(let n of[k,Xe,A])if(await this.pathExists(this.getRunDirectory(e,n,t)))return n;return null}async listWorkspaceRuns(e){let t=new Map;for(let n of[k,Xe,A]){let r=this.getRunStageDirectory(e,n);if(!await this.pathExists(r))continue;let i=await f(r,{withFileTypes:!0});for(let a of i){if(!a.isDirectory())continue;let i=x(r,a.name,j);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===k?(await this.reconcileStaleRunningRecord(e,t),this.findRunStage(e,t)):n}async inspectRunningRecord(e,t){let n=x(this.getRunDirectory(e,k,t),j),r=await this.readRunRecord(n);return!r.pid||this.isProcessAlive(r.pid)?r:{...r,stale:!0,staleReason:`${et}: pid ${r.pid}`}}async readListableRunRecord(e,t,n,r){try{return t===k?await this.inspectRunningRecord(e,n):await this.readRunRecord(r)}catch(e){if(e instanceof Oe)return null;throw e}}async reconcileStaleRunningRecord(e,t){let n=this.getRunDirectory(e,k,t),r=x(n,j),i=await this.readRunRecord(r);if(!i.pid||this.isProcessAlive(i.pid))return i;let a=this.getRunDirectory(e,A,t),o={...i,errorMessage:`${et}: pid ${i.pid}`,exitCode:130,finishedAt:new Date().toISOString(),outcome:`interrupted`,stage:A};return await m(a,{recursive:!0,force:!0}),await p(n,a),await this.writeRunRecord(x(a,j),o),o}async listWorkspaceNames(){let e=x(this.homeDir,Ze);return await this.pathExists(e)?(await f(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 g(e,`${JSON.stringify(t,null,2)}\n`,`utf-8`)}validateRunRecord(e,t){let n=Je.safeParse(e);if(!n.success)throw new Oe(t,n.error.message);return n.data}getRunStageDirectory(e,t){return x(this.homeDir,Ze,e,t)}getRunDirectory(e,t,n){return x(this.getRunStageDirectory(e,t),n)}async pathExists(e){try{return await l(e),!0}catch(e){if(e.code===`ENOENT`)return!1;throw e}}async removeFileIfExists(e){try{await h(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 nt=r.object({page:r.number().int().min(1).optional().describe(`Page number to return. Defaults to 1.`),pageSize:r.number().int().min(1).max(100).optional().describe(`Number of workflow runs per page. Defaults to 20 and is capped at 100.`),workspace:r.string().optional().describe(`Optional workspace filter. When omitted, runs from all workspaces are returned.`)});var rt=class e{static TOOL_NAME=`list_workflow_statuses`;constructor(e=new tt){this.registry=e}getInputSchema(){return nt}getDefinition(){return{name:e.TOOL_NAME,description:`List tracked workflow runs from the local workflow registry, including their workspace, stage, outcome, and timestamps.`,inputSchema:r.toJSONSchema(nt)}}async execute(e={}){try{let t=nt.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 ue(`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}}}};const it=v.dirname(S(import.meta.url));var at=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`}},M=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 N=`\x1B[0m`,ot=`\x1B[1m`,st=`\x1B[2m`,ct=`\x1B[31m`,lt=`\x1B[34m`,ut=[`pnpm-workspace.yaml`,`nx.json`,`.git`];function dt(e){let t=x(e);for(;;){for(let e of ut)if(C(b(t,e)))return t;let n=y(t);if(n===t)return e;t=n}}var ft=class{runningServices=[];processRegistry=new se(process.env.PROCESS_REGISTRY_PATH);portRegistry=new oe(process.env.PORT_REGISTRY_PATH);constructor(e){this.logger=e}resolveWorkingDirectory(e,t){return e[`working-directory`]?x(t,e[`working-directory`]):t}getRunningServices(){return this.runningServices}async startService(e,t,n,r){let i=e.run;if(r)return this.logger.info(` ${lt}dry ${N} ${e.name}`),this.logger.info(` ${st}$ ${i}${N}`),null;this.logger.info(` ${lt}start${N} ${e.name}`),this.logger.info(` ${st}$ ${i}${N}`);let a={...process.env,...t};if(e.env)for(let[t,n]of Object.entries(e.env))a[t]=String(n);let o=this.resolveWorkingDirectory(e,n),c=dt(o),l=a.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,a.PORT??=String(d),a.SERVICE_PORT??=String(d),a.HOST??=u,a.SERVICE_HOST??=u}let f=s(i,[],{stdio:[`ignore`,`pipe`,`pipe`],cwd:o,env:{...a,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=`${lt}[${e.name}]${N}`;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:i,args:[],metadata:{workingDirectory:o,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:a,release:m};return this.runningServices.push(h),h}async waitForServiceReady(e,t,n,r){if(!e[`ready-check`]||typeof e[`ready-check`]!=`string`)return!0;let i=(e[`ready-timeout`]||30)*1e3,a=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(` ${st}wait${N} Waiting for ${e.name} to be ready...`);Date.now()-a<i;){if(r?.())return!1;try{return o(s,{stdio:`ignore`,cwd:c,env:l,shell:`/bin/zsh`,timeout:5e3}),this.logger.info(` [32mready${N} ${e.name}`),!0}catch{if(r?.())return!1}await new Promise(e=>{setTimeout(e,1e3)})}return r?.()||this.logger.error(` ${ct}timeout${N} ${e.name} not ready after ${e[`ready-timeout`]||30}s`),!1}async stopAll(){if(this.runningServices.length!==0){this.logger.info(`\n ${lt}${ot}pre${N} ${ot}Stopping services${N}`),this.logger.info(` ${`─`.repeat(50)}`);for(let e of this.runningServices){if(!e.process.killed&&e.process.pid&&this.logger.info(` ${st}stop${N} ${e.name} (pid ${e.process.pid})`),e.release)try{await e.release();continue}catch(t){this.logger.warn(` ${ct}warn${N} 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 P=`\x1B[0m`,pt=`\x1B[1m`,F=`\x1B[2m`,mt=`\x1B[33m`,ht=`WORKFLOW_JOB_ID`,gt=`WORKFLOW_JOB_NAME`,_t=`PROCESS_REGISTRY_TAG`;var vt=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=E(),s=Object.keys(o).length>0?` ${F}(${Object.entries(o).map(([e,t])=>`${e}=${t}`).join(`, `)})${P}`:``;this.logger.info(`\n [35m${pt}job${P} ${pt}${e}${P}${s}`),this.logger.info(` ${`─`.repeat(50)}`);let c={...n.workflowEnv,[ht]:i,[gt]:e,[_t]:i};if(t.env)for(let[e,r]of Object.entries(t.env))c[e]=this.parser.interpolate(String(r),{inputs:n.inputs,matrix:o,secrets:n.secrets,env:c});c[ht]=i,c[gt]=e,c[_t]=i,this.logger.info(` ${F}jobid${P} ${i}`);let l=[];if(c.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
|
-
`);l.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 ${c.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&&l.push(this.parser.interpolate(n.workflowSystemPrompt,{inputs:n.inputs,matrix:o,secrets:n.secrets,env:c})),t[`system-prompt`]&&l.push(this.parser.interpolate(t[`system-prompt`],{inputs:n.inputs,matrix:o,secrets:n.secrets,env:c})),l.length>0){let e=l.join(`
|
|
8
|
-
|
|
9
|
-
`);c.JOB_SYSTEM_PROMPT=e,this.logger.info(` ${F}prompt${P} ${e.split(`
|
|
10
|
-
`)[0].slice(0,80)}${e.includes(`
|
|
11
|
-
`)?`...`:``}`)}let u=t.context?this.parser.interpolate(t.context,{inputs:n.inputs,matrix:o,secrets:n.secrets,env:c}):c.CONTEXT_FILE;if(u){let e=x(r.workflowDir,u);C(e)?(c.WORKFLOW_CONTEXT=e,c.WORKFLOW_CONTEXT_FILE=e,this.logger.info(` ${F}ctx ${P} ${u}`)):r.dryRun||this.logger.warn(` ${mt}warn${P} context file not found: ${u}`)}if(c.CHANGELOG_FILE){let e=x(r.workflowDir,c.CHANGELOG_FILE),t=y(e);C(t)||w(t,{recursive:!0}),C(e)||ae(e,`# Changelog
|
|
12
|
-
`,`utf-8`),c.WORKFLOW_CHANGELOG=e,this.logger.info(` ${F}log ${P} changelog at ${c.CHANGELOG_FILE}`)}let d={inputs:n.inputs,matrix:o,secrets:n.secrets,env:c},f=!1,p=t.preJob?.steps??[],m=t.steps??[],h=t.postJob?.steps??[];for(let t=1;t<=a;t++){t>1&&(this.logger.info(`\n ${mt}retry${P} ${pt}${e}${P}${s} (attempt ${t}/${a})`),this.logger.info(` ${`─`.repeat(50)}`));let n=!0;if(p.length>0&&(n=await this.runStepSequence(p,d,r)),n&&=await this.runStepSequence(m,d,r),h.length>0){let e=await this.runStepSequence(h,d,r);n&&=e}if(n){f=!0;break}t<a&&this.logger.warn(` ${mt}Job "${e}" failed, retrying...${P}`)}if(f)this.logger.info(` [32mJob "${e}" completed${P}${s}`);else if(this.logger.error(`\n [31mJob "${e}" failed after ${a} attempts${P}`),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}},yt=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`}},bt=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 xt=`/backend-api`;var St=class{codexHome;fetchFn;now;readTextFile;constructor(e={}){this.codexHome=e.codexHome??b(_(),`.codex`),this.fetchFn=e.fetchFn??fetch,this.now=e.now??(()=>Date.now()),this.readTextFile=e.readTextFile??(e=>d(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(b(this.codexHome,`auth.json`));return e?JSON.parse(e):null}async readChatgptBaseUrl(){let e=(await this.readOptionalTextFile(b(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(xt)?`${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(xt)&&(t=`${t}${xt}`),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 I=`\x1B[0m`,L=`\x1B[2m`,Ct=`\x1B[36m`,wt=`\x1B[32m`,R=`\x1B[33m`,z=`\x1B[31m`,Tt=`SIGINT`,B=`SIGTERM`,Et=`unnamed`,Dt=`/bin/zsh`,Ot=`FORCE_COLOR`,V=`inherit`,kt=`pipe`,At=` `,jt=`
|
|
13
|
-
${At}\$ `,Mt=`skip`,H=`warn`,U=`error`,W=`fail`,G=`interrupted`,Nt=`already stopping`,Pt=`failed to signal`,K=`(continue-on-error)`,Ft=`ESRCH`,It=process.platform!==`win32`,Lt=`interactiveRun`,Rt=`timeout-minutes`,zt=`timeout-retries`,Bt=`retry`,q=`SIGKILL`,Vt=1e3,Ht=1e3,Ut=6e4,Wt=Math.floor(2147483647/Ut),Gt=`WORKFLOW_STEP_DISPLAY`,Kt=`workflow-mcp`,qt={status:`status_completed`},Jt=new Set([`ENOENT`,`EBUSY`,`EAGAIN`,`EPERM`]),Yt=1e3,Xt=`done`,Zt=`stop`,Qt=1e3,$t=`stop_requested`,en=r.number().positive().finite().max(Wt),tn=r.number().int().min(0).max(10).default(2);var nn=class{activeStep=null;activeQuotaWait=null;constructor(e,t,n=new St,r=ie){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(` ${L}${Mt}${I} ${this.activeQuotaWait.stepName} ${Nt}`);return}this.activeQuotaWait.signal=e,this.activeQuotaWait.controller.abort(),this.logger.info(` ${R}${G}${I} cancelling quota wait for ${this.activeQuotaWait.stepName}`);return}if(!this.activeStep){this.logger.info(` ${L}${Mt}${I} no active step to stop`);return}if(this.activeStep.process.killed){this.logger.info(` ${L}${Mt}${I} ${this.activeStep.stepName} ${Nt}`);return}this.logger.info(` ${R}${G}${I} sending ${e} to ${this.activeStep.stepName}`),this.killActiveStep(e)||this.logger.warn(` ${R}${H}${I} ${Pt} ${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(` ${L}${Mt}${I} ${n}`),!0):(this.logger.warn(` ${R}${H}${I} Unsupported action: ${n} (skipped)`),!0)}if(!i)return!0;if(i.missingRunner)return this.logger.error(` ${z}${U}${I} 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(` ${R}${W}${I} ${a} ${K}\n`),!0):(this.logger.error(` ${z}${W}${I} ${a}\n`),!1);let{command:o,interactive:s}=i,c=this.quotaService.isCodexCommand(o,i.agentKey),l=e[`continue-on-error`]||n.continueOnError,u;try{u=this.resolveTimeoutMs(e,a)}catch(e){if(!(e instanceof bt))throw e;return this.logger.error(` ${z}${U}${I} invalid ${e.context.configKey} for "${a}": ${String(e.context.timeoutValue)}`),l?(this.logger.warn(` ${R}${W}${I} ${a} ${K}\n`),!0):(this.logger.error(` ${z}${W}${I} ${a}\n`),!1)}let d={...process.env,...t.env};if(e.env)for(let[n,r]of Object.entries(e.env))d[n]=this.parser.interpolate(String(r),t);d.WORKFLOW_STEP_NAME=a,d[Gt]=this.formatStepDisplay(d.WORKFLOW_JOB_NAME,a);let f;s&&(f=this.createStatusFile(),d.WORKFLOW_STATUS_FILE=f,this.logger.info(` ${L}status-file${I} ${f}`));let p=e[`working-directory`]?x(n.workflowDir,this.parser.interpolate(e[`working-directory`],t)):n.workflowDir;if(n.dryRun)return this.logger.info(` ${Ct}dry ${I} ${a}`),this.logger.info(`${At}${L}\$ ${o.split(`
|
|
14
|
-
`).join(jt)}${I}`),!0;let m;try{m=this.resolveTimeoutRetries(e,a)}catch(e){if(!(e instanceof bt))throw e;return this.logger.error(` ${z}${U}${I} invalid ${e.context.configKey} for "${a}": ${String(e.context.timeoutValue)}`),l?(this.logger.warn(` ${R}${W}${I} ${a} ${K}\n`),!0):(this.logger.error(` ${z}${W}${I} ${a}\n`),!1)}let h=!1;for(let e=0;e<=m;e++){if(await this.waitForCodexQuotaAvailability(a,o,c),e>0?this.logger.warn(` ${R}${Bt}${I} retrying step after timeout "${a}" (attempt ${e+1}/${m+1})`):this.logger.info(` ${Ct}run ${I} ${a}`),this.logger.info(`${At}${L}\$ ${o.split(`
|
|
15
|
-
`)[0]}${o.includes(`
|
|
16
|
-
`)?` ...`:``}${I}`),n.stopRequestPath&&C(n.stopRequestPath))throw this.logger.info(`\n ${R}${G}${I} ${a} (${Zt})`),new M(B,{cause:Error(`Workflow stop requested before "${a}"`),context:this.createInterruptContext(a,o)});let t=s?this.withInteractiveTerminalTitle(o,d):o,r=this.executeCommand(t,p,d,a,s,u),i=s&&f?await this.raceInteractiveCompletion(r,f,a,n.stopRequestPath):n.stopRequestPath?await this.raceStopRequest(r,a,n.stopRequestPath):await r;if(i.status===`stop_requested`)throw this.logger.info(`\n ${R}${G}${I} ${a} (${Zt})`),new M(B,{cause:Error(`Workflow stop requested during "${a}"`),context:this.createInterruptContext(a,o)});if(i.status===`signaled`)throw this.logger.info(`\n ${R}${G}${I} ${a}`),new M(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?Tt:B;throw this.logger.info(`\n ${R}${G}${I} ${a}`),new M(e,{cause:Error(`Step process exited with ${i.exitCode}`),context:this.createInterruptContext(a,o)})}if(i.status===`spawn_error`)return this.logger.error(` ${z}${U}${I} unable to start step "${a}" in ${i.error.context.workDir} (${this.formatSpawnError(i.error)})`),l?(this.logger.warn(` ${R}${W}${I} ${a} ${K}\n`),!0):(this.logger.error(` ${z}${W}${I} ${a}\n`),!1);if(i.status===`timed_out`){if(this.logger.error(` ${z}${U}${I} step timed out after ${this.formatTimeoutMs(i.timeoutMs)} "${a}" (${o.split(`
|
|
17
|
-
`)[0]})`),e<m){if(f)try{ae(f,``,`utf-8`)}catch(e){this.logger.warn(` ${R}${H}${I} status-file reset failed for ${a}: ${e instanceof Error?e.message:String(e)}`)}continue}return f&&this.cleanupStatusFile(f),l?(this.logger.warn(` ${R}${W}${I} ${a} ${K}\n`),!0):(this.logger.error(` ${z}${W}${I} ${a}\n`),!1)}if(f&&this.cleanupStatusFile(f),i.status===`status_completed`)return this.logger.info(`\n ${wt}${Xt}${I} ${a} (status-file signaled completion)\n`),!0;if(i.status===`completed`&&i.exitCode===0)return this.logger.info(`\n ${wt}pass${I} ${a}\n`),!0;if(i.status===`completed`&&this.logger.error(` ${z}${U}${I} step "${a}" exited with code ${i.exitCode} (${o.split(`
|
|
18
|
-
`)[0]})`),!h&&await this.shouldRetryForCodexQuota(a,c)){h=!0,this.logger.warn(` ${R}${Bt}${I} codex quota reached after failed step, retrying after reset`),--e;continue}return l?(this.logger.warn(` ${R}${W}${I} ${a} ${K}\n`),!0):(this.logger.error(` ${z}${W}${I} ${a}\n`),!1)}return!1}async executeCommand(e,t,n,r,i,a){return i&&this.logger.info(` ${L}(interactive: output renders directly to terminal)${I}`),await new Promise(o=>{let c=i?this.captureTerminalState():null,l=i?s(e,[],{stdio:[V,V,V],cwd:t,detached:!1,env:{...n,[Ot]:`1`},shell:Dt}):s(e,[],{stdio:[V,kt,kt],cwd:t,detached:It,env:{...n,[Ot]:`1`},shell:Dt});this.activeStep={command:e,interactive:i,lastSignaledProcessIds:[],process:l,processGroupId:It&&!i&&typeof l.pid==`number`?l.pid:null,rootProcessId:typeof l.pid==`number`?l.pid:null,stepName:r};let u=null,d=!1,f=!1,p=async e=>{f||(f=!0,m&&clearTimeout(m),u&&clearTimeout(u),i&&(await this.cleanupInteractiveProcessTree(B),this.restoreTerminalState(c)),this.activeStep=null,o(e))},m=a===null?null:setTimeout(()=>{if(d=!0,this.killActiveStep(B)){u=setTimeout(()=>{f||(this.logger.warn(` ${R}${H}${I} ${r} forcing SIGKILL`),this.killActiveStep(q))},Vt);return}p({status:`timed_out`,timeoutMs:a})},a);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 yt({commandPreview:e.split(`
|
|
21
|
-
`)[0],source:`StepRunnerService.executeCommand`,stepName:r,workDir:t},{cause:n})})}),l.on(`exit`,(e,t)=>{if(i||this.cleanupProcessGroup(),d&&a!==null){p({status:`timed_out`,timeoutMs:a});return}if(t===Tt){p({status:`signaled`,signal:Tt});return}if(t===B){p({status:`signaled`,signal:B});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[Lt]!=null,i=r?{command:e[Lt],interactive:!0}:{command:e.run,interactive:!1},a=r?{command:e.run,interactive:!1}:{command:e[Lt],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[Lt],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=`${Kt}: ${t[Gt]??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(Kt)}`;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+Qt,a=Math.max(i-Date.now(),Qt),o=new Date(i).toLocaleString(),s=n?.planType?` (${n.planType})`:``;this.logger.warn(` ${R}wait ${I} 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(` ${R}${H}${I} 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 M(this.activeQuotaWait?.signal??B,{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[Rt];if(n===void 0)return null;try{let e=en.parse(n);return Math.ceil(e*Ut)}catch(e){throw new bt({stepName:t,configKey:Rt,timeoutValue:n},{cause:e})}}resolveTimeoutRetries(e,t){let n=e[zt];try{return tn.parse(n)}catch(e){throw new bt({stepName:t,configKey:zt,timeoutValue:n},{cause:e})}}formatTimeoutMs(e){return e%Ut===0?`${e/Ut} minute(s)`:`${e}ms`}cleanupProcessGroup(){if(!this.activeStep)return;let{processGroupId:e,stepName:t}=this.activeStep;if(e!==null)try{process.kill(-e,B)}catch(n){let r=n.code;r===Ft?this.logger.info(` ${L}cleanup${I} process group ${e} already exited (ESRCH)`):this.logger.warn(` ${R}${H}${I} 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,Ht),e!==q){let e=this.uniqueLiveProcessIds(t);e.length>0&&(this.signalProcessIds(e,q),await this.waitForProcessIdsToExit(e,Ht))}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=a(`ps`,[`-axo`,`pid=,ppid=`],{encoding:`utf-8`,stdio:[`ignore`,kt,`ignore`]})}catch(t){return this.logger.warn(` ${R}${H}${I} 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 r=[],i=new Set,o=(e,t)=>{if(!i.has(e)){i.add(e),e!==process.pid&&r.push({depth:t,pid:e});for(let r of n.get(e)??[])o(r,t+1)}};return o(e,0),r.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!==Ft&&this.logger.warn(` ${R}${H}${I} ${Pt} 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===Ft?this.logger.info(` ${L}signal${I} process group for ${this.activeStep.stepName} already exited (ESRCH)`):this.logger.warn(` ${R}${H}${I} ${Pt} process group for ${this.activeStep.stepName}`)}try{return t.kill(e)}catch(e){return this.logger.warn(` ${R}${H}${I} ${Pt} ${this.activeStep.stepName} (${e.message})`),!1}}createStatusFile(){let e=b(ee(),`workflow-step-${E()}.status`);return ae(e,``,`utf-8`),e}async raceInteractiveCompletion(e,t,n,r){return new Promise(i=>{let a=!1,o=!1,s=!1,c=null,l=null,u=null,d=null,f=null,p=e=>{a||(a=!0,l&&=(l.close(),null),u&&=(u.close(),null),d&&=(clearInterval(d),null),f&&=(clearInterval(f),null),c&&=(clearTimeout(c),null),i(e))},m=!1,h=!1,g=()=>{try{return C(t)&&T(t,`utf-8`).trim()===`YES`}catch(e){let t=e.code;return!t||!Jt.has(t)?this.logger.warn(` ${R}${H}${I} status-file read failed for ${n}: ${e instanceof Error?e.message:String(e)}`):this.logger.info(` ${L}status-file${I} transient FS error (${t}) polling ${n}`),!1}},_=()=>{if(!(o||!g())){if(o=!0,m=this.killActiveStep(B),!m){this.logger.warn(` ${R}${H}${I} ${n} status-file: SIGTERM could not be delivered, process may have already exited`);return}c=setTimeout(()=>{a||(h=!0,this.logger.warn(` ${R}${H}${I} ${n} did not exit after status-file SIGTERM, forcing SIGKILL`),this.killActiveStep(q))},Vt)}},ee=()=>!!(r&&C(r)),v=()=>{if(!(s||!ee())){if(s=!0,m=this.killActiveStep(B),!m){this.logger.warn(` ${R}${H}${I} ${n} stop request: SIGTERM could not be delivered, process may have already exited`);return}c=setTimeout(()=>{a||(h=!0,this.logger.warn(` ${R}${H}${I} ${n} did not exit after stop-request SIGTERM, forcing SIGKILL`),this.killActiveStep(q))},Vt)}};try{l=this.watchStatusFile(t,()=>_())}catch(e){this.logger.warn(` ${R}${H}${I} status-file watcher setup failed for ${n}: ${e instanceof Error?e.message:String(e)}`)}if(d=setInterval(_,1e3),r){try{u=this.watchStatusFile(r,()=>v())}catch(e){e.code!==`ENOENT`&&this.logger.warn(` ${R}${H}${I} stop-request watcher setup failed for ${n}: ${e instanceof Error?e.message:String(e)}`)}f=setInterval(v,Yt)}_(),v(),e.then(e=>{if(s){h&&this.logger.warn(` ${R}${H}${I} ${n} required SIGKILL escalation after stop request`),p({status:$t});return}if(!(o||g())){p(e);return}switch(o||(o=!0,this.logger.info(` ${wt}${Xt}${I} ${n} status-file observed during process exit handling`)),e.status){case`spawn_error`:case`timed_out`:p(e);return;case`signaled`:h&&this.logger.warn(` ${R}${H}${I} ${n} required SIGKILL escalation (work completed via status-file)`),p(qt);return;case`completed`:e.exitCode===0||e.exitCode>128||h||!m?(h&&this.logger.warn(` ${R}${H}${I} ${n} required SIGKILL escalation (work completed via status-file, exit code ${e.exitCode})`),p(qt)):p(e);return;case`status_completed`:p(e);return;case $t:p(e);return;default:p(e)}})})}async raceStopRequest(e,t,n){return new Promise(r=>{let i=!1,a=!1,o=null,s=null,c=null,l=e=>{i||(i=!0,c&&=(c.close(),null),s&&=(clearInterval(s),null),o&&=(clearTimeout(o),null),r(e))},u=()=>{if(!(a||!C(n))){if(a=!0,!this.killActiveStep(B)){this.logger.warn(` ${R}${H}${I} ${t} stop request: SIGTERM could not be delivered, process may have already exited`);return}o=setTimeout(()=>{i||(this.logger.warn(` ${R}${H}${I} ${t} did not exit after stop-request SIGTERM, forcing SIGKILL`),this.killActiveStep(q))},Vt)}};try{c=this.watchStatusFile(n,()=>u())}catch(e){e.code!==`ENOENT`&&this.logger.warn(` ${R}${H}${I} stop-request watcher setup failed for ${t}: ${e instanceof Error?e.message:String(e)}`)}s=setInterval(u,Yt),u(),e.then(e=>{l(a?{status:$t}:e)})})}captureTerminalState(){if(!process.stdin.isTTY)return null;try{return a(`stty`,[`-g`],{encoding:`utf-8`,stdio:[V,kt,`ignore`]}).trim()}catch{return null}}restoreTerminalState(e){if(e&&process.stdin.isTTY)try{a(`stty`,[e],{stdio:[V,`ignore`,`ignore`]})}catch{}process.stdout.isTTY&&process.stdout.write(`\x1B[0m\x1B[?25h\x1B[?1049l`)}cleanupStatusFile(e){try{C(e)&&re(e)}catch(e){this.logger.warn(` ${R}${H}${I} status-file cleanup failed: ${e instanceof Error?e.message:String(e)}`)}}};const J=`\x1B[0m`,rn=`\x1B[1m`,an=`\x1B[2m`,on=`\x1B[36m`,sn=`\x1B[32m`,cn=`\x1B[31m`,ln=`\x1B[34m`;var un=class{activePrompt=null;activePromptSignal=null;constructor(e,t){this.stepRunner=e,this.logger=t}async pathExists(e){try{return await l(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 ${ln}${rn}trigger${J} ${rn}user_prompt${J}`),this.logger.info(` ${`─`.repeat(50)}`);let a=t,o=n.CONTEXT_FILE;if(!a&&o){let e=x(r,o);await this.pathExists(e)&&(a=(await d(e,`utf-8`)).trim(),a&&this.logger.info(` ${sn}found${J} Existing context.md -> ${o}`))}if(!a)if(i)this.logger.info(` ${on}dry ${J} Would prompt user for input`),a=`(dry-run: no prompt collected)`;else{this.logger.info(` ${on}input${J} Enter your prompt (press Enter twice to finish):\n`);let e=[],t=ce({input:process.stdin,output:process.stdout});this.activePrompt=t,this.activePromptSignal=null,a=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 M(t));return}e.length>0&&e[e.length-1]===``&&e.pop(),n(e.join(`
|
|
27
|
-
`))}))})}if(!a)return this.logger.error(` ${cn}fail${J} No prompt provided. Aborting.`),!1;if(o){let e=x(r,o);await u(y(e),{recursive:!0}),await g(e,a,`utf-8`),this.logger.info(` ${sn}saved${J} ${a.split(`
|
|
28
|
-
`).length} line(s) -> ${o}`)}return n.USER_PROMPT=a,this.logger.info(` ${an}prompt${J} ${a.split(`
|
|
29
|
-
`)[0].slice(0,80)}${a.includes(`
|
|
30
|
-
`)?`...`:``}`),!0}async handleAgentDecision(e,t,n,r,i,a){let o=e.on?.agent_decision;if(!o?.steps)return!0;this.logger.info(`\n ${ln}${rn}trigger${J} ${rn}agent_decision${J}`),this.logger.info(` ${`─`.repeat(50)}`),a&&(t.HEARTBEAT_PROMPT=a,this.logger.info(` ${an}prompt${J} ${a.split(`
|
|
31
|
-
`)[0].slice(0,80)}${a.includes(`
|
|
32
|
-
`)?`...`:``}`));let s=t.CONTEXT_FILE;s&&await u(y(x(i.workflowDir,s)),{recursive:!0});let c={inputs:n,matrix:{},secrets:r,env:t};for(let e of o.steps)if(!await this.stepRunner.runStep(e,c,i))return this.logger.error(`\n ${cn}Agent decision aborted workflow${J}`),!1;if(s&&!i.dryRun){let e=x(i.workflowDir,s);if(!await this.pathExists(e))return this.logger.error(` ${cn}fail${J} Context file not created: ${s}`),!1;let t=(await d(e,`utf-8`)).trim();if(!t)return this.logger.error(` ${cn}fail${J} Context file is empty: ${s}`),!1;this.logger.info(` ${sn}ready${J} context has ${t.split(`
|
|
33
|
-
`).length} line(s)`)}return!0}};const dn=[`actions/checkout`,`actions/setup-node`,`actions/cache`,`actions/upload-artifact`,`actions/download-artifact`,`pnpm/action-setup`],fn=`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(`.`),pn=`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 mn=class{generateHumanReadableId(){return`${fn[Math.floor(Math.random()*fn.length)]}-${pn[Math.floor(Math.random()*pn.length)]}`}parseWorkflowFile(e){let t=x(e);if(!C(t))throw Error(`Workflow file not found: ${t}`);let n=T(t,`utf-8`),r=Ge.parse(D(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=x(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=x(i,t);if(!C(a))throw Error(`Imported workflow file not found: ${a} (from ${r})`);let o=D(T(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(!C(e))return t;let n=T(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 r=n.trim().split(`.`);if(r[0]===`inputs`&&r[1])return t.inputs.get(r[1])||``;if(r[0]===`matrix`&&r[1])return t.matrix[r[1]]||``;if(r[0]===`secrets`&&r[1])return t.secrets.get(r[1])||process.env[r[1]]||``;if(r[0]===`env`&&r[1])return t.env[r[1]]||process.env[r[1]]||``;if(r[0]===`runner`&&r[1]===`os`)return`macOS`;if(r[0]===`github`&&r[1]===`sha`)try{return o(`git rev-parse HEAD`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{return`local`}return n.includes(`hashFiles`)?`local`:`\${{ ${n} }}`})}isActionSkipped(e){return dn.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 hn=`\x1B[0m`,gn=`\x1B[2m`;var _n=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),r=n.slice(9),i=x(y(e),`${te(e)}-worktree`),a=x(i,r);C(i)||w(i,{recursive:!0}),C(a)&&(this.logger.info(` ${gn}clean${hn} Removing stale worktree at ${a}`),this.cleanupWorktreePath(e,a));try{o(`git branch -D "${n}"`,{cwd:e,stdio:[`ignore`,`pipe`,`ignore`]})}catch{}return this.logger.info(` [36mcreate${hn} Worktree at ${a} (branch: ${n})`),o(`git worktree add -b "${n}" "${a}" HEAD`,{cwd:e,stdio:[`ignore`,`pipe`,`ignore`]}),a}removeWorktree(e,t){if(C(t)){this.logger.info(` ${gn}remove${hn} Worktree at ${t}`);try{this.cleanupWorktreePath(e,t)}catch{this.logger.warn(` [33mwarn${hn} Failed to remove worktree (may need manual cleanup)`)}}}cleanupWorktreePath(e,t){if(this.isRegisteredWorktree(e,t)){o(`git worktree remove --force "${t}"`,{cwd:e,stdio:[`ignore`,`pipe`,`ignore`]});return}ne(t,{recursive:!0,force:!0})}isRegisteredWorktree(e,t){try{return o(`git worktree list --porcelain`,{cwd:e,encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`]}).split(`
|
|
35
|
-
`).some(e=>e.startsWith(`worktree `)&&x(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 Y=`\x1B[0m`,X=`\x1B[1m`,vn=`\x1B[2m`,yn=`\x1B[32m`,Z=`\x1B[33m`,Q=`\x1B[31m`,$=`\x1B[34m`,bn=`SIGINT`,xn=`SIGTERM`,Sn=`workflow_execution`,Cn=`restart-from`,wn=`user_prompt`,Tn=`agent_decision`,En=`workflow_dispatch`,Dn=`Workflow post-cleanup failed`;function On(e,t){let n=Number(e);return Number.isInteger(n)&&n>0?n:t}var kn=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 mn,this.serviceManager=t.serviceManager??new ft(this.logger),this.stepRunner=t.stepRunner??new nn(this.parser,this.logger),this.jobRunner=t.jobRunner??new vt(this.parser,this.stepRunner,this.logger),this.triggerService=t.triggerService??new un(this.stepRunner,this.logger),this.worktreeService=t.worktreeService??new _n(this.stepRunner,this.logger),this.registry=t.registry??new tt}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(bn),o=()=>i(xn);process.on(bn,a),process.on(xn,o);try{return await this.executeWorkflow(e,t,n,e.keepWorktree??!1)}catch(e){if(e instanceof M||this.isInterrupted())return await this.serviceManager.stopAll(),this.createInterruptedResult();throw e}finally{process.off(bn,a),process.off(xn,o),await this.serviceManager.stopAll()}}interrupt(e,t={phase:Sn}){if(this.interruptedSignal)return;this.interruptedSignal=e;let n=t.phase===Sn?``:` (${t.phase})`;this.logger.info(`\n\n${Z}${X}Interrupted — shutting down...${Y}${n}`),this.triggerService.abortActivePrompt(e),this.stepRunner.stopActiveStep(e),this.serviceManager.stopAll()}isInterrupted(){return this.interruptedSignal===bn||this.interruptedSignal===xn}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(`${Cn}:`)){let e=n.slice(`${Cn}:`.length).trim();return t.includes(e)?{restartFrom:e}:{invalidTarget:e}}}return{}}async pathExists(e){try{return await l(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,r=!1){let i=x(e.workflowPath),a=this.parser.parseWorkflowFile(i),s=this.resolveRunner(e),c=null,l=null,u=null,f=y(i),p=f,m=null,_=!1,ee=!1,v=!!a.worktree,b=new Map,S=e.secretFile?this.parser.loadSecrets(e.secretFile):new Map,C={};try{let u=a.on?.[En]?.inputs||{};for(let[e,t]of Object.entries(u))t.default&&b.set(e,t.default);if(e.inputs)for(let[t,n]of Object.entries(e.inputs))b.set(t,n);if(a.env)for(let[e,t]of Object.entries(a.env))C[e]=String(t);if(e.env)for(let[t,n]of Object.entries(e.env))C[t]=n;s&&(C.WORKFLOW_RUNNER=s,C.WORKFLOW_CLI_AGENT=s);let w=f;for(;w!==`/`;){if(await this.pathExists(x(w,`.git`))){f=w;break}w=y(w)}let T=a[`max-workflows`];if(T){let t=this.registry.resolveWorkspace(e.workspace,a.workspace),n=await this.registry.countRunningWorkflows(t);if(n>=T)throw new at(t,n,T)}let ne=a[`launch-command`];if(ne&&!e.skipLaunch){let t=this.buildLaunchCliCommand(e,i),n=e.name||a.name||te(i),r=e.name?n:`${n}-${this.parser.generateHumanReadableId()}`,s=ne.replace(`{name}`,r).replace(`{command}`,t);this.logger.info(`${vn}Delegating via launch-command: ${s}${Y}`);try{return o(s,{stdio:`inherit`,cwd:f,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||a.name||te(i),ie=e.name?re:`${re}-${this.parser.generateHumanReadableId()}`;if(c=await this.registry.createRun({displayName:ie,dryRun:t,workflowPath:i,workflowWorkspace:a.workspace,workspace:e.workspace}),C.WORKFLOW_RUN_DIR=c.runDir,C.WORKFLOW_WORKSPACE=c.workspace,C.CONTEXT_FILE&&(C.CONTEXT_FILE=c.contextPath,C.CHANGELOG_FILE=c.changelogPath),this.logger.info(`${X}${`═`.repeat(60)}${Y}`),this.logger.info(` ${X}run-workflow${Y} - ${c.displayName}`),this.logger.info(`${X}${`═`.repeat(60)}${Y}`),this.logger.info(` File: ${i}`),this.logger.info(` Workspace: ${c.workspace||`default`}`),this.logger.info(` Run Dir: ${c.runDir}`),this.logger.info(` CWD: ${f}`),b.size>0&&this.logger.info(` Inputs: ${[...b.entries()].map(([e,t])=>`${e}=${t}`).join(`, `)}`),S.size>0&&this.logger.info(` Secrets: ${[...S.keys()].join(`, `)}`),e.job&&this.logger.info(` Target: ${e.job}`),s&&this.logger.info(` Runner: ${s}`),a.pre){let e=a.pre.services?.length||0,t=a.pre.steps?.length||0;this.logger.info(` Pre: ${e} service(s), ${t} setup step(s)`)}if(a.post){let e=a.post.services?.length||0,t=a.post.steps?.length||0;this.logger.info(` Post: ${e} service(s), ${t} cleanup step(s)`)}v&&this.logger.info(` Worktree: enabled${r?` (keep on completion)`:``}`);let ae=a.on&&wn in a.on?wn:a.on?.[Tn]?Tn:En;if(this.logger.info(` Trigger: ${ae}`),this.logger.info(` Mode: ${t?`dry-run`:`execute`}`),this.logger.info(`${X}${`═`.repeat(60)}${Y}`),!await this.triggerService.handleUserPrompt(a,e.prompt||null,C,f,t))return this.isInterrupted()?(l=this.createInterruptedResult(),l):(this.logger.error(`\n${Q}${X}Workflow aborted: no prompt provided${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
40
|
-
`)},l);if(!await this.triggerService.handleAgentDecision(a,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:c.stopRequestPath,workflowDir:f},e.prompt||null))return this.isInterrupted()?(l=this.createInterruptedResult(),l):(this.logger.info(`\n${Z}${X}Workflow skipped: agent decided nothing to do${Y}`),l={exitCode:2,output:this.outputLines.join(`
|
|
41
|
-
`)},l);if(p=f,v&&!t){if(a.worktree?.beforeCreate&&(this.logger.info(`\n ${$}${X}worktree${Y} ${X}beforeCreate${Y}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(a.worktree.beforeCreate,f,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:c.stopRequestPath})||(this.isInterrupted()?l=this.createInterruptedResult():(this.logger.error(`\n${Q}${X}Workflow aborted: worktree beforeCreate hook failed${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
42
|
-
`)}))),!l){this.logger.info(`\n ${$}${X}worktree${Y} ${X}Creating worktree${Y}`),this.logger.info(` ${`─`.repeat(50)}`);let i=this.worktreeService.getWorktreeBranchName(c.displayName);m=this.worktreeService.createWorktree(f,c.displayName),f=m,C.WORKTREE_PATH=m,C.WORKTREE_BRANCH=i,C.WORKFLOW_NAME=c.displayName,C.ORIGINAL_REPO_PATH=p,a.worktree?.afterCreate&&(this.logger.info(`\n ${$}${X}worktree${Y} ${X}afterCreate${Y}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(a.worktree.afterCreate,m,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:c.stopRequestPath})||(this.isInterrupted()?l=this.createInterruptedResult():(this.logger.error(`\n${Q}${X}Workflow aborted: worktree afterCreate hook failed${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
43
|
-
`)}),ee=!r))}}else if(v&&t){let e=y(f),t=te(f),n=c.displayName.toLowerCase().replace(/[^a-z0-9-]+/g,`-`).replace(/^-|-$/g,``),r=x(e,`${t}-worktree`,n);this.logger.info(`\n ${$}${X}worktree${Y} ${X}(dry-run)${Y} Would create at ${r}`)}!l&&a.pre&&(await this.runPreBlock(a.pre,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:c.stopRequestPath,workflowDir:f})||(this.isInterrupted()?l=this.createInterruptedResult():(this.logger.error(`\n${Q}${X}Workflow aborted: pre-conditions failed${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
44
|
-
`)}),ee=!!(m&&!r)));let oe=On(C.MAX_RETRIES,3),se=On(C.FIX_LOOP_MAX_CYCLES,3),E=this.parser.resolveJobOrder(a.jobs,e.job||null);this.logger.info(`\n ${vn}Job order: ${E.join(` -> `)} (max retries: ${oe})${Y}`);let ce=new Set,D=C.WORKFLOW_RUN_DIR?x(C.WORKFLOW_RUN_DIR,`fix.md`):null,le=0,O=0;for(;!l&&O<E.length;){let r=E[O],i=a.jobs[r],o=await this.jobRunner.runJob(r,i,{inputs:b,secrets:S,workflowEnv:C,workflowSystemPrompt:a[`system-prompt`],jobOrder:E,allJobs:a.jobs},{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,stopRequestPath:c.stopRequestPath,workflowDir:f,maxRetries:oe});if(this.isInterrupted()&&(l=this.createInterruptedResult()),!l&&!o&&(this.logger.error(`\n${Q}${X}Workflow failed at job "${r}"${Y}`),await this.serviceManager.stopAll(),l={exitCode:1,output:this.outputLines.join(`
|
|
45
|
-
`)}),!l){if(ce.add(r),D&&await this.pathExists(D)&&!t){if(le++,le>se){this.logger.error(`\n${Q}${X}Fix loop limit reached (${se} cycles). Aborting.${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
46
|
-
`)};continue}let e=(await d(D,`utf-8`)).trim();await h(D);let{restartFrom:t,invalidTarget:n}=this.parseFixRestartFrom(e,E);if(this.logger.info(`\n${Z}${X}fix.md detected after "${r}" (cycle ${le}/${se})${Y}`),this.logger.info(`${vn}${e.split(`
|
|
47
|
-
`)[0].slice(0,100)}${e.includes(`
|
|
48
|
-
`)?`...`:``}${Y}`),n&&this.logger.warn(`${Z}fix.md restart-from "${n}" is not a valid job (available: ${E.join(`, `)}), falling back to default${Y}`),C.CONTEXT_FILE&&await this.pathExists(C.CONTEXT_FILE)){let t=await d(C.CONTEXT_FILE,`utf-8`);await g(C.CONTEXT_FILE,`${t}\n\n---\n## Fix Request (cycle ${le})\n${e}`,`utf-8`)}let i=t?E.indexOf(t):-1,a=E.indexOf(`development`);O=i>=0?i:a>=0?a:1,this.logger.info(`${Z}Restarting from job "${E[O]}"${Y}\n`);continue}O++}}!l&&this.isInterrupted()&&(l=this.createInterruptedResult()),l||=(_=!0,this.logger.info(`\n${X}${`═`.repeat(60)}${Y}`),this.logger.info(` ${yn}${X}Workflow completed successfully${Y}`),this.logger.info(` Jobs run: ${[...ce].join(`, `)}`),m&&this.logger.info(` Worktree: ${m}`),this.logger.info(`${X}${`═`.repeat(60)}${Y}\n`),{exitCode:0,output:this.outputLines.join(`
|
|
49
|
-
`)})}catch(e){if(u=e,e instanceof M||this.isInterrupted())l=this.createInterruptedResult();else throw e}finally{if(_&&l?.exitCode===0&&v&&a.worktree?.beforeCompleted&&!t&&m&&(this.logger.info(`\n ${$}${X}worktree${Y} ${X}beforeCompleted${Y}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(a.worktree.beforeCompleted,m,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n})||(this.isInterrupted()?l=this.createInterruptedResult():(this.logger.error(`\n${Q}${X}worktree beforeCompleted hook failed${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
50
|
-
`)}))),a.post)try{await this.runPostBlock(a.post,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n,workflowDir:f})||(this.logger.error(`\n${Q}${X}${Dn}${Y}`),(!l||l.exitCode===0||l.exitCode===2)&&(l={exitCode:1,output:this.outputLines.join(`
|
|
51
|
-
`)}))}catch(e){u??=e,this.logger.error(`\n${Q}${X}${Dn}${Y}`),(!l||l.exitCode===0||l.exitCode===2)&&(l={exitCode:1,output:this.outputLines.join(`
|
|
52
|
-
`)})}_&&l?.exitCode===0&&v&&a.worktree?.afterCompleted&&!t&&!r&&(this.logger.info(`\n ${$}${X}worktree${Y} ${X}afterCompleted${Y}`),this.logger.info(` ${`─`.repeat(50)}`),await this.worktreeService.runHook(a.worktree.afterCompleted,p,C,b,S,{runner:s,cliAgent:e.cliAgent,dryRun:t,continueOnError:n})||(this.isInterrupted()?l=this.createInterruptedResult():(this.logger.error(`\n${Q}${X}worktree afterCompleted hook failed${Y}`),l={exitCode:1,output:this.outputLines.join(`
|
|
53
|
-
`)}))),ee&&m&&this.worktreeService.removeWorktree(p,m),l&&={...l,output:this.outputLines.join(`
|
|
54
|
-
`)},c&&await this.finalizeRegistryRun(c,l,u)}return l??{exitCode:1,output:this.outputLines.join(`
|
|
55
|
-
`)}}async finalizeRegistryRun(e,t,n){let r=t?.exitCode===130||n instanceof M,i=t?.exitCode??(r?130:1),a=i===0||i===2?`completed`:`error`,o=i===0?`success`:i===2?`skipped`:r||this.isInterrupted()?`interrupted`:`failed`,s=n instanceof Error?n.message:i===1?this.lastMeaningfulOutputLine():void 0;await this.registry.finalizeRun(e,{errorMessage:s,exitCode:i,outcome:o,stage:a})}resolveCliPath(){let e=it,t=x(`/`);for(;e!==t;){let t=x(e,`cli.cjs`);if(C(t))return t;let n=x(e,`dist`,`cli.cjs`);if(C(n))return n;e=y(e)}throw Error(`Unable to locate workflow-mcp cli.cjs from `+it)}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 ${$}${X}pre${Y} ${X}Pre-conditions${Y}`),this.logger.info(` ${`─`.repeat(50)}`),t.CONTEXT_FILE&&await u(y(x(i.workflowDir,t.CONTEXT_FILE)),{recursive:!0}),e.services&&e.services.length>0){this.logger.info(`\n ${$}${X}services${Y}`);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 ${$}${X}setup${Y}`);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${Y}`),await this.serviceManager.stopAll(),!1}return this.logger.info(`\n ${yn}Pre-conditions ready${Y}`),!0}async runPostBlock(e,t,n,r,i){this.logger.info(`\n ${$}${X}post${Y} ${X}Cleanup${Y}`),this.logger.info(` ${`─`.repeat(50)}`),await this.serviceManager.stopAll();try{if(e.services&&e.services.length>0){this.logger.info(`\n ${$}${X}services${Y}`);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 ${$}${X}cleanup${Y}`);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${Y}`),!1}return this.logger.info(`\n ${yn}Cleanup complete${Y}`),!0}finally{await this.serviceManager.stopAll()}}};const An=r.object({workflowPath:r.string().describe(`Path to the workflow YAML file (e.g., .github/workflows/deploy.yml)`),runner:r.string().optional().describe(`Preferred runner key for step command maps (e.g. ollama, claude, codex)`),cliAgent:r.string().optional().describe(`Deprecated alias for runner. Preferred runner command key for step command maps.`),job:r.string().optional().describe(`Run only this job (and its dependencies)`),inputs:r.record(r.string(),r.string()).optional().describe(`Workflow dispatch inputs as key-value pairs`),env:r.record(r.string(),r.string()).optional().describe(`Extra environment variables as key-value pairs`),secretFile:r.string().optional().describe(`Path to a dotenv-style secrets file`),dryRun:r.boolean().optional().describe(`Print steps without executing`),continueOnError:r.boolean().optional().describe(`Continue past step failures`),keepWorktree:r.boolean().optional().describe(`Keep worktree on completion (skip merge and cleanup for retry)`),prompt:r.string().optional().describe(`User prompt for user_prompt trigger workflows`),name:r.string().optional().describe(`Name for the workflow run context directory`),workspace:r.string().optional().describe(`Workspace for workflow registry storage`)});var jn=class e{static TOOL_NAME=`run_workflow`;constructor(e=new kn){this.service=e}getInputSchema(){return An}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:r.toJSONSchema(An)}}async execute(e){try{let t=An.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}}}},Mn=class e{static TOOL_NAME=`schedule-cron`;constructor(e=new Ee){this.service=e}getInputSchema(){return xe}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:r.toJSONSchema(xe)}}async execute(t){try{let e=xe.parse(t),n=await this.service.schedule(e);return{content:[{type:`text`,text:JSON.stringify(n,null,2)}]}}catch(n){let r=new ue(`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 Nn=r.object({reason:r.string().optional().describe(`Optional stop reason to record.`),runKey:r.string().min(1).describe(`Run key of the running workflow to stop.`),workspace:r.string().optional().describe(`Workspace containing the running workflow. Defaults to default.`)});var Pn=class e{static TOOL_NAME=`stop_workflow`;constructor(e=new tt){this.registry=e}getInputSchema(){return Nn}getDefinition(){return{name:e.TOOL_NAME,description:`Request a running workflow to stop gracefully without taking over terminal stdin.`,inputSchema:r.toJSONSchema(Nn)}}async execute(e){try{let t=Nn.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 Fn=[jn.TOOL_NAME,rt.TOOL_NAME,Mn.TOOL_NAME,De.TOOL_NAME,Pn.TOOL_NAME];function In(){let r=new e({name:`workflow-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),i=new jn,a=new rt,o=new Mn,s=new De,c=new Pn;return r.setRequestHandler(n,async()=>({tools:[i.getDefinition(),a.getDefinition(),o.getDefinition(),s.getDefinition(),c.getDefinition()]})),r.setRequestHandler(t,async e=>{let{name:t,arguments:n}=e.params;if(t===jn.TOOL_NAME)return await i.execute(n);if(t===rt.TOOL_NAME)return await a.execute(n);if(t===Mn.TOOL_NAME)return await o.execute(n);if(t===De.TOOL_NAME)return await s.execute();if(t===Pn.TOOL_NAME)return await c.execute(n);throw new O(t,Fn)}),r}var Ln=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new le,await this.server.connect(this.transport),console.error(`workflow-mcp MCP server started on stdio`)}async stop(){this.transport&&=(await this.transport.close(),null)}};export{tt as a,xe as c,St as i,In as n,Ee as o,kn as r,ge as s,Ln as t};
|