@workermill/agent 0.8.10 → 0.8.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +16 -16
  2. package/dist/index.js +12 -12
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
- var ft=Object.defineProperty;var ht=(e,o)=>()=>(e&&(o=e(e=0)),o);var $t=(e,o)=>{for(var n in o)ft(e,n,{get:o[n],enumerable:!0})};var yo={};$t(yo,{checkPrerequisites:()=>oo,findClaudePath:()=>se,getConfigDir:()=>Et,getConfigFile:()=>oe,getLogFile:()=>Ce,getPidFile:()=>ce,getSystemInfo:()=>De,loadConfig:()=>$o,loadConfigFromFile:()=>fe,saveConfigToFile:()=>eo,validatePrerequisites:()=>wo});import{existsSync as Me,readFileSync as wt,mkdirSync as yt,writeFileSync as It,chmodSync as At}from"fs";import{execSync as me}from"child_process";import{hostname as ho,homedir as Ee}from"os";import{join as ee}from"path";function Et(){return Te}function oe(){return Se}function ce(){return kt}function Ce(){return bt}function fe(){Me(Se)||(console.error("No config found. Run 'workermill-agent setup' first."),process.exit(1));let e;try{e=wt(Se,"utf-8")}catch{console.error("Failed to read config file:",Se),process.exit(1)}let o;try{o=JSON.parse(e)}catch{console.error("Config file is corrupted. Re-run 'workermill-agent setup'."),process.exit(1)}(!o.apiUrl||!o.apiKey)&&(console.error("Config file is missing required fields (apiUrl, apiKey). Re-run 'workermill-agent setup'."),process.exit(1));let n=o.workerImage||"workermill-worker:local";return{apiUrl:o.apiUrl,apiKey:o.apiKey,agentId:o.agentId,maxWorkers:o.maxWorkers||4,pollIntervalMs:o.pollIntervalMs||5e3,heartbeatIntervalMs:o.heartbeatIntervalMs||3e4,githubToken:o.tokens?.github||"",bitbucketToken:o.tokens?.bitbucket||"",gitlabToken:o.tokens?.gitlab||"",workerImage:n}}function eo(e){Me(Te)||yt(Te,{recursive:!0}),It(Se,JSON.stringify(e,null,2),"utf-8");try{At(Se,384)}catch{}}function $o(){let e=process.env.WORKERMILL_API_URL,o=process.env.WORKERMILL_API_KEY;return e||(console.error("WORKERMILL_API_URL is required in .env.remote"),process.exit(1)),o||(console.error("WORKERMILL_API_KEY is required in .env.remote"),console.error("Get your API key from Settings > Integrations on the WorkerMill dashboard."),process.exit(1)),{apiUrl:e.replace(/\/$/,""),apiKey:o,agentId:process.env.AGENT_ID||`agent-${ho()}`,maxWorkers:parseInt(process.env.MAX_WORKERS||"4",10),pollIntervalMs:parseInt(process.env.POLL_INTERVAL_MS||"5000",10),heartbeatIntervalMs:parseInt(process.env.HEARTBEAT_INTERVAL_MS||"30000",10),githubToken:process.env.GITHUB_TOKEN||"",bitbucketToken:process.env.BITBUCKET_TOKEN||"",gitlabToken:process.env.GITLAB_TOKEN||"",workerImage:process.env.WORKER_IMAGE||"workermill-worker:local"}}function se(){let e=process.platform==="win32",o=e?"where":"which";try{return me(`${o} claude`,{stdio:"ignore",timeout:1e4}),"claude"}catch{}let n=[];e?n.push(ee(process.env.ProgramFiles||"C:\\Program Files","ClaudeCode","claude.exe"),ee(process.env.LOCALAPPDATA||"","Programs","ClaudeCode","claude.exe"),ee(Ee(),"AppData","Local","Programs","ClaudeCode","claude.exe"),ee(Ee(),".local","bin","claude.exe")):n.push(ee(Ee(),".local","bin","claude"),"/opt/homebrew/bin/claude","/usr/local/bin/claude");for(let t of n)if(t&&Me(t))return t;return null}function oo(e){let o=[],n=e||"workermill-worker:local";try{let l=me("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim();o.push({name:"Docker",ok:!0,detail:l})}catch{o.push({name:"Docker",ok:!1,detail:"Not running or not installed"})}let t=se();if(t)try{let l=me(`"${t}" --version`,{encoding:"utf-8",timeout:1e4}).trim();o.push({name:"Claude CLI",ok:!0,detail:l})}catch{o.push({name:"Claude CLI",ok:!0,detail:t})}else o.push({name:"Claude CLI",ok:!1,detail:"Not installed"});let r=Ee(),i=ee(r,".claude",".credentials.json");Me(i)?o.push({name:"Claude auth",ok:!0,detail:"Credentials found"}):o.push({name:"Claude auth",ok:!1,detail:"Run 'claude' and complete sign-in"});let s=process.version;parseInt(s.slice(1).split(".")[0],10)>=20?o.push({name:"Node.js",ok:!0,detail:s}):o.push({name:"Node.js",ok:!1,detail:`${s} (need >= 20)`});try{me(`docker image inspect ${n}`,{stdio:"ignore",timeout:1e4}),o.push({name:"Worker image",ok:!0,detail:n})}catch{o.push({name:"Worker image",ok:!1,detail:`'${n}' not found`})}return o}function wo(){try{me("docker version",{stdio:"ignore"})}catch{console.error("Docker is not available. Please install Docker and ensure it's running."),process.exit(1)}let e=process.env.WORKER_IMAGE||"workermill-worker:local";try{me(`docker image inspect ${e}`,{stdio:"ignore"})}catch{console.error(`Worker image '${e}' not found.`),console.error(e==="workermill-worker:local"?"Build it with: ./bin/local-workermill build-worker":`Pull it with: docker pull ${e}`),process.exit(1)}se()||(console.error("Claude CLI is not installed."),console.error("Install it: curl -fsSL https://claude.ai/install.sh | bash"),process.exit(1));let o=Ee(),n=ee(o,".claude",".credentials.json");Me(n)||(console.error("Claude credentials not found."),console.error("Run 'claude' and complete the sign-in flow to authenticate."),process.exit(1))}function De(){let e="unknown";try{e=me("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim()}catch{}let o="unknown",n=se();if(n)try{o=me(`"${n}" --version`,{encoding:"utf-8",timeout:1e4}).trim()}catch{}return{hostname:ho(),platform:process.platform,nodeVersion:process.version,dockerVersion:e,claudeVersion:o}}var Te,Se,kt,bt,te=ht(()=>{"use strict";Te=ee(Ee(),".workermill"),Se=ee(Te,"config.json"),kt=ee(Te,"agent.pid"),bt=ee(Te,"agent.log")});import{existsSync as Un}from"fs";import{Command as Wn}from"commander";te();import u from"chalk";import Re from"ora";import to from"inquirer";import{execSync as de,spawnSync as Io}from"child_process";import{existsSync as We}from"fs";import{hostname as St,homedir as ko,totalmem as Tt,cpus as Ct}from"os";import{join as Ue}from"path";import Rt from"axios";var Ie=process.platform==="win32";function ro(e){try{return de(`${Ie?"where":"which"} ${e}`,{stdio:"ignore",timeout:1e4}),!0}catch{return!1}}function no(e){try{return de(`"${e}" --version`,{encoding:"utf-8",timeout:1e4}).trim()}catch{return null}}function Ao(){if(!Ie)return null;if(ro("git"))try{let o=de("where git",{encoding:"utf-8",timeout:1e4}).trim().split(`
3
- `)[0],n=Ue(o,"..","..","bin","bash.exe");if(We(n))return n}catch{}let e=[Ue(process.env.ProgramFiles||"C:\\Program Files","Git","bin","bash.exe"),"C:\\Program Files\\Git\\bin\\bash.exe","C:\\Program Files (x86)\\Git\\bin\\bash.exe",Ue(ko(),"scoop","apps","git","current","bin","bash.exe")];for(let o of e)if(We(o))return o;return null}function _t(){if(Ie)try{return de("winget install Anthropic.ClaudeCode --accept-package-agreements --accept-source-agreements",{stdio:"inherit",timeout:18e4}),!0}catch{return!1}else{if(process.platform==="darwin")try{return de("brew install --cask claude-code",{stdio:"inherit",timeout:18e4}),!0}catch{}try{return de("curl -fsSL https://claude.ai/install.sh | bash",{stdio:"inherit",timeout:12e4}),!0}catch{return!1}}}async function io(){console.log(),console.log(u.bold.cyan(" WorkerMill Remote Agent Setup")),console.log(u.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),console.log(" Run AI workers locally with your Claude Max subscription."),console.log(" Workers execute on your machine, logs stream to the cloud dashboard."),console.log();let e=Math.round(Tt()/(1024*1024*1024)),o=Ct().length;console.log(u.dim(" System")),console.log(` ${u.dim("RAM:")} ${e} GB ${u.dim("CPUs:")} ${o}`),e<8?(console.log(),console.log(u.red(" \u2717 Insufficient RAM")),console.log(u.yellow(" WorkerMill requires at least 8 GB of RAM (16 GB recommended).")),console.log(u.yellow(` Your system has ${e} GB.`)),console.log(),process.exit(1)):e<16?(console.log(u.yellow(` \u26A0 ${e} GB RAM is below the recommended 16 GB.`)),console.log(u.yellow(" Workers may run slowly or be killed by the OS under memory pressure."))):console.log(u.green(" \u2713 System meets requirements")),console.log();let n=Re("Checking Docker...").start();if(ro("docker")){let c=no("docker");try{let A=de("docker info --format {{.OSType}}",{encoding:"utf-8",timeout:15e3}).trim();Ie&&A==="windows"&&(n.fail("Docker is running in Windows containers mode"),console.log(),console.log(u.yellow(" WorkerMill workers require Linux containers.")),console.log(u.yellow(" Right-click the Docker Desktop icon in the system tray \u2192")),console.log(u.yellow(" 'Switch to Linux containers...'")),console.log(),console.log(" Then re-run: workermill-agent"),process.exit(1))}catch{n.fail("Docker is installed but the daemon is not running"),console.log(),console.log(u.yellow(" Start Docker Desktop, wait for it to fully initialize,")),console.log(u.yellow(" then re-run: workermill-agent")),process.exit(1)}n.succeed(`Docker ${u.dim(c?`(${c})`:"")}`)}else n.fail("Docker is not installed"),console.log(),console.log(u.yellow(" Docker Desktop is required but must be installed manually:")),console.log(` ${u.cyan("https://docs.docker.com/get-docker/")}`),console.log(),console.log(" Install Docker, start it, then re-run: workermill-agent"),process.exit(1);if(Ie){let c=Re("Checking Git for Windows...").start(),A=Ao();A?(c.succeed(`Git for Windows ${u.dim(`(${A})`)}`),process.env.CLAUDE_CODE_GIT_BASH_PATH=A):(c.fail("Git for Windows is not installed"),console.log(),console.log(u.yellow(" Claude Code on Windows requires Git for Windows (Git Bash):")),console.log(` ${u.cyan("https://git-scm.com/downloads/win")}`),console.log(),console.log(" Install Git for Windows, then re-run: workermill-agent"),process.exit(1))}let t=Re("Checking Claude CLI...").start(),r=se();if(r){let c=no(r);t.succeed(`Claude CLI ${u.dim(c?`(${c})`:"")}`)}else if(t.warn("Claude CLI not found \u2014 installing..."),console.log(),_t()&&(r=se()),r){let A=no(r);console.log(),console.log(u.green(` \u2713 Claude CLI installed ${u.dim(A?`(${A})`:"")}`))}else console.log(),console.log(u.red(" Claude CLI was installed but could not be found.")),console.log(),Ie?(console.log(u.yellow(" Winget updated your PATH but this shell doesn't have it yet.")),console.log(u.yellow(" Close this terminal, open a new one, and re-run: workermill-agent"))):(console.log(" Try manually:"),console.log(u.cyan(" curl -fsSL https://claude.ai/install.sh | bash")),console.log(" Then re-run: workermill-agent")),process.exit(1);let i=Re("Checking Claude authentication...").start(),s=Ue(ko(),".claude",".credentials.json");if(We(s))i.succeed("Claude authenticated");else{i.warn("Not authenticated \u2014 launching Claude..."),console.log(),console.log(u.dim(" Claude will open and prompt you to authenticate.")),console.log(u.dim(" Sign in with your Claude Max account, then exit Claude (Ctrl+C).")),console.log();let c={...process.env};if(Ie){let A=Ao();A&&(c.CLAUDE_CODE_GIT_BASH_PATH=A)}Io(r,[],{stdio:"inherit",timeout:3e5,env:c}),We(s)?(console.log(),console.log(u.green(" \u2713 Claude authenticated"))):(console.log(),console.log(u.red(" Authentication failed or was cancelled.")),console.log(" Try manually: open a new terminal and run 'claude'"),console.log(" Then re-run: workermill-agent"),process.exit(1))}console.log(u.green(" \u2713")+` Node.js ${u.dim(`(${process.version})`)}`),console.log(),console.log(u.bold("Configuration")),console.log();let{apiUrl:a}=await to.prompt([{type:"input",name:"apiUrl",message:"WorkerMill API URL:",default:"https://workermill.com",validate:c=>c.startsWith("http://")||c.startsWith("https://")?!0:"Must be a valid URL"}]),{apiKey:l}=await to.prompt([{type:"password",name:"apiKey",message:"API Key (from Settings > Integrations):",mask:"*",validate:c=>c.length>0?!0:"API key is required"}]),m=Re("Validating API key...").start(),$="github",g="",h="";try{let c=await Rt.get(`${a.replace(/\/$/,"")}/api/agent/config`,{headers:{"x-api-key":l},timeout:15e3});$=c.data.scmProvider||"github",g=c.data.workerImageUrl||"",h=c.data.ecrRegistry||"",m.succeed(`Connected! SCM provider: ${$}`)}catch(c){c.response?.status===401?m.fail("Invalid API key. Check Settings > Integrations on the dashboard."):m.fail("Failed to connect to WorkerMill API. Check the URL and try again."),process.exit(1)}console.log(u.dim(" SCM tokens are managed via Settings > Integrations on the dashboard.")),console.log();let{agentId:P}=await to.prompt([{type:"input",name:"agentId",message:"Agent name:",default:`agent-${St()}`}]);console.log();let d=g||"workermill-worker:local",w=h.length>0,T=Re("Checking AWS CLI...").start();if(ro("aws"))try{de("aws sts get-caller-identity",{stdio:"pipe",timeout:15e3}),T.succeed("AWS CLI configured")}catch{T.warn("AWS CLI found but credentials not configured"),console.log(),console.log(u.yellow(" Configure AWS credentials for private ECR image access:")),console.log(u.cyan(" aws configure")),console.log(u.dim(" Contact your WorkerMill admin for AWS access key / secret key.")),console.log(u.dim(" Setup will continue \u2014 you can configure AWS later before starting."))}else T.warn("AWS CLI not found"),console.log(),console.log(u.yellow(" AWS CLI is required for pulling worker images from private ECR.")),console.log(u.cyan(" Install: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html")),console.log(u.dim(" Setup will continue \u2014 install AWS CLI before starting the agent."));let x=!1;if(w){console.log(),console.log(u.dim(" Authenticating with private ECR..."));let c=h.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/),A=c?c[1]:"us-east-1";try{de(`aws ecr get-login-password --region ${A} | docker login --username AWS --password-stdin ${h}`,{stdio:"pipe",timeout:3e4}),console.log(u.green(" \u2713 ECR authenticated")),x=!0}catch{console.log(u.yellow(" \u26A0 ECR authentication failed (AWS credentials may not be configured yet)")),console.log(u.dim(" Worker image pull will be skipped \u2014 authenticate later with:")),console.log(u.dim(` aws ecr get-login-password --region ${A} | docker login --username AWS --password-stdin ${h}`))}}if(x||!w){console.log(),console.log(u.dim(` Pulling worker image: ${d}`)),console.log(u.dim(" This may take a few minutes on first run (~1.1 GB)...")),console.log();let c=Io("docker",["pull",d],{stdio:"inherit",timeout:6e5});c.status===0?(console.log(),console.log(u.green(" \u2713 Worker image pulled"))):(console.log(),console.log(u.red(" \u2717 Failed to pull worker image.")),c.error&&console.log(u.yellow(` Error: ${c.error.message}`)),console.log(u.dim(" Setup will continue \u2014 you can pull the image later before starting.")))}let W={apiUrl:a.replace(/\/$/,""),apiKey:l,agentId:P,maxWorkers:1,pollIntervalMs:5e3,heartbeatIntervalMs:3e4,tokens:{github:"",bitbucket:"",gitlab:""},workerImage:d,setupCompletedAt:new Date().toISOString()};eo(W),console.log(),console.log(u.green.bold(" Setup complete!")),console.log(),console.log(` Config saved to: ${u.dim(oe())}`),console.log(),console.log(` Start the agent with: ${u.cyan("workermill-agent start")}`),console.log()}import O from"chalk";import{totalmem as wn}from"os";import{spawn as yn}from"child_process";import{writeFileSync as at,existsSync as In,unlinkSync as An,openSync as kn,createWriteStream as bn}from"fs";import{createRequire as Pt}from"module";var xt=Pt(import.meta.url),vt=xt("../package.json"),H=vt.version;te();import K from"chalk";import Mt from"axios";var so=null;function Ge(e,o){so=Mt.create({baseURL:e,headers:{"Content-Type":"application/json","x-api-key":o},timeout:3e4})}var M=new Proxy({},{get(e,o){if(!so)throw new Error("API client not initialized. Call initApi() first.");return so[o]}});import S from"chalk";te();import p from"chalk";import{spawn as on,execSync as uo}from"child_process";import{spawn as Wt}from"child_process";import $e from"chalk";import Ke from"axios";var Lt=16384,Nt=6e5;async function bo(e,o,n,t,r){let i=r?.maxTokens??Lt,s=r?.temperature??.7,a=r?.timeoutMs??Nt;switch(e){case"anthropic":return Ot(o,n,t,i,s,a);case"openai":return Ft(o,n,t,i,s,a);case"google":return Dt(o,n,t,i,s,a);case"ollama":return Ut(o,n,t,i,s,a);default:throw new Error(`Unsupported AI provider: ${e}`)}}async function Ot(e,o,n,t,r,i){let a=(await Ke.post("https://api.anthropic.com/v1/messages",{model:e,max_tokens:t,temperature:r,messages:[{role:"user",content:o}]},{headers:{"x-api-key":n,"anthropic-version":"2023-06-01","Content-Type":"application/json"},timeout:i})).data?.content;if(Array.isArray(a))return a.filter(l=>l.type==="text").map(l=>l.text).join("");throw new Error("Unexpected Anthropic API response format")}async function Ft(e,o,n,t,r,i){let a=(await Ke.post("https://api.openai.com/v1/chat/completions",{model:e,max_tokens:t,temperature:r,messages:[{role:"user",content:o}]},{headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},timeout:i})).data?.choices?.[0]?.message;if(a?.content)return a.content;throw new Error("Unexpected OpenAI API response format")}async function Dt(e,o,n,t,r,i){let l=(await Ke.post(`https://generativelanguage.googleapis.com/v1beta/models/${e}:generateContent?key=${n}`,{contents:[{parts:[{text:o}]}],generationConfig:{maxOutputTokens:t,temperature:r}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.candidates?.[0]?.content?.parts?.[0]?.text;if(l)return l;throw new Error("Unexpected Google AI API response format")}async function Ut(e,o,n,t,r,i){let s=n||"http://localhost:11434",l=(await Ke.post(`${s}/api/generate`,{model:e,prompt:o,stream:!1,options:{temperature:r}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.response;if(l)return l;throw new Error("Unexpected Ollama API response format")}var he=null;async function So(){if(he)return he;try{let{data:e}=await M.get("/api/agent/critic-prompt");return he={promptTemplate:e.promptTemplate,approvalThreshold:e.approvalThreshold??85,maxTargetFiles:e.maxTargetFiles??15},ne=he.approvalThreshold,Le=he.maxTargetFiles,he}catch{return console.warn("Failed to fetch critic config from API"),null}}var Le=15,ne=85;function To(e){let o=e.indexOf("```json");if(o!==-1){let t=o+7,r=e.indexOf("{",t);if(r!==-1){let i=Eo(e,r);if(i)return JSON.parse(i)}}let n=e.indexOf('"stories"');if(n!==-1){let r=e.substring(0,n).lastIndexOf("{");if(r!==-1){let i=Eo(e,r);if(i)return JSON.parse(i)}}throw new Error("Could not find JSON execution plan in output")}function Eo(e,o){let n=0,t=!1,r=!1;for(let i=o;i<e.length;i++){let s=e[i];if(r){r=!1;continue}if(s==="\\"){t&&(r=!0);continue}if(s==='"'){t=!t;continue}if(!t){if(s==="{")n++;else if(s==="}"&&(n--,n===0))return e.substring(o,i+1)}}return null}function Co(e){let o=0,n=[];for(let t of e.stories)if(!t.targetFiles||!Array.isArray(t.targetFiles))t.targetFiles=[];else if(t.targetFiles.length>Le){let r=t.targetFiles.slice(Le);n.push(`${t.id}: ${t.targetFiles.length} files \u2192 ${Le} (dropped: ${r.join(", ")})`),t.targetFiles=t.targetFiles.slice(0,Le),o++}return{truncatedCount:o,details:n}}function Ro(e,o){if(e.stories.length<=o)return{droppedCount:0,details:[]};let n=e.stories.length-o,r=e.stories.slice(o).map(s=>`${s.id}: "${s.title}" (${s.persona})`);e.stories=e.stories.slice(0,o);let i=new Set(e.stories.map(s=>s.id));for(let s of e.stories)s.dependencies=s.dependencies.filter(a=>i.has(a));return{droppedCount:n,details:r}}function _o(e){let o=new Map,n=0,t=[];for(let r of e.stories){if(!r.targetFiles||r.targetFiles.length===0)continue;let i=[],s=[];for(let a of r.targetFiles)o.get(a)?s.push(a):(o.set(a,r.id),i.push(a));s.length>0&&(r.targetFiles=i,n+=s.length,t.push(`${r.id}: removed ${s.join(", ")} (owned by ${s.map(a=>o.get(a)).join(", ")})`))}return{resolvedCount:n,details:t}}function Po(e){return"```json\n"+JSON.stringify(e,null,2)+"\n```"}function Gt(e,o){if(!he)return null;let n=JSON.stringify(o,null,2);return he.promptTemplate.replace("{{PRD}}",e).replace("{{PLAN}}",n)}function Kt(e){let o=e.trim();if(o.includes("```")){let r=o.match(/```(?:json)?\s*([\s\S]*?)```/);r&&(o=r[1].trim())}let n=o.indexOf("{");n>0&&(o=o.substring(n));let t=JSON.parse(o);return{approved:t.approved,score:Math.max(0,Math.min(100,Math.round(t.score))),risks:t.risks||[],suggestions:t.suggestions,storyFeedback:Array.isArray(t.storyFeedback)?t.storyFeedback:void 0}}function Bt(e,o,n,t,r){return new Promise((i,s)=>{let a=Wt(e,["--print","--model",o,"--permission-mode","bypassPermissions"],{env:t,stdio:["pipe","pipe","pipe"]});a.stdin.write(n),a.stdin.end();let l="",m="";a.stdout.on("data",g=>{let h=g.toString();l+=h;let P=h.split(`
4
- `).filter(d=>d.trim());for(let d of P){let w=d.trim().length>200?d.trim().substring(0,200)+"\u2026":d.trim();w&&(r&&Mo(r,`${vo} [critic] ${w}`,"output"),console.log(`${Ne()} ${$e.dim("\u{1F50D}")} ${$e.dim(w)}`))}}),a.stderr.on("data",g=>{m+=g.toString()});let $=setTimeout(()=>{a.kill("SIGTERM"),s(new Error("Critic CLI timed out after 20 minutes"))},12e5);a.on("exit",g=>{clearTimeout($),g!==0?s(new Error(`Critic CLI failed (exit ${g}): ${m.substring(0,300)}`)):i(l)}),a.on("error",g=>{clearTimeout($),s(g)})})}function xo(e){let o=["","## CRITIC FEEDBACK \u2014 Your previous plan was REJECTED","",`Score: ${e.score}/100 (need >= ${ne} to pass)`,""];if(e.risks.length>0){o.push("### Risks Identified:");for(let n of e.risks)o.push(`- ${n}`);o.push("")}if(e.suggestions&&e.suggestions.length>0){o.push("### Required Changes:");for(let n of e.suggestions)o.push(`- ${n}`);o.push("")}if(e.storyFeedback&&e.storyFeedback.length>0){o.push("### Per-Story Feedback:");for(let n of e.storyFeedback)if(o.push(`- **${n.storyId}**: ${n.feedback}`),n.suggestedChanges)for(let t of n.suggestedChanges)o.push(` - ${t}`);o.push("")}return o.push("**You MUST address ALL feedback above.** Each story must target at most 5 files.","Stories MUST NOT overlap on targetFiles. Generate a revised plan."),o.join(`
5
- `)}var vo="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Ne(){return $e.dim(new Date().toLocaleTimeString())}async function Mo(e,o,n="system",t="info"){try{await M.post("/api/control-center/logs",{taskId:e,type:n,message:o,severity:t})}catch{}}async function Lo(e,o,n,t,r,i,s,a,l){let m=Gt(n,t);if(!m)return console.warn(`${Ne()} ${i} ${$e.yellow("\u26A0")} Critic config not available \u2014 skipping validation`),null;let $=s||"anthropic";console.log(`${Ne()} ${i} ${$e.dim(`Running critic validation (${$})...`)}`),l&&Mo(l,`${vo} Running critic validation (${$})...`);try{let g;if($==="anthropic")g=await Bt(e,o,m,r,l);else{if(!a)throw new Error(`No API key for critic provider "${$}"`);g=await bo($,o,m,a,{maxTokens:4096,temperature:.3,timeoutMs:12e5})}let h=Kt(g),P=h.score>=ne?$e.green("\u2713"):$e.red("\u2717");return console.log(`${Ne()} ${i} ${P} Critic score: ${h.score}/100 (threshold: ${ne})`),h}catch(g){let h=g instanceof Error?g.message:String(g);return console.error(`${Ne()} ${i} ${$e.yellow("\u26A0")} Critic failed: ${h.substring(0,100)}`),null}}import{generateText as jt,tool as ao,stepCountIs as Vt}from"ai";import{createOpenAI as No}from"@ai-sdk/openai";import{createAnthropic as qt}from"@ai-sdk/anthropic";import{createGoogleGenerativeAI as zt}from"@ai-sdk/google";import{z as we}from"zod";import{execSync as lo}from"child_process";import{readFileSync as Ht,existsSync as Yt}from"fs";function Jt(e,o,n){switch(e){case"anthropic":return qt({apiKey:n})(o);case"openai":return No({apiKey:n})(o);case"google":return zt({apiKey:n})(o);case"ollama":return No({baseURL:n||"http://localhost:11434/v1",apiKey:"ollama"})(o);default:throw new Error(`Unsupported AI provider: ${e}`)}}var Xt=we.object({pattern:we.string().describe("Glob pattern like '**/*.ts', 'src/**/*.js', 'package.json'")}),Qt=we.object({path:we.string().describe("File path relative to the working directory"),limit:we.number().optional().describe("Max number of lines to read (default: 500)")}),Zt=we.object({pattern:we.string().describe("Search pattern (regex supported)"),glob:we.string().optional().describe("File glob to filter (e.g. '*.ts', '*.py')")});function en(e){return{glob:ao({description:"Find files matching a glob pattern. Returns file paths relative to the working directory.",inputSchema:Xt,execute:async o=>{try{let n=lo(`find . -path './.git' -prune -o -path './node_modules' -prune -o -name '${o.pattern.replace(/\*\*/g,"*")}' -print 2>/dev/null | head -200`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim();return n||lo("find . -path './.git' -prune -o -path './node_modules' -prune -o -type f -print 2>/dev/null | head -500",{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No files found"}catch{return"Error running glob search"}}}),read_file:ao({description:"Read the contents of a file. Returns the file text.",inputSchema:Qt,execute:async o=>{try{let n=`${e}/${o.path}`.replace(/\/\//g,"/");if(!Yt(n))return`File not found: ${o.path}`;let t=Ht(n,"utf-8"),r=t.split(`
2
+ var ht=Object.defineProperty;var $t=(e,o)=>()=>(e&&(o=e(e=0)),o);var wt=(e,o)=>{for(var n in o)ht(e,n,{get:o[n],enumerable:!0})};var Io={};wt(Io,{checkPrerequisites:()=>to,findClaudePath:()=>se,getConfigDir:()=>St,getConfigFile:()=>oe,getLogFile:()=>Ce,getPidFile:()=>ce,getSystemInfo:()=>De,loadConfig:()=>wo,loadConfigFromFile:()=>fe,saveConfigToFile:()=>oo,validatePrerequisites:()=>yo});import{existsSync as Me,readFileSync as yt,mkdirSync as It,writeFileSync as At,chmodSync as kt}from"fs";import{execSync as me}from"child_process";import{hostname as $o,homedir as Ee}from"os";import{join as ee}from"path";function St(){return Te}function oe(){return Se}function ce(){return bt}function Ce(){return Et}function fe(){Me(Se)||(console.error("No config found. Run 'workermill-agent setup' first."),process.exit(1));let e;try{e=yt(Se,"utf-8")}catch{console.error("Failed to read config file:",Se),process.exit(1)}let o;try{o=JSON.parse(e)}catch{console.error("Config file is corrupted. Re-run 'workermill-agent setup'."),process.exit(1)}(!o.apiUrl||!o.apiKey)&&(console.error("Config file is missing required fields (apiUrl, apiKey). Re-run 'workermill-agent setup'."),process.exit(1));let n=o.workerImage||"workermill-worker:local";return{apiUrl:o.apiUrl,apiKey:o.apiKey,agentId:o.agentId,maxWorkers:o.maxWorkers||4,pollIntervalMs:o.pollIntervalMs||5e3,heartbeatIntervalMs:o.heartbeatIntervalMs||3e4,githubToken:o.tokens?.github||"",bitbucketToken:o.tokens?.bitbucket||"",gitlabToken:o.tokens?.gitlab||"",workerImage:n}}function oo(e){Me(Te)||It(Te,{recursive:!0}),At(Se,JSON.stringify(e,null,2),"utf-8");try{kt(Se,384)}catch{}}function wo(){let e=process.env.WORKERMILL_API_URL,o=process.env.WORKERMILL_API_KEY;return e||(console.error("WORKERMILL_API_URL is required in .env.remote"),process.exit(1)),o||(console.error("WORKERMILL_API_KEY is required in .env.remote"),console.error("Get your API key from Settings > Integrations on the WorkerMill dashboard."),process.exit(1)),{apiUrl:e.replace(/\/$/,""),apiKey:o,agentId:process.env.AGENT_ID||`agent-${$o()}`,maxWorkers:parseInt(process.env.MAX_WORKERS||"4",10),pollIntervalMs:parseInt(process.env.POLL_INTERVAL_MS||"5000",10),heartbeatIntervalMs:parseInt(process.env.HEARTBEAT_INTERVAL_MS||"30000",10),githubToken:process.env.GITHUB_TOKEN||"",bitbucketToken:process.env.BITBUCKET_TOKEN||"",gitlabToken:process.env.GITLAB_TOKEN||"",workerImage:process.env.WORKER_IMAGE||"workermill-worker:local"}}function se(){let e=process.platform==="win32",o=e?"where":"which";try{return me(`${o} claude`,{stdio:"ignore",timeout:1e4}),"claude"}catch{}let n=[];e?n.push(ee(process.env.ProgramFiles||"C:\\Program Files","ClaudeCode","claude.exe"),ee(process.env.LOCALAPPDATA||"","Programs","ClaudeCode","claude.exe"),ee(Ee(),"AppData","Local","Programs","ClaudeCode","claude.exe"),ee(Ee(),".local","bin","claude.exe")):n.push(ee(Ee(),".local","bin","claude"),"/opt/homebrew/bin/claude","/usr/local/bin/claude");for(let t of n)if(t&&Me(t))return t;return null}function to(e){let o=[],n=e||"workermill-worker:local";try{let l=me("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim();o.push({name:"Docker",ok:!0,detail:l})}catch{o.push({name:"Docker",ok:!1,detail:"Not running or not installed"})}let t=se();if(t)try{let l=me(`"${t}" --version`,{encoding:"utf-8",timeout:1e4}).trim();o.push({name:"Claude CLI",ok:!0,detail:l})}catch{o.push({name:"Claude CLI",ok:!0,detail:t})}else o.push({name:"Claude CLI",ok:!1,detail:"Not installed"});let r=Ee(),i=ee(r,".claude",".credentials.json");Me(i)?o.push({name:"Claude auth",ok:!0,detail:"Credentials found"}):o.push({name:"Claude auth",ok:!1,detail:"Run 'claude' and complete sign-in"});let s=process.version;parseInt(s.slice(1).split(".")[0],10)>=20?o.push({name:"Node.js",ok:!0,detail:s}):o.push({name:"Node.js",ok:!1,detail:`${s} (need >= 20)`});try{me(`docker image inspect ${n}`,{stdio:"ignore",timeout:1e4}),o.push({name:"Worker image",ok:!0,detail:n})}catch{o.push({name:"Worker image",ok:!1,detail:`'${n}' not found`})}return o}function yo(){try{me("docker version",{stdio:"ignore"})}catch{console.error("Docker is not available. Please install Docker and ensure it's running."),process.exit(1)}let e=process.env.WORKER_IMAGE||"workermill-worker:local";try{me(`docker image inspect ${e}`,{stdio:"ignore"})}catch{console.error(`Worker image '${e}' not found.`),console.error(e==="workermill-worker:local"?"Build it with: ./bin/local-workermill build-worker":`Pull it with: docker pull ${e}`),process.exit(1)}se()||(console.error("Claude CLI is not installed."),console.error("Install it: curl -fsSL https://claude.ai/install.sh | bash"),process.exit(1));let o=Ee(),n=ee(o,".claude",".credentials.json");Me(n)||(console.error("Claude credentials not found."),console.error("Run 'claude' and complete the sign-in flow to authenticate."),process.exit(1))}function De(){let e="unknown";try{e=me("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim()}catch{}let o="unknown",n=se();if(n)try{o=me(`"${n}" --version`,{encoding:"utf-8",timeout:1e4}).trim()}catch{}return{hostname:$o(),platform:process.platform,nodeVersion:process.version,dockerVersion:e,claudeVersion:o}}var Te,Se,bt,Et,te=$t(()=>{"use strict";Te=ee(Ee(),".workermill"),Se=ee(Te,"config.json"),bt=ee(Te,"agent.pid"),Et=ee(Te,"agent.log")});import{existsSync as Kn}from"fs";import{Command as Gn}from"commander";te();import u from"chalk";import _e from"ora";import no from"inquirer";import{execSync as de,spawnSync as Ao}from"child_process";import{existsSync as We}from"fs";import{hostname as Tt,homedir as bo,totalmem as Ct,cpus as _t}from"os";import{join as Ue}from"path";import Rt from"axios";var Ie=process.platform==="win32";function io(e){try{return de(`${Ie?"where":"which"} ${e}`,{stdio:"ignore",timeout:1e4}),!0}catch{return!1}}function ro(e){try{return de(`"${e}" --version`,{encoding:"utf-8",timeout:1e4}).trim()}catch{return null}}function ko(){if(!Ie)return null;if(io("git"))try{let o=de("where git",{encoding:"utf-8",timeout:1e4}).trim().split(`
3
+ `)[0],n=Ue(o,"..","..","bin","bash.exe");if(We(n))return n}catch{}let e=[Ue(process.env.ProgramFiles||"C:\\Program Files","Git","bin","bash.exe"),"C:\\Program Files\\Git\\bin\\bash.exe","C:\\Program Files (x86)\\Git\\bin\\bash.exe",Ue(bo(),"scoop","apps","git","current","bin","bash.exe")];for(let o of e)if(We(o))return o;return null}function Pt(){if(Ie)try{return de("winget install Anthropic.ClaudeCode --accept-package-agreements --accept-source-agreements",{stdio:"inherit",timeout:18e4}),!0}catch{return!1}else{if(process.platform==="darwin")try{return de("brew install --cask claude-code",{stdio:"inherit",timeout:18e4}),!0}catch{}try{return de("curl -fsSL https://claude.ai/install.sh | bash",{stdio:"inherit",timeout:12e4}),!0}catch{return!1}}}async function so(){console.log(),console.log(u.bold.cyan(" WorkerMill Remote Agent Setup")),console.log(u.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),console.log(" Run AI workers locally with your Claude Max subscription."),console.log(" Workers execute on your machine, logs stream to the cloud dashboard."),console.log();let e=Math.round(Ct()/(1024*1024*1024)),o=_t().length;console.log(u.dim(" System")),console.log(` ${u.dim("RAM:")} ${e} GB ${u.dim("CPUs:")} ${o}`),e<8?(console.log(),console.log(u.red(" \u2717 Insufficient RAM")),console.log(u.yellow(" WorkerMill requires at least 8 GB of RAM (16 GB recommended).")),console.log(u.yellow(` Your system has ${e} GB.`)),console.log(),process.exit(1)):e<16?(console.log(u.yellow(` \u26A0 ${e} GB RAM is below the recommended 16 GB.`)),console.log(u.yellow(" Workers may run slowly or be killed by the OS under memory pressure."))):console.log(u.green(" \u2713 System meets requirements")),console.log();let n=_e("Checking Docker...").start();if(io("docker")){let c=ro("docker");try{let A=de("docker info --format {{.OSType}}",{encoding:"utf-8",timeout:15e3}).trim();Ie&&A==="windows"&&(n.fail("Docker is running in Windows containers mode"),console.log(),console.log(u.yellow(" WorkerMill workers require Linux containers.")),console.log(u.yellow(" Right-click the Docker Desktop icon in the system tray \u2192")),console.log(u.yellow(" 'Switch to Linux containers...'")),console.log(),console.log(" Then re-run: workermill-agent"),process.exit(1))}catch{n.fail("Docker is installed but the daemon is not running"),console.log(),console.log(u.yellow(" Start Docker Desktop, wait for it to fully initialize,")),console.log(u.yellow(" then re-run: workermill-agent")),process.exit(1)}n.succeed(`Docker ${u.dim(c?`(${c})`:"")}`)}else n.fail("Docker is not installed"),console.log(),console.log(u.yellow(" Docker Desktop is required but must be installed manually:")),console.log(` ${u.cyan("https://docs.docker.com/get-docker/")}`),console.log(),console.log(" Install Docker, start it, then re-run: workermill-agent"),process.exit(1);if(Ie){let c=_e("Checking Git for Windows...").start(),A=ko();A?(c.succeed(`Git for Windows ${u.dim(`(${A})`)}`),process.env.CLAUDE_CODE_GIT_BASH_PATH=A):(c.fail("Git for Windows is not installed"),console.log(),console.log(u.yellow(" Claude Code on Windows requires Git for Windows (Git Bash):")),console.log(` ${u.cyan("https://git-scm.com/downloads/win")}`),console.log(),console.log(" Install Git for Windows, then re-run: workermill-agent"),process.exit(1))}let t=_e("Checking Claude CLI...").start(),r=se();if(r){let c=ro(r);t.succeed(`Claude CLI ${u.dim(c?`(${c})`:"")}`)}else if(t.warn("Claude CLI not found \u2014 installing..."),console.log(),Pt()&&(r=se()),r){let A=ro(r);console.log(),console.log(u.green(` \u2713 Claude CLI installed ${u.dim(A?`(${A})`:"")}`))}else console.log(),console.log(u.red(" Claude CLI was installed but could not be found.")),console.log(),Ie?(console.log(u.yellow(" Winget updated your PATH but this shell doesn't have it yet.")),console.log(u.yellow(" Close this terminal, open a new one, and re-run: workermill-agent"))):(console.log(" Try manually:"),console.log(u.cyan(" curl -fsSL https://claude.ai/install.sh | bash")),console.log(" Then re-run: workermill-agent")),process.exit(1);let i=_e("Checking Claude authentication...").start(),s=Ue(bo(),".claude",".credentials.json");if(We(s))i.succeed("Claude authenticated");else{i.warn("Not authenticated \u2014 launching Claude..."),console.log(),console.log(u.dim(" Claude will open and prompt you to authenticate.")),console.log(u.dim(" Sign in with your Claude Max account, then exit Claude (Ctrl+C).")),console.log();let c={...process.env};if(Ie){let A=ko();A&&(c.CLAUDE_CODE_GIT_BASH_PATH=A)}Ao(r,[],{stdio:"inherit",timeout:3e5,env:c}),We(s)?(console.log(),console.log(u.green(" \u2713 Claude authenticated"))):(console.log(),console.log(u.red(" Authentication failed or was cancelled.")),console.log(" Try manually: open a new terminal and run 'claude'"),console.log(" Then re-run: workermill-agent"),process.exit(1))}console.log(u.green(" \u2713")+` Node.js ${u.dim(`(${process.version})`)}`),console.log(),console.log(u.bold("Configuration")),console.log();let{apiUrl:a}=await no.prompt([{type:"input",name:"apiUrl",message:"WorkerMill API URL:",default:"https://workermill.com",validate:c=>c.startsWith("http://")||c.startsWith("https://")?!0:"Must be a valid URL"}]),{apiKey:l}=await no.prompt([{type:"password",name:"apiKey",message:"API Key (from Settings > Integrations):",mask:"*",validate:c=>c.length>0?!0:"API key is required"}]),m=_e("Validating API key...").start(),$="github",g="",h="";try{let c=await Rt.get(`${a.replace(/\/$/,"")}/api/agent/config`,{headers:{"x-api-key":l},timeout:15e3});$=c.data.scmProvider||"github",g=c.data.workerImageUrl||"",h=c.data.ecrRegistry||"",m.succeed(`Connected! SCM provider: ${$}`)}catch(c){c.response?.status===401?m.fail("Invalid API key. Check Settings > Integrations on the dashboard."):m.fail("Failed to connect to WorkerMill API. Check the URL and try again."),process.exit(1)}console.log(u.dim(" SCM tokens are managed via Settings > Integrations on the dashboard.")),console.log();let{agentId:P}=await no.prompt([{type:"input",name:"agentId",message:"Agent name:",default:`agent-${Tt()}`}]);console.log();let d=g||"workermill-worker:local",w=h.length>0,T=_e("Checking AWS CLI...").start();if(io("aws"))try{de("aws sts get-caller-identity",{stdio:"pipe",timeout:15e3}),T.succeed("AWS CLI configured")}catch{T.warn("AWS CLI found but credentials not configured"),console.log(),console.log(u.yellow(" Configure AWS credentials for private ECR image access:")),console.log(u.cyan(" aws configure")),console.log(u.dim(" Contact your WorkerMill admin for AWS access key / secret key.")),console.log(u.dim(" Setup will continue \u2014 you can configure AWS later before starting."))}else T.warn("AWS CLI not found"),console.log(),console.log(u.yellow(" AWS CLI is required for pulling worker images from private ECR.")),console.log(u.cyan(" Install: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html")),console.log(u.dim(" Setup will continue \u2014 install AWS CLI before starting the agent."));let x=!1;if(w){console.log(),console.log(u.dim(" Authenticating with private ECR..."));let c=h.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/),A=c?c[1]:"us-east-1";try{de(`aws ecr get-login-password --region ${A} | docker login --username AWS --password-stdin ${h}`,{stdio:"pipe",timeout:3e4}),console.log(u.green(" \u2713 ECR authenticated")),x=!0}catch{console.log(u.yellow(" \u26A0 ECR authentication failed (AWS credentials may not be configured yet)")),console.log(u.dim(" Worker image pull will be skipped \u2014 authenticate later with:")),console.log(u.dim(` aws ecr get-login-password --region ${A} | docker login --username AWS --password-stdin ${h}`))}}if(x||!w){console.log(),console.log(u.dim(` Pulling worker image: ${d}`)),console.log(u.dim(" This may take a few minutes on first run (~1.1 GB)...")),console.log();let c=Ao("docker",["pull",d],{stdio:"inherit",timeout:6e5});c.status===0?(console.log(),console.log(u.green(" \u2713 Worker image pulled"))):(console.log(),console.log(u.red(" \u2717 Failed to pull worker image.")),c.error&&console.log(u.yellow(` Error: ${c.error.message}`)),console.log(u.dim(" Setup will continue \u2014 you can pull the image later before starting.")))}let W={apiUrl:a.replace(/\/$/,""),apiKey:l,agentId:P,maxWorkers:1,pollIntervalMs:5e3,heartbeatIntervalMs:3e4,tokens:{github:"",bitbucket:"",gitlab:""},workerImage:d,setupCompletedAt:new Date().toISOString()};oo(W),console.log(),console.log(u.green.bold(" Setup complete!")),console.log(),console.log(` Config saved to: ${u.dim(oe())}`),console.log(),console.log(` Start the agent with: ${u.cyan("workermill-agent start")}`),console.log()}import O from"chalk";import{totalmem as In}from"os";import{spawn as An}from"child_process";import{writeFileSync as lt,existsSync as kn,unlinkSync as bn,openSync as En,createWriteStream as Sn}from"fs";import{createRequire as xt}from"module";var vt=xt(import.meta.url),Mt=vt("../package.json"),H=Mt.version;te();import G from"chalk";import Lt from"axios";var ao=null;function Ke(e,o){ao=Lt.create({baseURL:e,headers:{"Content-Type":"application/json","x-api-key":o},timeout:3e4})}var M=new Proxy({},{get(e,o){if(!ao)throw new Error("API client not initialized. Call initApi() first.");return ao[o]}});import S from"chalk";te();import p from"chalk";import{spawn as tn,execSync as go}from"child_process";import{spawn as Kt}from"child_process";import $e from"chalk";import Ge from"axios";var Nt=16384,Ot=6e5;async function Eo(e,o,n,t,r){let i=r?.maxTokens??Nt,s=r?.temperature??.7,a=r?.timeoutMs??Ot;switch(e){case"anthropic":return Ft(o,n,t,i,s,a);case"openai":return Dt(o,n,t,i,s,a);case"google":return Ut(o,n,t,i,s,a);case"ollama":return Wt(o,n,t,i,s,a);default:throw new Error(`Unsupported AI provider: ${e}`)}}async function Ft(e,o,n,t,r,i){let a=(await Ge.post("https://api.anthropic.com/v1/messages",{model:e,max_tokens:t,temperature:r,messages:[{role:"user",content:o}]},{headers:{"x-api-key":n,"anthropic-version":"2023-06-01","Content-Type":"application/json"},timeout:i})).data?.content;if(Array.isArray(a))return a.filter(l=>l.type==="text").map(l=>l.text).join("");throw new Error("Unexpected Anthropic API response format")}async function Dt(e,o,n,t,r,i){let a=(await Ge.post("https://api.openai.com/v1/chat/completions",{model:e,max_tokens:t,temperature:r,messages:[{role:"user",content:o}]},{headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},timeout:i})).data?.choices?.[0]?.message;if(a?.content)return a.content;throw new Error("Unexpected OpenAI API response format")}async function Ut(e,o,n,t,r,i){let l=(await Ge.post(`https://generativelanguage.googleapis.com/v1beta/models/${e}:generateContent?key=${n}`,{contents:[{parts:[{text:o}]}],generationConfig:{maxOutputTokens:t,temperature:r}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.candidates?.[0]?.content?.parts?.[0]?.text;if(l)return l;throw new Error("Unexpected Google AI API response format")}async function Wt(e,o,n,t,r,i){let s=n||"http://localhost:11434",l=(await Ge.post(`${s}/api/generate`,{model:e,prompt:o,stream:!1,options:{temperature:r}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.response;if(l)return l;throw new Error("Unexpected Ollama API response format")}var he=null;async function To(){if(he)return he;try{let{data:e}=await M.get("/api/agent/critic-prompt");return he={promptTemplate:e.promptTemplate,approvalThreshold:e.approvalThreshold??85,maxTargetFiles:e.maxTargetFiles??15},ne=he.approvalThreshold,Le=he.maxTargetFiles,he}catch{return console.warn("Failed to fetch critic config from API"),null}}var Le=15,ne=85;function Co(e){let o=e.indexOf("```json");if(o!==-1){let t=o+7,r=e.indexOf("{",t);if(r!==-1){let i=So(e,r);if(i)return JSON.parse(i)}}let n=e.indexOf('"stories"');if(n!==-1){let r=e.substring(0,n).lastIndexOf("{");if(r!==-1){let i=So(e,r);if(i)return JSON.parse(i)}}throw new Error("Could not find JSON execution plan in output")}function So(e,o){let n=0,t=!1,r=!1;for(let i=o;i<e.length;i++){let s=e[i];if(r){r=!1;continue}if(s==="\\"){t&&(r=!0);continue}if(s==='"'){t=!t;continue}if(!t){if(s==="{")n++;else if(s==="}"&&(n--,n===0))return e.substring(o,i+1)}}return null}function _o(e){let o=0,n=[];for(let t of e.stories)if(!t.targetFiles||!Array.isArray(t.targetFiles))t.targetFiles=[];else if(t.targetFiles.length>Le){let r=t.targetFiles.slice(Le);n.push(`${t.id}: ${t.targetFiles.length} files \u2192 ${Le} (dropped: ${r.join(", ")})`),t.targetFiles=t.targetFiles.slice(0,Le),o++}return{truncatedCount:o,details:n}}function Ro(e,o){if(e.stories.length<=o)return{droppedCount:0,details:[]};let n=e.stories.length-o,r=e.stories.slice(o).map(s=>`${s.id}: "${s.title}" (${s.persona})`);e.stories=e.stories.slice(0,o);let i=new Set(e.stories.map(s=>s.id));for(let s of e.stories)s.dependencies=s.dependencies.filter(a=>i.has(a));return{droppedCount:n,details:r}}function Po(e){let o=new Map,n=0,t=[];for(let r of e.stories){if(!r.targetFiles||r.targetFiles.length===0)continue;let i=[],s=[];for(let a of r.targetFiles)o.get(a)?s.push(a):(o.set(a,r.id),i.push(a));s.length>0&&(r.targetFiles=i,n+=s.length,t.push(`${r.id}: removed ${s.join(", ")} (owned by ${s.map(a=>o.get(a)).join(", ")})`))}return{resolvedCount:n,details:t}}function xo(e){return"```json\n"+JSON.stringify(e,null,2)+"\n```"}function Gt(e,o){if(!he)return null;let n=JSON.stringify(o,null,2);return he.promptTemplate.replace("{{PRD}}",e).replace("{{PLAN}}",n)}function Bt(e){let o=e.trim();if(o.includes("```")){let r=o.match(/```(?:json)?\s*([\s\S]*?)```/);r&&(o=r[1].trim())}let n=o.indexOf("{");n>0&&(o=o.substring(n));let t=JSON.parse(o);return{approved:t.approved,score:Math.max(0,Math.min(100,Math.round(t.score))),risks:t.risks||[],suggestions:t.suggestions,storyFeedback:Array.isArray(t.storyFeedback)?t.storyFeedback:void 0}}function jt(e,o,n,t,r){return new Promise((i,s)=>{let a=Kt(e,["--print","--model",o,"--permission-mode","bypassPermissions"],{env:t,stdio:["pipe","pipe","pipe"]});a.stdin.write(n),a.stdin.end();let l="",m="";a.stdout.on("data",g=>{let h=g.toString();l+=h;let P=h.split(`
4
+ `).filter(d=>d.trim());for(let d of P){let w=d.trim().length>200?d.trim().substring(0,200)+"\u2026":d.trim();w&&(r&&Lo(r,`${Mo} [critic] ${w}`,"output"),console.log(`${Ne()} ${$e.dim("\u{1F50D}")} ${$e.dim(w)}`))}}),a.stderr.on("data",g=>{m+=g.toString()});let $=setTimeout(()=>{a.kill("SIGTERM"),s(new Error("Critic CLI timed out after 20 minutes"))},12e5);a.on("exit",g=>{clearTimeout($),g!==0?s(new Error(`Critic CLI failed (exit ${g}): ${m.substring(0,300)}`)):i(l)}),a.on("error",g=>{clearTimeout($),s(g)})})}function vo(e){let o=["","## CRITIC FEEDBACK \u2014 Your previous plan was REJECTED","",`Score: ${e.score}/100 (need >= ${ne} to pass)`,""];if(e.risks.length>0){o.push("### Risks Identified:");for(let n of e.risks)o.push(`- ${n}`);o.push("")}if(e.suggestions&&e.suggestions.length>0){o.push("### Required Changes:");for(let n of e.suggestions)o.push(`- ${n}`);o.push("")}if(e.storyFeedback&&e.storyFeedback.length>0){o.push("### Per-Story Feedback:");for(let n of e.storyFeedback)if(o.push(`- **${n.storyId}**: ${n.feedback}`),n.suggestedChanges)for(let t of n.suggestedChanges)o.push(` - ${t}`);o.push("")}return o.push("**You MUST address ALL feedback above.** Each story must target at most 5 files.","Stories MUST NOT overlap on targetFiles. Generate a revised plan."),o.join(`
5
+ `)}var Mo="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Ne(){return $e.dim(new Date().toLocaleTimeString())}async function Lo(e,o,n="system",t="info"){try{await M.post("/api/control-center/logs",{taskId:e,type:n,message:o,severity:t})}catch{}}async function No(e,o,n,t,r,i,s,a,l){let m=Gt(n,t);if(!m)return console.warn(`${Ne()} ${i} ${$e.yellow("\u26A0")} Critic config not available \u2014 skipping validation`),null;let $=s||"anthropic";console.log(`${Ne()} ${i} ${$e.dim(`Running critic validation (${$})...`)}`),l&&Lo(l,`${Mo} Running critic validation (${$})...`);try{let g;if($==="anthropic")g=await jt(e,o,m,r,l);else{if(!a)throw new Error(`No API key for critic provider "${$}"`);g=await Eo($,o,m,a,{maxTokens:4096,temperature:.3,timeoutMs:12e5})}let h=Bt(g),P=h.score>=ne?$e.green("\u2713"):$e.red("\u2717");return console.log(`${Ne()} ${i} ${P} Critic score: ${h.score}/100 (threshold: ${ne})`),h}catch(g){let h=g instanceof Error?g.message:String(g);return console.error(`${Ne()} ${i} ${$e.yellow("\u26A0")} Critic failed: ${h.substring(0,100)}`),null}}import{generateText as Vt,tool as lo,stepCountIs as zt}from"ai";import{createOpenAI as Oo}from"@ai-sdk/openai";import{createAnthropic as qt}from"@ai-sdk/anthropic";import{createGoogleGenerativeAI as Ht}from"@ai-sdk/google";import{z as we}from"zod";import{execSync as co}from"child_process";import{readFileSync as Yt,existsSync as Jt}from"fs";function Xt(e,o,n){switch(e){case"anthropic":return qt({apiKey:n})(o);case"openai":return Oo({apiKey:n})(o);case"google":return Ht({apiKey:n})(o);case"ollama":return Oo({baseURL:n||"http://localhost:11434/v1",apiKey:"ollama"})(o);default:throw new Error(`Unsupported AI provider: ${e}`)}}var Qt=we.object({pattern:we.string().describe("Glob pattern like '**/*.ts', 'src/**/*.js', 'package.json'")}),Zt=we.object({path:we.string().describe("File path relative to the working directory"),limit:we.number().optional().describe("Max number of lines to read (default: 500)")}),en=we.object({pattern:we.string().describe("Search pattern (regex supported)"),glob:we.string().optional().describe("File glob to filter (e.g. '*.ts', '*.py')")});function on(e){return{glob:lo({description:"Find files matching a glob pattern. Returns file paths relative to the working directory.",inputSchema:Qt,execute:async o=>{try{let n=co(`find . -path './.git' -prune -o -path './node_modules' -prune -o -name '${o.pattern.replace(/\*\*/g,"*")}' -print 2>/dev/null | head -200`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim();return n||co("find . -path './.git' -prune -o -path './node_modules' -prune -o -type f -print 2>/dev/null | head -500",{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No files found"}catch{return"Error running glob search"}}}),read_file:lo({description:"Read the contents of a file. Returns the file text.",inputSchema:Zt,execute:async o=>{try{let n=`${e}/${o.path}`.replace(/\/\//g,"/");if(!Jt(n))return`File not found: ${o.path}`;let t=Yt(n,"utf-8"),r=t.split(`
6
6
  `),i=o.limit||500;return r.length>i?r.slice(0,i).join(`
7
7
  `)+`
8
- ... (truncated, ${r.length-i} more lines)`:t}catch(n){return`Error reading file: ${n instanceof Error?n.message:String(n)}`}}}),grep:ao({description:"Search for a pattern in files. Returns matching lines with file paths and line numbers.",inputSchema:Zt,execute:async o=>{try{let n=o.glob?`--include='${o.glob}'`:"";return lo(`grep -rn ${n} --exclude-dir=node_modules --exclude-dir=.git '${o.pattern.replace(/'/g,"'\\''")}' . 2>/dev/null | head -100`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No matches found"}catch{return"No matches found"}}})}}async function Oo(e){let{provider:o,model:n,apiKey:t,prompt:r,systemPrompt:i,workingDir:s,maxTokens:a=16384,temperature:l=.7,timeoutMs:m=6e5,maxSteps:$=15,enableTools:g=!0}=e,h=Jt(o,n,t),P=g&&s?en(s):void 0,d=new AbortController,w=setTimeout(()=>d.abort(),m);try{return(await jt({model:h,prompt:r,system:i,maxOutputTokens:a,temperature:l,tools:P,stopWhen:P?Vt($):void 0,abortSignal:d.signal})).text}finally{clearTimeout(w)}}function tn(e,o){let n=[e.usage,e.message?.usage,e.result?.usage];for(let t of n)if(t&&typeof t=="object"){let r=t;typeof r.input_tokens=="number"&&(o.inputTokens=Math.max(o.inputTokens,r.input_tokens)),typeof r.output_tokens=="number"&&(o.outputTokens=Math.max(o.outputTokens,r.output_tokens)),typeof r.cache_creation_input_tokens=="number"&&(o.cacheCreationTokens=Math.max(o.cacheCreationTokens,r.cache_creation_input_tokens)),typeof r.cache_read_input_tokens=="number"&&(o.cacheReadTokens=Math.max(o.cacheReadTokens,r.cache_read_input_tokens))}}async function Fo(e,o,n,t){if(!(o.inputTokens===0&&o.outputTokens===0))try{await M.post(`/api/tasks/${e}/usage/partial`,{inputTokens:o.inputTokens,outputTokens:o.outputTokens,cacheCreationTokens:o.cacheCreationTokens,cacheReadTokens:o.cacheReadTokens,model:n,mode:t})}catch{}}var ue=3;function k(){return p.dim(new Date().toLocaleTimeString())}var _e=[],ye=null;async function Uo(){for(;_e.length>0;){let e=_e.splice(0,50);try{await M.post("/api/control-center/logs/batch",{entries:e},{timeout:5e3})}catch{}}}async function _(e,o,n="system",t="info"){_e.length>=200&&_e.shift(),_e.push({taskId:e,message:o,type:n,severity:t}),ye||(ye=Uo().finally(()=>{ye=null}))}async function nn(){ye&&await ye,_e.length>0&&(ye=Uo().finally(()=>{ye=null}),await ye)}async function Pe(e,o,n,t,r,i){try{await M.post("/api/agent/planning-progress",{taskId:e,phase:o,elapsedSeconds:n,detail:t,charsGenerated:r,toolCallCount:i})}catch{}}var E="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Be(e){let o=Math.floor(e/60),n=e%60;return o>0?`${o}m ${n}s`:`${n}s`}function Do(e,o){switch(e){case"initializing":return`${E} Starting planning agent...`;case"reading_repo":return`${E} Reading repository structure...`;case"analyzing":return`${E} Analyzing requirements...`;case"generating_plan":return`${E} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Be(o)} elapsed)`;case"validating":return`${E} Validating plan...`;case"complete":return`${E} Planning complete`}}function rn(e,o,n,t,r,i,s){let a=p.cyan(r.slice(0,8));return new Promise((l,m)=>{let g=on(e,["--print","--verbose","--output-format","stream-json","--model",o,"--permission-mode","bypassPermissions"],{cwd:s,env:t,stdio:["pipe","pipe","pipe"]});g.stdin.write(n),g.stdin.end();let h="",P="",d="",w=0,T=0,x={inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0},W=o,c="";function A(C=!1){if(!c)return;let z=c.split(`
9
- `),le=C?"":z.pop()||"";for(let X of z)if(X.trim()){_(r,`${E} ${X}`,"output");let L=X.trim().length>160?X.trim().substring(0,160)+"\u2026":X.trim();console.log(`${k()} ${a} ${p.dim("\u{1F4AD}")} ${p.dim(L)}`)}c=le}let b="initializing",G=!1,y={started:!0,reading:!1,analyzing:!1,generating:!1};function F(C){if(C===b)return;b=C;let z=Math.round((Date.now()-i)/1e3),le=Do(C,z);_(r,le),console.log(`${k()} ${a} ${p.dim(le)}`)}let D=setInterval(()=>A(),500),B=setInterval(()=>{let C=Math.round((Date.now()-i)/1e3);Pe(r,b,C,Do(b,C),w,T)},2e3),j=0,ie=setInterval(()=>{let C=Math.round((Date.now()-i)/1e3);if(b==="initializing"&&C>=5?F("reading_repo"):b==="reading_repo"&&C>=15&&!G&&F("analyzing"),b==="generating_plan"&&C-j>=30){j=C;let z=`${E} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Be(C)} elapsed)`;_(r,z),console.log(`${k()} ${a} ${p.dim(z)}`)}},5e3),J="";g.stdout.on("data",C=>{J+=C.toString();let z=J.split(`
10
- `);J=z.pop()||"";for(let le of z){let X=le.trim();if(X)try{let L=JSON.parse(X);if(L.type==="assistant"&&L.message?.content){let Q=L.message.content;if(Array.isArray(Q))for(let I of Q)I.type==="text"&&I.text?(h+=I.text,w+=I.text.length,c+=I.text,G||(G=!0,T>0&&!y.analyzing&&(F("analyzing"),y.analyzing=!0)),w>500&&!y.generating&&(F("generating_plan"),y.generating=!0,j=Math.round((Date.now()-i)/1e3))):I.type==="tool_use"&&(T++,y.reading||(F("reading_repo"),y.reading=!0));else typeof Q=="string"&&Q&&(h+=Q,w+=Q.length,c+=Q)}else L.type==="content_block_delta"&&L.delta?.text?(h+=L.delta.text,w+=L.delta.text.length,c+=L.delta.text,G||(G=!0,T>0&&!y.analyzing&&(F("analyzing"),y.analyzing=!0)),w>500&&!y.generating&&(F("generating_plan"),y.generating=!0,j=Math.round((Date.now()-i)/1e3))):L.type==="content_block_start"&&L.content_block?.type==="tool_use"?(T++,y.reading||(F("reading_repo"),y.reading=!0)):L.type==="result"&&L.result&&(P=typeof L.result=="string"?L.result:"");if(tn(L,x),L.type==="result"&&L.total_cost_usd!==void 0&&L.modelUsage&&typeof L.modelUsage=="object"){let Q=Object.keys(L.modelUsage);Q.length>0&&(W=Q[0])}}catch{h+=X+`
11
- `,w+=X.length}}}),g.stderr.on("data",C=>{d+=C.toString()});let pe=setInterval(()=>{(x.inputTokens>0||x.outputTokens>0)&&Fo(r,x,W,"greatest").catch(()=>{})},3e4);function ve(){clearInterval(ie),clearInterval(B),clearInterval(D),clearInterval(pe),A(!0)}let q=setTimeout(()=>{ve(),g.kill("SIGTERM"),m(new Error("Claude CLI timed out after 20 minutes"))},12e5);g.on("exit",C=>{clearTimeout(q),ve();let z=Math.round((Date.now()-i)/1e3);Pe(r,"validating",z,"Validating plan...",w,T),Fo(r,x,W,"greatest").catch(()=>{}),C!==0?m(new Error(`Claude CLI failed (exit ${C}): ${d.substring(0,300)}`)):l(P||h)}),g.on("error",C=>{clearTimeout(q),ve(),m(C)})})}function sn(e,o){if(o)switch(e){case"anthropic":return o.anthropicApiKey;case"openai":return o.openaiApiKey;case"google":return o.googleApiKey;case"ollama":return o.ollamaBaseUrl||"http://localhost:11434";default:return}}function an(e,o,n){switch(n){case"bitbucket":return`https://x-token-auth:${o}@bitbucket.org/${e}.git`;case"gitlab":return`https://oauth2:${o}@gitlab.com/${e}.git`;default:return`https://x-access-token:${o}@github.com/${e}.git`}}async function ln(e,o,n,t){let r=p.cyan(t.slice(0,8)),i=`/tmp/workermill-planning-${t.slice(0,8)}-${Date.now()}`;try{let s=an(e,o,n);return console.log(`${k()} ${r} ${p.dim("Cloning repo for planner...")}`),uo(`git clone --depth 1 --single-branch "${s}" "${i}"`,{stdio:"ignore",timeout:6e4}),console.log(`${k()} ${r} ${p.green("\u2713")} Repo cloned to ${p.dim(i)}`),i}catch(s){let a=s instanceof Error?s.message:String(s);console.error(`${k()} ${r} ${p.yellow("\u26A0")} Clone failed, planner will run without repo access: ${a.substring(0,100)}`);try{uo(`rm -rf "${i}"`,{stdio:"ignore"})}catch{}return null}}async function Wo(e,o,n){let t=p.cyan(e.id.slice(0,8));console.log(`${k()} ${t} Fetching planning prompt...`),await _(e.id,`${E} Fetching planning prompt from cloud API...`);let r=await M.get("/api/agent/planning-prompt",{params:{taskId:e.id}}),{prompt:i,model:s,provider:a,maxStories:l}=r.data,m=typeof l=="number"?l:8,$=s,g=a||"anthropic",h=g==="anthropic",P=process.env.CLAUDE_CLI_PATH||se()||"claude",d={...process.env};delete d.CLAUDE_CODE_OAUTH_TOKEN;let w=sn(g,n),T=Date.now(),x=e.description||e.summary,W=null;if(e.githubRepo){let D=e.scmProvider||"github",B=D==="bitbucket"?o.bitbucketToken:D==="gitlab"?o.gitlabToken:o.githubToken;B?W=await ln(e.githubRepo,B,D,e.id):console.log(`${k()} ${t} ${p.yellow("\u26A0")} No SCM token for ${D}, planner will run without repo access`)}let c=i,A=null,b=0,G=[],y=0,F=await So();F||(console.log(`${k()} ${t} ${p.yellow("\u26A0")} Could not fetch critic config \u2014 critic validation will be skipped`),await _(e.id,`${E} \u26A0\uFE0F Could not fetch critic config from API \u2014 critic validation will be skipped`));try{for(let B=1;B<=ue;B++){let j=ue>1?` (attempt ${B}/${ue})`:"",ie=`${g}/${$}`;B>1?(console.log(`${k()} ${t} Running planner${j} ${p.dim(`(${p.yellow(ie)})`)}`),await _(e.id,`${E} Re-planning${j} using ${ie}`)):(console.log(`${k()} ${t} Running planner ${p.dim(`(${p.yellow(ie)})`)}`),await _(e.id,`${E} Starting planning agent using ${ie}`));let J;try{if(h)J=await rn(P,$,c,d,e.id,T,W||void 0);else{if(!w)throw new Error(`No API key available for provider "${g}". Configure it in Settings > Integrations.`);let R=Math.round((Date.now()-T)/1e3);await Pe(e.id,"generating_plan",R,"Generating plan via AI SDK...",0,0),J=await Oo({provider:g,model:$,apiKey:w,prompt:c,workingDir:W||void 0,enableTools:!!W,maxSteps:10});let v=Math.round((Date.now()-T)/1e3);await Pe(e.id,"validating",v,"Validating plan...",J.length,0)}}catch(R){let v=Math.round((Date.now()-T)/1e3),Z=R instanceof Error?R.message:String(R);return console.error(`${k()} ${t} ${p.red("\u2717")} Failed after ${v}s: ${Z.substring(0,100)}`),await _(e.id,`${E} Planning failed after ${Be(v)}: ${Z.substring(0,200)}`,"error","error"),!1}let pe=Math.round((Date.now()-T)/1e3),ve=h?"Claude CLI":`${g} API`;console.log(`${k()} ${t} ${p.green("\u2713")} ${ve} done ${p.dim(`(${pe}s, ${J.length} chars)`)}`);let q;try{q=To(J)}catch(R){let v=R instanceof Error?R.message:String(R);return console.error(`${k()} ${t} ${p.red("\u2717")} Plan parse failed: ${v.substring(0,100)}`),await _(e.id,`${E} Failed to parse execution plan from Claude output: ${v.substring(0,200)}`,"error","error"),await cn(e.id,J,o.agentId,t,pe)}let{truncatedCount:C,details:z}=Co(q);if(C>0){y+=C;let R=`${E} File cap applied: ${C} stories truncated to max ${F?.maxTargetFiles??15} targetFiles`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${R}`),await _(e.id,R);for(let v of z)console.log(`${k()} ${t} ${p.dim(v)}`)}let{droppedCount:le,details:X}=Ro(q,m);if(le>0){let R=`${E} Story cap applied: ${le} stories dropped (max ${m})`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${R}`),await _(e.id,R);for(let v of X)console.log(`${k()} ${t} ${p.dim(v)}`)}let{resolvedCount:L,details:Q}=_o(q);if(L>0){let R=`${E} File overlap resolved: ${L} shared file(s) de-duped across stories`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${R}`),await _(e.id,R);for(let v of Q)console.log(`${k()} ${t} ${p.dim(v)}`)}console.log(`${k()} ${t} Plan: ${p.bold(q.stories.length)} stories (max ${m})`),await _(e.id,`${E} Plan generated: ${q.stories.length} stories (${Be(pe)}). Running critic validation...`);let I=await Lo(P,$,x,q,d,t,g,w,e.id);if(I&&I.score>b?(A=q,b=I.score):!I&&!A&&(A=q),I&&G.push({iteration:B,score:I.score,approved:I.approved||I.score>=ne,risks:I.risks,suggestions:I.suggestions,filesCapApplied:C>0?C:void 0}),!I){let R=`${E} \u26A0\uFE0F CRITIC BYPASSED \u2014 Critic validation failed (timeout/parse error). Posting plan WITHOUT quality gate.`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${R}`),await _(e.id,R,"error","warning");let v=Date.now()-T;return await co(e.id,q,o.agentId,t,pe,void 0,void 0,G,y,v,B)}if(I.approved||I.score>=ne){let R=`${E} Critic approved (score: ${I.score}/100)`;if(console.log(`${k()} ${t} ${p.green("\u2713")} ${R}`),await _(e.id,R),I.risks.length>0){let Z=`${E} Critic risks (non-blocking): ${I.risks.join("; ")}`;console.log(`${k()} ${t} ${p.dim(Z)}`),await _(e.id,Z)}let v=Date.now()-T;return await co(e.id,q,o.agentId,t,pe,I.score,I.risks,G,y,v,B)}if(B<ue){let R=xo(I);c=i+`
8
+ ... (truncated, ${r.length-i} more lines)`:t}catch(n){return`Error reading file: ${n instanceof Error?n.message:String(n)}`}}}),grep:lo({description:"Search for a pattern in files. Returns matching lines with file paths and line numbers.",inputSchema:en,execute:async o=>{try{let n=o.glob?`--include='${o.glob}'`:"";return co(`grep -rn ${n} --exclude-dir=node_modules --exclude-dir=.git '${o.pattern.replace(/'/g,"'\\''")}' . 2>/dev/null | head -100`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No matches found"}catch{return"No matches found"}}})}}async function Fo(e){let{provider:o,model:n,apiKey:t,prompt:r,systemPrompt:i,workingDir:s,maxTokens:a=16384,temperature:l=.7,timeoutMs:m=6e5,maxSteps:$=15,enableTools:g=!0}=e,h=Xt(o,n,t),P=g&&s?on(s):void 0,d=new AbortController,w=setTimeout(()=>d.abort(),m);try{return(await Vt({model:h,prompt:r,system:i,maxOutputTokens:a,temperature:l,tools:P,stopWhen:P?zt($):void 0,abortSignal:d.signal})).text}finally{clearTimeout(w)}}function nn(e,o){let n=[e.usage,e.message?.usage,e.result?.usage];for(let t of n)if(t&&typeof t=="object"){let r=t;typeof r.input_tokens=="number"&&(o.inputTokens=Math.max(o.inputTokens,r.input_tokens)),typeof r.output_tokens=="number"&&(o.outputTokens=Math.max(o.outputTokens,r.output_tokens)),typeof r.cache_creation_input_tokens=="number"&&(o.cacheCreationTokens=Math.max(o.cacheCreationTokens,r.cache_creation_input_tokens)),typeof r.cache_read_input_tokens=="number"&&(o.cacheReadTokens=Math.max(o.cacheReadTokens,r.cache_read_input_tokens))}}async function Do(e,o,n,t){if(!(o.inputTokens===0&&o.outputTokens===0))try{await M.post(`/api/tasks/${e}/usage/partial`,{inputTokens:o.inputTokens,outputTokens:o.outputTokens,cacheCreationTokens:o.cacheCreationTokens,cacheReadTokens:o.cacheReadTokens,model:n,mode:t})}catch{}}var ue=3;function k(){return p.dim(new Date().toLocaleTimeString())}var Re=[],ye=null;async function Wo(){for(;Re.length>0;){let e=Re.splice(0,50);try{await M.post("/api/control-center/logs/batch",{entries:e},{timeout:5e3})}catch{}}}async function R(e,o,n="system",t="info"){Re.length>=200&&Re.shift(),Re.push({taskId:e,message:o,type:n,severity:t}),ye||(ye=Wo().finally(()=>{ye=null}))}async function rn(){ye&&await ye,Re.length>0&&(ye=Wo().finally(()=>{ye=null}),await ye)}async function Pe(e,o,n,t,r,i){try{await M.post("/api/agent/planning-progress",{taskId:e,phase:o,elapsedSeconds:n,detail:t,charsGenerated:r,toolCallCount:i})}catch{}}var E="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Be(e){let o=Math.floor(e/60),n=e%60;return o>0?`${o}m ${n}s`:`${n}s`}function Uo(e,o){switch(e){case"initializing":return`${E} Starting planning agent...`;case"reading_repo":return`${E} Reading repository structure...`;case"analyzing":return`${E} Analyzing requirements...`;case"generating_plan":return`${E} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Be(o)} elapsed)`;case"validating":return`${E} Validating plan...`;case"complete":return`${E} Planning complete`}}function sn(e,o,n,t,r,i,s){let a=p.cyan(r.slice(0,8));return new Promise((l,m)=>{let g=tn(e,["--print","--verbose","--output-format","stream-json","--model",o,"--permission-mode","bypassPermissions"],{cwd:s,env:t,stdio:["pipe","pipe","pipe"]});g.stdin.write(n),g.stdin.end();let h="",P="",d="",w=0,T=0,x={inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0},W=o,c="";function A(C=!1){if(!c)return;let q=c.split(`
9
+ `),le=C?"":q.pop()||"";for(let X of q)if(X.trim()){R(r,`${E} ${X}`,"output");let L=X.trim().length>160?X.trim().substring(0,160)+"\u2026":X.trim();console.log(`${k()} ${a} ${p.dim("\u{1F4AD}")} ${p.dim(L)}`)}c=le}let b="initializing",K=!1,y={started:!0,reading:!1,analyzing:!1,generating:!1};function F(C){if(C===b)return;b=C;let q=Math.round((Date.now()-i)/1e3),le=Uo(C,q);R(r,le),console.log(`${k()} ${a} ${p.dim(le)}`)}let D=setInterval(()=>A(),500),B=setInterval(()=>{let C=Math.round((Date.now()-i)/1e3);Pe(r,b,C,Uo(b,C),w,T)},2e3),j=0,ie=setInterval(()=>{let C=Math.round((Date.now()-i)/1e3);if(b==="initializing"&&C>=5?F("reading_repo"):b==="reading_repo"&&C>=15&&!K&&F("analyzing"),b==="generating_plan"&&C-j>=30){j=C;let q=`${E} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Be(C)} elapsed)`;R(r,q),console.log(`${k()} ${a} ${p.dim(q)}`)}},5e3),J="";g.stdout.on("data",C=>{J+=C.toString();let q=J.split(`
10
+ `);J=q.pop()||"";for(let le of q){let X=le.trim();if(X)try{let L=JSON.parse(X);if(L.type==="assistant"&&L.message?.content){let Q=L.message.content;if(Array.isArray(Q))for(let I of Q)I.type==="text"&&I.text?(h+=I.text,w+=I.text.length,c+=I.text,K||(K=!0,T>0&&!y.analyzing&&(F("analyzing"),y.analyzing=!0)),w>500&&!y.generating&&(F("generating_plan"),y.generating=!0,j=Math.round((Date.now()-i)/1e3))):I.type==="tool_use"&&(T++,y.reading||(F("reading_repo"),y.reading=!0));else typeof Q=="string"&&Q&&(h+=Q,w+=Q.length,c+=Q)}else L.type==="content_block_delta"&&L.delta?.text?(h+=L.delta.text,w+=L.delta.text.length,c+=L.delta.text,K||(K=!0,T>0&&!y.analyzing&&(F("analyzing"),y.analyzing=!0)),w>500&&!y.generating&&(F("generating_plan"),y.generating=!0,j=Math.round((Date.now()-i)/1e3))):L.type==="content_block_start"&&L.content_block?.type==="tool_use"?(T++,y.reading||(F("reading_repo"),y.reading=!0)):L.type==="result"&&L.result&&(P=typeof L.result=="string"?L.result:"");if(nn(L,x),L.type==="result"&&L.total_cost_usd!==void 0&&L.modelUsage&&typeof L.modelUsage=="object"){let Q=Object.keys(L.modelUsage);Q.length>0&&(W=Q[0])}}catch{h+=X+`
11
+ `,w+=X.length}}}),g.stderr.on("data",C=>{d+=C.toString()});let pe=setInterval(()=>{(x.inputTokens>0||x.outputTokens>0)&&Do(r,x,W,"greatest").catch(()=>{})},3e4);function ve(){clearInterval(ie),clearInterval(B),clearInterval(D),clearInterval(pe),A(!0)}let z=setTimeout(()=>{ve(),g.kill("SIGTERM"),m(new Error("Claude CLI timed out after 20 minutes"))},12e5);g.on("exit",C=>{clearTimeout(z),ve();let q=Math.round((Date.now()-i)/1e3);Pe(r,"validating",q,"Validating plan...",w,T),Do(r,x,W,"greatest").catch(()=>{}),C!==0?m(new Error(`Claude CLI failed (exit ${C}): ${d.substring(0,300)}`)):l(P||h)}),g.on("error",C=>{clearTimeout(z),ve(),m(C)})})}function an(e,o){if(o)switch(e){case"anthropic":return o.anthropicApiKey;case"openai":return o.openaiApiKey;case"google":return o.googleApiKey;case"ollama":return o.ollamaBaseUrl||"http://localhost:11434";default:return}}function ln(e,o,n){switch(n){case"bitbucket":return`https://x-token-auth:${o}@bitbucket.org/${e}.git`;case"gitlab":return`https://oauth2:${o}@gitlab.com/${e}.git`;default:return`https://x-access-token:${o}@github.com/${e}.git`}}async function cn(e,o,n,t){let r=p.cyan(t.slice(0,8)),i=`/tmp/workermill-planning-${t.slice(0,8)}-${Date.now()}`;try{let s=ln(e,o,n);return console.log(`${k()} ${r} ${p.dim("Cloning repo for planner...")}`),go(`git clone --depth 1 --single-branch "${s}" "${i}"`,{stdio:"ignore",timeout:6e4}),console.log(`${k()} ${r} ${p.green("\u2713")} Repo cloned to ${p.dim(i)}`),i}catch(s){let a=s instanceof Error?s.message:String(s);console.error(`${k()} ${r} ${p.yellow("\u26A0")} Clone failed, planner will run without repo access: ${a.substring(0,100)}`);try{go(`rm -rf "${i}"`,{stdio:"ignore"})}catch{}return null}}async function Ko(e,o,n){let t=p.cyan(e.id.slice(0,8));console.log(`${k()} ${t} Fetching planning prompt...`),await R(e.id,`${E} Fetching planning prompt from cloud API...`);let r=await M.get("/api/agent/planning-prompt",{params:{taskId:e.id}}),{prompt:i,model:s,provider:a,maxStories:l}=r.data,m=typeof l=="number"?l:8,$=s,g=a||"anthropic",h=g==="anthropic",P=process.env.CLAUDE_CLI_PATH||se()||"claude",d={...process.env};delete d.CLAUDE_CODE_OAUTH_TOKEN;let w=an(g,n),T=Date.now(),x=e.description||e.summary,W=null;if(e.githubRepo){let D=e.scmProvider||"github",B=D==="bitbucket"?o.bitbucketToken:D==="gitlab"?o.gitlabToken:o.githubToken;B?W=await cn(e.githubRepo,B,D,e.id):console.log(`${k()} ${t} ${p.yellow("\u26A0")} No SCM token for ${D}, planner will run without repo access`)}let c=i,A=null,b=0,K=[],y=0,F=await To();F||(console.log(`${k()} ${t} ${p.yellow("\u26A0")} Could not fetch critic config \u2014 critic validation will be skipped`),await R(e.id,`${E} \u26A0\uFE0F Could not fetch critic config from API \u2014 critic validation will be skipped`));try{for(let B=1;B<=ue;B++){let j=ue>1?` (attempt ${B}/${ue})`:"",ie=`${g}/${$}`;B>1?(console.log(`${k()} ${t} Running planner${j} ${p.dim(`(${p.yellow(ie)})`)}`),await R(e.id,`${E} Re-planning${j} using ${ie}`)):(console.log(`${k()} ${t} Running planner ${p.dim(`(${p.yellow(ie)})`)}`),await R(e.id,`${E} Starting planning agent using ${ie}`));let J;try{if(h)J=await sn(P,$,c,d,e.id,T,W||void 0);else{if(!w)throw new Error(`No API key available for provider "${g}". Configure it in Settings > Integrations.`);let _=Math.round((Date.now()-T)/1e3);await Pe(e.id,"generating_plan",_,"Generating plan via AI SDK...",0,0),J=await Fo({provider:g,model:$,apiKey:w,prompt:c,workingDir:W||void 0,enableTools:!!W,maxSteps:10});let v=Math.round((Date.now()-T)/1e3);await Pe(e.id,"validating",v,"Validating plan...",J.length,0)}}catch(_){let v=Math.round((Date.now()-T)/1e3),Z=_ instanceof Error?_.message:String(_);return console.error(`${k()} ${t} ${p.red("\u2717")} Failed after ${v}s: ${Z.substring(0,100)}`),await R(e.id,`${E} Planning failed after ${Be(v)}: ${Z.substring(0,200)}`,"error","error"),!1}let pe=Math.round((Date.now()-T)/1e3),ve=h?"Claude CLI":`${g} API`;console.log(`${k()} ${t} ${p.green("\u2713")} ${ve} done ${p.dim(`(${pe}s, ${J.length} chars)`)}`);let z;try{z=Co(J)}catch(_){let v=_ instanceof Error?_.message:String(_);return console.error(`${k()} ${t} ${p.red("\u2717")} Plan parse failed: ${v.substring(0,100)}`),await R(e.id,`${E} Failed to parse execution plan from Claude output: ${v.substring(0,200)}`,"error","error"),await dn(e.id,J,o.agentId,t,pe)}let{truncatedCount:C,details:q}=_o(z);if(C>0){y+=C;let _=`${E} File cap applied: ${C} stories truncated to max ${F?.maxTargetFiles??15} targetFiles`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${_}`),await R(e.id,_);for(let v of q)console.log(`${k()} ${t} ${p.dim(v)}`)}let{droppedCount:le,details:X}=Ro(z,m);if(le>0){let _=`${E} Story cap applied: ${le} stories dropped (max ${m})`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${_}`),await R(e.id,_);for(let v of X)console.log(`${k()} ${t} ${p.dim(v)}`)}let{resolvedCount:L,details:Q}=Po(z);if(L>0){let _=`${E} File overlap resolved: ${L} shared file(s) de-duped across stories`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${_}`),await R(e.id,_);for(let v of Q)console.log(`${k()} ${t} ${p.dim(v)}`)}console.log(`${k()} ${t} Plan: ${p.bold(z.stories.length)} stories (max ${m})`),await R(e.id,`${E} Plan generated: ${z.stories.length} stories (${Be(pe)}). Running critic validation...`);let I=await No(P,$,x,z,d,t,g,w,e.id);if(I&&I.score>b?(A=z,b=I.score):!I&&!A&&(A=z),I&&K.push({iteration:B,score:I.score,approved:I.approved||I.score>=ne,risks:I.risks,suggestions:I.suggestions,filesCapApplied:C>0?C:void 0}),!I){let _=`${E} \u26A0\uFE0F CRITIC BYPASSED \u2014 Critic validation failed (timeout/parse error). Posting plan WITHOUT quality gate.`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${_}`),await R(e.id,_,"error","warning");let v=Date.now()-T;return await uo(e.id,z,o.agentId,t,pe,void 0,void 0,K,y,v,B)}if(I.approved||I.score>=ne){let _=`${E} Critic approved (score: ${I.score}/100)`;if(console.log(`${k()} ${t} ${p.green("\u2713")} ${_}`),await R(e.id,_),I.risks.length>0){let Z=`${E} Critic risks (non-blocking): ${I.risks.join("; ")}`;console.log(`${k()} ${t} ${p.dim(Z)}`),await R(e.id,Z)}let v=Date.now()-T;return await uo(e.id,z,o.agentId,t,pe,I.score,I.risks,K,y,v,B)}if(B<ue){let _=vo(I);c=i+`
12
12
 
13
- `+R;let v=`${E} Critic rejected (score: ${I.score}/100, threshold: ${ne}). Re-planning with feedback...`;if(console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${v}`),await _(e.id,v),I.risks.length>0){let Z=`${E} Critic risks: ${I.risks.join("; ")}`;console.log(`${k()} ${t} ${p.dim(Z)}`),await _(e.id,Z)}if(I.suggestions&&I.suggestions.length>0){let Z=`${E} Critic suggestions: ${I.suggestions.join("; ")}`;console.log(`${k()} ${t} ${p.dim(Z)}`),await _(e.id,Z)}}else{let R=`${E} Critic rejected after ${ue} iterations (best score: ${b}/100, threshold: ${ne})`;if(console.error(`${k()} ${t} ${p.red("\u2717")} ${R}`),await _(e.id,R,"error","error"),I.risks.length>0){let v=`${E} Final risks: ${I.risks.join("; ")}`;console.error(`${k()} ${t} ${v}`),await _(e.id,v,"error","error")}if(I.suggestions&&I.suggestions.length>0){let v=`${E} Suggestions: ${I.suggestions.join("; ")}`;console.error(`${k()} ${t} ${v}`),await _(e.id,v,"error","error")}}}let D=50;if(A&&b>=D){let B=Math.round((Date.now()-T)/1e3),j=`${E} Best-plan fallback: posting plan with score ${b}/100 (below ${ne} threshold, above ${D} minimum)`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${j}`),await _(e.id,j);let ie=Date.now()-T;if(await co(e.id,A,o.agentId,t,B,b,[`Best-plan fallback: critic rejected after ${ue} iterations`],G,y,ie,ue))return!0;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${E} Fallback post rejected by server, reporting plan-failed`),await _(e.id,`${E} Fallback plan rejected by server \u2014 reporting failure`)}try{let B=A&&b>=D?`Best-plan fallback rejected by server after ${ue} iterations (best score: ${b}/100)`:`Critic rejected after ${ue} iterations (best score: ${b}/100, threshold: ${ne}, fallback minimum: ${D})`;await M.post("/api/agent/plan-failed",{taskId:e.id,agentId:o.agentId,reason:B,criticHistory:G})}catch{}return!1}finally{if(await nn(),W)try{uo(`rm -rf "${W}"`,{stdio:"ignore"})}catch{}}}async function co(e,o,n,t,r,i,s,a,l,m,$){let g=Po(o);try{let P=(await M.post("/api/agent/plan-result",{taskId:e,rawOutput:g,agentId:n,criticScore:i,criticRisks:s,criticHistory:a,criticIterations:$,fileCapTruncations:l,planningDurationMs:m})).data.storyCount;return console.log(`${k()} ${t} ${p.green("\u2713")} Plan validated: ${p.bold(P)} stories \u2192 ${p.green("queued")}`),await _(e,`${E} Plan validated: ${P} stories. Task queued for execution.`),await Pe(e,"complete",r,"Planning complete",0,0),!0}catch(h){let P=h,d=P.response?.data?.error||P.response?.data?.detail||String(h),w=P.response?.status?` (${P.response.status})`:"";return console.error(`${k()} ${t} ${p.red("\u2717")} Server validation failed${w}: ${d.substring(0,100)}`),await _(e,`${E} Server-side plan validation failed${w}: ${d.substring(0,200)}`,"error","error"),!1}}async function cn(e,o,n,t,r){try{let s=(await M.post("/api/agent/plan-result",{taskId:e,rawOutput:o,agentId:n})).data.storyCount;return console.log(`${k()} ${t} ${p.green("\u2713")} Plan validated (server-side): ${p.bold(s)} stories \u2192 ${p.green("queued")}`),await _(e,`${E} Plan validated: ${s} stories. Task queued for execution.`),await Pe(e,"complete",r,"Planning complete",0,0),!0}catch(i){let a=i.response?.data?.detail||String(i);return console.error(`${k()} ${t} ${p.red("\u2717")} Validation failed: ${a.substring(0,100)}`),await _(e,`${E} Plan validation failed: ${a.substring(0,200)}`,"error","error"),!1}}import f from"chalk";import{spawn as Ko,execSync as Ve}from"child_process";import*as je from"path";import*as ae from"fs";import*as qe from"os";function N(){return f.dim(new Date().toLocaleTimeString())}var Y=new Map;function dn(){if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return ae.readFileSync("/proc/version","utf-8").toLowerCase().includes("microsoft")}catch{return!1}}var ze=dn(),Bo=ze||process.platform==="darwin"||process.platform==="win32";function jo(){if(!ze)return null;try{let e=Ve("hostname -I",{encoding:"utf-8"}).trim().split(/\s+/)[0];if(e&&/^\d+\.\d+\.\d+\.\d+$/.test(e))return e}catch{}return null}function Vo(e){if(!ze)return e;let o=e.match(/^\/mnt\/([a-zA-Z])\/(.*)$/);return o?`${o[1].toUpperCase()}:/${o[2]}`:e}function qo(){let e=je.join(qe.homedir(),".claude");if(ae.existsSync(e))return e;if(ze){let o="/mnt/c/Users";if(ae.existsSync(o))try{for(let n of ae.readdirSync(o)){if(["Public","Default","Default User","All Users"].includes(n))continue;let t=je.join(o,n,".claude");if(ae.existsSync(t))return t}}catch{}}return null}var Go=0;function zo(e){let o=e.match(/^(\d+\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)/);return o?o[1]:null}function un(e){let o=e.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/);return o?o[1]:"us-east-1"}function Ho(e){if(Date.now()<Go)return!0;let o=un(e);try{return Ve(`aws ecr get-login-password --region ${o} | docker login --username AWS --password-stdin ${e}`,{stdio:"pipe",timeout:3e4}),Go=Date.now()+660*60*1e3,!0}catch{return!1}}function Yo(e,o,n){if(n?.scmToken)return n.scmToken;switch(e){case"bitbucket":return o.bitbucketToken;case"gitlab":return o.gitlabToken;default:return o.githubToken}}function gn(e){let o=e.jiraFields;if(!o)return!1;let n=o.labels;if(Array.isArray(n)&&n.some(i=>typeof i=="string"&&i.toLowerCase()==="self-review"))return!0;let r=o.issue?.labels;return!!(Array.isArray(r)&&r.some(i=>typeof i=="string"?i.toLowerCase()==="self-review":i&&typeof i=="object"&&"name"in i?(i.name||"").toLowerCase()==="self-review":!1))}async function Jo(e,o,n,t){let r=f.cyan(e.id.slice(0,8));if(Y.has(e.id)){console.log(`${N()} ${r} ${f.dim("Already running, skipping")}`);return}let i=`workermill-${e.id.slice(0,8)}`,a=["run","--rm",...!o.workerImage.includes("/")?[]:["--pull","always"],"--name",i],l=Math.round(qe.totalmem()/(1024*1024*1024));if(l<=16?a.push("--memory","6g","--memory-swap","10g","--cpus","2"):(l<=32,a.push("--memory","6g","--memory-swap","12g","--cpus","4")),Bo){let y=jo();a.push(`--add-host=host.docker.internal:${y||"host-gateway"}`)}else a.push("--network","host");let m=e.workerProvider||"anthropic",$=qo();if(!$&&m==="anthropic"){console.error(`${N()} ${r} ${f.red("\u2717")} Claude credentials not found. Run 'claude' and complete the sign-in flow.`);return}if($){let y=je.join($,".credentials.json");try{ae.chmodSync(y,438)}catch{}let F=Vo($);a.push("-v",`${F}:/home/worker/.claude`)}else console.log(`${N()} ${r} ${f.dim("Skipping Claude mount (non-Anthropic worker)")}`);let g=o.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),h=e.scmProvider||"github",P=Yo(h,o,t),d=t?.githubToken||o.githubToken,w=h==="bitbucket"&&t?.scmToken||o.bitbucketToken,T=h==="gitlab"&&t?.scmToken||o.gitlabToken,x={NODE_OPTIONS:"--max-old-space-size=3072",EPIC_MODE:"true",EXECUTION_MODE:"local",TASK_ID:e.id,ORG_ID:e.orgId||"",JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",TASK_SUMMARY:e.summary||"",TASK_DESCRIPTION:e.description||"",WORKER_PERSONA:e.workerPersona||"",RETRY_NUMBER:String(e.retryCount??0),TICKET_KEY:e.jiraIssueKey||"",API_BASE_URL:g,ORG_API_KEY:o.apiKey,SCM_PROVIDER:h,SCM_TOKEN:P,SCM_BASE_URL:t?.scmBaseUrl||"",GITHUB_TOKEN:d,GH_TOKEN:d,GITHUB_REVIEWER_TOKEN:t?.githubReviewerToken||"",BITBUCKET_TOKEN:w,BITBUCKET_USERNAME:t?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:T,TARGET_REPO:e.githubRepo||"",GITHUB_REPO:e.githubRepo||"",WORKER_MODEL:e.workerModel||String(n.defaultWorkerModel||""),CLAUDE_MODEL:e.workerModel||String(n.defaultWorkerModel||""),JIRA_BASE_URL:t?.jiraBaseUrl||"",JIRA_EMAIL:t?.jiraEmail||"",JIRA_API_TOKEN:t?.jiraApiToken||"",TICKET_SYSTEM:t?.issueTrackerProvider||"jira",LINEAR_API_KEY:t?.linearApiKey||"",AWS_ACCESS_KEY_ID:t?.customerAwsAccessKeyId||"",AWS_SECRET_ACCESS_KEY:t?.customerAwsSecretAccessKey||"",AWS_DEFAULT_REGION:t?.customerAwsRegion||"",AWS_REGION:t?.customerAwsRegion||"",CUSTOMER_AWS_ROLE_ARN:t?.customerAwsRoleArn||"",CUSTOMER_AWS_EXTERNAL_ID:t?.customerAwsExternalId||"",CUSTOMER_AWS_REGION:t?.customerAwsRegion||"",MANAGER_PROVIDER:t?.managerProvider||"anthropic",MANAGER_MODEL:t?.managerModelId||"",BITBUCKET_EMAIL:t?.bitbucketEmail||"",DEPLOYMENT_ENABLED:e.deploymentEnabled||e.parentTaskId?"true":"false",PRD_CHILD_TASK:e.parentTaskId?"true":"false",IMPROVEMENT_ENABLED:e.improvementEnabled?"true":"false",QUALITY_GATE_BYPASS:e.qualityGateBypass?"true":"false",STANDARD_SDK_MODE:e.standardSdkMode?"true":"false",MAX_REVIEW_REVISIONS:String(n.maxReviewRevisions??3),CODEBASE_INDEXING_ENABLED:n.codebaseIndexingEnabled===!0?"true":"false",EXISTING_PR_URL:e.githubPrUrl||"",EXISTING_PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",PARENT_TASK_ID:e.parentTaskId||e.id,PARENT_JIRA_KEY:e.jiraIssueKey&&/-S\d+$/.test(e.jiraIssueKey)&&e.jiraFields?.parentJiraKey||"",TARGET_BRANCH:e.jiraFields?.targetBranch||"",STORY_BRANCH:e.jiraFields?.storyBranch||"",TASK_NOTES:e.taskNotes||"",TARGET_FILES:JSON.stringify(e.jiraFields?.targetFiles||[]),REFERENCE_FILES:JSON.stringify(e.jiraFields?.referenceFiles||[]),PIPELINE_VERSION:e.jiraFields?.pipelineVersion||e.pipelineVersion||"",V2_STEP_INPUT:e.jiraFields?.v2StepInput?JSON.stringify(e.jiraFields.v2StepInput):"",EXECUTION_MODE_SETTING:e.jiraFields?.executionMode||"autonomous",ANTHROPIC_API_KEY:$?"":t?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",WORKER_PROVIDER:e.workerProvider||"anthropic",OPENAI_API_KEY:t?.openaiApiKey||"",GOOGLE_API_KEY:t?.googleApiKey||"",GOOGLE_GENERATIVE_AI_API_KEY:t?.googleApiKey||"",OLLAMA_HOST:t?.ollamaBaseUrl||"",OLLAMA_CONTEXT_WINDOW:t?.ollamaContextWindow?String(t.ollamaContextWindow):"",VLLM_BASE_URL:t?.vllmBaseUrl||"",BLOCKER_MAX_AUTO_RETRIES:String(n.blockerMaxAutoRetries??3),BLOCKER_AUTO_RETRY_ENABLED:n.blockerAutoRetryEnabled!==!1?"true":"false",PUSH_AFTER_COMMIT:n.pushAfterCommit!==!1?"true":"false",GRACEFUL_SHUTDOWN_ENABLED:n.gracefulShutdownEnabled!==!1?"true":"false",MAX_PARALLEL_EXPERTS:String(n.maxParallelExperts??4),REVIEW_ENABLED:e.skipManagerReview===!1?"true":"false",SELF_REVIEW_ENABLED:gn(e)||n.selfReviewEnabled!==!1?"true":"false"};for(let[y,F]of Object.entries(x))F!==""&&a.push("-e",`${y}=${F}`);let W=o.workerImage,c=zo(W);c&&Ho(c),a.push(W);let A=e.skipManagerReview===!1;console.log(`${N()} ${r} ${f.dim("Starting container")} ${f.yellow(i)}`),console.log(`${N()} ${r} ${f.dim(` skipManagerReview=${e.skipManagerReview} \u2192 REVIEW_ENABLED=${A}`)}`),console.log(`${N()} ${r} ${f.dim(` model=${e.workerModel} repo=${e.githubRepo}`)}`),console.log(`${N()} ${r} ${f.dim(` totalRamGB=${l} docker args:`)} ${a.slice(0,10).join(" ")}`);let b=Ko("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!b.pid){console.error(`${N()} ${r} ${f.red("\u2717")} Failed to spawn container`);return}let G={taskId:e.id,containerName:i,process:b,startedAt:new Date,status:"running",resultEmitted:!1};Y.set(e.id,G),b.stdout?.on("data",y=>{let F=y.toString().split(`
14
- `).filter(D=>D.trim());for(let D of F)console.log(`${N()} ${r} ${f.dim(D)}`),D.includes("::result::")&&(G.resultEmitted=!0)}),b.stderr?.on("data",y=>{let F=y.toString().split(`
15
- `).filter(D=>D.trim());for(let D of F)console.log(`${N()} ${r} ${f.red(D)}`)}),b.on("exit",y=>{G.status=y===0?"completed":"failed";let F=Math.round((Date.now()-G.startedAt.getTime())/1e3),D=y===0?f.green("\u2713"):f.red("\u2717"),B=y===0?f.green("completed"):f.red(`failed (exit ${y})`);console.log(`${N()} ${r} ${D} Container ${B} ${f.dim(`(${F}s)`)}`),G.resultEmitted||(console.log(`${N()} ${r} ${f.yellow("\u26A0")} No ::result:: marker seen \u2014 posting fallback completion in 15s`),setTimeout(async()=>{try{let j=y===0?"completed":"failed",ie=y!==0?`Worker container exited with code ${y} without reporting completion`:void 0;(await(await fetch(`${o.apiUrl}/api/tasks/${e.id}/worker-complete`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":o.apiKey},body:JSON.stringify({exitCode:y??1,result:j,errorMessage:ie})})).json()).status==="ignored"?console.log(`${N()} ${r} ${f.dim("Fallback completion ignored (task already transitioned)")}`):console.log(`${N()} ${r} ${f.yellow("\u26A0")} Fallback completion applied: ${j}`)}catch(j){console.error(`${N()} ${r} ${f.red("\u2717")} Fallback completion failed:`,j instanceof Error?j.message:j)}},15e3)),setTimeout(()=>Y.delete(e.id),9e4)}),b.on("error",y=>{G.status="failed",console.error(`${N()} ${r} ${f.red("\u2717")} Container error: ${y.message}`)})}function go(){return Array.from(Y.values()).filter(e=>e.status==="running").length}function Xo(){return Array.from(Y.values()).filter(e=>e.status==="running").map(e=>e.taskId)}function Qo(e){let o=Y.get(e);if(!o||o.status!=="running")return;let n=f.cyan(e.slice(0,8));console.log(`${N()} ${n} ${f.red("\u25A0")} Stopping container (cancelled by dashboard)`);try{Ve(`docker stop ${o.containerName}`,{stdio:"ignore",timeout:15e3}),o.status="completed"}catch{}Y.delete(e)}async function Zo(){console.log(`${N()} ${f.dim(`Stopping ${Y.size} containers...`)}`);for(let[,e]of Y)if(e.status==="running")try{Ve(`docker stop ${e.containerName}`,{stdio:"ignore",timeout:15e3}),e.status="completed"}catch{}Y.clear()}async function et(e,o,n){let t=f.cyan(e.id.slice(0,8)),r=`manager-${e.id}`;if(Y.has(r))return;let i=`wm-manager-${e.id.slice(0,8)}-${Date.now()}`,s=qo(),a=["run","--rm","--name",i,"--memory=4g","--cpus=2"];if(Bo){let c=jo();a.push(`--add-host=host.docker.internal:${c||"host-gateway"}`)}else a.push("--network","host");if(s){let c=Vo(s);a.push("-v",`${c}:/home/worker/.claude`)}let l=o.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),m=e.scmProvider||"github",$=Yo(m,o,n),g=n?.githubToken||o.githubToken,h=m==="bitbucket"&&n?.scmToken||o.bitbucketToken,P=m==="gitlab"&&n?.scmToken||o.gitlabToken,d={NODE_OPTIONS:"--max-old-space-size=3072",TASK_ID:e.id,MANAGER_ACTION:e.managerAction,JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",GITHUB_REPO:e.githubRepo||"",PR_URL:e.githubPrUrl||"",PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",API_BASE_URL:l,ORG_API_KEY:o.apiKey,SCM_PROVIDER:m,SCM_TOKEN:$,GITHUB_TOKEN:g,GH_TOKEN:g,BITBUCKET_TOKEN:h,BITBUCKET_USERNAME:n?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:P,MANAGER_PROVIDER:n?.managerProvider||"anthropic",MANAGER_MODEL:n?.managerModelId||"",ANTHROPIC_API_KEY:s?"":n?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",OPENAI_API_KEY:n?.openaiApiKey||"",GOOGLE_API_KEY:n?.googleApiKey||"",JIRA_BASE_URL:n?.jiraBaseUrl||"",JIRA_EMAIL:n?.jiraEmail||"",JIRA_API_TOKEN:n?.jiraApiToken||"",TICKET_SYSTEM:n?.issueTrackerProvider||"jira",LINEAR_API_KEY:n?.linearApiKey||""};for(let[c,A]of Object.entries(d))A!==""&&a.push("-e",`${c}=${A}`);let w=o.workerImage,T=zo(w);T&&Ho(T),a.push("--entrypoint","/bin/bash"),a.push(w),a.push("/app/manager-entrypoint.sh"),console.log(`${N()} ${t} ${f.magenta("\u25C6 MANAGER")} Starting ${e.managerAction} container ${f.yellow(i)}`);let x=Ko("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!x.pid){console.error(`${N()} ${t} ${f.red("\u2717")} Failed to spawn manager container`);return}let W={taskId:r,containerName:i,process:x,startedAt:new Date,status:"running",resultEmitted:!1};Y.set(r,W),x.stdout?.on("data",c=>{let A=c.toString().split(`
16
- `).filter(b=>b.trim());for(let b of A)console.log(`${N()} ${t} ${f.magenta("MGR")} ${f.dim(b)}`)}),x.stderr?.on("data",c=>{let A=c.toString().split(`
17
- `).filter(b=>b.trim());for(let b of A)console.log(`${N()} ${t} ${f.magenta("MGR")} ${f.red(b)}`)}),x.on("exit",c=>{W.status=c===0?"completed":"failed";let A=Math.round((Date.now()-W.startedAt.getTime())/1e3),b=c===0?f.green("\u2713"):f.red("\u2717"),G=c===0?f.green("completed"):f.red(`failed (exit ${c})`);console.log(`${N()} ${t} ${f.magenta("MGR")} ${b} Manager ${e.managerAction} ${G} ${f.dim(`(${A}s)`)}`),setTimeout(()=>Y.delete(r),6e4)}),x.on("error",c=>{W.status="failed",console.error(`${N()} ${t} ${f.magenta("MGR")} ${f.red("\u2717")} Container error: ${c.message}`)})}import{spawn as ot}from"child_process";import Oe from"chalk";async function xe(){return console.log(Oe.cyan(" Updating @workermill/agent...")),new Promise(e=>{let o=ot("npm",["install","-g","@workermill/agent@latest"],{stdio:"inherit",shell:!0});o.on("error",n=>{console.error(Oe.red(` Update failed: ${n.message}`)),e(!1)}),o.on("close",n=>{n===0?(console.log(Oe.green(" Update successful.")),e(!0)):(console.error(Oe.red(` Update failed with exit code ${n}`)),e(!1))})})}function He(){console.log(Oe.cyan(" Restarting agent...")),ot(process.argv[0],process.argv.slice(1),{stdio:"inherit",detached:!0}).unref(),process.exit(0)}var Ae=new Set,ke=new Set,Ye=null,tt=!1,Je=!1;function U(){return S.dim(new Date().toLocaleTimeString())}async function pn(){if(Ye)return Ye;try{return Ye=(await M.get("/api/agent/config")).data,Ye}catch{return console.error(`${U()} ${S.red("\u2717")} Failed to fetch org config`),{}}}async function nt(e){if(!Je)try{let o=await M.get("/api/agent/poll",{params:{agentId:e.agentId}}),n=o.data.tasks;if(n.length===0)return;for(let r of n)r.status==="planning"&&!Ae.has(r.id)?await mn(r,e):r.status==="queued"&&await fn(r,e);let t=o.data.managerTasks;if(t&&t.length>0)for(let r of t)ke.has(r.id)||await hn(r,e)}catch(o){let n=o,t=Ae.size>0||go()>0||ke.size>0;n.response?.status===401?t||console.error(`${U()} ${S.red("\u2717")} Authentication failed. Check your API key.`):t||console.warn(`${U()} ${S.yellow("\u26A0")} Poll error: ${n.message||String(o)}`)}}async function mn(e,o){let n,t;try{let s=await M.post("/api/agent/claim",{taskId:e.id,agentId:o.agentId});if(!s.data.claimed)return;n=s.data.credentials,t=s.data.task}catch{return}let r=S.cyan(e.id.slice(0,8));if(t&&(t.retryCount??0)>0&&t.executionPlanV2!=null){console.log(),console.log(`${U()} ${S.magenta("\u25C6 RESUME")} ${r} ${e.summary.substring(0,60)}`),console.log(`${U()} ${r} Retry #${t.retryCount} with existing plan \u2014 skipping planning`),Ae.add(e.id);try{await M.post("/api/agent/resume-plan",{taskId:e.id,agentId:o.agentId}),console.log(`${U()} ${r} ${S.green("\u2713")} Resumed with existing plan \u2192 ${S.green("queued")}`)}catch(s){let a=s,l=a.response?.data?.error||a.message||String(s);console.error(`${U()} ${r} ${S.red("\u2717")} Resume failed: ${l}`)}Ae.delete(e.id);return}console.log(),console.log(`${U()} ${S.magenta("\u25C6 PLANNING")} ${r} ${e.summary.substring(0,60)}`),Ae.add(e.id),Wo(e,o,n).then(s=>{console.log(s?`${U()} ${S.green("\u2713")} Planning complete for ${r}`:`${U()} ${S.red("\u2717")} Planning failed for ${r}`)}).catch(s=>console.error(`${U()} ${S.red("\u2717")} Planning error for ${r}:`,s.message||s)).finally(()=>Ae.delete(e.id))}async function fn(e,o){if(go()>=o.maxWorkers)return;let t;try{if(t=(await M.post("/api/agent/claim",{taskId:e.id,agentId:o.agentId})).data,!t.claimed)return}catch{return}try{await M.post("/api/agent/started",{taskId:e.id,agentId:o.agentId})}catch{let m=S.cyan(e.id.slice(0,8));console.warn(`${U()} ${S.yellow("\u26A0")} Failed to report started for ${m}`)}let r=S.cyan(e.id.slice(0,8));console.log(),console.log(`${U()} ${S.blue("\u25B6 EXECUTING")} ${r} ${e.summary.substring(0,60)}`);let i=await pn(),s=t.task||{id:e.id,summary:e.summary,description:e.description,jiraIssueKey:e.jiraIssueKey,workerModel:e.workerModel,workerProvider:e.workerProvider,githubRepo:e.githubRepo,scmProvider:e.scmProvider,skipManagerReview:e.skipManagerReview,deploymentEnabled:e.deploymentEnabled,improvementEnabled:e.improvementEnabled,qualityGateBypass:e.qualityGateBypass,standardSdkMode:e.standardSdkMode,parentTaskId:e.parentTaskId,taskNotes:e.taskNotes,githubPrUrl:e.githubPrUrl,githubPrNumber:e.githubPrNumber,executionPlanV2:e.executionPlanV2,jiraFields:e.jiraFields||{}},a=t.credentials;Jo(s,o,i,a).catch(l=>console.error(`${U()} ${S.red("\u2717")} Spawn failed for ${r}:`,l.message||l))}async function hn(e,o){let n=S.cyan(e.id.slice(0,8));ke.add(e.id);try{let t=await M.post("/api/agent/claim-manager",{taskId:e.id,agentId:o.agentId,action:e.managerAction});if(!t.data.claimed){ke.delete(e.id);return}let r=t.data.task,i=t.data.credentials||{},s=e.managerAction==="analyze_logs"?S.yellow("LOG ANALYSIS"):S.yellow("PR REVIEW");console.log(),console.log(`${U()} ${S.magenta("\u25C6 MANAGER")} ${s} ${n} ${e.summary.substring(0,60)}`);let a={id:e.id,summary:r?.summary||e.summary,description:r?.description||e.description,jiraIssueKey:r?.jiraIssueKey||e.jiraIssueKey,githubRepo:r?.githubRepo||e.githubRepo,scmProvider:r?.scmProvider||e.scmProvider,githubPrUrl:r?.githubPrUrl||e.githubPrUrl,githubPrNumber:r?.githubPrNumber||e.githubPrNumber,managerAction:e.managerAction};et(a,o,i).then(()=>{console.log(`${U()} ${n} ${S.magenta("MGR")} ${S.green("\u2713")} Manager ${e.managerAction} dispatched`)}).catch(l=>{console.error(`${U()} ${n} ${S.magenta("MGR")} ${S.red("\u2717")} Manager spawn failed:`,l.message||l)}).finally(()=>ke.delete(e.id))}catch(t){ke.delete(e.id);let r=t;console.error(`${U()} ${n} ${S.red("\u2717")} Failed to claim manager task:`,r.message||String(t))}}var Xe=null,Qe=null;function rt(){Xe&&(clearInterval(Xe),Xe=null),Qe&&(clearInterval(Qe),Qe=null)}function it(e){console.log(` ${S.dim("Polling every")} ${e.pollIntervalMs/1e3}s ${S.dim("\xB7 waiting for tasks...")}`),nt(e),Xe=setInterval(()=>nt(e),e.pollIntervalMs)}function st(e){Qe=setInterval(async()=>{let o=Xo(),n=Array.from(Ae),t=Array.from(ke),r=[...o,...n,...t];try{let i=await M.post("/api/agent/heartbeat",{agentId:e.agentId,activeTasks:r,agentVersion:H}),s=i.data?.cancelledTasks;if(s&&s.length>0)for(let $ of s)Qo($);let{updateAvailable:a,updateRequired:l,latestVersion:m}=i.data??{};l&&!Je?(Je=!0,console.log(`${U()} ${S.red("\u26A0 Agent update required")} (current: ${H}, required: ${m})`),console.log(`${U()} ${S.yellow("Refusing new tasks until updated.")}`),await xe()?He():(console.log(`${U()} ${S.red("Auto-update failed.")} Run: npm install -g @workermill/agent@latest`),Je=!1)):a&&!tt&&!l&&(tt=!0,console.log(`${U()} ${S.yellow(`Update available: ${m}`)} (current: ${H}). Run: workermill-agent update`))}catch{}},e.heartbeatIntervalMs)}te();async function po(e){console.log(),console.log(K.bold.cyan(" WorkerMill Remote Agent")),console.log(K.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),Ge(e.apiUrl,e.apiKey);try{let o=await M.get("/api/agent/config"),n=o.data.maxConcurrentWorkers;n&&typeof n=="number"&&(e.maxWorkers=n),o.data.workerImageUrl&&(e.workerImage=o.data.workerImageUrl),console.log(` ${K.green("\u25CF")} Connected to ${K.cyan(e.apiUrl)}`),console.log(` ${K.dim("Agent:")} ${e.agentId}`),console.log(` ${K.dim("Workers:")} ${K.yellow(String(e.maxWorkers))} parallel`),console.log(` ${K.dim("Image:")} ${e.workerImage}`),console.log(` ${K.dim("SCM:")} ${o.data.scmProvider}`),console.log(` ${K.dim("Model:")} ${K.yellow(o.data.defaultWorkerModel)}`),console.log()}catch(o){let n=o;throw n.response?.status===401?new Error("Authentication failed. Check your API key."):new Error(`Failed to connect to WorkerMill API: ${n.message||String(o)}`)}try{let o=await M.post("/api/agent/register",{agentId:e.agentId,maxWorkers:e.maxWorkers,agentVersion:H}),{updateAvailable:n,updateRequired:t,latestVersion:r}=o.data;t?(console.log(K.red(` \u26A0 Agent update required (current: ${H}, required: ${r})`)),await xe()?He():console.log(K.yellow(" Auto-update failed. Run: npm install -g @workermill/agent@latest"))):n&&console.log(K.yellow(` Update available: ${r} (current: ${H}). Run: workermill-agent update`))}catch{}return it(e),st(e),console.log(K.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` ${K.green("\u25CF")} Agent is running. ${K.dim("Press Ctrl+C to stop.")}`),console.log(),async()=>{console.log(),console.log(K.dim(" Shutting down...")),rt();try{await M.post("/api/agent/deregister",{agentId:e.agentId})}catch{}await Zo(),console.log(` ${K.red("\u25CF")} Agent stopped.`)}}var $n=typeof process<"u"&&process.argv[1]&&(process.argv[1].endsWith("/index.ts")||process.argv[1].endsWith("/index.js"));if($n){try{await import("dotenv/config")}catch{}let{loadConfig:e,validatePrerequisites:o}=await Promise.resolve().then(()=>(te(),yo)),n=e();o(),console.log(K.dim(" Prerequisites validated."));let t=await po(n);process.on("SIGINT",async()=>{await t(),process.exit(0)}),process.on("SIGTERM",async()=>{await t(),process.exit(0)})}async function mo(e){In(oe())||(console.log(O.red("No configuration found.")),console.log(`Run ${O.cyan("workermill-agent setup")} first.`),process.exit(1));let o=fe(),t=oo(o.workerImage).filter(d=>!d.ok),r=t.find(d=>d.name==="Worker image"),i=new Set(["Claude CLI","Claude auth"]),s=t.filter(d=>d.name!=="Worker image"&&!i.has(d.name)),a=t.filter(d=>i.has(d.name));if(s.length>0){console.log(O.red("Prerequisites check failed:"));for(let d of s)console.log(O.red(` \u2717 ${d.name}: ${d.detail}`));process.exit(1)}if(a.length>0)for(let d of a)console.log(O.yellow(` \u26A0 ${d.name}: ${d.detail} (required for Anthropic provider)`));if(r){console.log(O.yellow(` Worker image not found locally. Pulling ${o.workerImage}...`));let{spawnSync:d}=await import("child_process");d("docker",["pull",o.workerImage],{stdio:"inherit",timeout:6e5}).status!==0&&(console.log(O.red(" Failed to pull worker image.")),process.exit(1)),console.log(O.green(" \u2713 Worker image pulled"))}if(e.detach){let d=Ce(),w=ce();console.log(O.dim("Starting agent in background...")),console.log(O.dim(` Logs: ${d}`)),console.log(O.dim(` PID: ${w}`));let T=kn(d,"a"),x=yn("workermill-agent",["start"],{detached:!0,stdio:["ignore",T,T],shell:!0});x.pid?(at(w,String(x.pid),"utf-8"),x.unref(),console.log(O.green(`Agent started (PID: ${x.pid})`)),console.log(`Check status with: ${O.cyan("workermill-agent status")}`)):(console.log(O.red("Failed to start agent in background.")),process.exit(1));return}let l=Ce(),m=bn(l,{flags:"a"}),$=process.stdout.write.bind(process.stdout),g=process.stderr.write.bind(process.stderr);process.stdout.write=(d,...w)=>(m.write(d),$(d,...w)),process.stderr.write=(d,...w)=>(m.write(d),g(d,...w)),console.log(),console.log(O.bold.cyan(" WorkerMill Remote Agent")),console.log(O.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log();let h=Math.round(wn()/(1024*1024*1024));h<8?(console.log(O.red(` \u2717 Insufficient RAM: ${h} GB (minimum 8 GB, recommended 16 GB)`)),process.exit(1)):h<16&&console.log(O.yellow(` \u26A0 RAM: ${h} GB (below recommended 16 GB \u2014 workers may be slow)`));let P=De();console.log(O.dim(` Agent: ${o.agentId}`)),console.log(O.dim(` Version: ${H}`)),console.log(O.dim(` Image: ${o.workerImage}`)),console.log();try{let d=await po(o);at(ce(),String(process.pid),"utf-8");let w=!1,T=async()=>{w&&(console.log(O.red(`
13
+ `+_;let v=`${E} Critic rejected (score: ${I.score}/100, threshold: ${ne}). Re-planning with feedback...`;if(console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${v}`),await R(e.id,v),I.risks.length>0){let Z=`${E} Critic risks: ${I.risks.join("; ")}`;console.log(`${k()} ${t} ${p.dim(Z)}`),await R(e.id,Z)}if(I.suggestions&&I.suggestions.length>0){let Z=`${E} Critic suggestions: ${I.suggestions.join("; ")}`;console.log(`${k()} ${t} ${p.dim(Z)}`),await R(e.id,Z)}}else{let _=`${E} Critic rejected after ${ue} iterations (best score: ${b}/100, threshold: ${ne})`;if(console.error(`${k()} ${t} ${p.red("\u2717")} ${_}`),await R(e.id,_,"error","error"),I.risks.length>0){let v=`${E} Final risks: ${I.risks.join("; ")}`;console.error(`${k()} ${t} ${v}`),await R(e.id,v,"error","error")}if(I.suggestions&&I.suggestions.length>0){let v=`${E} Suggestions: ${I.suggestions.join("; ")}`;console.error(`${k()} ${t} ${v}`),await R(e.id,v,"error","error")}}}let D=50;if(A&&b>=D){let B=Math.round((Date.now()-T)/1e3),j=`${E} Best-plan fallback: posting plan with score ${b}/100 (below ${ne} threshold, above ${D} minimum)`;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${j}`),await R(e.id,j);let ie=Date.now()-T;if(await uo(e.id,A,o.agentId,t,B,b,[`Best-plan fallback: critic rejected after ${ue} iterations`],K,y,ie,ue))return!0;console.log(`${k()} ${t} ${p.yellow("\u26A0")} ${E} Fallback post rejected by server, reporting plan-failed`),await R(e.id,`${E} Fallback plan rejected by server \u2014 reporting failure`)}try{let B=A&&b>=D?`Best-plan fallback rejected by server after ${ue} iterations (best score: ${b}/100)`:`Critic rejected after ${ue} iterations (best score: ${b}/100, threshold: ${ne}, fallback minimum: ${D})`;await M.post("/api/agent/plan-failed",{taskId:e.id,agentId:o.agentId,reason:B,criticHistory:K})}catch{}return!1}finally{if(await rn(),W)try{go(`rm -rf "${W}"`,{stdio:"ignore"})}catch{}}}async function uo(e,o,n,t,r,i,s,a,l,m,$){let g=xo(o);try{let P=(await M.post("/api/agent/plan-result",{taskId:e,rawOutput:g,agentId:n,criticScore:i,criticRisks:s,criticHistory:a,criticIterations:$,fileCapTruncations:l,planningDurationMs:m})).data.storyCount;return console.log(`${k()} ${t} ${p.green("\u2713")} Plan validated: ${p.bold(P)} stories \u2192 ${p.green("queued")}`),await R(e,`${E} Plan validated: ${P} stories. Task queued for execution.`),await Pe(e,"complete",r,"Planning complete",0,0),!0}catch(h){let P=h,d=P.response?.data?.error||P.response?.data?.detail||String(h),w=P.response?.status?` (${P.response.status})`:"";return console.error(`${k()} ${t} ${p.red("\u2717")} Server validation failed${w}: ${d.substring(0,100)}`),await R(e,`${E} Server-side plan validation failed${w}: ${d.substring(0,200)}`,"error","error"),!1}}async function dn(e,o,n,t,r){try{let s=(await M.post("/api/agent/plan-result",{taskId:e,rawOutput:o,agentId:n})).data.storyCount;return console.log(`${k()} ${t} ${p.green("\u2713")} Plan validated (server-side): ${p.bold(s)} stories \u2192 ${p.green("queued")}`),await R(e,`${E} Plan validated: ${s} stories. Task queued for execution.`),await Pe(e,"complete",r,"Planning complete",0,0),!0}catch(i){let a=i.response?.data?.detail||String(i);return console.error(`${k()} ${t} ${p.red("\u2717")} Validation failed: ${a.substring(0,100)}`),await R(e,`${E} Plan validation failed: ${a.substring(0,200)}`,"error","error"),!1}}import f from"chalk";import{spawn as Bo,execSync as ze}from"child_process";import*as je from"path";import*as ae from"fs";import*as qe from"os";function N(){return f.dim(new Date().toLocaleTimeString())}var un=[[/(:\/\/[^:/?#]+:)[^@]+(@)/g,"$1***$2"],[/\b(ghp_|gho_|ghs_|github_pat_)[A-Za-z0-9_]+/g,"$1***"],[/\bglpat-[A-Za-z0-9\-_]+/g,"glpat-***"],[/\b(AKIA)[A-Z0-9]{16}\b/g,"$1***"],[/(Bearer\s+)[A-Za-z0-9._\-]+/gi,"$1***"],[/(x-api-key:\s*)[^\s,'"]+/gi,"$1***"]];function Ve(e){let o=e;for(let[n,t]of un)o=o.replace(n,t);return o}var Y=new Map;function gn(){if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return ae.readFileSync("/proc/version","utf-8").toLowerCase().includes("microsoft")}catch{return!1}}var He=gn(),jo=He||process.platform==="darwin"||process.platform==="win32";function Vo(){if(!He)return null;try{let e=ze("hostname -I",{encoding:"utf-8"}).trim().split(/\s+/)[0];if(e&&/^\d+\.\d+\.\d+\.\d+$/.test(e))return e}catch{}return null}function zo(e){if(!He)return e;let o=e.match(/^\/mnt\/([a-zA-Z])\/(.*)$/);return o?`${o[1].toUpperCase()}:/${o[2]}`:e}function qo(){let e=je.join(qe.homedir(),".claude");if(ae.existsSync(e))return e;if(He){let o="/mnt/c/Users";if(ae.existsSync(o))try{for(let n of ae.readdirSync(o)){if(["Public","Default","Default User","All Users"].includes(n))continue;let t=je.join(o,n,".claude");if(ae.existsSync(t))return t}}catch{}}return null}var Go=0;function Ho(e){let o=e.match(/^(\d+\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)/);return o?o[1]:null}function pn(e){let o=e.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/);return o?o[1]:"us-east-1"}function Yo(e){if(Date.now()<Go)return!0;let o=pn(e);try{return ze(`aws ecr get-login-password --region ${o} | docker login --username AWS --password-stdin ${e}`,{stdio:"pipe",timeout:3e4}),Go=Date.now()+660*60*1e3,!0}catch{return!1}}function Jo(e,o,n){if(n?.scmToken)return n.scmToken;switch(e){case"bitbucket":return o.bitbucketToken;case"gitlab":return o.gitlabToken;default:return o.githubToken}}function mn(e){let o=e.jiraFields;if(!o)return!1;let n=o.labels;if(Array.isArray(n)&&n.some(i=>typeof i=="string"&&i.toLowerCase()==="self-review"))return!0;let r=o.issue?.labels;return!!(Array.isArray(r)&&r.some(i=>typeof i=="string"?i.toLowerCase()==="self-review":i&&typeof i=="object"&&"name"in i?(i.name||"").toLowerCase()==="self-review":!1))}async function Xo(e,o,n,t){let r=f.cyan(e.id.slice(0,8));if(Y.has(e.id)){console.log(`${N()} ${r} ${f.dim("Already running, skipping")}`);return}let i=`workermill-${e.id.slice(0,8)}`,a=["run","--rm",...!o.workerImage.includes("/")?[]:["--pull","always"],"--name",i],l=Math.round(qe.totalmem()/(1024*1024*1024));if(l<=16?a.push("--memory","6g","--memory-swap","10g","--cpus","2"):(l<=32,a.push("--memory","6g","--memory-swap","12g","--cpus","4")),jo){let y=Vo();a.push(`--add-host=host.docker.internal:${y||"host-gateway"}`)}else a.push("--network","host");let m=e.workerProvider||"anthropic",$=qo();if(!$&&m==="anthropic"){console.error(`${N()} ${r} ${f.red("\u2717")} Claude credentials not found. Run 'claude' and complete the sign-in flow.`);return}if($){let y=je.join($,".credentials.json");try{ae.chmodSync(y,438)}catch{}let F=zo($);a.push("-v",`${F}:/home/worker/.claude`)}else console.log(`${N()} ${r} ${f.dim("Skipping Claude mount (non-Anthropic worker)")}`);let g=o.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),h=e.scmProvider||"github",P=Jo(h,o,t),d=t?.githubToken||o.githubToken,w=h==="bitbucket"&&t?.scmToken||o.bitbucketToken,T=h==="gitlab"&&t?.scmToken||o.gitlabToken,x={NODE_OPTIONS:"--max-old-space-size=3072",EPIC_MODE:"true",EXECUTION_MODE:"local",TASK_ID:e.id,ORG_ID:e.orgId||"",JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",TASK_SUMMARY:e.summary||"",TASK_DESCRIPTION:e.description||"",WORKER_PERSONA:e.workerPersona||"",RETRY_NUMBER:String(e.retryCount??0),TICKET_KEY:e.jiraIssueKey||"",API_BASE_URL:g,ORG_API_KEY:o.apiKey,SCM_PROVIDER:h,SCM_TOKEN:P,SCM_BASE_URL:t?.scmBaseUrl||"",GITHUB_TOKEN:d,GH_TOKEN:d,GITHUB_REVIEWER_TOKEN:t?.githubReviewerToken||"",BITBUCKET_TOKEN:w,BITBUCKET_USERNAME:t?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:T,TARGET_REPO:e.githubRepo||"",GITHUB_REPO:e.githubRepo||"",WORKER_MODEL:e.workerModel||String(n.defaultWorkerModel||""),CLAUDE_MODEL:e.workerModel||String(n.defaultWorkerModel||""),JIRA_BASE_URL:t?.jiraBaseUrl||"",JIRA_EMAIL:t?.jiraEmail||"",JIRA_API_TOKEN:t?.jiraApiToken||"",TICKET_SYSTEM:t?.issueTrackerProvider||"jira",LINEAR_API_KEY:t?.linearApiKey||"",AWS_ACCESS_KEY_ID:t?.customerAwsAccessKeyId||"",AWS_SECRET_ACCESS_KEY:t?.customerAwsSecretAccessKey||"",AWS_DEFAULT_REGION:t?.customerAwsRegion||"",AWS_REGION:t?.customerAwsRegion||"",CUSTOMER_AWS_ROLE_ARN:t?.customerAwsRoleArn||"",CUSTOMER_AWS_EXTERNAL_ID:t?.customerAwsExternalId||"",CUSTOMER_AWS_REGION:t?.customerAwsRegion||"",MANAGER_PROVIDER:t?.managerProvider||"anthropic",MANAGER_MODEL:t?.managerModelId||"",BITBUCKET_EMAIL:t?.bitbucketEmail||"",DEPLOYMENT_ENABLED:e.deploymentEnabled||e.parentTaskId?"true":"false",PRD_CHILD_TASK:e.parentTaskId?"true":"false",IMPROVEMENT_ENABLED:e.improvementEnabled?"true":"false",QUALITY_GATE_BYPASS:e.qualityGateBypass?"true":"false",STANDARD_SDK_MODE:e.standardSdkMode?"true":"false",MAX_REVIEW_REVISIONS:String(n.maxReviewRevisions??3),CODEBASE_INDEXING_ENABLED:n.codebaseIndexingEnabled===!0?"true":"false",EXISTING_PR_URL:e.githubPrUrl||"",EXISTING_PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",PARENT_TASK_ID:e.parentTaskId||e.id,PARENT_JIRA_KEY:e.jiraIssueKey&&/-S\d+$/.test(e.jiraIssueKey)&&e.jiraFields?.parentJiraKey||"",TARGET_BRANCH:e.jiraFields?.targetBranch||"",STORY_BRANCH:e.jiraFields?.storyBranch||"",TASK_NOTES:e.taskNotes||"",TARGET_FILES:JSON.stringify(e.jiraFields?.targetFiles||[]),REFERENCE_FILES:JSON.stringify(e.jiraFields?.referenceFiles||[]),PIPELINE_VERSION:e.jiraFields?.pipelineVersion||e.pipelineVersion||"",V2_STEP_INPUT:e.jiraFields?.v2StepInput?JSON.stringify(e.jiraFields.v2StepInput):"",EXECUTION_MODE_SETTING:e.jiraFields?.executionMode||"autonomous",ANTHROPIC_API_KEY:$?"":t?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",WORKER_PROVIDER:e.workerProvider||"anthropic",OPENAI_API_KEY:t?.openaiApiKey||"",GOOGLE_API_KEY:t?.googleApiKey||"",GOOGLE_GENERATIVE_AI_API_KEY:t?.googleApiKey||"",OLLAMA_HOST:t?.ollamaBaseUrl||"",OLLAMA_CONTEXT_WINDOW:t?.ollamaContextWindow?String(t.ollamaContextWindow):"",VLLM_BASE_URL:t?.vllmBaseUrl||"",BLOCKER_MAX_AUTO_RETRIES:String(n.blockerMaxAutoRetries??3),BLOCKER_AUTO_RETRY_ENABLED:n.blockerAutoRetryEnabled!==!1?"true":"false",PUSH_AFTER_COMMIT:n.pushAfterCommit!==!1?"true":"false",GRACEFUL_SHUTDOWN_ENABLED:n.gracefulShutdownEnabled!==!1?"true":"false",MAX_PARALLEL_EXPERTS:String(n.maxParallelExperts??4),REVIEW_ENABLED:e.skipManagerReview===!1?"true":"false",SELF_REVIEW_ENABLED:mn(e)||n.selfReviewEnabled!==!1?"true":"false"};for(let[y,F]of Object.entries(x))F!==""&&a.push("-e",`${y}=${F}`);let W=o.workerImage,c=Ho(W);c&&Yo(c),a.push(W);let A=e.skipManagerReview===!1;console.log(`${N()} ${r} ${f.dim("Starting container")} ${f.yellow(i)}`),console.log(`${N()} ${r} ${f.dim(` skipManagerReview=${e.skipManagerReview} \u2192 REVIEW_ENABLED=${A}`)}`),console.log(`${N()} ${r} ${f.dim(` model=${e.workerModel} repo=${e.githubRepo}`)}`),console.log(`${N()} ${r} ${f.dim(` totalRamGB=${l} docker args:`)} ${a.slice(0,10).join(" ")}`);let b=Bo("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!b.pid){console.error(`${N()} ${r} ${f.red("\u2717")} Failed to spawn container`);return}let K={taskId:e.id,containerName:i,process:b,startedAt:new Date,status:"running",resultEmitted:!1};Y.set(e.id,K),b.stdout?.on("data",y=>{let F=y.toString().split(`
14
+ `).filter(D=>D.trim());for(let D of F)console.log(`${N()} ${r} ${f.dim(Ve(D))}`),D.includes("::result::")&&(K.resultEmitted=!0)}),b.stderr?.on("data",y=>{let F=y.toString().split(`
15
+ `).filter(D=>D.trim());for(let D of F)console.log(`${N()} ${r} ${f.red(Ve(D))}`)}),b.on("exit",y=>{K.status=y===0?"completed":"failed";let F=Math.round((Date.now()-K.startedAt.getTime())/1e3),D=y===0?f.green("\u2713"):f.red("\u2717"),B=y===0?f.green("completed"):f.red(`failed (exit ${y})`);console.log(`${N()} ${r} ${D} Container ${B} ${f.dim(`(${F}s)`)}`),K.resultEmitted||(console.log(`${N()} ${r} ${f.yellow("\u26A0")} No ::result:: marker seen \u2014 posting fallback completion in 15s`),setTimeout(async()=>{try{let j=y===0?"completed":"failed",ie=y!==0?`Worker container exited with code ${y} without reporting completion`:void 0;(await(await fetch(`${o.apiUrl}/api/tasks/${e.id}/worker-complete`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":o.apiKey},body:JSON.stringify({exitCode:y??1,result:j,errorMessage:ie})})).json()).status==="ignored"?console.log(`${N()} ${r} ${f.dim("Fallback completion ignored (task already transitioned)")}`):console.log(`${N()} ${r} ${f.yellow("\u26A0")} Fallback completion applied: ${j}`)}catch(j){console.error(`${N()} ${r} ${f.red("\u2717")} Fallback completion failed:`,j instanceof Error?j.message:j)}},15e3)),setTimeout(()=>Y.delete(e.id),9e4)}),b.on("error",y=>{K.status="failed",console.error(`${N()} ${r} ${f.red("\u2717")} Container error: ${y.message}`)})}function po(){return Array.from(Y.values()).filter(e=>e.status==="running").length}function Qo(){return Array.from(Y.values()).filter(e=>e.status==="running").map(e=>e.taskId)}function Zo(e){let o=Y.get(e);if(!o||o.status!=="running")return;let n=f.cyan(e.slice(0,8));console.log(`${N()} ${n} ${f.red("\u25A0")} Stopping container (cancelled by dashboard)`);try{ze(`docker stop ${o.containerName}`,{stdio:"ignore",timeout:15e3}),o.status="completed"}catch{}Y.delete(e)}async function et(){console.log(`${N()} ${f.dim(`Stopping ${Y.size} containers...`)}`);for(let[,e]of Y)if(e.status==="running")try{ze(`docker stop ${e.containerName}`,{stdio:"ignore",timeout:15e3}),e.status="completed"}catch{}Y.clear()}async function ot(e,o,n){let t=f.cyan(e.id.slice(0,8)),r=`manager-${e.id}`;if(Y.has(r))return;let i=`wm-manager-${e.id.slice(0,8)}-${Date.now()}`,s=qo(),a=["run","--rm","--name",i,"--memory=4g","--cpus=2"];if(jo){let c=Vo();a.push(`--add-host=host.docker.internal:${c||"host-gateway"}`)}else a.push("--network","host");if(s){let c=zo(s);a.push("-v",`${c}:/home/worker/.claude`)}let l=o.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),m=e.scmProvider||"github",$=Jo(m,o,n),g=n?.githubToken||o.githubToken,h=m==="bitbucket"&&n?.scmToken||o.bitbucketToken,P=m==="gitlab"&&n?.scmToken||o.gitlabToken,d={NODE_OPTIONS:"--max-old-space-size=3072",TASK_ID:e.id,MANAGER_ACTION:e.managerAction,JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",GITHUB_REPO:e.githubRepo||"",PR_URL:e.githubPrUrl||"",PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",API_BASE_URL:l,ORG_API_KEY:o.apiKey,SCM_PROVIDER:m,SCM_TOKEN:$,GITHUB_TOKEN:g,GH_TOKEN:g,BITBUCKET_TOKEN:h,BITBUCKET_USERNAME:n?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:P,MANAGER_PROVIDER:n?.managerProvider||"anthropic",MANAGER_MODEL:n?.managerModelId||"",ANTHROPIC_API_KEY:s?"":n?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",OPENAI_API_KEY:n?.openaiApiKey||"",GOOGLE_API_KEY:n?.googleApiKey||"",JIRA_BASE_URL:n?.jiraBaseUrl||"",JIRA_EMAIL:n?.jiraEmail||"",JIRA_API_TOKEN:n?.jiraApiToken||"",TICKET_SYSTEM:n?.issueTrackerProvider||"jira",LINEAR_API_KEY:n?.linearApiKey||""};for(let[c,A]of Object.entries(d))A!==""&&a.push("-e",`${c}=${A}`);let w=o.workerImage,T=Ho(w);T&&Yo(T),a.push("--entrypoint","/bin/bash"),a.push(w),a.push("/app/manager-entrypoint.sh"),console.log(`${N()} ${t} ${f.magenta("\u25C6 MANAGER")} Starting ${e.managerAction} container ${f.yellow(i)}`);let x=Bo("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!x.pid){console.error(`${N()} ${t} ${f.red("\u2717")} Failed to spawn manager container`);return}let W={taskId:r,containerName:i,process:x,startedAt:new Date,status:"running",resultEmitted:!1};Y.set(r,W),x.stdout?.on("data",c=>{let A=c.toString().split(`
16
+ `).filter(b=>b.trim());for(let b of A)console.log(`${N()} ${t} ${f.magenta("MGR")} ${f.dim(Ve(b))}`)}),x.stderr?.on("data",c=>{let A=c.toString().split(`
17
+ `).filter(b=>b.trim());for(let b of A)console.log(`${N()} ${t} ${f.magenta("MGR")} ${f.red(Ve(b))}`)}),x.on("exit",c=>{W.status=c===0?"completed":"failed";let A=Math.round((Date.now()-W.startedAt.getTime())/1e3),b=c===0?f.green("\u2713"):f.red("\u2717"),K=c===0?f.green("completed"):f.red(`failed (exit ${c})`);console.log(`${N()} ${t} ${f.magenta("MGR")} ${b} Manager ${e.managerAction} ${K} ${f.dim(`(${A}s)`)}`),setTimeout(()=>Y.delete(r),6e4)}),x.on("error",c=>{W.status="failed",console.error(`${N()} ${t} ${f.magenta("MGR")} ${f.red("\u2717")} Container error: ${c.message}`)})}import{spawn as tt}from"child_process";import Oe from"chalk";async function xe(){return console.log(Oe.cyan(" Updating @workermill/agent...")),new Promise(e=>{let o=tt("npm",["install","-g","@workermill/agent@latest"],{stdio:"inherit",shell:!0});o.on("error",n=>{console.error(Oe.red(` Update failed: ${n.message}`)),e(!1)}),o.on("close",n=>{n===0?(console.log(Oe.green(" Update successful.")),e(!0)):(console.error(Oe.red(` Update failed with exit code ${n}`)),e(!1))})})}function Ye(){console.log(Oe.cyan(" Restarting agent...")),tt(process.argv[0],process.argv.slice(1),{stdio:"inherit",detached:!0}).unref(),process.exit(0)}var Ae=new Set,ke=new Set,Je=null,nt=!1,Xe=!1;function U(){return S.dim(new Date().toLocaleTimeString())}async function fn(){if(Je)return Je;try{return Je=(await M.get("/api/agent/config")).data,Je}catch{return console.error(`${U()} ${S.red("\u2717")} Failed to fetch org config`),{}}}async function rt(e){if(!Xe)try{let o=await M.get("/api/agent/poll",{params:{agentId:e.agentId}}),n=o.data.tasks;if(n.length===0)return;for(let r of n)r.status==="planning"&&!Ae.has(r.id)?await hn(r,e):r.status==="queued"&&await $n(r,e);let t=o.data.managerTasks;if(t&&t.length>0)for(let r of t)ke.has(r.id)||await wn(r,e)}catch(o){let n=o,t=Ae.size>0||po()>0||ke.size>0;n.response?.status===401?t||console.error(`${U()} ${S.red("\u2717")} Authentication failed. Check your API key.`):t||console.warn(`${U()} ${S.yellow("\u26A0")} Poll error: ${n.message||String(o)}`)}}async function hn(e,o){let n,t;try{let s=await M.post("/api/agent/claim",{taskId:e.id,agentId:o.agentId});if(!s.data.claimed)return;n=s.data.credentials,t=s.data.task}catch{return}let r=S.cyan(e.id.slice(0,8));if(t&&(t.retryCount??0)>0&&t.executionPlanV2!=null){console.log(),console.log(`${U()} ${S.magenta("\u25C6 RESUME")} ${r} ${e.summary.substring(0,60)}`),console.log(`${U()} ${r} Retry #${t.retryCount} with existing plan \u2014 skipping planning`),Ae.add(e.id);try{await M.post("/api/agent/resume-plan",{taskId:e.id,agentId:o.agentId}),console.log(`${U()} ${r} ${S.green("\u2713")} Resumed with existing plan \u2192 ${S.green("queued")}`)}catch(s){let a=s,l=a.response?.data?.error||a.message||String(s);console.error(`${U()} ${r} ${S.red("\u2717")} Resume failed: ${l}`)}Ae.delete(e.id);return}console.log(),console.log(`${U()} ${S.magenta("\u25C6 PLANNING")} ${r} ${e.summary.substring(0,60)}`),Ae.add(e.id),Ko(e,o,n).then(s=>{console.log(s?`${U()} ${S.green("\u2713")} Planning complete for ${r}`:`${U()} ${S.red("\u2717")} Planning failed for ${r}`)}).catch(s=>console.error(`${U()} ${S.red("\u2717")} Planning error for ${r}:`,s.message||s)).finally(()=>Ae.delete(e.id))}async function $n(e,o){if(po()>=o.maxWorkers)return;let t;try{if(t=(await M.post("/api/agent/claim",{taskId:e.id,agentId:o.agentId})).data,!t.claimed)return}catch{return}try{await M.post("/api/agent/started",{taskId:e.id,agentId:o.agentId})}catch{let m=S.cyan(e.id.slice(0,8));console.warn(`${U()} ${S.yellow("\u26A0")} Failed to report started for ${m}`)}let r=S.cyan(e.id.slice(0,8));console.log(),console.log(`${U()} ${S.blue("\u25B6 EXECUTING")} ${r} ${e.summary.substring(0,60)}`);let i=await fn(),s=t.task||{id:e.id,summary:e.summary,description:e.description,jiraIssueKey:e.jiraIssueKey,workerModel:e.workerModel,workerProvider:e.workerProvider,githubRepo:e.githubRepo,scmProvider:e.scmProvider,skipManagerReview:e.skipManagerReview,deploymentEnabled:e.deploymentEnabled,improvementEnabled:e.improvementEnabled,qualityGateBypass:e.qualityGateBypass,standardSdkMode:e.standardSdkMode,parentTaskId:e.parentTaskId,taskNotes:e.taskNotes,githubPrUrl:e.githubPrUrl,githubPrNumber:e.githubPrNumber,executionPlanV2:e.executionPlanV2,jiraFields:e.jiraFields||{}},a=t.credentials;Xo(s,o,i,a).catch(l=>console.error(`${U()} ${S.red("\u2717")} Spawn failed for ${r}:`,l.message||l))}async function wn(e,o){let n=S.cyan(e.id.slice(0,8));ke.add(e.id);try{let t=await M.post("/api/agent/claim-manager",{taskId:e.id,agentId:o.agentId,action:e.managerAction});if(!t.data.claimed){ke.delete(e.id);return}let r=t.data.task,i=t.data.credentials||{},s=e.managerAction==="analyze_logs"?S.yellow("LOG ANALYSIS"):S.yellow("PR REVIEW");console.log(),console.log(`${U()} ${S.magenta("\u25C6 MANAGER")} ${s} ${n} ${e.summary.substring(0,60)}`);let a={id:e.id,summary:r?.summary||e.summary,description:r?.description||e.description,jiraIssueKey:r?.jiraIssueKey||e.jiraIssueKey,githubRepo:r?.githubRepo||e.githubRepo,scmProvider:r?.scmProvider||e.scmProvider,githubPrUrl:r?.githubPrUrl||e.githubPrUrl,githubPrNumber:r?.githubPrNumber||e.githubPrNumber,managerAction:e.managerAction};ot(a,o,i).then(()=>{console.log(`${U()} ${n} ${S.magenta("MGR")} ${S.green("\u2713")} Manager ${e.managerAction} dispatched`)}).catch(l=>{console.error(`${U()} ${n} ${S.magenta("MGR")} ${S.red("\u2717")} Manager spawn failed:`,l.message||l)}).finally(()=>ke.delete(e.id))}catch(t){ke.delete(e.id);let r=t;console.error(`${U()} ${n} ${S.red("\u2717")} Failed to claim manager task:`,r.message||String(t))}}var Qe=null,Ze=null;function it(){Qe&&(clearInterval(Qe),Qe=null),Ze&&(clearInterval(Ze),Ze=null)}function st(e){console.log(` ${S.dim("Polling every")} ${e.pollIntervalMs/1e3}s ${S.dim("\xB7 waiting for tasks...")}`),rt(e),Qe=setInterval(()=>rt(e),e.pollIntervalMs)}function at(e){Ze=setInterval(async()=>{let o=Qo(),n=Array.from(Ae),t=Array.from(ke),r=[...o,...n,...t];try{let i=await M.post("/api/agent/heartbeat",{agentId:e.agentId,activeTasks:r,agentVersion:H}),s=i.data?.cancelledTasks;if(s&&s.length>0)for(let $ of s)Zo($);let{updateAvailable:a,updateRequired:l,latestVersion:m}=i.data??{};l&&!Xe?(Xe=!0,console.log(`${U()} ${S.red("\u26A0 Agent update required")} (current: ${H}, required: ${m})`),console.log(`${U()} ${S.yellow("Refusing new tasks until updated.")}`),await xe()?Ye():(console.log(`${U()} ${S.red("Auto-update failed.")} Run: npm install -g @workermill/agent@latest`),Xe=!1)):a&&!nt&&!l&&(nt=!0,console.log(`${U()} ${S.yellow(`Update available: ${m}`)} (current: ${H}). Run: workermill-agent update`))}catch{}},e.heartbeatIntervalMs)}te();async function mo(e){console.log(),console.log(G.bold.cyan(" WorkerMill Remote Agent")),console.log(G.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),Ke(e.apiUrl,e.apiKey);try{let o=await M.get("/api/agent/config"),n=o.data.maxConcurrentWorkers;n&&typeof n=="number"&&(e.maxWorkers=n),o.data.workerImageUrl&&(e.workerImage=o.data.workerImageUrl),console.log(` ${G.green("\u25CF")} Connected to ${G.cyan(e.apiUrl)}`),console.log(` ${G.dim("Agent:")} ${e.agentId}`),console.log(` ${G.dim("Workers:")} ${G.yellow(String(e.maxWorkers))} parallel`),console.log(` ${G.dim("Image:")} ${e.workerImage}`),console.log(` ${G.dim("SCM:")} ${o.data.scmProvider}`),console.log(` ${G.dim("Model:")} ${G.yellow(o.data.defaultWorkerModel)}`),console.log()}catch(o){let n=o;throw n.response?.status===401?new Error("Authentication failed. Check your API key."):new Error(`Failed to connect to WorkerMill API: ${n.message||String(o)}`)}try{let o=await M.post("/api/agent/register",{agentId:e.agentId,maxWorkers:e.maxWorkers,agentVersion:H}),{updateAvailable:n,updateRequired:t,latestVersion:r}=o.data;t?(console.log(G.red(` \u26A0 Agent update required (current: ${H}, required: ${r})`)),await xe()?Ye():console.log(G.yellow(" Auto-update failed. Run: npm install -g @workermill/agent@latest"))):n&&console.log(G.yellow(` Update available: ${r} (current: ${H}). Run: workermill-agent update`))}catch{}return st(e),at(e),console.log(G.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` ${G.green("\u25CF")} Agent is running. ${G.dim("Press Ctrl+C to stop.")}`),console.log(),async()=>{console.log(),console.log(G.dim(" Shutting down...")),it();try{await M.post("/api/agent/deregister",{agentId:e.agentId})}catch{}await et(),console.log(` ${G.red("\u25CF")} Agent stopped.`)}}var yn=typeof process<"u"&&process.argv[1]&&(process.argv[1].endsWith("/index.ts")||process.argv[1].endsWith("/index.js"));if(yn){try{await import("dotenv/config")}catch{}let{loadConfig:e,validatePrerequisites:o}=await Promise.resolve().then(()=>(te(),Io)),n=e();o(),console.log(G.dim(" Prerequisites validated."));let t=await mo(n);process.on("SIGINT",async()=>{await t(),process.exit(0)}),process.on("SIGTERM",async()=>{await t(),process.exit(0)})}async function fo(e){kn(oe())||(console.log(O.red("No configuration found.")),console.log(`Run ${O.cyan("workermill-agent setup")} first.`),process.exit(1));let o=fe(),t=to(o.workerImage).filter(d=>!d.ok),r=t.find(d=>d.name==="Worker image"),i=new Set(["Claude CLI","Claude auth"]),s=t.filter(d=>d.name!=="Worker image"&&!i.has(d.name)),a=t.filter(d=>i.has(d.name));if(s.length>0){console.log(O.red("Prerequisites check failed:"));for(let d of s)console.log(O.red(` \u2717 ${d.name}: ${d.detail}`));process.exit(1)}if(a.length>0)for(let d of a)console.log(O.yellow(` \u26A0 ${d.name}: ${d.detail} (required for Anthropic provider)`));if(r){console.log(O.yellow(` Worker image not found locally. Pulling ${o.workerImage}...`));let{spawnSync:d}=await import("child_process");d("docker",["pull",o.workerImage],{stdio:"inherit",timeout:6e5}).status!==0&&(console.log(O.red(" Failed to pull worker image.")),process.exit(1)),console.log(O.green(" \u2713 Worker image pulled"))}if(e.detach){let d=Ce(),w=ce();console.log(O.dim("Starting agent in background...")),console.log(O.dim(` Logs: ${d}`)),console.log(O.dim(` PID: ${w}`));let T=En(d,"a"),x=An("workermill-agent",["start"],{detached:!0,stdio:["ignore",T,T],shell:!0});x.pid?(lt(w,String(x.pid),"utf-8"),x.unref(),console.log(O.green(`Agent started (PID: ${x.pid})`)),console.log(`Check status with: ${O.cyan("workermill-agent status")}`)):(console.log(O.red("Failed to start agent in background.")),process.exit(1));return}let l=Ce(),m=Sn(l,{flags:"a"}),$=process.stdout.write.bind(process.stdout),g=process.stderr.write.bind(process.stderr);process.stdout.write=(d,...w)=>(m.write(d),$(d,...w)),process.stderr.write=(d,...w)=>(m.write(d),g(d,...w)),console.log(),console.log(O.bold.cyan(" WorkerMill Remote Agent")),console.log(O.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log();let h=Math.round(In()/(1024*1024*1024));h<8?(console.log(O.red(` \u2717 Insufficient RAM: ${h} GB (minimum 8 GB, recommended 16 GB)`)),process.exit(1)):h<16&&console.log(O.yellow(` \u26A0 RAM: ${h} GB (below recommended 16 GB \u2014 workers may be slow)`));let P=De();console.log(O.dim(` Agent: ${o.agentId}`)),console.log(O.dim(` Version: ${H}`)),console.log(O.dim(` Image: ${o.workerImage}`)),console.log();try{let d=await mo(o);lt(ce(),String(process.pid),"utf-8");let w=!1,T=async()=>{w&&(console.log(O.red(`
18
18
  Force exit.`)),process.exit(1)),w=!0;let x=setTimeout(()=>{console.log(O.red(`
19
- Cleanup timed out. Force exit.`)),process.exit(1)},1e4);x.unref(),await d(),clearTimeout(x);try{An(ce())}catch{}process.exit(0)};process.on("SIGINT",T),process.on("SIGTERM",T)}catch(d){console.log(O.red(`Failed to start: ${d instanceof Error?d.message:String(d)}`)),process.exit(1)}}te();import be from"chalk";import{existsSync as En,readFileSync as Sn,unlinkSync as fo}from"fs";async function lt(){let e=ce();if(!En(e)){console.log(be.yellow("No running agent found (no PID file).")),console.log(be.dim(`Expected PID file at: ${e}`));return}let o=Sn(e,"utf-8").trim(),n=parseInt(o,10);if(isNaN(n)){console.log(be.red(`Invalid PID in ${e}: ${o}`)),fo(e);return}try{process.kill(n,0)}catch{console.log(be.yellow(`Agent (PID ${n}) is not running. Cleaning up PID file.`)),fo(e);return}console.log(be.dim(`Stopping agent (PID ${n})...`));try{process.kill(n,"SIGTERM")}catch(r){console.log(be.red(`Failed to stop agent: ${r instanceof Error?r.message:String(r)}`));return}let t=Date.now();for(;Date.now()-t<15e3;)try{process.kill(n,0),await new Promise(r=>setTimeout(r,500))}catch{break}try{fo(e)}catch{}console.log(be.green("Agent stopped."))}te();import V from"chalk";import{existsSync as ct,readFileSync as Tn}from"fs";import{execSync as Cn}from"child_process";import Rn from"axios";async function dt(){if(console.log(),console.log(V.bold("WorkerMill Remote Agent Status")),console.log(V.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),!ct(oe())){console.log(V.yellow(" Not configured. Run 'workermill-agent setup' first."));return}let e=fe(),o=ce(),n=!1,t=null;if(ct(o)){let i=Tn(o,"utf-8").trim();if(t=parseInt(i,10),!isNaN(t))try{process.kill(t,0),n=!0}catch{n=!1}}let r=n?V.green("\u25CF online"):V.red("\u25CF offline");console.log(` Status: ${r}${t?V.dim(` (PID ${t})`):""}`),console.log(` Agent ID: ${e.agentId}`),console.log(` API URL: ${e.apiUrl}`),console.log(` Max workers: ${e.maxWorkers}`),console.log(` Image: ${e.workerImage}`),console.log(),console.log(V.bold(" Active Containers"));try{let i=Cn('docker ps --filter "name=workermill-" --format "{{.Names}} {{.Status}} {{.RunningFor}}"',{encoding:"utf-8",timeout:1e4}).trim();if(i){let s=i.split(`
20
- `);console.log(V.dim(` Found ${s.length} container(s):`));for(let a of s){let[l,m,$]=a.split(" ");console.log(` ${V.cyan(l)} ${m} ${V.dim($||"")}`)}}else console.log(V.dim(" No active containers"))}catch{console.log(V.dim(" Could not query Docker"))}console.log(),console.log(V.bold(" API Connectivity"));try{let i=await Rn.get(`${e.apiUrl}/api/agent/config`,{headers:{"x-api-key":e.apiKey},timeout:1e4});console.log(` ${V.green("\u2713")} Connected to ${e.apiUrl}`),console.log(V.dim(` SCM: ${i.data.scmProvider}, Model: ${i.data.defaultWorkerModel}`))}catch(i){i.response?.status===401?console.log(` ${V.red("\u2717")} Authentication failed (invalid API key)`):console.log(` ${V.red("\u2717")} Cannot reach ${e.apiUrl}`)}console.log()}te();import Ze from"chalk";import{existsSync as _n,readFileSync as Pn,watchFile as xn,statSync as ut,openSync as vn,readSync as Mn,closeSync as Ln}from"fs";async function gt(e){let o=Ce();if(!_n(o)){console.log(Ze.yellow("No log file found.")),console.log(Ze.dim(`Expected at: ${o}`)),console.log(Ze.dim("Start the agent with --detach to generate logs."));return}let n=parseInt(e.lines||"50",10),r=Pn(o,"utf-8").split(`
19
+ Cleanup timed out. Force exit.`)),process.exit(1)},1e4);x.unref(),await d(),clearTimeout(x);try{bn(ce())}catch{}process.exit(0)};process.on("SIGINT",T),process.on("SIGTERM",T)}catch(d){console.log(O.red(`Failed to start: ${d instanceof Error?d.message:String(d)}`)),process.exit(1)}}te();import be from"chalk";import{existsSync as Tn,readFileSync as Cn,unlinkSync as ho}from"fs";async function ct(){let e=ce();if(!Tn(e)){console.log(be.yellow("No running agent found (no PID file).")),console.log(be.dim(`Expected PID file at: ${e}`));return}let o=Cn(e,"utf-8").trim(),n=parseInt(o,10);if(isNaN(n)){console.log(be.red(`Invalid PID in ${e}: ${o}`)),ho(e);return}try{process.kill(n,0)}catch{console.log(be.yellow(`Agent (PID ${n}) is not running. Cleaning up PID file.`)),ho(e);return}console.log(be.dim(`Stopping agent (PID ${n})...`));try{process.kill(n,"SIGTERM")}catch(r){console.log(be.red(`Failed to stop agent: ${r instanceof Error?r.message:String(r)}`));return}let t=Date.now();for(;Date.now()-t<15e3;)try{process.kill(n,0),await new Promise(r=>setTimeout(r,500))}catch{break}try{ho(e)}catch{}console.log(be.green("Agent stopped."))}te();import V from"chalk";import{existsSync as dt,readFileSync as _n}from"fs";import{execSync as Rn}from"child_process";import Pn from"axios";async function ut(){if(console.log(),console.log(V.bold("WorkerMill Remote Agent Status")),console.log(V.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),!dt(oe())){console.log(V.yellow(" Not configured. Run 'workermill-agent setup' first."));return}let e=fe(),o=ce(),n=!1,t=null;if(dt(o)){let i=_n(o,"utf-8").trim();if(t=parseInt(i,10),!isNaN(t))try{process.kill(t,0),n=!0}catch{n=!1}}let r=n?V.green("\u25CF online"):V.red("\u25CF offline");console.log(` Status: ${r}${t?V.dim(` (PID ${t})`):""}`),console.log(` Agent ID: ${e.agentId}`),console.log(` API URL: ${e.apiUrl}`),console.log(` Max workers: ${e.maxWorkers}`),console.log(` Image: ${e.workerImage}`),console.log(),console.log(V.bold(" Active Containers"));try{let i=Rn('docker ps --filter "name=workermill-" --format "{{.Names}} {{.Status}} {{.RunningFor}}"',{encoding:"utf-8",timeout:1e4}).trim();if(i){let s=i.split(`
20
+ `);console.log(V.dim(` Found ${s.length} container(s):`));for(let a of s){let[l,m,$]=a.split(" ");console.log(` ${V.cyan(l)} ${m} ${V.dim($||"")}`)}}else console.log(V.dim(" No active containers"))}catch{console.log(V.dim(" Could not query Docker"))}console.log(),console.log(V.bold(" API Connectivity"));try{let i=await Pn.get(`${e.apiUrl}/api/agent/config`,{headers:{"x-api-key":e.apiKey},timeout:1e4});console.log(` ${V.green("\u2713")} Connected to ${e.apiUrl}`),console.log(V.dim(` SCM: ${i.data.scmProvider}, Model: ${i.data.defaultWorkerModel}`))}catch(i){i.response?.status===401?console.log(` ${V.red("\u2717")} Authentication failed (invalid API key)`):console.log(` ${V.red("\u2717")} Cannot reach ${e.apiUrl}`)}console.log()}te();import eo from"chalk";import{existsSync as xn,readFileSync as vn,watchFile as Mn,statSync as gt,openSync as Ln,readSync as Nn,closeSync as On}from"fs";async function pt(e){let o=Ce();if(!xn(o)){console.log(eo.yellow("No log file found.")),console.log(eo.dim(`Expected at: ${o}`)),console.log(eo.dim("Start the agent with --detach to generate logs."));return}let n=parseInt(e.lines||"50",10),r=vn(o,"utf-8").split(`
21
21
  `),i=Math.max(0,r.length-n),s=r.slice(i).join(`
22
22
  `);s.trim()&&(process.stdout.write(s),s.endsWith(`
23
23
  `)||process.stdout.write(`
24
- `)),console.log(Ze.dim(`\u2500\u2500 Following ${o} (Ctrl+C to stop) \u2500\u2500`));let a=ut(o).size;xn(o,{interval:500},()=>{try{let l=ut(o).size;if(l<=a){a=l;return}let m=vn(o,"r"),$=Buffer.alloc(l-a);Mn(m,$,0,$.length,a),Ln(m),process.stdout.write($.toString()),a=l}catch{}}),await new Promise(()=>{})}te();import re from"chalk";import{spawnSync as Nn,execSync as On}from"child_process";import{existsSync as Fn}from"fs";function Dn(e){let o=e.match(/^(\d+\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)/);return o?o[1]:null}async function pt(){Fn(oe())||(console.log(re.red(" No config found. Run 'workermill-agent setup' first.")),process.exit(1));let e=fe(),o=e.workerImage;try{Ge(e.apiUrl,e.apiKey);let{data:r}=await M.get("/api/agent/config");r.workerImageUrl&&(o=r.workerImageUrl)}catch{console.log(re.yellow(" \u26A0 Could not fetch server config \u2014 using local image setting"))}let n=Dn(o);if(n){console.log(re.dim(" Authenticating to private ECR..."));let r=n.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/),i=r?r[1]:"us-east-1";try{On(`aws ecr get-login-password --region ${i} | docker login --username AWS --password-stdin ${n}`,{stdio:"pipe",timeout:3e4})}catch{console.log(re.red(" \u2717 ECR authentication failed.")),console.log(re.yellow(" Ensure AWS CLI is configured: aws configure")),process.exit(1)}}console.log(),console.log(re.dim(` Pulling worker image: ${o}`)),console.log(re.dim(" This may take a few minutes...")),console.log();let t=Nn("docker",["pull",o],{stdio:"inherit",timeout:6e5});console.log(),t.status===0?console.log(re.green(" \u2713 Worker image updated")):(console.log(re.red(" \u2717 Failed to pull worker image.")),t.error&&console.log(re.yellow(` Error: ${t.error.message}`)),console.log(re.yellow(" Is Docker Desktop running?")),process.exit(1))}import Fe from"chalk";async function mt(){console.log(),console.log(Fe.bold.cyan(" WorkerMill Agent Update")),console.log(Fe.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` ${Fe.dim("Current version:")} ${H}`),console.log(),await xe()?(console.log(),console.log(Fe.green(" Update complete. If the agent is running, restart it to use the new version."))):(console.log(),console.error(Fe.red(" Update failed. Try running manually: npm install -g @workermill/agent@latest")),process.exitCode=1)}te();var ge=new Wn;ge.name("workermill-agent").description("WorkerMill Remote Agent - Run AI workers locally with your Claude Max subscription").version(H);ge.command("setup").description("Interactive setup wizard - configure API key, validate prerequisites, pull worker image").action(io);ge.command("start").description("Start the agent, polling the cloud API for tasks").option("--detach","Run in background (daemon mode)").action(mo);ge.command("stop").description("Stop the running agent").action(lt);ge.command("status").description("Show agent status, active containers, and API connectivity").action(dt);ge.command("logs").description("Live tail of agent logs (like tail -f)").option("-n, --lines <count>","Number of lines to show initially","50").action(gt);ge.command("pull").description("Pull the latest worker Docker image").action(pt);ge.command("update").description("Update the agent to the latest version").action(mt);process.argv.length<=2?Un(oe())?mo({detach:!1}):io():ge.parse();
24
+ `)),console.log(eo.dim(`\u2500\u2500 Following ${o} (Ctrl+C to stop) \u2500\u2500`));let a=gt(o).size;Mn(o,{interval:500},()=>{try{let l=gt(o).size;if(l<=a){a=l;return}let m=Ln(o,"r"),$=Buffer.alloc(l-a);Nn(m,$,0,$.length,a),On(m),process.stdout.write($.toString()),a=l}catch{}}),await new Promise(()=>{})}te();import re from"chalk";import{spawnSync as Fn,execSync as Dn}from"child_process";import{existsSync as Un}from"fs";function Wn(e){let o=e.match(/^(\d+\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)/);return o?o[1]:null}async function mt(){Un(oe())||(console.log(re.red(" No config found. Run 'workermill-agent setup' first.")),process.exit(1));let e=fe(),o=e.workerImage;try{Ke(e.apiUrl,e.apiKey);let{data:r}=await M.get("/api/agent/config");r.workerImageUrl&&(o=r.workerImageUrl)}catch{console.log(re.yellow(" \u26A0 Could not fetch server config \u2014 using local image setting"))}let n=Wn(o);if(n){console.log(re.dim(" Authenticating to private ECR..."));let r=n.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/),i=r?r[1]:"us-east-1";try{Dn(`aws ecr get-login-password --region ${i} | docker login --username AWS --password-stdin ${n}`,{stdio:"pipe",timeout:3e4})}catch{console.log(re.red(" \u2717 ECR authentication failed.")),console.log(re.yellow(" Ensure AWS CLI is configured: aws configure")),process.exit(1)}}console.log(),console.log(re.dim(` Pulling worker image: ${o}`)),console.log(re.dim(" This may take a few minutes...")),console.log();let t=Fn("docker",["pull",o],{stdio:"inherit",timeout:6e5});console.log(),t.status===0?console.log(re.green(" \u2713 Worker image updated")):(console.log(re.red(" \u2717 Failed to pull worker image.")),t.error&&console.log(re.yellow(` Error: ${t.error.message}`)),console.log(re.yellow(" Is Docker Desktop running?")),process.exit(1))}import Fe from"chalk";async function ft(){console.log(),console.log(Fe.bold.cyan(" WorkerMill Agent Update")),console.log(Fe.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` ${Fe.dim("Current version:")} ${H}`),console.log(),await xe()?(console.log(),console.log(Fe.green(" Update complete. If the agent is running, restart it to use the new version."))):(console.log(),console.error(Fe.red(" Update failed. Try running manually: npm install -g @workermill/agent@latest")),process.exitCode=1)}te();var ge=new Gn;ge.name("workermill-agent").description("WorkerMill Remote Agent - Run AI workers locally with your Claude Max subscription").version(H);ge.command("setup").description("Interactive setup wizard - configure API key, validate prerequisites, pull worker image").action(so);ge.command("start").description("Start the agent, polling the cloud API for tasks").option("--detach","Run in background (daemon mode)").action(fo);ge.command("stop").description("Stop the running agent").action(ct);ge.command("status").description("Show agent status, active containers, and API connectivity").action(ut);ge.command("logs").description("Live tail of agent logs (like tail -f)").option("-n, --lines <count>","Number of lines to show initially","50").action(pt);ge.command("pull").description("Pull the latest worker Docker image").action(mt);ge.command("update").description("Update the agent to the latest version").action(ft);process.argv.length<=2?Kn(oe())?fo({detach:!1}):so():ge.parse();
package/dist/index.js CHANGED
@@ -1,15 +1,15 @@
1
- var xt=Object.defineProperty;var Mt=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ot=(e,t)=>{for(var r in t)xt(e,r,{get:t[r],enumerable:!0})};var ze={};Ot(ze,{checkPrerequisites:()=>zt,findClaudePath:()=>ce,getConfigDir:()=>Gt,getConfigFile:()=>Bt,getLogFile:()=>Vt,getPidFile:()=>Wt,getSystemInfo:()=>Ye,loadConfig:()=>We,loadConfigFromFile:()=>Be,saveConfigToFile:()=>Yt,validatePrerequisites:()=>Ve});import{existsSync as ye,readFileSync as Lt,mkdirSync as Ut,writeFileSync as Dt,chmodSync as Ft}from"fs";import{execSync as re}from"child_process";import{hostname as Ge,homedir as pe}from"os";import{join as J}from"path";function Gt(){return me}function Bt(){return ge}function Wt(){return Kt}function Vt(){return jt}function Be(){ye(ge)||(console.error("No config found. Run 'workermill-agent setup' first."),process.exit(1));let e;try{e=Lt(ge,"utf-8")}catch{console.error("Failed to read config file:",ge),process.exit(1)}let t;try{t=JSON.parse(e)}catch{console.error("Config file is corrupted. Re-run 'workermill-agent setup'."),process.exit(1)}(!t.apiUrl||!t.apiKey)&&(console.error("Config file is missing required fields (apiUrl, apiKey). Re-run 'workermill-agent setup'."),process.exit(1));let r=t.workerImage||"workermill-worker:local";return{apiUrl:t.apiUrl,apiKey:t.apiKey,agentId:t.agentId,maxWorkers:t.maxWorkers||4,pollIntervalMs:t.pollIntervalMs||5e3,heartbeatIntervalMs:t.heartbeatIntervalMs||3e4,githubToken:t.tokens?.github||"",bitbucketToken:t.tokens?.bitbucket||"",gitlabToken:t.tokens?.gitlab||"",workerImage:r}}function Yt(e){ye(me)||Ut(me,{recursive:!0}),Dt(ge,JSON.stringify(e,null,2),"utf-8");try{Ft(ge,384)}catch{}}function We(){let e=process.env.WORKERMILL_API_URL,t=process.env.WORKERMILL_API_KEY;return e||(console.error("WORKERMILL_API_URL is required in .env.remote"),process.exit(1)),t||(console.error("WORKERMILL_API_KEY is required in .env.remote"),console.error("Get your API key from Settings > Integrations on the WorkerMill dashboard."),process.exit(1)),{apiUrl:e.replace(/\/$/,""),apiKey:t,agentId:process.env.AGENT_ID||`agent-${Ge()}`,maxWorkers:parseInt(process.env.MAX_WORKERS||"4",10),pollIntervalMs:parseInt(process.env.POLL_INTERVAL_MS||"5000",10),heartbeatIntervalMs:parseInt(process.env.HEARTBEAT_INTERVAL_MS||"30000",10),githubToken:process.env.GITHUB_TOKEN||"",bitbucketToken:process.env.BITBUCKET_TOKEN||"",gitlabToken:process.env.GITLAB_TOKEN||"",workerImage:process.env.WORKER_IMAGE||"workermill-worker:local"}}function ce(){let e=process.platform==="win32",t=e?"where":"which";try{return re(`${t} claude`,{stdio:"ignore",timeout:1e4}),"claude"}catch{}let r=[];e?r.push(J(process.env.ProgramFiles||"C:\\Program Files","ClaudeCode","claude.exe"),J(process.env.LOCALAPPDATA||"","Programs","ClaudeCode","claude.exe"),J(pe(),"AppData","Local","Programs","ClaudeCode","claude.exe"),J(pe(),".local","bin","claude.exe")):r.push(J(pe(),".local","bin","claude"),"/opt/homebrew/bin/claude","/usr/local/bin/claude");for(let o of r)if(o&&ye(o))return o;return null}function zt(e){let t=[],r=e||"workermill-worker:local";try{let l=re("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim();t.push({name:"Docker",ok:!0,detail:l})}catch{t.push({name:"Docker",ok:!1,detail:"Not running or not installed"})}let o=ce();if(o)try{let l=re(`"${o}" --version`,{encoding:"utf-8",timeout:1e4}).trim();t.push({name:"Claude CLI",ok:!0,detail:l})}catch{t.push({name:"Claude CLI",ok:!0,detail:o})}else t.push({name:"Claude CLI",ok:!1,detail:"Not installed"});let n=pe(),i=J(n,".claude",".credentials.json");ye(i)?t.push({name:"Claude auth",ok:!0,detail:"Credentials found"}):t.push({name:"Claude auth",ok:!1,detail:"Run 'claude' and complete sign-in"});let s=process.version;parseInt(s.slice(1).split(".")[0],10)>=20?t.push({name:"Node.js",ok:!0,detail:s}):t.push({name:"Node.js",ok:!1,detail:`${s} (need >= 20)`});try{re(`docker image inspect ${r}`,{stdio:"ignore",timeout:1e4}),t.push({name:"Worker image",ok:!0,detail:r})}catch{t.push({name:"Worker image",ok:!1,detail:`'${r}' not found`})}return t}function Ve(){try{re("docker version",{stdio:"ignore"})}catch{console.error("Docker is not available. Please install Docker and ensure it's running."),process.exit(1)}let e=process.env.WORKER_IMAGE||"workermill-worker:local";try{re(`docker image inspect ${e}`,{stdio:"ignore"})}catch{console.error(`Worker image '${e}' not found.`),console.error(e==="workermill-worker:local"?"Build it with: ./bin/local-workermill build-worker":`Pull it with: docker pull ${e}`),process.exit(1)}ce()||(console.error("Claude CLI is not installed."),console.error("Install it: curl -fsSL https://claude.ai/install.sh | bash"),process.exit(1));let t=pe(),r=J(t,".claude",".credentials.json");ye(r)||(console.error("Claude credentials not found."),console.error("Run 'claude' and complete the sign-in flow to authenticate."),process.exit(1))}function Ye(){let e="unknown";try{e=re("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim()}catch{}let t="unknown",r=ce();if(r)try{t=re(`"${r}" --version`,{encoding:"utf-8",timeout:1e4}).trim()}catch{}return{hostname:Ge(),platform:process.platform,nodeVersion:process.version,dockerVersion:e,claudeVersion:t}}var me,ge,Kt,jt,Ae=Mt(()=>{"use strict";me=J(pe(),".workermill"),ge=J(me,"config.json"),Kt=J(me,"agent.pid"),jt=J(me,"agent.log")});import D from"chalk";import Nt from"axios";var Ne=null;function je(e,t){Ne=Nt.create({baseURL:e,headers:{"Content-Type":"application/json","x-api-key":t},timeout:3e4})}var P=new Proxy({},{get(e,t){if(!Ne)throw new Error("API client not initialized. Call initApi() first.");return Ne[t]}});import h from"chalk";Ae();import c from"chalk";import{spawn as $o,execSync as Fe}from"child_process";import{spawn as eo}from"child_process";import ie from"chalk";import be from"axios";var Ht=16384,qt=6e5;async function He(e,t,r,o,n){let i=n?.maxTokens??Ht,s=n?.temperature??.7,a=n?.timeoutMs??qt;switch(e){case"anthropic":return Jt(t,r,o,i,s,a);case"openai":return Xt(t,r,o,i,s,a);case"google":return Qt(t,r,o,i,s,a);case"ollama":return Zt(t,r,o,i,s,a);default:throw new Error(`Unsupported AI provider: ${e}`)}}async function Jt(e,t,r,o,n,i){let a=(await be.post("https://api.anthropic.com/v1/messages",{model:e,max_tokens:o,temperature:n,messages:[{role:"user",content:t}]},{headers:{"x-api-key":r,"anthropic-version":"2023-06-01","Content-Type":"application/json"},timeout:i})).data?.content;if(Array.isArray(a))return a.filter(l=>l.type==="text").map(l=>l.text).join("");throw new Error("Unexpected Anthropic API response format")}async function Xt(e,t,r,o,n,i){let a=(await be.post("https://api.openai.com/v1/chat/completions",{model:e,max_tokens:o,temperature:n,messages:[{role:"user",content:t}]},{headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},timeout:i})).data?.choices?.[0]?.message;if(a?.content)return a.content;throw new Error("Unexpected OpenAI API response format")}async function Qt(e,t,r,o,n,i){let l=(await be.post(`https://generativelanguage.googleapis.com/v1beta/models/${e}:generateContent?key=${r}`,{contents:[{parts:[{text:t}]}],generationConfig:{maxOutputTokens:o,temperature:n}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.candidates?.[0]?.content?.parts?.[0]?.text;if(l)return l;throw new Error("Unexpected Google AI API response format")}async function Zt(e,t,r,o,n,i){let s=r||"http://localhost:11434",l=(await be.post(`${s}/api/generate`,{model:e,prompt:t,stream:!1,options:{temperature:n}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.response;if(l)return l;throw new Error("Unexpected Ollama API response format")}var ne=null;async function Je(){if(ne)return ne;try{let{data:e}=await P.get("/api/agent/critic-prompt");return ne={promptTemplate:e.promptTemplate,approvalThreshold:e.approvalThreshold??85,maxTargetFiles:e.maxTargetFiles??15},X=ne.approvalThreshold,we=ne.maxTargetFiles,ne}catch{return console.warn("Failed to fetch critic config from API"),null}}var we=15,X=85;function Xe(e){let t=e.indexOf("```json");if(t!==-1){let o=t+7,n=e.indexOf("{",o);if(n!==-1){let i=qe(e,n);if(i)return JSON.parse(i)}}let r=e.indexOf('"stories"');if(r!==-1){let n=e.substring(0,r).lastIndexOf("{");if(n!==-1){let i=qe(e,n);if(i)return JSON.parse(i)}}throw new Error("Could not find JSON execution plan in output")}function qe(e,t){let r=0,o=!1,n=!1;for(let i=t;i<e.length;i++){let s=e[i];if(n){n=!1;continue}if(s==="\\"){o&&(n=!0);continue}if(s==='"'){o=!o;continue}if(!o){if(s==="{")r++;else if(s==="}"&&(r--,r===0))return e.substring(t,i+1)}}return null}function Qe(e){let t=0,r=[];for(let o of e.stories)if(!o.targetFiles||!Array.isArray(o.targetFiles))o.targetFiles=[];else if(o.targetFiles.length>we){let n=o.targetFiles.slice(we);r.push(`${o.id}: ${o.targetFiles.length} files \u2192 ${we} (dropped: ${n.join(", ")})`),o.targetFiles=o.targetFiles.slice(0,we),t++}return{truncatedCount:t,details:r}}function Ze(e,t){if(e.stories.length<=t)return{droppedCount:0,details:[]};let r=e.stories.length-t,n=e.stories.slice(t).map(s=>`${s.id}: "${s.title}" (${s.persona})`);e.stories=e.stories.slice(0,t);let i=new Set(e.stories.map(s=>s.id));for(let s of e.stories)s.dependencies=s.dependencies.filter(a=>i.has(a));return{droppedCount:r,details:n}}function et(e){let t=new Map,r=0,o=[];for(let n of e.stories){if(!n.targetFiles||n.targetFiles.length===0)continue;let i=[],s=[];for(let a of n.targetFiles)t.get(a)?s.push(a):(t.set(a,n.id),i.push(a));s.length>0&&(n.targetFiles=i,r+=s.length,o.push(`${n.id}: removed ${s.join(", ")} (owned by ${s.map(a=>t.get(a)).join(", ")})`))}return{resolvedCount:r,details:o}}function tt(e){return"```json\n"+JSON.stringify(e,null,2)+"\n```"}function to(e,t){if(!ne)return null;let r=JSON.stringify(t,null,2);return ne.promptTemplate.replace("{{PRD}}",e).replace("{{PLAN}}",r)}function oo(e){let t=e.trim();if(t.includes("```")){let n=t.match(/```(?:json)?\s*([\s\S]*?)```/);n&&(t=n[1].trim())}let r=t.indexOf("{");r>0&&(t=t.substring(r));let o=JSON.parse(t);return{approved:o.approved,score:Math.max(0,Math.min(100,Math.round(o.score))),risks:o.risks||[],suggestions:o.suggestions,storyFeedback:Array.isArray(o.storyFeedback)?o.storyFeedback:void 0}}function ro(e,t,r,o,n){return new Promise((i,s)=>{let a=eo(e,["--print","--model",t,"--permission-mode","bypassPermissions"],{env:o,stdio:["pipe","pipe","pipe"]});a.stdin.write(r),a.stdin.end();let l="",y="";a.stdout.on("data",d=>{let w=d.toString();l+=w;let k=w.split(`
2
- `).filter(C=>C.trim());for(let C of k){let _=C.trim().length>200?C.trim().substring(0,200)+"\u2026":C.trim();_&&(n&&nt(n,`${rt} [critic] ${_}`,"output"),console.log(`${Ie()} ${ie.dim("\u{1F50D}")} ${ie.dim(_)}`))}}),a.stderr.on("data",d=>{y+=d.toString()});let E=setTimeout(()=>{a.kill("SIGTERM"),s(new Error("Critic CLI timed out after 20 minutes"))},12e5);a.on("exit",d=>{clearTimeout(E),d!==0?s(new Error(`Critic CLI failed (exit ${d}): ${y.substring(0,300)}`)):i(l)}),a.on("error",d=>{clearTimeout(E),s(d)})})}function ot(e){let t=["","## CRITIC FEEDBACK \u2014 Your previous plan was REJECTED","",`Score: ${e.score}/100 (need >= ${X} to pass)`,""];if(e.risks.length>0){t.push("### Risks Identified:");for(let r of e.risks)t.push(`- ${r}`);t.push("")}if(e.suggestions&&e.suggestions.length>0){t.push("### Required Changes:");for(let r of e.suggestions)t.push(`- ${r}`);t.push("")}if(e.storyFeedback&&e.storyFeedback.length>0){t.push("### Per-Story Feedback:");for(let r of e.storyFeedback)if(t.push(`- **${r.storyId}**: ${r.feedback}`),r.suggestedChanges)for(let o of r.suggestedChanges)t.push(` - ${o}`);t.push("")}return t.push("**You MUST address ALL feedback above.** Each story must target at most 5 files.","Stories MUST NOT overlap on targetFiles. Generate a revised plan."),t.join(`
3
- `)}var rt="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Ie(){return ie.dim(new Date().toLocaleTimeString())}async function nt(e,t,r="system",o="info"){try{await P.post("/api/control-center/logs",{taskId:e,type:r,message:t,severity:o})}catch{}}async function it(e,t,r,o,n,i,s,a,l){let y=to(r,o);if(!y)return console.warn(`${Ie()} ${i} ${ie.yellow("\u26A0")} Critic config not available \u2014 skipping validation`),null;let E=s||"anthropic";console.log(`${Ie()} ${i} ${ie.dim(`Running critic validation (${E})...`)}`),l&&nt(l,`${rt} Running critic validation (${E})...`);try{let d;if(E==="anthropic")d=await ro(e,t,y,n,l);else{if(!a)throw new Error(`No API key for critic provider "${E}"`);d=await He(E,t,y,a,{maxTokens:4096,temperature:.3,timeoutMs:12e5})}let w=oo(d),k=w.score>=X?ie.green("\u2713"):ie.red("\u2717");return console.log(`${Ie()} ${i} ${k} Critic score: ${w.score}/100 (threshold: ${X})`),w}catch(d){let w=d instanceof Error?d.message:String(d);return console.error(`${Ie()} ${i} ${ie.yellow("\u26A0")} Critic failed: ${w.substring(0,100)}`),null}}import{generateText as no,tool as Le,stepCountIs as io}from"ai";import{createOpenAI as st}from"@ai-sdk/openai";import{createAnthropic as so}from"@ai-sdk/anthropic";import{createGoogleGenerativeAI as ao}from"@ai-sdk/google";import{z as se}from"zod";import{execSync as Ue}from"child_process";import{readFileSync as lo,existsSync as co}from"fs";function uo(e,t,r){switch(e){case"anthropic":return so({apiKey:r})(t);case"openai":return st({apiKey:r})(t);case"google":return ao({apiKey:r})(t);case"ollama":return st({baseURL:r||"http://localhost:11434/v1",apiKey:"ollama"})(t);default:throw new Error(`Unsupported AI provider: ${e}`)}}var po=se.object({pattern:se.string().describe("Glob pattern like '**/*.ts', 'src/**/*.js', 'package.json'")}),go=se.object({path:se.string().describe("File path relative to the working directory"),limit:se.number().optional().describe("Max number of lines to read (default: 500)")}),mo=se.object({pattern:se.string().describe("Search pattern (regex supported)"),glob:se.string().optional().describe("File glob to filter (e.g. '*.ts', '*.py')")});function fo(e){return{glob:Le({description:"Find files matching a glob pattern. Returns file paths relative to the working directory.",inputSchema:po,execute:async t=>{try{let r=Ue(`find . -path './.git' -prune -o -path './node_modules' -prune -o -name '${t.pattern.replace(/\*\*/g,"*")}' -print 2>/dev/null | head -200`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim();return r||Ue("find . -path './.git' -prune -o -path './node_modules' -prune -o -type f -print 2>/dev/null | head -500",{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No files found"}catch{return"Error running glob search"}}}),read_file:Le({description:"Read the contents of a file. Returns the file text.",inputSchema:go,execute:async t=>{try{let r=`${e}/${t.path}`.replace(/\/\//g,"/");if(!co(r))return`File not found: ${t.path}`;let o=lo(r,"utf-8"),n=o.split(`
1
+ var Mt=Object.defineProperty;var Ot=(e,t)=>()=>(e&&(t=e(e=0)),t);var Nt=(e,t)=>{for(var r in t)Mt(e,r,{get:t[r],enumerable:!0})};var He={};Nt(He,{checkPrerequisites:()=>Ht,findClaudePath:()=>ce,getConfigDir:()=>Bt,getConfigFile:()=>Wt,getLogFile:()=>Yt,getPidFile:()=>Vt,getSystemInfo:()=>ze,loadConfig:()=>Ve,loadConfigFromFile:()=>We,saveConfigToFile:()=>zt,validatePrerequisites:()=>Ye});import{existsSync as ye,readFileSync as Ut,mkdirSync as Dt,writeFileSync as Ft,chmodSync as Kt}from"fs";import{execSync as re}from"child_process";import{hostname as Be,homedir as pe}from"os";import{join as J}from"path";function Bt(){return me}function Wt(){return ge}function Vt(){return jt}function Yt(){return Gt}function We(){ye(ge)||(console.error("No config found. Run 'workermill-agent setup' first."),process.exit(1));let e;try{e=Ut(ge,"utf-8")}catch{console.error("Failed to read config file:",ge),process.exit(1)}let t;try{t=JSON.parse(e)}catch{console.error("Config file is corrupted. Re-run 'workermill-agent setup'."),process.exit(1)}(!t.apiUrl||!t.apiKey)&&(console.error("Config file is missing required fields (apiUrl, apiKey). Re-run 'workermill-agent setup'."),process.exit(1));let r=t.workerImage||"workermill-worker:local";return{apiUrl:t.apiUrl,apiKey:t.apiKey,agentId:t.agentId,maxWorkers:t.maxWorkers||4,pollIntervalMs:t.pollIntervalMs||5e3,heartbeatIntervalMs:t.heartbeatIntervalMs||3e4,githubToken:t.tokens?.github||"",bitbucketToken:t.tokens?.bitbucket||"",gitlabToken:t.tokens?.gitlab||"",workerImage:r}}function zt(e){ye(me)||Dt(me,{recursive:!0}),Ft(ge,JSON.stringify(e,null,2),"utf-8");try{Kt(ge,384)}catch{}}function Ve(){let e=process.env.WORKERMILL_API_URL,t=process.env.WORKERMILL_API_KEY;return e||(console.error("WORKERMILL_API_URL is required in .env.remote"),process.exit(1)),t||(console.error("WORKERMILL_API_KEY is required in .env.remote"),console.error("Get your API key from Settings > Integrations on the WorkerMill dashboard."),process.exit(1)),{apiUrl:e.replace(/\/$/,""),apiKey:t,agentId:process.env.AGENT_ID||`agent-${Be()}`,maxWorkers:parseInt(process.env.MAX_WORKERS||"4",10),pollIntervalMs:parseInt(process.env.POLL_INTERVAL_MS||"5000",10),heartbeatIntervalMs:parseInt(process.env.HEARTBEAT_INTERVAL_MS||"30000",10),githubToken:process.env.GITHUB_TOKEN||"",bitbucketToken:process.env.BITBUCKET_TOKEN||"",gitlabToken:process.env.GITLAB_TOKEN||"",workerImage:process.env.WORKER_IMAGE||"workermill-worker:local"}}function ce(){let e=process.platform==="win32",t=e?"where":"which";try{return re(`${t} claude`,{stdio:"ignore",timeout:1e4}),"claude"}catch{}let r=[];e?r.push(J(process.env.ProgramFiles||"C:\\Program Files","ClaudeCode","claude.exe"),J(process.env.LOCALAPPDATA||"","Programs","ClaudeCode","claude.exe"),J(pe(),"AppData","Local","Programs","ClaudeCode","claude.exe"),J(pe(),".local","bin","claude.exe")):r.push(J(pe(),".local","bin","claude"),"/opt/homebrew/bin/claude","/usr/local/bin/claude");for(let o of r)if(o&&ye(o))return o;return null}function Ht(e){let t=[],r=e||"workermill-worker:local";try{let l=re("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim();t.push({name:"Docker",ok:!0,detail:l})}catch{t.push({name:"Docker",ok:!1,detail:"Not running or not installed"})}let o=ce();if(o)try{let l=re(`"${o}" --version`,{encoding:"utf-8",timeout:1e4}).trim();t.push({name:"Claude CLI",ok:!0,detail:l})}catch{t.push({name:"Claude CLI",ok:!0,detail:o})}else t.push({name:"Claude CLI",ok:!1,detail:"Not installed"});let n=pe(),i=J(n,".claude",".credentials.json");ye(i)?t.push({name:"Claude auth",ok:!0,detail:"Credentials found"}):t.push({name:"Claude auth",ok:!1,detail:"Run 'claude' and complete sign-in"});let s=process.version;parseInt(s.slice(1).split(".")[0],10)>=20?t.push({name:"Node.js",ok:!0,detail:s}):t.push({name:"Node.js",ok:!1,detail:`${s} (need >= 20)`});try{re(`docker image inspect ${r}`,{stdio:"ignore",timeout:1e4}),t.push({name:"Worker image",ok:!0,detail:r})}catch{t.push({name:"Worker image",ok:!1,detail:`'${r}' not found`})}return t}function Ye(){try{re("docker version",{stdio:"ignore"})}catch{console.error("Docker is not available. Please install Docker and ensure it's running."),process.exit(1)}let e=process.env.WORKER_IMAGE||"workermill-worker:local";try{re(`docker image inspect ${e}`,{stdio:"ignore"})}catch{console.error(`Worker image '${e}' not found.`),console.error(e==="workermill-worker:local"?"Build it with: ./bin/local-workermill build-worker":`Pull it with: docker pull ${e}`),process.exit(1)}ce()||(console.error("Claude CLI is not installed."),console.error("Install it: curl -fsSL https://claude.ai/install.sh | bash"),process.exit(1));let t=pe(),r=J(t,".claude",".credentials.json");ye(r)||(console.error("Claude credentials not found."),console.error("Run 'claude' and complete the sign-in flow to authenticate."),process.exit(1))}function ze(){let e="unknown";try{e=re("docker version --format {{.Server.Version}}",{encoding:"utf-8",timeout:1e4}).trim()}catch{}let t="unknown",r=ce();if(r)try{t=re(`"${r}" --version`,{encoding:"utf-8",timeout:1e4}).trim()}catch{}return{hostname:Be(),platform:process.platform,nodeVersion:process.version,dockerVersion:e,claudeVersion:t}}var me,ge,jt,Gt,Ae=Ot(()=>{"use strict";me=J(pe(),".workermill"),ge=J(me,"config.json"),jt=J(me,"agent.pid"),Gt=J(me,"agent.log")});import D from"chalk";import Lt from"axios";var Le=null;function Ge(e,t){Le=Lt.create({baseURL:e,headers:{"Content-Type":"application/json","x-api-key":t},timeout:3e4})}var P=new Proxy({},{get(e,t){if(!Le)throw new Error("API client not initialized. Call initApi() first.");return Le[t]}});import h from"chalk";Ae();import c from"chalk";import{spawn as ho,execSync as Ke}from"child_process";import{spawn as to}from"child_process";import ie from"chalk";import be from"axios";var qt=16384,Jt=6e5;async function qe(e,t,r,o,n){let i=n?.maxTokens??qt,s=n?.temperature??.7,a=n?.timeoutMs??Jt;switch(e){case"anthropic":return Xt(t,r,o,i,s,a);case"openai":return Qt(t,r,o,i,s,a);case"google":return Zt(t,r,o,i,s,a);case"ollama":return eo(t,r,o,i,s,a);default:throw new Error(`Unsupported AI provider: ${e}`)}}async function Xt(e,t,r,o,n,i){let a=(await be.post("https://api.anthropic.com/v1/messages",{model:e,max_tokens:o,temperature:n,messages:[{role:"user",content:t}]},{headers:{"x-api-key":r,"anthropic-version":"2023-06-01","Content-Type":"application/json"},timeout:i})).data?.content;if(Array.isArray(a))return a.filter(l=>l.type==="text").map(l=>l.text).join("");throw new Error("Unexpected Anthropic API response format")}async function Qt(e,t,r,o,n,i){let a=(await be.post("https://api.openai.com/v1/chat/completions",{model:e,max_tokens:o,temperature:n,messages:[{role:"user",content:t}]},{headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},timeout:i})).data?.choices?.[0]?.message;if(a?.content)return a.content;throw new Error("Unexpected OpenAI API response format")}async function Zt(e,t,r,o,n,i){let l=(await be.post(`https://generativelanguage.googleapis.com/v1beta/models/${e}:generateContent?key=${r}`,{contents:[{parts:[{text:t}]}],generationConfig:{maxOutputTokens:o,temperature:n}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.candidates?.[0]?.content?.parts?.[0]?.text;if(l)return l;throw new Error("Unexpected Google AI API response format")}async function eo(e,t,r,o,n,i){let s=r||"http://localhost:11434",l=(await be.post(`${s}/api/generate`,{model:e,prompt:t,stream:!1,options:{temperature:n}},{headers:{"Content-Type":"application/json"},timeout:i})).data?.response;if(l)return l;throw new Error("Unexpected Ollama API response format")}var ne=null;async function Xe(){if(ne)return ne;try{let{data:e}=await P.get("/api/agent/critic-prompt");return ne={promptTemplate:e.promptTemplate,approvalThreshold:e.approvalThreshold??85,maxTargetFiles:e.maxTargetFiles??15},X=ne.approvalThreshold,we=ne.maxTargetFiles,ne}catch{return console.warn("Failed to fetch critic config from API"),null}}var we=15,X=85;function Qe(e){let t=e.indexOf("```json");if(t!==-1){let o=t+7,n=e.indexOf("{",o);if(n!==-1){let i=Je(e,n);if(i)return JSON.parse(i)}}let r=e.indexOf('"stories"');if(r!==-1){let n=e.substring(0,r).lastIndexOf("{");if(n!==-1){let i=Je(e,n);if(i)return JSON.parse(i)}}throw new Error("Could not find JSON execution plan in output")}function Je(e,t){let r=0,o=!1,n=!1;for(let i=t;i<e.length;i++){let s=e[i];if(n){n=!1;continue}if(s==="\\"){o&&(n=!0);continue}if(s==='"'){o=!o;continue}if(!o){if(s==="{")r++;else if(s==="}"&&(r--,r===0))return e.substring(t,i+1)}}return null}function Ze(e){let t=0,r=[];for(let o of e.stories)if(!o.targetFiles||!Array.isArray(o.targetFiles))o.targetFiles=[];else if(o.targetFiles.length>we){let n=o.targetFiles.slice(we);r.push(`${o.id}: ${o.targetFiles.length} files \u2192 ${we} (dropped: ${n.join(", ")})`),o.targetFiles=o.targetFiles.slice(0,we),t++}return{truncatedCount:t,details:r}}function et(e,t){if(e.stories.length<=t)return{droppedCount:0,details:[]};let r=e.stories.length-t,n=e.stories.slice(t).map(s=>`${s.id}: "${s.title}" (${s.persona})`);e.stories=e.stories.slice(0,t);let i=new Set(e.stories.map(s=>s.id));for(let s of e.stories)s.dependencies=s.dependencies.filter(a=>i.has(a));return{droppedCount:r,details:n}}function tt(e){let t=new Map,r=0,o=[];for(let n of e.stories){if(!n.targetFiles||n.targetFiles.length===0)continue;let i=[],s=[];for(let a of n.targetFiles)t.get(a)?s.push(a):(t.set(a,n.id),i.push(a));s.length>0&&(n.targetFiles=i,r+=s.length,o.push(`${n.id}: removed ${s.join(", ")} (owned by ${s.map(a=>t.get(a)).join(", ")})`))}return{resolvedCount:r,details:o}}function ot(e){return"```json\n"+JSON.stringify(e,null,2)+"\n```"}function oo(e,t){if(!ne)return null;let r=JSON.stringify(t,null,2);return ne.promptTemplate.replace("{{PRD}}",e).replace("{{PLAN}}",r)}function ro(e){let t=e.trim();if(t.includes("```")){let n=t.match(/```(?:json)?\s*([\s\S]*?)```/);n&&(t=n[1].trim())}let r=t.indexOf("{");r>0&&(t=t.substring(r));let o=JSON.parse(t);return{approved:o.approved,score:Math.max(0,Math.min(100,Math.round(o.score))),risks:o.risks||[],suggestions:o.suggestions,storyFeedback:Array.isArray(o.storyFeedback)?o.storyFeedback:void 0}}function no(e,t,r,o,n){return new Promise((i,s)=>{let a=to(e,["--print","--model",t,"--permission-mode","bypassPermissions"],{env:o,stdio:["pipe","pipe","pipe"]});a.stdin.write(r),a.stdin.end();let l="",y="";a.stdout.on("data",d=>{let w=d.toString();l+=w;let k=w.split(`
2
+ `).filter(C=>C.trim());for(let C of k){let _=C.trim().length>200?C.trim().substring(0,200)+"\u2026":C.trim();_&&(n&&it(n,`${nt} [critic] ${_}`,"output"),console.log(`${Ee()} ${ie.dim("\u{1F50D}")} ${ie.dim(_)}`))}}),a.stderr.on("data",d=>{y+=d.toString()});let I=setTimeout(()=>{a.kill("SIGTERM"),s(new Error("Critic CLI timed out after 20 minutes"))},12e5);a.on("exit",d=>{clearTimeout(I),d!==0?s(new Error(`Critic CLI failed (exit ${d}): ${y.substring(0,300)}`)):i(l)}),a.on("error",d=>{clearTimeout(I),s(d)})})}function rt(e){let t=["","## CRITIC FEEDBACK \u2014 Your previous plan was REJECTED","",`Score: ${e.score}/100 (need >= ${X} to pass)`,""];if(e.risks.length>0){t.push("### Risks Identified:");for(let r of e.risks)t.push(`- ${r}`);t.push("")}if(e.suggestions&&e.suggestions.length>0){t.push("### Required Changes:");for(let r of e.suggestions)t.push(`- ${r}`);t.push("")}if(e.storyFeedback&&e.storyFeedback.length>0){t.push("### Per-Story Feedback:");for(let r of e.storyFeedback)if(t.push(`- **${r.storyId}**: ${r.feedback}`),r.suggestedChanges)for(let o of r.suggestedChanges)t.push(` - ${o}`);t.push("")}return t.push("**You MUST address ALL feedback above.** Each story must target at most 5 files.","Stories MUST NOT overlap on targetFiles. Generate a revised plan."),t.join(`
3
+ `)}var nt="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Ee(){return ie.dim(new Date().toLocaleTimeString())}async function it(e,t,r="system",o="info"){try{await P.post("/api/control-center/logs",{taskId:e,type:r,message:t,severity:o})}catch{}}async function st(e,t,r,o,n,i,s,a,l){let y=oo(r,o);if(!y)return console.warn(`${Ee()} ${i} ${ie.yellow("\u26A0")} Critic config not available \u2014 skipping validation`),null;let I=s||"anthropic";console.log(`${Ee()} ${i} ${ie.dim(`Running critic validation (${I})...`)}`),l&&it(l,`${nt} Running critic validation (${I})...`);try{let d;if(I==="anthropic")d=await no(e,t,y,n,l);else{if(!a)throw new Error(`No API key for critic provider "${I}"`);d=await qe(I,t,y,a,{maxTokens:4096,temperature:.3,timeoutMs:12e5})}let w=ro(d),k=w.score>=X?ie.green("\u2713"):ie.red("\u2717");return console.log(`${Ee()} ${i} ${k} Critic score: ${w.score}/100 (threshold: ${X})`),w}catch(d){let w=d instanceof Error?d.message:String(d);return console.error(`${Ee()} ${i} ${ie.yellow("\u26A0")} Critic failed: ${w.substring(0,100)}`),null}}import{generateText as io,tool as Ue,stepCountIs as so}from"ai";import{createOpenAI as at}from"@ai-sdk/openai";import{createAnthropic as ao}from"@ai-sdk/anthropic";import{createGoogleGenerativeAI as lo}from"@ai-sdk/google";import{z as se}from"zod";import{execSync as De}from"child_process";import{readFileSync as co,existsSync as uo}from"fs";function po(e,t,r){switch(e){case"anthropic":return ao({apiKey:r})(t);case"openai":return at({apiKey:r})(t);case"google":return lo({apiKey:r})(t);case"ollama":return at({baseURL:r||"http://localhost:11434/v1",apiKey:"ollama"})(t);default:throw new Error(`Unsupported AI provider: ${e}`)}}var go=se.object({pattern:se.string().describe("Glob pattern like '**/*.ts', 'src/**/*.js', 'package.json'")}),mo=se.object({path:se.string().describe("File path relative to the working directory"),limit:se.number().optional().describe("Max number of lines to read (default: 500)")}),fo=se.object({pattern:se.string().describe("Search pattern (regex supported)"),glob:se.string().optional().describe("File glob to filter (e.g. '*.ts', '*.py')")});function $o(e){return{glob:Ue({description:"Find files matching a glob pattern. Returns file paths relative to the working directory.",inputSchema:go,execute:async t=>{try{let r=De(`find . -path './.git' -prune -o -path './node_modules' -prune -o -name '${t.pattern.replace(/\*\*/g,"*")}' -print 2>/dev/null | head -200`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim();return r||De("find . -path './.git' -prune -o -path './node_modules' -prune -o -type f -print 2>/dev/null | head -500",{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No files found"}catch{return"Error running glob search"}}}),read_file:Ue({description:"Read the contents of a file. Returns the file text.",inputSchema:mo,execute:async t=>{try{let r=`${e}/${t.path}`.replace(/\/\//g,"/");if(!uo(r))return`File not found: ${t.path}`;let o=co(r,"utf-8"),n=o.split(`
4
4
  `),i=t.limit||500;return n.length>i?n.slice(0,i).join(`
5
5
  `)+`
6
- ... (truncated, ${n.length-i} more lines)`:o}catch(r){return`Error reading file: ${r instanceof Error?r.message:String(r)}`}}}),grep:Le({description:"Search for a pattern in files. Returns matching lines with file paths and line numbers.",inputSchema:mo,execute:async t=>{try{let r=t.glob?`--include='${t.glob}'`:"";return Ue(`grep -rn ${r} --exclude-dir=node_modules --exclude-dir=.git '${t.pattern.replace(/'/g,"'\\''")}' . 2>/dev/null | head -100`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No matches found"}catch{return"No matches found"}}})}}async function at(e){let{provider:t,model:r,apiKey:o,prompt:n,systemPrompt:i,workingDir:s,maxTokens:a=16384,temperature:l=.7,timeoutMs:y=6e5,maxSteps:E=15,enableTools:d=!0}=e,w=uo(t,r,o),k=d&&s?fo(s):void 0,C=new AbortController,_=setTimeout(()=>C.abort(),y);try{return(await no({model:w,prompt:n,system:i,maxOutputTokens:a,temperature:l,tools:k,stopWhen:k?io(E):void 0,abortSignal:C.signal})).text}finally{clearTimeout(_)}}function ho(e,t){let r=[e.usage,e.message?.usage,e.result?.usage];for(let o of r)if(o&&typeof o=="object"){let n=o;typeof n.input_tokens=="number"&&(t.inputTokens=Math.max(t.inputTokens,n.input_tokens)),typeof n.output_tokens=="number"&&(t.outputTokens=Math.max(t.outputTokens,n.output_tokens)),typeof n.cache_creation_input_tokens=="number"&&(t.cacheCreationTokens=Math.max(t.cacheCreationTokens,n.cache_creation_input_tokens)),typeof n.cache_read_input_tokens=="number"&&(t.cacheReadTokens=Math.max(t.cacheReadTokens,n.cache_read_input_tokens))}}async function lt(e,t,r,o){if(!(t.inputTokens===0&&t.outputTokens===0))try{await P.post(`/api/tasks/${e}/usage/partial`,{inputTokens:t.inputTokens,outputTokens:t.outputTokens,cacheCreationTokens:t.cacheCreationTokens,cacheReadTokens:t.cacheReadTokens,model:r,mode:o})}catch{}}var te=3;function m(){return c.dim(new Date().toLocaleTimeString())}var fe=[],ae=null;async function ut(){for(;fe.length>0;){let e=fe.splice(0,50);try{await P.post("/api/control-center/logs/batch",{entries:e},{timeout:5e3})}catch{}}}async function T(e,t,r="system",o="info"){fe.length>=200&&fe.shift(),fe.push({taskId:e,message:t,type:r,severity:o}),ae||(ae=ut().finally(()=>{ae=null}))}async function yo(){ae&&await ae,fe.length>0&&(ae=ut().finally(()=>{ae=null}),await ae)}async function $e(e,t,r,o,n,i){try{await P.post("/api/agent/planning-progress",{taskId:e,phase:t,elapsedSeconds:r,detail:o,charsGenerated:n,toolCallCount:i})}catch{}}var $="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Te(e){let t=Math.floor(e/60),r=e%60;return t>0?`${t}m ${r}s`:`${r}s`}function ct(e,t){switch(e){case"initializing":return`${$} Starting planning agent...`;case"reading_repo":return`${$} Reading repository structure...`;case"analyzing":return`${$} Analyzing requirements...`;case"generating_plan":return`${$} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Te(t)} elapsed)`;case"validating":return`${$} Validating plan...`;case"complete":return`${$} Planning complete`}}function wo(e,t,r,o,n,i,s){let a=c.cyan(n.slice(0,8));return new Promise((l,y)=>{let d=$o(e,["--print","--verbose","--output-format","stream-json","--model",t,"--permission-mode","bypassPermissions"],{cwd:s,env:o,stdio:["pipe","pipe","pipe"]});d.stdin.write(r),d.stdin.end();let w="",k="",C="",_=0,x=0,G={inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0},K=t,I="";function L(A=!1){if(!I)return;let W=I.split(`
7
- `),ee=A?"":W.pop()||"";for(let z of W)if(z.trim()){T(n,`${$} ${z}`,"output");let S=z.trim().length>160?z.trim().substring(0,160)+"\u2026":z.trim();console.log(`${m()} ${a} ${c.dim("\u{1F4AD}")} ${c.dim(S)}`)}I=ee}let f="initializing",U=!1,p={started:!0,reading:!1,analyzing:!1,generating:!1};function M(A){if(A===f)return;f=A;let W=Math.round((Date.now()-i)/1e3),ee=ct(A,W);T(n,ee),console.log(`${m()} ${a} ${c.dim(ee)}`)}let O=setInterval(()=>L(),500),F=setInterval(()=>{let A=Math.round((Date.now()-i)/1e3);$e(n,f,A,ct(f,A),_,x)},2e3),j=0,Q=setInterval(()=>{let A=Math.round((Date.now()-i)/1e3);if(f==="initializing"&&A>=5?M("reading_repo"):f==="reading_repo"&&A>=15&&!U&&M("analyzing"),f==="generating_plan"&&A-j>=30){j=A;let W=`${$} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Te(A)} elapsed)`;T(n,W),console.log(`${m()} ${a} ${c.dim(W)}`)}},5e3),Y="";d.stdout.on("data",A=>{Y+=A.toString();let W=Y.split(`
8
- `);Y=W.pop()||"";for(let ee of W){let z=ee.trim();if(z)try{let S=JSON.parse(z);if(S.type==="assistant"&&S.message?.content){let H=S.message.content;if(Array.isArray(H))for(let g of H)g.type==="text"&&g.text?(w+=g.text,_+=g.text.length,I+=g.text,U||(U=!0,x>0&&!p.analyzing&&(M("analyzing"),p.analyzing=!0)),_>500&&!p.generating&&(M("generating_plan"),p.generating=!0,j=Math.round((Date.now()-i)/1e3))):g.type==="tool_use"&&(x++,p.reading||(M("reading_repo"),p.reading=!0));else typeof H=="string"&&H&&(w+=H,_+=H.length,I+=H)}else S.type==="content_block_delta"&&S.delta?.text?(w+=S.delta.text,_+=S.delta.text.length,I+=S.delta.text,U||(U=!0,x>0&&!p.analyzing&&(M("analyzing"),p.analyzing=!0)),_>500&&!p.generating&&(M("generating_plan"),p.generating=!0,j=Math.round((Date.now()-i)/1e3))):S.type==="content_block_start"&&S.content_block?.type==="tool_use"?(x++,p.reading||(M("reading_repo"),p.reading=!0)):S.type==="result"&&S.result&&(k=typeof S.result=="string"?S.result:"");if(ho(S,G),S.type==="result"&&S.total_cost_usd!==void 0&&S.modelUsage&&typeof S.modelUsage=="object"){let H=Object.keys(S.modelUsage);H.length>0&&(K=H[0])}}catch{w+=z+`
9
- `,_+=z.length}}}),d.stderr.on("data",A=>{C+=A.toString()});let oe=setInterval(()=>{(G.inputTokens>0||G.outputTokens>0)&&lt(n,G,K,"greatest").catch(()=>{})},3e4);function he(){clearInterval(Q),clearInterval(F),clearInterval(O),clearInterval(oe),L(!0)}let B=setTimeout(()=>{he(),d.kill("SIGTERM"),y(new Error("Claude CLI timed out after 20 minutes"))},12e5);d.on("exit",A=>{clearTimeout(B),he();let W=Math.round((Date.now()-i)/1e3);$e(n,"validating",W,"Validating plan...",_,x),lt(n,G,K,"greatest").catch(()=>{}),A!==0?y(new Error(`Claude CLI failed (exit ${A}): ${C.substring(0,300)}`)):l(k||w)}),d.on("error",A=>{clearTimeout(B),he(),y(A)})})}function Io(e,t){if(t)switch(e){case"anthropic":return t.anthropicApiKey;case"openai":return t.openaiApiKey;case"google":return t.googleApiKey;case"ollama":return t.ollamaBaseUrl||"http://localhost:11434";default:return}}function Eo(e,t,r){switch(r){case"bitbucket":return`https://x-token-auth:${t}@bitbucket.org/${e}.git`;case"gitlab":return`https://oauth2:${t}@gitlab.com/${e}.git`;default:return`https://x-access-token:${t}@github.com/${e}.git`}}async function Ao(e,t,r,o){let n=c.cyan(o.slice(0,8)),i=`/tmp/workermill-planning-${o.slice(0,8)}-${Date.now()}`;try{let s=Eo(e,t,r);return console.log(`${m()} ${n} ${c.dim("Cloning repo for planner...")}`),Fe(`git clone --depth 1 --single-branch "${s}" "${i}"`,{stdio:"ignore",timeout:6e4}),console.log(`${m()} ${n} ${c.green("\u2713")} Repo cloned to ${c.dim(i)}`),i}catch(s){let a=s instanceof Error?s.message:String(s);console.error(`${m()} ${n} ${c.yellow("\u26A0")} Clone failed, planner will run without repo access: ${a.substring(0,100)}`);try{Fe(`rm -rf "${i}"`,{stdio:"ignore"})}catch{}return null}}async function dt(e,t,r){let o=c.cyan(e.id.slice(0,8));console.log(`${m()} ${o} Fetching planning prompt...`),await T(e.id,`${$} Fetching planning prompt from cloud API...`);let n=await P.get("/api/agent/planning-prompt",{params:{taskId:e.id}}),{prompt:i,model:s,provider:a,maxStories:l}=n.data,y=typeof l=="number"?l:8,E=s,d=a||"anthropic",w=d==="anthropic",k=process.env.CLAUDE_CLI_PATH||ce()||"claude",C={...process.env};delete C.CLAUDE_CODE_OAUTH_TOKEN;let _=Io(d,r),x=Date.now(),G=e.description||e.summary,K=null;if(e.githubRepo){let O=e.scmProvider||"github",F=O==="bitbucket"?t.bitbucketToken:O==="gitlab"?t.gitlabToken:t.githubToken;F?K=await Ao(e.githubRepo,F,O,e.id):console.log(`${m()} ${o} ${c.yellow("\u26A0")} No SCM token for ${O}, planner will run without repo access`)}let I=i,L=null,f=0,U=[],p=0,M=await Je();M||(console.log(`${m()} ${o} ${c.yellow("\u26A0")} Could not fetch critic config \u2014 critic validation will be skipped`),await T(e.id,`${$} \u26A0\uFE0F Could not fetch critic config from API \u2014 critic validation will be skipped`));try{for(let F=1;F<=te;F++){let j=te>1?` (attempt ${F}/${te})`:"",Q=`${d}/${E}`;F>1?(console.log(`${m()} ${o} Running planner${j} ${c.dim(`(${c.yellow(Q)})`)}`),await T(e.id,`${$} Re-planning${j} using ${Q}`)):(console.log(`${m()} ${o} Running planner ${c.dim(`(${c.yellow(Q)})`)}`),await T(e.id,`${$} Starting planning agent using ${Q}`));let Y;try{if(w)Y=await wo(k,E,I,C,e.id,x,K||void 0);else{if(!_)throw new Error(`No API key available for provider "${d}". Configure it in Settings > Integrations.`);let b=Math.round((Date.now()-x)/1e3);await $e(e.id,"generating_plan",b,"Generating plan via AI SDK...",0,0),Y=await at({provider:d,model:E,apiKey:_,prompt:I,workingDir:K||void 0,enableTools:!!K,maxSteps:10});let R=Math.round((Date.now()-x)/1e3);await $e(e.id,"validating",R,"Validating plan...",Y.length,0)}}catch(b){let R=Math.round((Date.now()-x)/1e3),q=b instanceof Error?b.message:String(b);return console.error(`${m()} ${o} ${c.red("\u2717")} Failed after ${R}s: ${q.substring(0,100)}`),await T(e.id,`${$} Planning failed after ${Te(R)}: ${q.substring(0,200)}`,"error","error"),!1}let oe=Math.round((Date.now()-x)/1e3),he=w?"Claude CLI":`${d} API`;console.log(`${m()} ${o} ${c.green("\u2713")} ${he} done ${c.dim(`(${oe}s, ${Y.length} chars)`)}`);let B;try{B=Xe(Y)}catch(b){let R=b instanceof Error?b.message:String(b);return console.error(`${m()} ${o} ${c.red("\u2717")} Plan parse failed: ${R.substring(0,100)}`),await T(e.id,`${$} Failed to parse execution plan from Claude output: ${R.substring(0,200)}`,"error","error"),await bo(e.id,Y,t.agentId,o,oe)}let{truncatedCount:A,details:W}=Qe(B);if(A>0){p+=A;let b=`${$} File cap applied: ${A} stories truncated to max ${M?.maxTargetFiles??15} targetFiles`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b);for(let R of W)console.log(`${m()} ${o} ${c.dim(R)}`)}let{droppedCount:ee,details:z}=Ze(B,y);if(ee>0){let b=`${$} Story cap applied: ${ee} stories dropped (max ${y})`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b);for(let R of z)console.log(`${m()} ${o} ${c.dim(R)}`)}let{resolvedCount:S,details:H}=et(B);if(S>0){let b=`${$} File overlap resolved: ${S} shared file(s) de-duped across stories`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b);for(let R of H)console.log(`${m()} ${o} ${c.dim(R)}`)}console.log(`${m()} ${o} Plan: ${c.bold(B.stories.length)} stories (max ${y})`),await T(e.id,`${$} Plan generated: ${B.stories.length} stories (${Te(oe)}). Running critic validation...`);let g=await it(k,E,G,B,C,o,d,_,e.id);if(g&&g.score>f?(L=B,f=g.score):!g&&!L&&(L=B),g&&U.push({iteration:F,score:g.score,approved:g.approved||g.score>=X,risks:g.risks,suggestions:g.suggestions,filesCapApplied:A>0?A:void 0}),!g){let b=`${$} \u26A0\uFE0F CRITIC BYPASSED \u2014 Critic validation failed (timeout/parse error). Posting plan WITHOUT quality gate.`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b,"error","warning");let R=Date.now()-x;return await De(e.id,B,t.agentId,o,oe,void 0,void 0,U,p,R,F)}if(g.approved||g.score>=X){let b=`${$} Critic approved (score: ${g.score}/100)`;if(console.log(`${m()} ${o} ${c.green("\u2713")} ${b}`),await T(e.id,b),g.risks.length>0){let q=`${$} Critic risks (non-blocking): ${g.risks.join("; ")}`;console.log(`${m()} ${o} ${c.dim(q)}`),await T(e.id,q)}let R=Date.now()-x;return await De(e.id,B,t.agentId,o,oe,g.score,g.risks,U,p,R,F)}if(F<te){let b=ot(g);I=i+`
6
+ ... (truncated, ${n.length-i} more lines)`:o}catch(r){return`Error reading file: ${r instanceof Error?r.message:String(r)}`}}}),grep:Ue({description:"Search for a pattern in files. Returns matching lines with file paths and line numbers.",inputSchema:fo,execute:async t=>{try{let r=t.glob?`--include='${t.glob}'`:"";return De(`grep -rn ${r} --exclude-dir=node_modules --exclude-dir=.git '${t.pattern.replace(/'/g,"'\\''")}' . 2>/dev/null | head -100`,{cwd:e,encoding:"utf-8",timeout:15e3}).trim()||"No matches found"}catch{return"No matches found"}}})}}async function lt(e){let{provider:t,model:r,apiKey:o,prompt:n,systemPrompt:i,workingDir:s,maxTokens:a=16384,temperature:l=.7,timeoutMs:y=6e5,maxSteps:I=15,enableTools:d=!0}=e,w=po(t,r,o),k=d&&s?$o(s):void 0,C=new AbortController,_=setTimeout(()=>C.abort(),y);try{return(await io({model:w,prompt:n,system:i,maxOutputTokens:a,temperature:l,tools:k,stopWhen:k?so(I):void 0,abortSignal:C.signal})).text}finally{clearTimeout(_)}}function yo(e,t){let r=[e.usage,e.message?.usage,e.result?.usage];for(let o of r)if(o&&typeof o=="object"){let n=o;typeof n.input_tokens=="number"&&(t.inputTokens=Math.max(t.inputTokens,n.input_tokens)),typeof n.output_tokens=="number"&&(t.outputTokens=Math.max(t.outputTokens,n.output_tokens)),typeof n.cache_creation_input_tokens=="number"&&(t.cacheCreationTokens=Math.max(t.cacheCreationTokens,n.cache_creation_input_tokens)),typeof n.cache_read_input_tokens=="number"&&(t.cacheReadTokens=Math.max(t.cacheReadTokens,n.cache_read_input_tokens))}}async function ct(e,t,r,o){if(!(t.inputTokens===0&&t.outputTokens===0))try{await P.post(`/api/tasks/${e}/usage/partial`,{inputTokens:t.inputTokens,outputTokens:t.outputTokens,cacheCreationTokens:t.cacheCreationTokens,cacheReadTokens:t.cacheReadTokens,model:r,mode:o})}catch{}}var te=3;function m(){return c.dim(new Date().toLocaleTimeString())}var fe=[],ae=null;async function dt(){for(;fe.length>0;){let e=fe.splice(0,50);try{await P.post("/api/control-center/logs/batch",{entries:e},{timeout:5e3})}catch{}}}async function T(e,t,r="system",o="info"){fe.length>=200&&fe.shift(),fe.push({taskId:e,message:t,type:r,severity:o}),ae||(ae=dt().finally(()=>{ae=null}))}async function wo(){ae&&await ae,fe.length>0&&(ae=dt().finally(()=>{ae=null}),await ae)}async function $e(e,t,r,o,n,i){try{await P.post("/api/agent/planning-progress",{taskId:e,phase:t,elapsedSeconds:r,detail:o,charsGenerated:n,toolCallCount:i})}catch{}}var $="[\u{1F5FA}\uFE0F planning_agent \u{1F916}]";function Te(e){let t=Math.floor(e/60),r=e%60;return t>0?`${t}m ${r}s`:`${r}s`}function ut(e,t){switch(e){case"initializing":return`${$} Starting planning agent...`;case"reading_repo":return`${$} Reading repository structure...`;case"analyzing":return`${$} Analyzing requirements...`;case"generating_plan":return`${$} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Te(t)} elapsed)`;case"validating":return`${$} Validating plan...`;case"complete":return`${$} Planning complete`}}function Eo(e,t,r,o,n,i,s){let a=c.cyan(n.slice(0,8));return new Promise((l,y)=>{let d=ho(e,["--print","--verbose","--output-format","stream-json","--model",t,"--permission-mode","bypassPermissions"],{cwd:s,env:o,stdio:["pipe","pipe","pipe"]});d.stdin.write(r),d.stdin.end();let w="",k="",C="",_=0,x=0,G={inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0},K=t,E="";function L(A=!1){if(!E)return;let W=E.split(`
7
+ `),ee=A?"":W.pop()||"";for(let z of W)if(z.trim()){T(n,`${$} ${z}`,"output");let S=z.trim().length>160?z.trim().substring(0,160)+"\u2026":z.trim();console.log(`${m()} ${a} ${c.dim("\u{1F4AD}")} ${c.dim(S)}`)}E=ee}let f="initializing",U=!1,p={started:!0,reading:!1,analyzing:!1,generating:!1};function M(A){if(A===f)return;f=A;let W=Math.round((Date.now()-i)/1e3),ee=ut(A,W);T(n,ee),console.log(`${m()} ${a} ${c.dim(ee)}`)}let O=setInterval(()=>L(),500),F=setInterval(()=>{let A=Math.round((Date.now()-i)/1e3);$e(n,f,A,ut(f,A),_,x)},2e3),j=0,Q=setInterval(()=>{let A=Math.round((Date.now()-i)/1e3);if(f==="initializing"&&A>=5?M("reading_repo"):f==="reading_repo"&&A>=15&&!U&&M("analyzing"),f==="generating_plan"&&A-j>=30){j=A;let W=`${$} Planning in progress \u2014 analyzing requirements and decomposing into steps (${Te(A)} elapsed)`;T(n,W),console.log(`${m()} ${a} ${c.dim(W)}`)}},5e3),Y="";d.stdout.on("data",A=>{Y+=A.toString();let W=Y.split(`
8
+ `);Y=W.pop()||"";for(let ee of W){let z=ee.trim();if(z)try{let S=JSON.parse(z);if(S.type==="assistant"&&S.message?.content){let H=S.message.content;if(Array.isArray(H))for(let g of H)g.type==="text"&&g.text?(w+=g.text,_+=g.text.length,E+=g.text,U||(U=!0,x>0&&!p.analyzing&&(M("analyzing"),p.analyzing=!0)),_>500&&!p.generating&&(M("generating_plan"),p.generating=!0,j=Math.round((Date.now()-i)/1e3))):g.type==="tool_use"&&(x++,p.reading||(M("reading_repo"),p.reading=!0));else typeof H=="string"&&H&&(w+=H,_+=H.length,E+=H)}else S.type==="content_block_delta"&&S.delta?.text?(w+=S.delta.text,_+=S.delta.text.length,E+=S.delta.text,U||(U=!0,x>0&&!p.analyzing&&(M("analyzing"),p.analyzing=!0)),_>500&&!p.generating&&(M("generating_plan"),p.generating=!0,j=Math.round((Date.now()-i)/1e3))):S.type==="content_block_start"&&S.content_block?.type==="tool_use"?(x++,p.reading||(M("reading_repo"),p.reading=!0)):S.type==="result"&&S.result&&(k=typeof S.result=="string"?S.result:"");if(yo(S,G),S.type==="result"&&S.total_cost_usd!==void 0&&S.modelUsage&&typeof S.modelUsage=="object"){let H=Object.keys(S.modelUsage);H.length>0&&(K=H[0])}}catch{w+=z+`
9
+ `,_+=z.length}}}),d.stderr.on("data",A=>{C+=A.toString()});let oe=setInterval(()=>{(G.inputTokens>0||G.outputTokens>0)&&ct(n,G,K,"greatest").catch(()=>{})},3e4);function he(){clearInterval(Q),clearInterval(F),clearInterval(O),clearInterval(oe),L(!0)}let B=setTimeout(()=>{he(),d.kill("SIGTERM"),y(new Error("Claude CLI timed out after 20 minutes"))},12e5);d.on("exit",A=>{clearTimeout(B),he();let W=Math.round((Date.now()-i)/1e3);$e(n,"validating",W,"Validating plan...",_,x),ct(n,G,K,"greatest").catch(()=>{}),A!==0?y(new Error(`Claude CLI failed (exit ${A}): ${C.substring(0,300)}`)):l(k||w)}),d.on("error",A=>{clearTimeout(B),he(),y(A)})})}function Io(e,t){if(t)switch(e){case"anthropic":return t.anthropicApiKey;case"openai":return t.openaiApiKey;case"google":return t.googleApiKey;case"ollama":return t.ollamaBaseUrl||"http://localhost:11434";default:return}}function Ao(e,t,r){switch(r){case"bitbucket":return`https://x-token-auth:${t}@bitbucket.org/${e}.git`;case"gitlab":return`https://oauth2:${t}@gitlab.com/${e}.git`;default:return`https://x-access-token:${t}@github.com/${e}.git`}}async function bo(e,t,r,o){let n=c.cyan(o.slice(0,8)),i=`/tmp/workermill-planning-${o.slice(0,8)}-${Date.now()}`;try{let s=Ao(e,t,r);return console.log(`${m()} ${n} ${c.dim("Cloning repo for planner...")}`),Ke(`git clone --depth 1 --single-branch "${s}" "${i}"`,{stdio:"ignore",timeout:6e4}),console.log(`${m()} ${n} ${c.green("\u2713")} Repo cloned to ${c.dim(i)}`),i}catch(s){let a=s instanceof Error?s.message:String(s);console.error(`${m()} ${n} ${c.yellow("\u26A0")} Clone failed, planner will run without repo access: ${a.substring(0,100)}`);try{Ke(`rm -rf "${i}"`,{stdio:"ignore"})}catch{}return null}}async function pt(e,t,r){let o=c.cyan(e.id.slice(0,8));console.log(`${m()} ${o} Fetching planning prompt...`),await T(e.id,`${$} Fetching planning prompt from cloud API...`);let n=await P.get("/api/agent/planning-prompt",{params:{taskId:e.id}}),{prompt:i,model:s,provider:a,maxStories:l}=n.data,y=typeof l=="number"?l:8,I=s,d=a||"anthropic",w=d==="anthropic",k=process.env.CLAUDE_CLI_PATH||ce()||"claude",C={...process.env};delete C.CLAUDE_CODE_OAUTH_TOKEN;let _=Io(d,r),x=Date.now(),G=e.description||e.summary,K=null;if(e.githubRepo){let O=e.scmProvider||"github",F=O==="bitbucket"?t.bitbucketToken:O==="gitlab"?t.gitlabToken:t.githubToken;F?K=await bo(e.githubRepo,F,O,e.id):console.log(`${m()} ${o} ${c.yellow("\u26A0")} No SCM token for ${O}, planner will run without repo access`)}let E=i,L=null,f=0,U=[],p=0,M=await Xe();M||(console.log(`${m()} ${o} ${c.yellow("\u26A0")} Could not fetch critic config \u2014 critic validation will be skipped`),await T(e.id,`${$} \u26A0\uFE0F Could not fetch critic config from API \u2014 critic validation will be skipped`));try{for(let F=1;F<=te;F++){let j=te>1?` (attempt ${F}/${te})`:"",Q=`${d}/${I}`;F>1?(console.log(`${m()} ${o} Running planner${j} ${c.dim(`(${c.yellow(Q)})`)}`),await T(e.id,`${$} Re-planning${j} using ${Q}`)):(console.log(`${m()} ${o} Running planner ${c.dim(`(${c.yellow(Q)})`)}`),await T(e.id,`${$} Starting planning agent using ${Q}`));let Y;try{if(w)Y=await Eo(k,I,E,C,e.id,x,K||void 0);else{if(!_)throw new Error(`No API key available for provider "${d}". Configure it in Settings > Integrations.`);let b=Math.round((Date.now()-x)/1e3);await $e(e.id,"generating_plan",b,"Generating plan via AI SDK...",0,0),Y=await lt({provider:d,model:I,apiKey:_,prompt:E,workingDir:K||void 0,enableTools:!!K,maxSteps:10});let R=Math.round((Date.now()-x)/1e3);await $e(e.id,"validating",R,"Validating plan...",Y.length,0)}}catch(b){let R=Math.round((Date.now()-x)/1e3),q=b instanceof Error?b.message:String(b);return console.error(`${m()} ${o} ${c.red("\u2717")} Failed after ${R}s: ${q.substring(0,100)}`),await T(e.id,`${$} Planning failed after ${Te(R)}: ${q.substring(0,200)}`,"error","error"),!1}let oe=Math.round((Date.now()-x)/1e3),he=w?"Claude CLI":`${d} API`;console.log(`${m()} ${o} ${c.green("\u2713")} ${he} done ${c.dim(`(${oe}s, ${Y.length} chars)`)}`);let B;try{B=Qe(Y)}catch(b){let R=b instanceof Error?b.message:String(b);return console.error(`${m()} ${o} ${c.red("\u2717")} Plan parse failed: ${R.substring(0,100)}`),await T(e.id,`${$} Failed to parse execution plan from Claude output: ${R.substring(0,200)}`,"error","error"),await To(e.id,Y,t.agentId,o,oe)}let{truncatedCount:A,details:W}=Ze(B);if(A>0){p+=A;let b=`${$} File cap applied: ${A} stories truncated to max ${M?.maxTargetFiles??15} targetFiles`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b);for(let R of W)console.log(`${m()} ${o} ${c.dim(R)}`)}let{droppedCount:ee,details:z}=et(B,y);if(ee>0){let b=`${$} Story cap applied: ${ee} stories dropped (max ${y})`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b);for(let R of z)console.log(`${m()} ${o} ${c.dim(R)}`)}let{resolvedCount:S,details:H}=tt(B);if(S>0){let b=`${$} File overlap resolved: ${S} shared file(s) de-duped across stories`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b);for(let R of H)console.log(`${m()} ${o} ${c.dim(R)}`)}console.log(`${m()} ${o} Plan: ${c.bold(B.stories.length)} stories (max ${y})`),await T(e.id,`${$} Plan generated: ${B.stories.length} stories (${Te(oe)}). Running critic validation...`);let g=await st(k,I,G,B,C,o,d,_,e.id);if(g&&g.score>f?(L=B,f=g.score):!g&&!L&&(L=B),g&&U.push({iteration:F,score:g.score,approved:g.approved||g.score>=X,risks:g.risks,suggestions:g.suggestions,filesCapApplied:A>0?A:void 0}),!g){let b=`${$} \u26A0\uFE0F CRITIC BYPASSED \u2014 Critic validation failed (timeout/parse error). Posting plan WITHOUT quality gate.`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${b}`),await T(e.id,b,"error","warning");let R=Date.now()-x;return await Fe(e.id,B,t.agentId,o,oe,void 0,void 0,U,p,R,F)}if(g.approved||g.score>=X){let b=`${$} Critic approved (score: ${g.score}/100)`;if(console.log(`${m()} ${o} ${c.green("\u2713")} ${b}`),await T(e.id,b),g.risks.length>0){let q=`${$} Critic risks (non-blocking): ${g.risks.join("; ")}`;console.log(`${m()} ${o} ${c.dim(q)}`),await T(e.id,q)}let R=Date.now()-x;return await Fe(e.id,B,t.agentId,o,oe,g.score,g.risks,U,p,R,F)}if(F<te){let b=rt(g);E=i+`
10
10
 
11
- `+b;let R=`${$} Critic rejected (score: ${g.score}/100, threshold: ${X}). Re-planning with feedback...`;if(console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${R}`),await T(e.id,R),g.risks.length>0){let q=`${$} Critic risks: ${g.risks.join("; ")}`;console.log(`${m()} ${o} ${c.dim(q)}`),await T(e.id,q)}if(g.suggestions&&g.suggestions.length>0){let q=`${$} Critic suggestions: ${g.suggestions.join("; ")}`;console.log(`${m()} ${o} ${c.dim(q)}`),await T(e.id,q)}}else{let b=`${$} Critic rejected after ${te} iterations (best score: ${f}/100, threshold: ${X})`;if(console.error(`${m()} ${o} ${c.red("\u2717")} ${b}`),await T(e.id,b,"error","error"),g.risks.length>0){let R=`${$} Final risks: ${g.risks.join("; ")}`;console.error(`${m()} ${o} ${R}`),await T(e.id,R,"error","error")}if(g.suggestions&&g.suggestions.length>0){let R=`${$} Suggestions: ${g.suggestions.join("; ")}`;console.error(`${m()} ${o} ${R}`),await T(e.id,R,"error","error")}}}let O=50;if(L&&f>=O){let F=Math.round((Date.now()-x)/1e3),j=`${$} Best-plan fallback: posting plan with score ${f}/100 (below ${X} threshold, above ${O} minimum)`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${j}`),await T(e.id,j);let Q=Date.now()-x;if(await De(e.id,L,t.agentId,o,F,f,[`Best-plan fallback: critic rejected after ${te} iterations`],U,p,Q,te))return!0;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${$} Fallback post rejected by server, reporting plan-failed`),await T(e.id,`${$} Fallback plan rejected by server \u2014 reporting failure`)}try{let F=L&&f>=O?`Best-plan fallback rejected by server after ${te} iterations (best score: ${f}/100)`:`Critic rejected after ${te} iterations (best score: ${f}/100, threshold: ${X}, fallback minimum: ${O})`;await P.post("/api/agent/plan-failed",{taskId:e.id,agentId:t.agentId,reason:F,criticHistory:U})}catch{}return!1}finally{if(await yo(),K)try{Fe(`rm -rf "${K}"`,{stdio:"ignore"})}catch{}}}async function De(e,t,r,o,n,i,s,a,l,y,E){let d=tt(t);try{let k=(await P.post("/api/agent/plan-result",{taskId:e,rawOutput:d,agentId:r,criticScore:i,criticRisks:s,criticHistory:a,criticIterations:E,fileCapTruncations:l,planningDurationMs:y})).data.storyCount;return console.log(`${m()} ${o} ${c.green("\u2713")} Plan validated: ${c.bold(k)} stories \u2192 ${c.green("queued")}`),await T(e,`${$} Plan validated: ${k} stories. Task queued for execution.`),await $e(e,"complete",n,"Planning complete",0,0),!0}catch(w){let k=w,C=k.response?.data?.error||k.response?.data?.detail||String(w),_=k.response?.status?` (${k.response.status})`:"";return console.error(`${m()} ${o} ${c.red("\u2717")} Server validation failed${_}: ${C.substring(0,100)}`),await T(e,`${$} Server-side plan validation failed${_}: ${C.substring(0,200)}`,"error","error"),!1}}async function bo(e,t,r,o,n){try{let s=(await P.post("/api/agent/plan-result",{taskId:e,rawOutput:t,agentId:r})).data.storyCount;return console.log(`${m()} ${o} ${c.green("\u2713")} Plan validated (server-side): ${c.bold(s)} stories \u2192 ${c.green("queued")}`),await T(e,`${$} Plan validated: ${s} stories. Task queued for execution.`),await $e(e,"complete",n,"Planning complete",0,0),!0}catch(i){let a=i.response?.data?.detail||String(i);return console.error(`${m()} ${o} ${c.red("\u2717")} Validation failed: ${a.substring(0,100)}`),await T(e,`${$} Plan validation failed: ${a.substring(0,200)}`,"error","error"),!1}}import u from"chalk";import{spawn as gt,execSync as Re}from"child_process";import*as _e from"path";import*as Z from"fs";import*as Se from"os";function v(){return u.dim(new Date().toLocaleTimeString())}var V=new Map;function To(){if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return Z.readFileSync("/proc/version","utf-8").toLowerCase().includes("microsoft")}catch{return!1}}var ke=To(),mt=ke||process.platform==="darwin"||process.platform==="win32";function ft(){if(!ke)return null;try{let e=Re("hostname -I",{encoding:"utf-8"}).trim().split(/\s+/)[0];if(e&&/^\d+\.\d+\.\d+\.\d+$/.test(e))return e}catch{}return null}function $t(e){if(!ke)return e;let t=e.match(/^\/mnt\/([a-zA-Z])\/(.*)$/);return t?`${t[1].toUpperCase()}:/${t[2]}`:e}function ht(){let e=_e.join(Se.homedir(),".claude");if(Z.existsSync(e))return e;if(ke){let t="/mnt/c/Users";if(Z.existsSync(t))try{for(let r of Z.readdirSync(t)){if(["Public","Default","Default User","All Users"].includes(r))continue;let o=_e.join(t,r,".claude");if(Z.existsSync(o))return o}}catch{}}return null}var pt=0;function yt(e){let t=e.match(/^(\d+\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)/);return t?t[1]:null}function _o(e){let t=e.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/);return t?t[1]:"us-east-1"}function wt(e){if(Date.now()<pt)return!0;let t=_o(e);try{return Re(`aws ecr get-login-password --region ${t} | docker login --username AWS --password-stdin ${e}`,{stdio:"pipe",timeout:3e4}),pt=Date.now()+660*60*1e3,!0}catch{return!1}}function It(e,t,r){if(r?.scmToken)return r.scmToken;switch(e){case"bitbucket":return t.bitbucketToken;case"gitlab":return t.gitlabToken;default:return t.githubToken}}function Ro(e){let t=e.jiraFields;if(!t)return!1;let r=t.labels;if(Array.isArray(r)&&r.some(i=>typeof i=="string"&&i.toLowerCase()==="self-review"))return!0;let n=t.issue?.labels;return!!(Array.isArray(n)&&n.some(i=>typeof i=="string"?i.toLowerCase()==="self-review":i&&typeof i=="object"&&"name"in i?(i.name||"").toLowerCase()==="self-review":!1))}async function Et(e,t,r,o){let n=u.cyan(e.id.slice(0,8));if(V.has(e.id)){console.log(`${v()} ${n} ${u.dim("Already running, skipping")}`);return}let i=`workermill-${e.id.slice(0,8)}`,a=["run","--rm",...!t.workerImage.includes("/")?[]:["--pull","always"],"--name",i],l=Math.round(Se.totalmem()/(1024*1024*1024));if(l<=16?a.push("--memory","6g","--memory-swap","10g","--cpus","2"):(l<=32,a.push("--memory","6g","--memory-swap","12g","--cpus","4")),mt){let p=ft();a.push(`--add-host=host.docker.internal:${p||"host-gateway"}`)}else a.push("--network","host");let y=e.workerProvider||"anthropic",E=ht();if(!E&&y==="anthropic"){console.error(`${v()} ${n} ${u.red("\u2717")} Claude credentials not found. Run 'claude' and complete the sign-in flow.`);return}if(E){let p=_e.join(E,".credentials.json");try{Z.chmodSync(p,438)}catch{}let M=$t(E);a.push("-v",`${M}:/home/worker/.claude`)}else console.log(`${v()} ${n} ${u.dim("Skipping Claude mount (non-Anthropic worker)")}`);let d=t.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),w=e.scmProvider||"github",k=It(w,t,o),C=o?.githubToken||t.githubToken,_=w==="bitbucket"&&o?.scmToken||t.bitbucketToken,x=w==="gitlab"&&o?.scmToken||t.gitlabToken,G={NODE_OPTIONS:"--max-old-space-size=3072",EPIC_MODE:"true",EXECUTION_MODE:"local",TASK_ID:e.id,ORG_ID:e.orgId||"",JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",TASK_SUMMARY:e.summary||"",TASK_DESCRIPTION:e.description||"",WORKER_PERSONA:e.workerPersona||"",RETRY_NUMBER:String(e.retryCount??0),TICKET_KEY:e.jiraIssueKey||"",API_BASE_URL:d,ORG_API_KEY:t.apiKey,SCM_PROVIDER:w,SCM_TOKEN:k,SCM_BASE_URL:o?.scmBaseUrl||"",GITHUB_TOKEN:C,GH_TOKEN:C,GITHUB_REVIEWER_TOKEN:o?.githubReviewerToken||"",BITBUCKET_TOKEN:_,BITBUCKET_USERNAME:o?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:x,TARGET_REPO:e.githubRepo||"",GITHUB_REPO:e.githubRepo||"",WORKER_MODEL:e.workerModel||String(r.defaultWorkerModel||""),CLAUDE_MODEL:e.workerModel||String(r.defaultWorkerModel||""),JIRA_BASE_URL:o?.jiraBaseUrl||"",JIRA_EMAIL:o?.jiraEmail||"",JIRA_API_TOKEN:o?.jiraApiToken||"",TICKET_SYSTEM:o?.issueTrackerProvider||"jira",LINEAR_API_KEY:o?.linearApiKey||"",AWS_ACCESS_KEY_ID:o?.customerAwsAccessKeyId||"",AWS_SECRET_ACCESS_KEY:o?.customerAwsSecretAccessKey||"",AWS_DEFAULT_REGION:o?.customerAwsRegion||"",AWS_REGION:o?.customerAwsRegion||"",CUSTOMER_AWS_ROLE_ARN:o?.customerAwsRoleArn||"",CUSTOMER_AWS_EXTERNAL_ID:o?.customerAwsExternalId||"",CUSTOMER_AWS_REGION:o?.customerAwsRegion||"",MANAGER_PROVIDER:o?.managerProvider||"anthropic",MANAGER_MODEL:o?.managerModelId||"",BITBUCKET_EMAIL:o?.bitbucketEmail||"",DEPLOYMENT_ENABLED:e.deploymentEnabled||e.parentTaskId?"true":"false",PRD_CHILD_TASK:e.parentTaskId?"true":"false",IMPROVEMENT_ENABLED:e.improvementEnabled?"true":"false",QUALITY_GATE_BYPASS:e.qualityGateBypass?"true":"false",STANDARD_SDK_MODE:e.standardSdkMode?"true":"false",MAX_REVIEW_REVISIONS:String(r.maxReviewRevisions??3),CODEBASE_INDEXING_ENABLED:r.codebaseIndexingEnabled===!0?"true":"false",EXISTING_PR_URL:e.githubPrUrl||"",EXISTING_PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",PARENT_TASK_ID:e.parentTaskId||e.id,PARENT_JIRA_KEY:e.jiraIssueKey&&/-S\d+$/.test(e.jiraIssueKey)&&e.jiraFields?.parentJiraKey||"",TARGET_BRANCH:e.jiraFields?.targetBranch||"",STORY_BRANCH:e.jiraFields?.storyBranch||"",TASK_NOTES:e.taskNotes||"",TARGET_FILES:JSON.stringify(e.jiraFields?.targetFiles||[]),REFERENCE_FILES:JSON.stringify(e.jiraFields?.referenceFiles||[]),PIPELINE_VERSION:e.jiraFields?.pipelineVersion||e.pipelineVersion||"",V2_STEP_INPUT:e.jiraFields?.v2StepInput?JSON.stringify(e.jiraFields.v2StepInput):"",EXECUTION_MODE_SETTING:e.jiraFields?.executionMode||"autonomous",ANTHROPIC_API_KEY:E?"":o?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",WORKER_PROVIDER:e.workerProvider||"anthropic",OPENAI_API_KEY:o?.openaiApiKey||"",GOOGLE_API_KEY:o?.googleApiKey||"",GOOGLE_GENERATIVE_AI_API_KEY:o?.googleApiKey||"",OLLAMA_HOST:o?.ollamaBaseUrl||"",OLLAMA_CONTEXT_WINDOW:o?.ollamaContextWindow?String(o.ollamaContextWindow):"",VLLM_BASE_URL:o?.vllmBaseUrl||"",BLOCKER_MAX_AUTO_RETRIES:String(r.blockerMaxAutoRetries??3),BLOCKER_AUTO_RETRY_ENABLED:r.blockerAutoRetryEnabled!==!1?"true":"false",PUSH_AFTER_COMMIT:r.pushAfterCommit!==!1?"true":"false",GRACEFUL_SHUTDOWN_ENABLED:r.gracefulShutdownEnabled!==!1?"true":"false",MAX_PARALLEL_EXPERTS:String(r.maxParallelExperts??4),REVIEW_ENABLED:e.skipManagerReview===!1?"true":"false",SELF_REVIEW_ENABLED:Ro(e)||r.selfReviewEnabled!==!1?"true":"false"};for(let[p,M]of Object.entries(G))M!==""&&a.push("-e",`${p}=${M}`);let K=t.workerImage,I=yt(K);I&&wt(I),a.push(K);let L=e.skipManagerReview===!1;console.log(`${v()} ${n} ${u.dim("Starting container")} ${u.yellow(i)}`),console.log(`${v()} ${n} ${u.dim(` skipManagerReview=${e.skipManagerReview} \u2192 REVIEW_ENABLED=${L}`)}`),console.log(`${v()} ${n} ${u.dim(` model=${e.workerModel} repo=${e.githubRepo}`)}`),console.log(`${v()} ${n} ${u.dim(` totalRamGB=${l} docker args:`)} ${a.slice(0,10).join(" ")}`);let f=gt("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!f.pid){console.error(`${v()} ${n} ${u.red("\u2717")} Failed to spawn container`);return}let U={taskId:e.id,containerName:i,process:f,startedAt:new Date,status:"running",resultEmitted:!1};V.set(e.id,U),f.stdout?.on("data",p=>{let M=p.toString().split(`
12
- `).filter(O=>O.trim());for(let O of M)console.log(`${v()} ${n} ${u.dim(O)}`),O.includes("::result::")&&(U.resultEmitted=!0)}),f.stderr?.on("data",p=>{let M=p.toString().split(`
13
- `).filter(O=>O.trim());for(let O of M)console.log(`${v()} ${n} ${u.red(O)}`)}),f.on("exit",p=>{U.status=p===0?"completed":"failed";let M=Math.round((Date.now()-U.startedAt.getTime())/1e3),O=p===0?u.green("\u2713"):u.red("\u2717"),F=p===0?u.green("completed"):u.red(`failed (exit ${p})`);console.log(`${v()} ${n} ${O} Container ${F} ${u.dim(`(${M}s)`)}`),U.resultEmitted||(console.log(`${v()} ${n} ${u.yellow("\u26A0")} No ::result:: marker seen \u2014 posting fallback completion in 15s`),setTimeout(async()=>{try{let j=p===0?"completed":"failed",Q=p!==0?`Worker container exited with code ${p} without reporting completion`:void 0;(await(await fetch(`${t.apiUrl}/api/tasks/${e.id}/worker-complete`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":t.apiKey},body:JSON.stringify({exitCode:p??1,result:j,errorMessage:Q})})).json()).status==="ignored"?console.log(`${v()} ${n} ${u.dim("Fallback completion ignored (task already transitioned)")}`):console.log(`${v()} ${n} ${u.yellow("\u26A0")} Fallback completion applied: ${j}`)}catch(j){console.error(`${v()} ${n} ${u.red("\u2717")} Fallback completion failed:`,j instanceof Error?j.message:j)}},15e3)),setTimeout(()=>V.delete(e.id),9e4)}),f.on("error",p=>{U.status="failed",console.error(`${v()} ${n} ${u.red("\u2717")} Container error: ${p.message}`)})}function Ke(){return Array.from(V.values()).filter(e=>e.status==="running").length}function At(){return Array.from(V.values()).filter(e=>e.status==="running").map(e=>e.taskId)}function bt(e){let t=V.get(e);if(!t||t.status!=="running")return;let r=u.cyan(e.slice(0,8));console.log(`${v()} ${r} ${u.red("\u25A0")} Stopping container (cancelled by dashboard)`);try{Re(`docker stop ${t.containerName}`,{stdio:"ignore",timeout:15e3}),t.status="completed"}catch{}V.delete(e)}async function Tt(){console.log(`${v()} ${u.dim(`Stopping ${V.size} containers...`)}`);for(let[,e]of V)if(e.status==="running")try{Re(`docker stop ${e.containerName}`,{stdio:"ignore",timeout:15e3}),e.status="completed"}catch{}V.clear()}async function _t(e,t,r){let o=u.cyan(e.id.slice(0,8)),n=`manager-${e.id}`;if(V.has(n))return;let i=`wm-manager-${e.id.slice(0,8)}-${Date.now()}`,s=ht(),a=["run","--rm","--name",i,"--memory=4g","--cpus=2"];if(mt){let I=ft();a.push(`--add-host=host.docker.internal:${I||"host-gateway"}`)}else a.push("--network","host");if(s){let I=$t(s);a.push("-v",`${I}:/home/worker/.claude`)}let l=t.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),y=e.scmProvider||"github",E=It(y,t,r),d=r?.githubToken||t.githubToken,w=y==="bitbucket"&&r?.scmToken||t.bitbucketToken,k=y==="gitlab"&&r?.scmToken||t.gitlabToken,C={NODE_OPTIONS:"--max-old-space-size=3072",TASK_ID:e.id,MANAGER_ACTION:e.managerAction,JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",GITHUB_REPO:e.githubRepo||"",PR_URL:e.githubPrUrl||"",PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",API_BASE_URL:l,ORG_API_KEY:t.apiKey,SCM_PROVIDER:y,SCM_TOKEN:E,GITHUB_TOKEN:d,GH_TOKEN:d,BITBUCKET_TOKEN:w,BITBUCKET_USERNAME:r?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:k,MANAGER_PROVIDER:r?.managerProvider||"anthropic",MANAGER_MODEL:r?.managerModelId||"",ANTHROPIC_API_KEY:s?"":r?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",OPENAI_API_KEY:r?.openaiApiKey||"",GOOGLE_API_KEY:r?.googleApiKey||"",JIRA_BASE_URL:r?.jiraBaseUrl||"",JIRA_EMAIL:r?.jiraEmail||"",JIRA_API_TOKEN:r?.jiraApiToken||"",TICKET_SYSTEM:r?.issueTrackerProvider||"jira",LINEAR_API_KEY:r?.linearApiKey||""};for(let[I,L]of Object.entries(C))L!==""&&a.push("-e",`${I}=${L}`);let _=t.workerImage,x=yt(_);x&&wt(x),a.push("--entrypoint","/bin/bash"),a.push(_),a.push("/app/manager-entrypoint.sh"),console.log(`${v()} ${o} ${u.magenta("\u25C6 MANAGER")} Starting ${e.managerAction} container ${u.yellow(i)}`);let G=gt("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!G.pid){console.error(`${v()} ${o} ${u.red("\u2717")} Failed to spawn manager container`);return}let K={taskId:n,containerName:i,process:G,startedAt:new Date,status:"running",resultEmitted:!1};V.set(n,K),G.stdout?.on("data",I=>{let L=I.toString().split(`
14
- `).filter(f=>f.trim());for(let f of L)console.log(`${v()} ${o} ${u.magenta("MGR")} ${u.dim(f)}`)}),G.stderr?.on("data",I=>{let L=I.toString().split(`
15
- `).filter(f=>f.trim());for(let f of L)console.log(`${v()} ${o} ${u.magenta("MGR")} ${u.red(f)}`)}),G.on("exit",I=>{K.status=I===0?"completed":"failed";let L=Math.round((Date.now()-K.startedAt.getTime())/1e3),f=I===0?u.green("\u2713"):u.red("\u2717"),U=I===0?u.green("completed"):u.red(`failed (exit ${I})`);console.log(`${v()} ${o} ${u.magenta("MGR")} ${f} Manager ${e.managerAction} ${U} ${u.dim(`(${L}s)`)}`),setTimeout(()=>V.delete(n),6e4)}),G.on("error",I=>{K.status="failed",console.error(`${v()} ${o} ${u.magenta("MGR")} ${u.red("\u2717")} Container error: ${I.message}`)})}import{createRequire as So}from"module";var ko=So(import.meta.url),Po=ko("../package.json"),le=Po.version;import{spawn as Rt}from"child_process";import Ee from"chalk";async function Pe(){return console.log(Ee.cyan(" Updating @workermill/agent...")),new Promise(e=>{let t=Rt("npm",["install","-g","@workermill/agent@latest"],{stdio:"inherit",shell:!0});t.on("error",r=>{console.error(Ee.red(` Update failed: ${r.message}`)),e(!1)}),t.on("close",r=>{r===0?(console.log(Ee.green(" Update successful.")),e(!0)):(console.error(Ee.red(` Update failed with exit code ${r}`)),e(!1))})})}function ve(){console.log(Ee.cyan(" Restarting agent...")),Rt(process.argv[0],process.argv.slice(1),{stdio:"inherit",detached:!0}).unref(),process.exit(0)}var ue=new Set,de=new Set,Ce=null,St=!1,xe=!1;function N(){return h.dim(new Date().toLocaleTimeString())}async function vo(){if(Ce)return Ce;try{return Ce=(await P.get("/api/agent/config")).data,Ce}catch{return console.error(`${N()} ${h.red("\u2717")} Failed to fetch org config`),{}}}async function kt(e){if(!xe)try{let t=await P.get("/api/agent/poll",{params:{agentId:e.agentId}}),r=t.data.tasks;if(r.length===0)return;for(let n of r)n.status==="planning"&&!ue.has(n.id)?await Co(n,e):n.status==="queued"&&await xo(n,e);let o=t.data.managerTasks;if(o&&o.length>0)for(let n of o)de.has(n.id)||await Mo(n,e)}catch(t){let r=t,o=ue.size>0||Ke()>0||de.size>0;r.response?.status===401?o||console.error(`${N()} ${h.red("\u2717")} Authentication failed. Check your API key.`):o||console.warn(`${N()} ${h.yellow("\u26A0")} Poll error: ${r.message||String(t)}`)}}async function Co(e,t){let r,o;try{let s=await P.post("/api/agent/claim",{taskId:e.id,agentId:t.agentId});if(!s.data.claimed)return;r=s.data.credentials,o=s.data.task}catch{return}let n=h.cyan(e.id.slice(0,8));if(o&&(o.retryCount??0)>0&&o.executionPlanV2!=null){console.log(),console.log(`${N()} ${h.magenta("\u25C6 RESUME")} ${n} ${e.summary.substring(0,60)}`),console.log(`${N()} ${n} Retry #${o.retryCount} with existing plan \u2014 skipping planning`),ue.add(e.id);try{await P.post("/api/agent/resume-plan",{taskId:e.id,agentId:t.agentId}),console.log(`${N()} ${n} ${h.green("\u2713")} Resumed with existing plan \u2192 ${h.green("queued")}`)}catch(s){let a=s,l=a.response?.data?.error||a.message||String(s);console.error(`${N()} ${n} ${h.red("\u2717")} Resume failed: ${l}`)}ue.delete(e.id);return}console.log(),console.log(`${N()} ${h.magenta("\u25C6 PLANNING")} ${n} ${e.summary.substring(0,60)}`),ue.add(e.id),dt(e,t,r).then(s=>{console.log(s?`${N()} ${h.green("\u2713")} Planning complete for ${n}`:`${N()} ${h.red("\u2717")} Planning failed for ${n}`)}).catch(s=>console.error(`${N()} ${h.red("\u2717")} Planning error for ${n}:`,s.message||s)).finally(()=>ue.delete(e.id))}async function xo(e,t){if(Ke()>=t.maxWorkers)return;let o;try{if(o=(await P.post("/api/agent/claim",{taskId:e.id,agentId:t.agentId})).data,!o.claimed)return}catch{return}try{await P.post("/api/agent/started",{taskId:e.id,agentId:t.agentId})}catch{let y=h.cyan(e.id.slice(0,8));console.warn(`${N()} ${h.yellow("\u26A0")} Failed to report started for ${y}`)}let n=h.cyan(e.id.slice(0,8));console.log(),console.log(`${N()} ${h.blue("\u25B6 EXECUTING")} ${n} ${e.summary.substring(0,60)}`);let i=await vo(),s=o.task||{id:e.id,summary:e.summary,description:e.description,jiraIssueKey:e.jiraIssueKey,workerModel:e.workerModel,workerProvider:e.workerProvider,githubRepo:e.githubRepo,scmProvider:e.scmProvider,skipManagerReview:e.skipManagerReview,deploymentEnabled:e.deploymentEnabled,improvementEnabled:e.improvementEnabled,qualityGateBypass:e.qualityGateBypass,standardSdkMode:e.standardSdkMode,parentTaskId:e.parentTaskId,taskNotes:e.taskNotes,githubPrUrl:e.githubPrUrl,githubPrNumber:e.githubPrNumber,executionPlanV2:e.executionPlanV2,jiraFields:e.jiraFields||{}},a=o.credentials;Et(s,t,i,a).catch(l=>console.error(`${N()} ${h.red("\u2717")} Spawn failed for ${n}:`,l.message||l))}async function Mo(e,t){let r=h.cyan(e.id.slice(0,8));de.add(e.id);try{let o=await P.post("/api/agent/claim-manager",{taskId:e.id,agentId:t.agentId,action:e.managerAction});if(!o.data.claimed){de.delete(e.id);return}let n=o.data.task,i=o.data.credentials||{},s=e.managerAction==="analyze_logs"?h.yellow("LOG ANALYSIS"):h.yellow("PR REVIEW");console.log(),console.log(`${N()} ${h.magenta("\u25C6 MANAGER")} ${s} ${r} ${e.summary.substring(0,60)}`);let a={id:e.id,summary:n?.summary||e.summary,description:n?.description||e.description,jiraIssueKey:n?.jiraIssueKey||e.jiraIssueKey,githubRepo:n?.githubRepo||e.githubRepo,scmProvider:n?.scmProvider||e.scmProvider,githubPrUrl:n?.githubPrUrl||e.githubPrUrl,githubPrNumber:n?.githubPrNumber||e.githubPrNumber,managerAction:e.managerAction};_t(a,t,i).then(()=>{console.log(`${N()} ${r} ${h.magenta("MGR")} ${h.green("\u2713")} Manager ${e.managerAction} dispatched`)}).catch(l=>{console.error(`${N()} ${r} ${h.magenta("MGR")} ${h.red("\u2717")} Manager spawn failed:`,l.message||l)}).finally(()=>de.delete(e.id))}catch(o){de.delete(e.id);let n=o;console.error(`${N()} ${r} ${h.red("\u2717")} Failed to claim manager task:`,n.message||String(o))}}var Me=null,Oe=null;function Pt(){Me&&(clearInterval(Me),Me=null),Oe&&(clearInterval(Oe),Oe=null)}function vt(e){console.log(` ${h.dim("Polling every")} ${e.pollIntervalMs/1e3}s ${h.dim("\xB7 waiting for tasks...")}`),kt(e),Me=setInterval(()=>kt(e),e.pollIntervalMs)}function Ct(e){Oe=setInterval(async()=>{let t=At(),r=Array.from(ue),o=Array.from(de),n=[...t,...r,...o];try{let i=await P.post("/api/agent/heartbeat",{agentId:e.agentId,activeTasks:n,agentVersion:le}),s=i.data?.cancelledTasks;if(s&&s.length>0)for(let E of s)bt(E);let{updateAvailable:a,updateRequired:l,latestVersion:y}=i.data??{};l&&!xe?(xe=!0,console.log(`${N()} ${h.red("\u26A0 Agent update required")} (current: ${le}, required: ${y})`),console.log(`${N()} ${h.yellow("Refusing new tasks until updated.")}`),await Pe()?ve():(console.log(`${N()} ${h.red("Auto-update failed.")} Run: npm install -g @workermill/agent@latest`),xe=!1)):a&&!St&&!l&&(St=!0,console.log(`${N()} ${h.yellow(`Update available: ${y}`)} (current: ${le}). Run: workermill-agent update`))}catch{}},e.heartbeatIntervalMs)}Ae();async function Oo(e){console.log(),console.log(D.bold.cyan(" WorkerMill Remote Agent")),console.log(D.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),je(e.apiUrl,e.apiKey);try{let t=await P.get("/api/agent/config"),r=t.data.maxConcurrentWorkers;r&&typeof r=="number"&&(e.maxWorkers=r),t.data.workerImageUrl&&(e.workerImage=t.data.workerImageUrl),console.log(` ${D.green("\u25CF")} Connected to ${D.cyan(e.apiUrl)}`),console.log(` ${D.dim("Agent:")} ${e.agentId}`),console.log(` ${D.dim("Workers:")} ${D.yellow(String(e.maxWorkers))} parallel`),console.log(` ${D.dim("Image:")} ${e.workerImage}`),console.log(` ${D.dim("SCM:")} ${t.data.scmProvider}`),console.log(` ${D.dim("Model:")} ${D.yellow(t.data.defaultWorkerModel)}`),console.log()}catch(t){let r=t;throw r.response?.status===401?new Error("Authentication failed. Check your API key."):new Error(`Failed to connect to WorkerMill API: ${r.message||String(t)}`)}try{let t=await P.post("/api/agent/register",{agentId:e.agentId,maxWorkers:e.maxWorkers,agentVersion:le}),{updateAvailable:r,updateRequired:o,latestVersion:n}=t.data;o?(console.log(D.red(` \u26A0 Agent update required (current: ${le}, required: ${n})`)),await Pe()?ve():console.log(D.yellow(" Auto-update failed. Run: npm install -g @workermill/agent@latest"))):r&&console.log(D.yellow(` Update available: ${n} (current: ${le}). Run: workermill-agent update`))}catch{}return vt(e),Ct(e),console.log(D.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` ${D.green("\u25CF")} Agent is running. ${D.dim("Press Ctrl+C to stop.")}`),console.log(),async()=>{console.log(),console.log(D.dim(" Shutting down...")),Pt();try{await P.post("/api/agent/deregister",{agentId:e.agentId})}catch{}await Tt(),console.log(` ${D.red("\u25CF")} Agent stopped.`)}}var No=typeof process<"u"&&process.argv[1]&&(process.argv[1].endsWith("/index.ts")||process.argv[1].endsWith("/index.js"));if(No){try{await import("dotenv/config")}catch{}let{loadConfig:e,validatePrerequisites:t}=await Promise.resolve().then(()=>(Ae(),ze)),r=e();t(),console.log(D.dim(" Prerequisites validated."));let o=await Oo(r);process.on("SIGINT",async()=>{await o(),process.exit(0)}),process.on("SIGTERM",async()=>{await o(),process.exit(0)})}export{ce as findClaudePath,Ye as getSystemInfo,We as loadConfig,Be as loadConfigFromFile,Oo as startAgent,Ve as validatePrerequisites};
11
+ `+b;let R=`${$} Critic rejected (score: ${g.score}/100, threshold: ${X}). Re-planning with feedback...`;if(console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${R}`),await T(e.id,R),g.risks.length>0){let q=`${$} Critic risks: ${g.risks.join("; ")}`;console.log(`${m()} ${o} ${c.dim(q)}`),await T(e.id,q)}if(g.suggestions&&g.suggestions.length>0){let q=`${$} Critic suggestions: ${g.suggestions.join("; ")}`;console.log(`${m()} ${o} ${c.dim(q)}`),await T(e.id,q)}}else{let b=`${$} Critic rejected after ${te} iterations (best score: ${f}/100, threshold: ${X})`;if(console.error(`${m()} ${o} ${c.red("\u2717")} ${b}`),await T(e.id,b,"error","error"),g.risks.length>0){let R=`${$} Final risks: ${g.risks.join("; ")}`;console.error(`${m()} ${o} ${R}`),await T(e.id,R,"error","error")}if(g.suggestions&&g.suggestions.length>0){let R=`${$} Suggestions: ${g.suggestions.join("; ")}`;console.error(`${m()} ${o} ${R}`),await T(e.id,R,"error","error")}}}let O=50;if(L&&f>=O){let F=Math.round((Date.now()-x)/1e3),j=`${$} Best-plan fallback: posting plan with score ${f}/100 (below ${X} threshold, above ${O} minimum)`;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${j}`),await T(e.id,j);let Q=Date.now()-x;if(await Fe(e.id,L,t.agentId,o,F,f,[`Best-plan fallback: critic rejected after ${te} iterations`],U,p,Q,te))return!0;console.log(`${m()} ${o} ${c.yellow("\u26A0")} ${$} Fallback post rejected by server, reporting plan-failed`),await T(e.id,`${$} Fallback plan rejected by server \u2014 reporting failure`)}try{let F=L&&f>=O?`Best-plan fallback rejected by server after ${te} iterations (best score: ${f}/100)`:`Critic rejected after ${te} iterations (best score: ${f}/100, threshold: ${X}, fallback minimum: ${O})`;await P.post("/api/agent/plan-failed",{taskId:e.id,agentId:t.agentId,reason:F,criticHistory:U})}catch{}return!1}finally{if(await wo(),K)try{Ke(`rm -rf "${K}"`,{stdio:"ignore"})}catch{}}}async function Fe(e,t,r,o,n,i,s,a,l,y,I){let d=ot(t);try{let k=(await P.post("/api/agent/plan-result",{taskId:e,rawOutput:d,agentId:r,criticScore:i,criticRisks:s,criticHistory:a,criticIterations:I,fileCapTruncations:l,planningDurationMs:y})).data.storyCount;return console.log(`${m()} ${o} ${c.green("\u2713")} Plan validated: ${c.bold(k)} stories \u2192 ${c.green("queued")}`),await T(e,`${$} Plan validated: ${k} stories. Task queued for execution.`),await $e(e,"complete",n,"Planning complete",0,0),!0}catch(w){let k=w,C=k.response?.data?.error||k.response?.data?.detail||String(w),_=k.response?.status?` (${k.response.status})`:"";return console.error(`${m()} ${o} ${c.red("\u2717")} Server validation failed${_}: ${C.substring(0,100)}`),await T(e,`${$} Server-side plan validation failed${_}: ${C.substring(0,200)}`,"error","error"),!1}}async function To(e,t,r,o,n){try{let s=(await P.post("/api/agent/plan-result",{taskId:e,rawOutput:t,agentId:r})).data.storyCount;return console.log(`${m()} ${o} ${c.green("\u2713")} Plan validated (server-side): ${c.bold(s)} stories \u2192 ${c.green("queued")}`),await T(e,`${$} Plan validated: ${s} stories. Task queued for execution.`),await $e(e,"complete",n,"Planning complete",0,0),!0}catch(i){let a=i.response?.data?.detail||String(i);return console.error(`${m()} ${o} ${c.red("\u2717")} Validation failed: ${a.substring(0,100)}`),await T(e,`${$} Plan validation failed: ${a.substring(0,200)}`,"error","error"),!1}}import u from"chalk";import{spawn as mt,execSync as Se}from"child_process";import*as _e from"path";import*as Z from"fs";import*as ke from"os";function v(){return u.dim(new Date().toLocaleTimeString())}var _o=[[/(:\/\/[^:/?#]+:)[^@]+(@)/g,"$1***$2"],[/\b(ghp_|gho_|ghs_|github_pat_)[A-Za-z0-9_]+/g,"$1***"],[/\bglpat-[A-Za-z0-9\-_]+/g,"glpat-***"],[/\b(AKIA)[A-Z0-9]{16}\b/g,"$1***"],[/(Bearer\s+)[A-Za-z0-9._\-]+/gi,"$1***"],[/(x-api-key:\s*)[^\s,'"]+/gi,"$1***"]];function Re(e){let t=e;for(let[r,o]of _o)t=t.replace(r,o);return t}var V=new Map;function Ro(){if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return Z.readFileSync("/proc/version","utf-8").toLowerCase().includes("microsoft")}catch{return!1}}var Pe=Ro(),ft=Pe||process.platform==="darwin"||process.platform==="win32";function $t(){if(!Pe)return null;try{let e=Se("hostname -I",{encoding:"utf-8"}).trim().split(/\s+/)[0];if(e&&/^\d+\.\d+\.\d+\.\d+$/.test(e))return e}catch{}return null}function ht(e){if(!Pe)return e;let t=e.match(/^\/mnt\/([a-zA-Z])\/(.*)$/);return t?`${t[1].toUpperCase()}:/${t[2]}`:e}function yt(){let e=_e.join(ke.homedir(),".claude");if(Z.existsSync(e))return e;if(Pe){let t="/mnt/c/Users";if(Z.existsSync(t))try{for(let r of Z.readdirSync(t)){if(["Public","Default","Default User","All Users"].includes(r))continue;let o=_e.join(t,r,".claude");if(Z.existsSync(o))return o}}catch{}}return null}var gt=0;function wt(e){let t=e.match(/^(\d+\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)/);return t?t[1]:null}function So(e){let t=e.match(/\.ecr\.([a-z0-9-]+)\.amazonaws\.com/);return t?t[1]:"us-east-1"}function Et(e){if(Date.now()<gt)return!0;let t=So(e);try{return Se(`aws ecr get-login-password --region ${t} | docker login --username AWS --password-stdin ${e}`,{stdio:"pipe",timeout:3e4}),gt=Date.now()+660*60*1e3,!0}catch{return!1}}function It(e,t,r){if(r?.scmToken)return r.scmToken;switch(e){case"bitbucket":return t.bitbucketToken;case"gitlab":return t.gitlabToken;default:return t.githubToken}}function ko(e){let t=e.jiraFields;if(!t)return!1;let r=t.labels;if(Array.isArray(r)&&r.some(i=>typeof i=="string"&&i.toLowerCase()==="self-review"))return!0;let n=t.issue?.labels;return!!(Array.isArray(n)&&n.some(i=>typeof i=="string"?i.toLowerCase()==="self-review":i&&typeof i=="object"&&"name"in i?(i.name||"").toLowerCase()==="self-review":!1))}async function At(e,t,r,o){let n=u.cyan(e.id.slice(0,8));if(V.has(e.id)){console.log(`${v()} ${n} ${u.dim("Already running, skipping")}`);return}let i=`workermill-${e.id.slice(0,8)}`,a=["run","--rm",...!t.workerImage.includes("/")?[]:["--pull","always"],"--name",i],l=Math.round(ke.totalmem()/(1024*1024*1024));if(l<=16?a.push("--memory","6g","--memory-swap","10g","--cpus","2"):(l<=32,a.push("--memory","6g","--memory-swap","12g","--cpus","4")),ft){let p=$t();a.push(`--add-host=host.docker.internal:${p||"host-gateway"}`)}else a.push("--network","host");let y=e.workerProvider||"anthropic",I=yt();if(!I&&y==="anthropic"){console.error(`${v()} ${n} ${u.red("\u2717")} Claude credentials not found. Run 'claude' and complete the sign-in flow.`);return}if(I){let p=_e.join(I,".credentials.json");try{Z.chmodSync(p,438)}catch{}let M=ht(I);a.push("-v",`${M}:/home/worker/.claude`)}else console.log(`${v()} ${n} ${u.dim("Skipping Claude mount (non-Anthropic worker)")}`);let d=t.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),w=e.scmProvider||"github",k=It(w,t,o),C=o?.githubToken||t.githubToken,_=w==="bitbucket"&&o?.scmToken||t.bitbucketToken,x=w==="gitlab"&&o?.scmToken||t.gitlabToken,G={NODE_OPTIONS:"--max-old-space-size=3072",EPIC_MODE:"true",EXECUTION_MODE:"local",TASK_ID:e.id,ORG_ID:e.orgId||"",JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",TASK_SUMMARY:e.summary||"",TASK_DESCRIPTION:e.description||"",WORKER_PERSONA:e.workerPersona||"",RETRY_NUMBER:String(e.retryCount??0),TICKET_KEY:e.jiraIssueKey||"",API_BASE_URL:d,ORG_API_KEY:t.apiKey,SCM_PROVIDER:w,SCM_TOKEN:k,SCM_BASE_URL:o?.scmBaseUrl||"",GITHUB_TOKEN:C,GH_TOKEN:C,GITHUB_REVIEWER_TOKEN:o?.githubReviewerToken||"",BITBUCKET_TOKEN:_,BITBUCKET_USERNAME:o?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:x,TARGET_REPO:e.githubRepo||"",GITHUB_REPO:e.githubRepo||"",WORKER_MODEL:e.workerModel||String(r.defaultWorkerModel||""),CLAUDE_MODEL:e.workerModel||String(r.defaultWorkerModel||""),JIRA_BASE_URL:o?.jiraBaseUrl||"",JIRA_EMAIL:o?.jiraEmail||"",JIRA_API_TOKEN:o?.jiraApiToken||"",TICKET_SYSTEM:o?.issueTrackerProvider||"jira",LINEAR_API_KEY:o?.linearApiKey||"",AWS_ACCESS_KEY_ID:o?.customerAwsAccessKeyId||"",AWS_SECRET_ACCESS_KEY:o?.customerAwsSecretAccessKey||"",AWS_DEFAULT_REGION:o?.customerAwsRegion||"",AWS_REGION:o?.customerAwsRegion||"",CUSTOMER_AWS_ROLE_ARN:o?.customerAwsRoleArn||"",CUSTOMER_AWS_EXTERNAL_ID:o?.customerAwsExternalId||"",CUSTOMER_AWS_REGION:o?.customerAwsRegion||"",MANAGER_PROVIDER:o?.managerProvider||"anthropic",MANAGER_MODEL:o?.managerModelId||"",BITBUCKET_EMAIL:o?.bitbucketEmail||"",DEPLOYMENT_ENABLED:e.deploymentEnabled||e.parentTaskId?"true":"false",PRD_CHILD_TASK:e.parentTaskId?"true":"false",IMPROVEMENT_ENABLED:e.improvementEnabled?"true":"false",QUALITY_GATE_BYPASS:e.qualityGateBypass?"true":"false",STANDARD_SDK_MODE:e.standardSdkMode?"true":"false",MAX_REVIEW_REVISIONS:String(r.maxReviewRevisions??3),CODEBASE_INDEXING_ENABLED:r.codebaseIndexingEnabled===!0?"true":"false",EXISTING_PR_URL:e.githubPrUrl||"",EXISTING_PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",PARENT_TASK_ID:e.parentTaskId||e.id,PARENT_JIRA_KEY:e.jiraIssueKey&&/-S\d+$/.test(e.jiraIssueKey)&&e.jiraFields?.parentJiraKey||"",TARGET_BRANCH:e.jiraFields?.targetBranch||"",STORY_BRANCH:e.jiraFields?.storyBranch||"",TASK_NOTES:e.taskNotes||"",TARGET_FILES:JSON.stringify(e.jiraFields?.targetFiles||[]),REFERENCE_FILES:JSON.stringify(e.jiraFields?.referenceFiles||[]),PIPELINE_VERSION:e.jiraFields?.pipelineVersion||e.pipelineVersion||"",V2_STEP_INPUT:e.jiraFields?.v2StepInput?JSON.stringify(e.jiraFields.v2StepInput):"",EXECUTION_MODE_SETTING:e.jiraFields?.executionMode||"autonomous",ANTHROPIC_API_KEY:I?"":o?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",WORKER_PROVIDER:e.workerProvider||"anthropic",OPENAI_API_KEY:o?.openaiApiKey||"",GOOGLE_API_KEY:o?.googleApiKey||"",GOOGLE_GENERATIVE_AI_API_KEY:o?.googleApiKey||"",OLLAMA_HOST:o?.ollamaBaseUrl||"",OLLAMA_CONTEXT_WINDOW:o?.ollamaContextWindow?String(o.ollamaContextWindow):"",VLLM_BASE_URL:o?.vllmBaseUrl||"",BLOCKER_MAX_AUTO_RETRIES:String(r.blockerMaxAutoRetries??3),BLOCKER_AUTO_RETRY_ENABLED:r.blockerAutoRetryEnabled!==!1?"true":"false",PUSH_AFTER_COMMIT:r.pushAfterCommit!==!1?"true":"false",GRACEFUL_SHUTDOWN_ENABLED:r.gracefulShutdownEnabled!==!1?"true":"false",MAX_PARALLEL_EXPERTS:String(r.maxParallelExperts??4),REVIEW_ENABLED:e.skipManagerReview===!1?"true":"false",SELF_REVIEW_ENABLED:ko(e)||r.selfReviewEnabled!==!1?"true":"false"};for(let[p,M]of Object.entries(G))M!==""&&a.push("-e",`${p}=${M}`);let K=t.workerImage,E=wt(K);E&&Et(E),a.push(K);let L=e.skipManagerReview===!1;console.log(`${v()} ${n} ${u.dim("Starting container")} ${u.yellow(i)}`),console.log(`${v()} ${n} ${u.dim(` skipManagerReview=${e.skipManagerReview} \u2192 REVIEW_ENABLED=${L}`)}`),console.log(`${v()} ${n} ${u.dim(` model=${e.workerModel} repo=${e.githubRepo}`)}`),console.log(`${v()} ${n} ${u.dim(` totalRamGB=${l} docker args:`)} ${a.slice(0,10).join(" ")}`);let f=mt("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!f.pid){console.error(`${v()} ${n} ${u.red("\u2717")} Failed to spawn container`);return}let U={taskId:e.id,containerName:i,process:f,startedAt:new Date,status:"running",resultEmitted:!1};V.set(e.id,U),f.stdout?.on("data",p=>{let M=p.toString().split(`
12
+ `).filter(O=>O.trim());for(let O of M)console.log(`${v()} ${n} ${u.dim(Re(O))}`),O.includes("::result::")&&(U.resultEmitted=!0)}),f.stderr?.on("data",p=>{let M=p.toString().split(`
13
+ `).filter(O=>O.trim());for(let O of M)console.log(`${v()} ${n} ${u.red(Re(O))}`)}),f.on("exit",p=>{U.status=p===0?"completed":"failed";let M=Math.round((Date.now()-U.startedAt.getTime())/1e3),O=p===0?u.green("\u2713"):u.red("\u2717"),F=p===0?u.green("completed"):u.red(`failed (exit ${p})`);console.log(`${v()} ${n} ${O} Container ${F} ${u.dim(`(${M}s)`)}`),U.resultEmitted||(console.log(`${v()} ${n} ${u.yellow("\u26A0")} No ::result:: marker seen \u2014 posting fallback completion in 15s`),setTimeout(async()=>{try{let j=p===0?"completed":"failed",Q=p!==0?`Worker container exited with code ${p} without reporting completion`:void 0;(await(await fetch(`${t.apiUrl}/api/tasks/${e.id}/worker-complete`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":t.apiKey},body:JSON.stringify({exitCode:p??1,result:j,errorMessage:Q})})).json()).status==="ignored"?console.log(`${v()} ${n} ${u.dim("Fallback completion ignored (task already transitioned)")}`):console.log(`${v()} ${n} ${u.yellow("\u26A0")} Fallback completion applied: ${j}`)}catch(j){console.error(`${v()} ${n} ${u.red("\u2717")} Fallback completion failed:`,j instanceof Error?j.message:j)}},15e3)),setTimeout(()=>V.delete(e.id),9e4)}),f.on("error",p=>{U.status="failed",console.error(`${v()} ${n} ${u.red("\u2717")} Container error: ${p.message}`)})}function je(){return Array.from(V.values()).filter(e=>e.status==="running").length}function bt(){return Array.from(V.values()).filter(e=>e.status==="running").map(e=>e.taskId)}function Tt(e){let t=V.get(e);if(!t||t.status!=="running")return;let r=u.cyan(e.slice(0,8));console.log(`${v()} ${r} ${u.red("\u25A0")} Stopping container (cancelled by dashboard)`);try{Se(`docker stop ${t.containerName}`,{stdio:"ignore",timeout:15e3}),t.status="completed"}catch{}V.delete(e)}async function _t(){console.log(`${v()} ${u.dim(`Stopping ${V.size} containers...`)}`);for(let[,e]of V)if(e.status==="running")try{Se(`docker stop ${e.containerName}`,{stdio:"ignore",timeout:15e3}),e.status="completed"}catch{}V.clear()}async function Rt(e,t,r){let o=u.cyan(e.id.slice(0,8)),n=`manager-${e.id}`;if(V.has(n))return;let i=`wm-manager-${e.id.slice(0,8)}-${Date.now()}`,s=yt(),a=["run","--rm","--name",i,"--memory=4g","--cpus=2"];if(ft){let E=$t();a.push(`--add-host=host.docker.internal:${E||"host-gateway"}`)}else a.push("--network","host");if(s){let E=ht(s);a.push("-v",`${E}:/home/worker/.claude`)}let l=t.apiUrl.replace(/localhost|127\.0\.0\.1/,"host.docker.internal"),y=e.scmProvider||"github",I=It(y,t,r),d=r?.githubToken||t.githubToken,w=y==="bitbucket"&&r?.scmToken||t.bitbucketToken,k=y==="gitlab"&&r?.scmToken||t.gitlabToken,C={NODE_OPTIONS:"--max-old-space-size=3072",TASK_ID:e.id,MANAGER_ACTION:e.managerAction,JIRA_ISSUE_KEY:e.jiraIssueKey||"",JIRA_SUMMARY:e.summary||"",JIRA_DESCRIPTION:e.description||"",GITHUB_REPO:e.githubRepo||"",PR_URL:e.githubPrUrl||"",PR_NUMBER:e.githubPrNumber?String(e.githubPrNumber):"",API_BASE_URL:l,ORG_API_KEY:t.apiKey,SCM_PROVIDER:y,SCM_TOKEN:I,GITHUB_TOKEN:d,GH_TOKEN:d,BITBUCKET_TOKEN:w,BITBUCKET_USERNAME:r?.bitbucketUsername||"x-token-auth",GITLAB_TOKEN:k,MANAGER_PROVIDER:r?.managerProvider||"anthropic",MANAGER_MODEL:r?.managerModelId||"",ANTHROPIC_API_KEY:s?"":r?.anthropicApiKey||process.env.ANTHROPIC_API_KEY||"",OPENAI_API_KEY:r?.openaiApiKey||"",GOOGLE_API_KEY:r?.googleApiKey||"",JIRA_BASE_URL:r?.jiraBaseUrl||"",JIRA_EMAIL:r?.jiraEmail||"",JIRA_API_TOKEN:r?.jiraApiToken||"",TICKET_SYSTEM:r?.issueTrackerProvider||"jira",LINEAR_API_KEY:r?.linearApiKey||""};for(let[E,L]of Object.entries(C))L!==""&&a.push("-e",`${E}=${L}`);let _=t.workerImage,x=wt(_);x&&Et(x),a.push("--entrypoint","/bin/bash"),a.push(_),a.push("/app/manager-entrypoint.sh"),console.log(`${v()} ${o} ${u.magenta("\u25C6 MANAGER")} Starting ${e.managerAction} container ${u.yellow(i)}`);let G=mt("docker",a,{stdio:["ignore","pipe","pipe"],detached:!1});if(!G.pid){console.error(`${v()} ${o} ${u.red("\u2717")} Failed to spawn manager container`);return}let K={taskId:n,containerName:i,process:G,startedAt:new Date,status:"running",resultEmitted:!1};V.set(n,K),G.stdout?.on("data",E=>{let L=E.toString().split(`
14
+ `).filter(f=>f.trim());for(let f of L)console.log(`${v()} ${o} ${u.magenta("MGR")} ${u.dim(Re(f))}`)}),G.stderr?.on("data",E=>{let L=E.toString().split(`
15
+ `).filter(f=>f.trim());for(let f of L)console.log(`${v()} ${o} ${u.magenta("MGR")} ${u.red(Re(f))}`)}),G.on("exit",E=>{K.status=E===0?"completed":"failed";let L=Math.round((Date.now()-K.startedAt.getTime())/1e3),f=E===0?u.green("\u2713"):u.red("\u2717"),U=E===0?u.green("completed"):u.red(`failed (exit ${E})`);console.log(`${v()} ${o} ${u.magenta("MGR")} ${f} Manager ${e.managerAction} ${U} ${u.dim(`(${L}s)`)}`),setTimeout(()=>V.delete(n),6e4)}),G.on("error",E=>{K.status="failed",console.error(`${v()} ${o} ${u.magenta("MGR")} ${u.red("\u2717")} Container error: ${E.message}`)})}import{createRequire as Po}from"module";var vo=Po(import.meta.url),Co=vo("../package.json"),le=Co.version;import{spawn as St}from"child_process";import Ie from"chalk";async function ve(){return console.log(Ie.cyan(" Updating @workermill/agent...")),new Promise(e=>{let t=St("npm",["install","-g","@workermill/agent@latest"],{stdio:"inherit",shell:!0});t.on("error",r=>{console.error(Ie.red(` Update failed: ${r.message}`)),e(!1)}),t.on("close",r=>{r===0?(console.log(Ie.green(" Update successful.")),e(!0)):(console.error(Ie.red(` Update failed with exit code ${r}`)),e(!1))})})}function Ce(){console.log(Ie.cyan(" Restarting agent...")),St(process.argv[0],process.argv.slice(1),{stdio:"inherit",detached:!0}).unref(),process.exit(0)}var ue=new Set,de=new Set,xe=null,kt=!1,Me=!1;function N(){return h.dim(new Date().toLocaleTimeString())}async function xo(){if(xe)return xe;try{return xe=(await P.get("/api/agent/config")).data,xe}catch{return console.error(`${N()} ${h.red("\u2717")} Failed to fetch org config`),{}}}async function Pt(e){if(!Me)try{let t=await P.get("/api/agent/poll",{params:{agentId:e.agentId}}),r=t.data.tasks;if(r.length===0)return;for(let n of r)n.status==="planning"&&!ue.has(n.id)?await Mo(n,e):n.status==="queued"&&await Oo(n,e);let o=t.data.managerTasks;if(o&&o.length>0)for(let n of o)de.has(n.id)||await No(n,e)}catch(t){let r=t,o=ue.size>0||je()>0||de.size>0;r.response?.status===401?o||console.error(`${N()} ${h.red("\u2717")} Authentication failed. Check your API key.`):o||console.warn(`${N()} ${h.yellow("\u26A0")} Poll error: ${r.message||String(t)}`)}}async function Mo(e,t){let r,o;try{let s=await P.post("/api/agent/claim",{taskId:e.id,agentId:t.agentId});if(!s.data.claimed)return;r=s.data.credentials,o=s.data.task}catch{return}let n=h.cyan(e.id.slice(0,8));if(o&&(o.retryCount??0)>0&&o.executionPlanV2!=null){console.log(),console.log(`${N()} ${h.magenta("\u25C6 RESUME")} ${n} ${e.summary.substring(0,60)}`),console.log(`${N()} ${n} Retry #${o.retryCount} with existing plan \u2014 skipping planning`),ue.add(e.id);try{await P.post("/api/agent/resume-plan",{taskId:e.id,agentId:t.agentId}),console.log(`${N()} ${n} ${h.green("\u2713")} Resumed with existing plan \u2192 ${h.green("queued")}`)}catch(s){let a=s,l=a.response?.data?.error||a.message||String(s);console.error(`${N()} ${n} ${h.red("\u2717")} Resume failed: ${l}`)}ue.delete(e.id);return}console.log(),console.log(`${N()} ${h.magenta("\u25C6 PLANNING")} ${n} ${e.summary.substring(0,60)}`),ue.add(e.id),pt(e,t,r).then(s=>{console.log(s?`${N()} ${h.green("\u2713")} Planning complete for ${n}`:`${N()} ${h.red("\u2717")} Planning failed for ${n}`)}).catch(s=>console.error(`${N()} ${h.red("\u2717")} Planning error for ${n}:`,s.message||s)).finally(()=>ue.delete(e.id))}async function Oo(e,t){if(je()>=t.maxWorkers)return;let o;try{if(o=(await P.post("/api/agent/claim",{taskId:e.id,agentId:t.agentId})).data,!o.claimed)return}catch{return}try{await P.post("/api/agent/started",{taskId:e.id,agentId:t.agentId})}catch{let y=h.cyan(e.id.slice(0,8));console.warn(`${N()} ${h.yellow("\u26A0")} Failed to report started for ${y}`)}let n=h.cyan(e.id.slice(0,8));console.log(),console.log(`${N()} ${h.blue("\u25B6 EXECUTING")} ${n} ${e.summary.substring(0,60)}`);let i=await xo(),s=o.task||{id:e.id,summary:e.summary,description:e.description,jiraIssueKey:e.jiraIssueKey,workerModel:e.workerModel,workerProvider:e.workerProvider,githubRepo:e.githubRepo,scmProvider:e.scmProvider,skipManagerReview:e.skipManagerReview,deploymentEnabled:e.deploymentEnabled,improvementEnabled:e.improvementEnabled,qualityGateBypass:e.qualityGateBypass,standardSdkMode:e.standardSdkMode,parentTaskId:e.parentTaskId,taskNotes:e.taskNotes,githubPrUrl:e.githubPrUrl,githubPrNumber:e.githubPrNumber,executionPlanV2:e.executionPlanV2,jiraFields:e.jiraFields||{}},a=o.credentials;At(s,t,i,a).catch(l=>console.error(`${N()} ${h.red("\u2717")} Spawn failed for ${n}:`,l.message||l))}async function No(e,t){let r=h.cyan(e.id.slice(0,8));de.add(e.id);try{let o=await P.post("/api/agent/claim-manager",{taskId:e.id,agentId:t.agentId,action:e.managerAction});if(!o.data.claimed){de.delete(e.id);return}let n=o.data.task,i=o.data.credentials||{},s=e.managerAction==="analyze_logs"?h.yellow("LOG ANALYSIS"):h.yellow("PR REVIEW");console.log(),console.log(`${N()} ${h.magenta("\u25C6 MANAGER")} ${s} ${r} ${e.summary.substring(0,60)}`);let a={id:e.id,summary:n?.summary||e.summary,description:n?.description||e.description,jiraIssueKey:n?.jiraIssueKey||e.jiraIssueKey,githubRepo:n?.githubRepo||e.githubRepo,scmProvider:n?.scmProvider||e.scmProvider,githubPrUrl:n?.githubPrUrl||e.githubPrUrl,githubPrNumber:n?.githubPrNumber||e.githubPrNumber,managerAction:e.managerAction};Rt(a,t,i).then(()=>{console.log(`${N()} ${r} ${h.magenta("MGR")} ${h.green("\u2713")} Manager ${e.managerAction} dispatched`)}).catch(l=>{console.error(`${N()} ${r} ${h.magenta("MGR")} ${h.red("\u2717")} Manager spawn failed:`,l.message||l)}).finally(()=>de.delete(e.id))}catch(o){de.delete(e.id);let n=o;console.error(`${N()} ${r} ${h.red("\u2717")} Failed to claim manager task:`,n.message||String(o))}}var Oe=null,Ne=null;function vt(){Oe&&(clearInterval(Oe),Oe=null),Ne&&(clearInterval(Ne),Ne=null)}function Ct(e){console.log(` ${h.dim("Polling every")} ${e.pollIntervalMs/1e3}s ${h.dim("\xB7 waiting for tasks...")}`),Pt(e),Oe=setInterval(()=>Pt(e),e.pollIntervalMs)}function xt(e){Ne=setInterval(async()=>{let t=bt(),r=Array.from(ue),o=Array.from(de),n=[...t,...r,...o];try{let i=await P.post("/api/agent/heartbeat",{agentId:e.agentId,activeTasks:n,agentVersion:le}),s=i.data?.cancelledTasks;if(s&&s.length>0)for(let I of s)Tt(I);let{updateAvailable:a,updateRequired:l,latestVersion:y}=i.data??{};l&&!Me?(Me=!0,console.log(`${N()} ${h.red("\u26A0 Agent update required")} (current: ${le}, required: ${y})`),console.log(`${N()} ${h.yellow("Refusing new tasks until updated.")}`),await ve()?Ce():(console.log(`${N()} ${h.red("Auto-update failed.")} Run: npm install -g @workermill/agent@latest`),Me=!1)):a&&!kt&&!l&&(kt=!0,console.log(`${N()} ${h.yellow(`Update available: ${y}`)} (current: ${le}). Run: workermill-agent update`))}catch{}},e.heartbeatIntervalMs)}Ae();async function Lo(e){console.log(),console.log(D.bold.cyan(" WorkerMill Remote Agent")),console.log(D.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(),Ge(e.apiUrl,e.apiKey);try{let t=await P.get("/api/agent/config"),r=t.data.maxConcurrentWorkers;r&&typeof r=="number"&&(e.maxWorkers=r),t.data.workerImageUrl&&(e.workerImage=t.data.workerImageUrl),console.log(` ${D.green("\u25CF")} Connected to ${D.cyan(e.apiUrl)}`),console.log(` ${D.dim("Agent:")} ${e.agentId}`),console.log(` ${D.dim("Workers:")} ${D.yellow(String(e.maxWorkers))} parallel`),console.log(` ${D.dim("Image:")} ${e.workerImage}`),console.log(` ${D.dim("SCM:")} ${t.data.scmProvider}`),console.log(` ${D.dim("Model:")} ${D.yellow(t.data.defaultWorkerModel)}`),console.log()}catch(t){let r=t;throw r.response?.status===401?new Error("Authentication failed. Check your API key."):new Error(`Failed to connect to WorkerMill API: ${r.message||String(t)}`)}try{let t=await P.post("/api/agent/register",{agentId:e.agentId,maxWorkers:e.maxWorkers,agentVersion:le}),{updateAvailable:r,updateRequired:o,latestVersion:n}=t.data;o?(console.log(D.red(` \u26A0 Agent update required (current: ${le}, required: ${n})`)),await ve()?Ce():console.log(D.yellow(" Auto-update failed. Run: npm install -g @workermill/agent@latest"))):r&&console.log(D.yellow(` Update available: ${n} (current: ${le}). Run: workermill-agent update`))}catch{}return Ct(e),xt(e),console.log(D.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` ${D.green("\u25CF")} Agent is running. ${D.dim("Press Ctrl+C to stop.")}`),console.log(),async()=>{console.log(),console.log(D.dim(" Shutting down...")),vt();try{await P.post("/api/agent/deregister",{agentId:e.agentId})}catch{}await _t(),console.log(` ${D.red("\u25CF")} Agent stopped.`)}}var Uo=typeof process<"u"&&process.argv[1]&&(process.argv[1].endsWith("/index.ts")||process.argv[1].endsWith("/index.js"));if(Uo){try{await import("dotenv/config")}catch{}let{loadConfig:e,validatePrerequisites:t}=await Promise.resolve().then(()=>(Ae(),He)),r=e();t(),console.log(D.dim(" Prerequisites validated."));let o=await Lo(r);process.on("SIGINT",async()=>{await o(),process.exit(0)}),process.on("SIGTERM",async()=>{await o(),process.exit(0)})}export{ce as findClaudePath,ze as getSystemInfo,Ve as loadConfig,We as loadConfigFromFile,Lo as startAgent,Ye as validatePrerequisites};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workermill/agent",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "description": "WorkerMill Remote Agent - Run AI workers locally with your Claude Max subscription",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",