@wular/coder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ <div align="center">
2
+
3
+ # Coder
4
+
5
+ ### Delegate the coding. Keep your context.
6
+
7
+ </div>
8
+
9
+ One runtime that hands your coding tasks to the best engine available and lets you steer, inspect, or stop any job while it works.
10
+
11
+ Plugs into Claude Code or Codex to enable the harness with Coder instances.
12
+
13
+ ## Why Coder
14
+
15
+ - **A clean main thread.** Installed in Claude Code or Codex, dispatch is a single command from your session; the actual coding runs in a Coder subagent. File dumps, test output, and retries stay there; only the outcome returns to your conversation.
16
+ - **Fast dispatch.** Coder keeps a single Codex engine warm in the background and hands tasks to it directly, instead of booting the CLI for every request. A handoff starts in seconds.
17
+ - **Unified permissions.** One permission model across agents, with three modes: **Read-only**, **Workspace-write**, and **Auto**. Auto is the default: safe commands run, dangerous ones are blocked, and anything in between is brought to you for a yes/no.
18
+ - **Jobs you can steer.** Every task is a job. Check on it, read its result, give it follow-up instructions, or stop it, all by id, while it keeps working in the background.
19
+ - **Graceful fallback.** If Codex can't start (auth, usage limits, not installed), the task flows to a Claude subagent with the same instructions. You configure the chain and the default model and effort for each agent.
20
+
21
+ ## Get started
22
+
23
+ ### Claude Code
24
+
25
+ ```
26
+ /plugin marketplace add muzam1l/wular-plugins
27
+
28
+ /plugin install Coder@wular-plugins
29
+
30
+ /coder:setup
31
+ ```
32
+
33
+ Or from the shell:
34
+
35
+ ```
36
+ npm install -g @wular/coder
37
+ coder setup --claude
38
+ ```
39
+
40
+ It shall use Coder automatically or you can also manually ask like:
41
+
42
+ > Use Coder to fix the failing date tests in packages/utils.
43
+
44
+ The work happens in the engine, and your session gets the result. Codex is the primary engine when available, with a Claude subagent as fallback.
45
+
46
+ ### Codex
47
+
48
+ ```
49
+ codex plugin marketplace add muzam1l/wular-plugins
50
+ codex plugin add Coder@wular-plugins
51
+ ```
52
+
53
+ Or from the npm directly:
54
+
55
+ ```
56
+ npm install -g @wular/coder
57
+ coder setup --codex
58
+ ```
59
+
60
+ Restart Codex and it dispatches coding tasks through the same runtime, with Claude run directly by Coder when it is the selected or fallback engine.
61
+
62
+ ## Configuration
63
+
64
+ Machine defaults live in `~/.coder/config.json`; drop a `coder.config.json` in a repo to override per project.
65
+
66
+ ```json
67
+ {
68
+ "chain": ["codex", "claude"],
69
+ "agents": {
70
+ "codex": { "model": "gpt-5.5", "effort": "medium", "permissions": "auto" },
71
+ "claude": { "model": "opus", "permissions": "auto" }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## Advanced usage
77
+
78
+ You rarely need these: the agent creates and controls jobs on its own. But you can ask it explicitly ("steer that job", "approve that command", "stop it"), or run the commands yourself:
79
+
80
+ ```bash
81
+ coder task "<text>" # run a task in the background (--wait to block)
82
+ coder status [job] # progress and pending approvals
83
+ coder result [job] # the final answer
84
+ coder steer <job> "<follow-up>" # continue a job with new instructions
85
+ coder stop <job> # interrupt it
86
+ coder approve <job> <id> # answer an escalated permission (--deny)
87
+ ```
88
+
89
+ Common flags:
90
+
91
+ | Flag | Values | Default |
92
+ | ---------------- | --------------------------------------------------------------- | ---------------- |
93
+ | `--wait` | run in the foreground and print the answer | background |
94
+ | `--agent` | `codex`, `claude` | first in `chain` |
95
+ | `--model` | `5.5`, `spark`, `opus`, `sonnet`, `fable` or any raw model slug | from config |
96
+ | `--effort` | `low`, `medium`, `high` | from config |
97
+ | `--permissions` | `read-only`, `workspace-write`, `auto` | `auto` |
98
+ | `--resume <job>` | continue that job's thread instead of starting fresh | fresh run |
package/bin/coder.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/cli.js";
package/dist/cli.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import S from"node:fs";import J from"node:path";import M from"node:process";import{spawn as Z1,spawnSync as D}from"node:child_process";import{fileURLToPath as s}from"node:url";function k($,W={}){let Z=new Set(W.valueOptions??[]),Q=new Set(W.booleanOptions??[]),V=W.aliasMap??{},Y={},K=[],G=!1;for(let X=0;X<$.length;X+=1){let H=$[X];if(G){K.push(H);continue}if(H==="--"){G=!0;continue}if(!H.startsWith("-")||H==="-"){K.push(H);continue}if(H.startsWith("--")){let[q,O]=H.slice(2).split("=",2),A=V[q]??q;if(Q.has(A)){Y[A]=O===void 0?!0:O!=="false";continue}if(Z.has(A)){let n=O??$[X+1];if(n===void 0)throw Error(`Missing value for --${q}`);if(Y[A]=n,O===void 0)X+=1;continue}K.push(H);continue}let z=H.slice(1),U=V[z]??z;if(Q.has(U)){Y[U]=!0;continue}if(Z.has(U)){let q=$[X+1];if(q===void 0)throw Error(`Missing value for -${z}`);Y[U]=q,X+=1;continue}K.push(H)}return{options:Y,positionals:K}}import{createHash as V0}from"node:crypto";import{execFileSync as Y0}from"node:child_process";import R from"node:fs";import G0 from"node:os";import C from"node:path";var K0="CODER_HOME";function $$(){return C.resolve(process.env[K0]||C.join(G0.homedir(),".coder"))}function w($){try{return Y0("git",["rev-parse","--show-toplevel"],{cwd:$,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return $}}function d($){let W=w($),Z=W;try{Z=R.realpathSync.native(W)}catch{Z=W}let V=(C.basename(W)||"workspace").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"workspace",Y=V0("sha256").update(Z).digest("hex").slice(0,12);return C.join($$(),"state",`${V}-${Y}`)}function C$($){return C.join(d($),"jobs")}function x($,W){return C.join(C$($),W)}function R$(){let $=Math.random().toString(36).slice(2,8);return`coder-${Date.now().toString(36)}-${$}`}function y($,W,Z){let Q=x($,W);R.mkdirSync(Q,{recursive:!0});let V=C.join(Q,"job.json"),K={...E($,W)??{id:W,createdAt:new Date().toISOString()},...Z,updatedAt:new Date().toISOString()};return R.writeFileSync(V,`${JSON.stringify(K,null,2)}
3
+ `,"utf8"),K}function E($,W){let Z=C.join(x($,W),"job.json");if(!R.existsSync(Z))return null;try{return JSON.parse(R.readFileSync(Z,"utf8"))}catch{return null}}function W$($){let W=C$($);if(!R.existsSync(W))return[];return R.readdirSync(W).map((Z)=>E($,Z)).filter(Boolean).sort((Z,Q)=>String(Q.updatedAt??"").localeCompare(String(Z.updatedAt??"")))}function Z$($,W){let Z=W$($);if(!W)return Z[0]??null;let Q=Z.find((Y)=>Y.id===W);if(Q)return Q;let V=Z.filter((Y)=>Y.id.startsWith(W));if(V.length===1)return V[0];if(V.length>1)throw Error(`Job reference "${W}" is ambiguous. Use a longer id.`);return null}function v($,W,Z){let Q=x($,W);R.mkdirSync(Q,{recursive:!0}),R.appendFileSync(C.join(Q,"log.jsonl"),`${JSON.stringify({at:new Date().toISOString(),...Z})}
4
+ `,"utf8")}function Q$($,W,Z=40){let Q=C.join(x($,W),"log.jsonl");if(!R.existsSync(Q))return[];return R.readFileSync(Q,"utf8").split(/\r?\n/).filter(Boolean).slice(-Z).map((Y)=>{try{return JSON.parse(Y)}catch{return{message:Y}}})}import R0 from"node:net";import m from"node:process";import{spawn as A0}from"node:child_process";import F0 from"node:readline";import A$ from"node:path";import H0 from"node:process";function X0($){return String($??"").replace(/[^A-Za-z0-9._-]/g,"-").replace(/^-+|-+$/g,"")}function F$($,W=H0.platform){if(W==="win32")return`pipe:\\\\.\\pipe\\${X0(`${A$.win32.basename($)}-codex-app-server`)}`;return`unix:${A$.join($,"broker.sock")}`}function b($){if(typeof $!=="string"||$.length===0)throw Error("Missing broker endpoint.");if($.startsWith("pipe:")){let W=$.slice(5);if(!W)throw Error("Broker pipe endpoint is missing its path.");return{kind:"pipe",path:W}}if($.startsWith("unix:")){let W=$.slice(5);if(!W)throw Error("Broker Unix socket endpoint is missing its path.");return{kind:"unix",path:W}}throw Error(`Unsupported broker endpoint: ${$}`)}import B from"node:fs";import z0 from"node:net";import q0 from"node:os";import T from"node:path";import V$ from"node:process";import{spawn as U0}from"node:child_process";import{fileURLToPath as M0}from"node:url";var O0="broker.json";function B0($="coder-"){return B.mkdtempSync(T.join(q0.tmpdir(),$))}function N0($){let W=b($);return z0.createConnection({path:W.path})}async function D$($,W=2000){let Z=Date.now();while(Date.now()-Z<W){if(await new Promise((V)=>{let Y=N0($);Y.on("connect",()=>{Y.end(),V(!0)}),Y.on("error",()=>V(!1))}))return!0;await new Promise((V)=>setTimeout(V,50))}return!1}function L0({scriptPath:$,cwd:W,endpoint:Z,pidFile:Q,logFile:V,env:Y=V$.env}){let K=B.openSync(V,"a"),G=U0(V$.execPath,[$,"serve","--endpoint",Z,"--cwd",W,"--pid-file",Q],{cwd:W,env:Y,detached:!0,stdio:["ignore",K,K]});return G.unref(),B.closeSync(K),G}function J0(){let $=[new URL("./lib/broker.js",import.meta.url),new URL("./broker.js",import.meta.url)];for(let W of $){let Z=M0(W);if(B.existsSync(Z))return Z}throw Error("Coder broker script not found next to the runtime. Rebuild with `bun run build`.")}function Y$($){return T.join(d($),O0)}function G$($){let W=Y$($);if(!B.existsSync(W))return null;try{return JSON.parse(B.readFileSync(W,"utf8"))}catch{return null}}function x0($,W){let Z=d($);B.mkdirSync(Z,{recursive:!0}),B.writeFileSync(Y$($),`${JSON.stringify(W,null,2)}
5
+ `,"utf8")}function y0($){let W=Y$($);if(B.existsSync(W))B.unlinkSync(W)}async function f0($){if(!$)return!1;try{return await D$($,150)}catch{return!1}}async function S$($,W={}){let Z=G$($);if(Z&&await f0(Z.endpoint))return Z;if(Z)P$({endpoint:Z.endpoint??null,pidFile:Z.pidFile??null,logFile:Z.logFile??null,sessionDir:Z.sessionDir??null,pid:Z.pid??null,killProcess:W.killProcess??null}),y0($);let Q=B0(),Y=(W.createBrokerEndpoint??F$)(Q,W.platform),K=T.join(Q,"broker.pid"),G=T.join(Q,"broker.log"),X=W.scriptPath??J0(),H=L0({scriptPath:X,cwd:$,endpoint:Y,pidFile:K,logFile:G,env:W.env??V$.env});if(!await D$(Y,W.timeoutMs??2000))return P$({endpoint:Y,pidFile:K,logFile:G,sessionDir:Q,pid:H.pid??null,killProcess:W.killProcess??null}),null;let U={endpoint:Y,pidFile:K,logFile:G,sessionDir:Q,pid:H.pid??null};return x0($,U),U}function P$({endpoint:$=null,pidFile:W,logFile:Z,sessionDir:Q=null,pid:V=null,killProcess:Y=null}){if(Number.isFinite(V)&&Y)try{Y(V)}catch{}if(W&&B.existsSync(W))B.unlinkSync(W);if(Z&&B.existsSync(Z))B.unlinkSync(Z);if($)try{let G=b($);if(G.kind==="unix"&&B.existsSync(G.path))B.unlinkSync(G.path)}catch{}let K=Q??(W?T.dirname(W):Z?T.dirname(Z):null);if(K&&B.existsSync(K))try{B.rmdirSync(K)}catch{}}import{spawnSync as _0}from"node:child_process";import g from"node:process";function E$($,W=[],Z={}){let Q=_0($,W,{cwd:Z.cwd,env:Z.env,encoding:"utf8",input:Z.input,maxBuffer:Z.maxBuffer,stdio:Z.stdio??"pipe",shell:g.platform==="win32"?g.env.SHELL||!0:!1,windowsHide:!0});return{command:$,args:W,status:Q.status??0,signal:Q.signal??null,stdout:Q.stdout??"",stderr:Q.stderr??"",error:Q.error??null}}function K$($,W=["--version"],Z={}){let Q=E$($,W,Z);if(Q.error&&Q.error.code==="ENOENT")return{available:!1,detail:"not found"};if(Q.error)return{available:!1,detail:Q.error.message};if(Q.status!==0)return{available:!1,detail:Q.stderr.trim()||Q.stdout.trim()||`exit ${Q.status}`};return{available:!0,detail:Q.stdout.trim()||Q.stderr.trim()||"ok"}}function k0($){return/not found|no running instance|cannot find|does not exist|no such process/i.test($)}function T$($,W={}){if(!Number.isFinite($))return{attempted:!1,delivered:!1,method:null};let Z=W.platform??g.platform,Q=W.runCommandImpl??E$,V=W.killImpl??g.kill.bind(g);if(Z==="win32"){let Y=Q("taskkill",["/PID",String($),"/T","/F"],{cwd:W.cwd,env:W.env});if(!Y.error&&Y.status===0)return{attempted:!0,delivered:!0,method:"taskkill",result:Y};let K=`${Y.stderr}
6
+ ${Y.stdout}`.trim();if(!Y.error&&k0(K))return{attempted:!0,delivered:!1,method:"taskkill",result:Y};if(Y.error?.code==="ENOENT")try{return V($),{attempted:!0,delivered:!0,method:"kill"}}catch(G){if(G?.code==="ESRCH")return{attempted:!0,delivered:!1,method:"kill"};throw G}if(Y.error)throw Y.error;throw Error(C0(Y))}try{return V(-$,"SIGTERM"),{attempted:!0,delivered:!0,method:"process-group"}}catch(Y){if(Y?.code!=="ESRCH")try{return V($,"SIGTERM"),{attempted:!0,delivered:!0,method:"process"}}catch(K){if(K?.code==="ESRCH")return{attempted:!0,delivered:!1,method:"process"};throw K}return{attempted:!0,delivered:!1,method:"process-group"}}}function C0($){let W=[`${$.command} ${$.args.join(" ")}`.trim()];if($.signal)W.push(`signal=${$.signal}`);else W.push(`exit=${$.status}`);let Z=($.stderr||"").trim(),Q=($.stdout||"").trim();if(Z)W.push(Z);else if(Q)W.push(Q);return W.join(": ")}var i="CODER_APP_SERVER_ENDPOINT",I$=-32001,j$={title:"Coder",name:"coder",version:"0.1.0"},h$={experimentalApi:!1,requestAttestation:!1,optOutNotificationMethods:["item/agentMessage/delta","item/reasoning/summaryTextDelta","item/reasoning/summaryPartAdded","item/reasoning/textDelta"]};function u$($,W,Z){return Z===void 0?{code:$,message:W}:{code:$,message:W,data:Z}}function H$($,W){let Z=Error($);if(Z.data=W,W?.code!==void 0)Z.rpcCode=W.code;return Z}class X${cwd;options;pending;nextId;stderr;closed;exitError;notificationHandler;serverRequestHandler;lineBuffer;transport;exitPromise;exitResolved;resolveExit;handleServerRequest;constructor($,W={}){this.cwd=$,this.options=W,this.pending=new Map,this.nextId=1,this.stderr="",this.closed=!1,this.exitError=null,this.notificationHandler=null,this.serverRequestHandler=null,this.lineBuffer="",this.transport="unknown",this.handleServerRequest=(Z)=>this.defaultHandleServerRequest(Z),this.exitPromise=new Promise((Z)=>{this.resolveExit=Z})}setNotificationHandler($){this.notificationHandler=$}setServerRequestHandler($){this.serverRequestHandler=$}request($,W){if(this.closed)throw Error("codex app-server client is closed.");let Z=this.nextId;return this.nextId+=1,new Promise((Q,V)=>{this.pending.set(Z,{resolve:Q,reject:V,method:$}),this.sendMessage({id:Z,method:$,params:W})})}notify($,W={}){if(this.closed)return;this.sendMessage({method:$,params:W})}handleChunk($){this.lineBuffer+=$;let W=this.lineBuffer.indexOf(`
7
+ `);while(W!==-1){let Z=this.lineBuffer.slice(0,W);this.lineBuffer=this.lineBuffer.slice(W+1),this.handleLine(Z),W=this.lineBuffer.indexOf(`
8
+ `)}}handleLine($){if(!$.trim())return;let W;try{W=JSON.parse($)}catch(Z){this.handleExit(H$(`Failed to parse codex app-server JSONL: ${Z.message}`,{line:$}));return}if(W.id!==void 0&&W.method){this.handleServerRequest(W);return}if(W.id!==void 0){let Z=this.pending.get(W.id);if(!Z)return;if(this.pending.delete(W.id),W.error)Z.reject(H$(W.error.message??`codex app-server ${Z.method} failed.`,W.error));else Z.resolve(W.result??{});return}if(W.method&&this.notificationHandler)this.notificationHandler(W)}defaultHandleServerRequest($){if(!this.serverRequestHandler){this.sendMessage({id:$.id,error:u$(-32601,`Unsupported server request: ${$.method}`)});return}Promise.resolve().then(()=>this.serverRequestHandler($.method,$.params??{})).then((W)=>{this.sendMessage({id:$.id,result:W??{}})}).catch((W)=>{this.sendMessage({id:$.id,error:u$(-32000,W instanceof Error?W.message:String(W))})})}handleExit($){if(this.exitResolved)return;this.exitResolved=!0,this.exitError=$??null;for(let W of this.pending.values())W.reject(this.exitError??Error("codex app-server connection closed."));this.pending.clear(),this.resolveExit(void 0)}sendMessage($){throw Error("sendMessage must be implemented by subclasses.")}}class w$ extends X${proc;readline;constructor($,W={}){super($,W);this.transport="direct"}async initialize(){this.proc=A0("codex",["app-server"],{cwd:this.cwd,env:this.options.env??m.env,stdio:["pipe","pipe","pipe"],shell:m.platform==="win32"?m.env.SHELL||!0:!1,windowsHide:!0}),this.proc.stdout.setEncoding("utf8"),this.proc.stderr.setEncoding("utf8"),this.proc.stderr.on("data",($)=>{this.stderr+=$}),this.proc.on("error",($)=>{this.handleExit($)}),this.proc.on("exit",($,W)=>{let Z=this.stderr.trim(),Q=$===0?null:H$(`codex app-server exited unexpectedly (${W?`signal ${W}`:`exit ${$}`}).${Z?`
9
+ ${Z}`:""}`);this.handleExit(Q)}),this.readline=F0.createInterface({input:this.proc.stdout}),this.readline.on("line",($)=>{this.handleLine($)}),await this.request("initialize",{clientInfo:this.options.clientInfo??j$,capabilities:this.options.capabilities??h$}),this.notify("initialized",{})}async close(){if(this.closed){await this.exitPromise;return}if(this.closed=!0,this.readline)this.readline.close();if(this.proc&&!this.proc.killed)this.proc.stdin.end(),setTimeout(()=>{if(this.proc&&!this.proc.killed&&this.proc.exitCode===null)if(m.platform==="win32")try{T$(this.proc.pid)}catch{}else this.proc.kill("SIGTERM")},50).unref?.();await this.exitPromise}sendMessage($){let W=`${JSON.stringify($)}
10
+ `,Z=this.proc?.stdin;if(!Z)throw Error("codex app-server stdin is not available.");Z.write(W)}}class v$ extends X${endpoint;socket;constructor($,W={}){super($,W);this.transport="broker",this.endpoint=W.brokerEndpoint}async initialize(){await new Promise(($,W)=>{let Z=b(this.endpoint);this.socket=R0.createConnection({path:Z.path}),this.socket.setEncoding("utf8"),this.socket.on("connect",$),this.socket.on("data",(Q)=>{this.handleChunk(Q)}),this.socket.on("error",(Q)=>{if(!this.exitResolved)W(Q);this.handleExit(Q)}),this.socket.on("close",()=>{this.handleExit(this.exitError)})}),await this.request("initialize",{clientInfo:this.options.clientInfo??j$,capabilities:this.options.capabilities??h$}),this.notify("initialized",{})}async close(){if(this.closed){await this.exitPromise;return}if(this.closed=!0,this.socket)this.socket.end();await this.exitPromise}sendMessage($){let W=`${JSON.stringify($)}
11
+ `,Z=this.socket;if(!Z)throw Error("codex app-server broker connection is not connected.");Z.write(W)}}class u{static async connect($,W={}){let Z=null;if(!W.disableBroker){if(Z=W.brokerEndpoint??W.env?.[i]??m.env[i]??null,!Z&&W.reuseExistingBroker)Z=G$($)?.endpoint??null;if(!Z&&!W.reuseExistingBroker)Z=(await S$($,{env:W.env}))?.endpoint??null}let Q=Z?new v$($,{...W,brokerEndpoint:Z}):new w$($,W);return await Q.initialize(),Q}}var P0="coder_runtime",b$="Coder Task";function D0($){return $.split(/\r?\n/).map((W)=>W.trimEnd()).filter((W)=>W&&!W.startsWith("WARNING: proceeding, even though we could not update PATH:")).join(`
12
+ `)}function r($,W=72){let Z=String($??"").trim().replace(/\s+/g," ");if(!Z)return"";if(Z.length<=W)return Z;return`${Z.slice(0,W-3)}...`}function S0($){let W=r($,56);return W?`${b$}: ${W}`:b$}function E0($){return $?.params?.threadId??null}function T0($){if($?.params?.turnId)return $.params.turnId;if($?.params?.turn?.id)return $.params.turn.id;return null}function u0($){let W=new Set;for(let Z of $)for(let Q of Z.changes??[])if(Q.path)W.add(Q.path);return[...W]}function c$($){return String($??"").replace(/\s+/g," ").trim()}function I($){if(!$)return[];if(typeof $==="string"){let W=c$($);return W?[W]:[]}if(Array.isArray($))return $.flatMap((W)=>I(W));if(typeof $==="object"){if(typeof $.text==="string")return I($.text);if("summary"in $)return I($.summary);if("content"in $)return I($.content);if("parts"in $)return I($.parts)}return[]}function I0($,W){let Z=[];for(let Q of[...$,...W]){let V=c$(Q);if(!V||Z.includes(V))continue;Z.push(V)}return Z}function P($,W,Z=null,Q={}){if(!$||!W)return;$({message:W,phase:Z,...Q})}function j0($){switch($.type){case"commandExecution":return{message:`Running command: ${r($.command,96)}`,phase:"running"};case"fileChange":return{message:`Applying ${$.changes.length} file change(s).`,phase:"editing"};case"mcpToolCall":return{message:`Calling ${$.server}/${$.tool}.`,phase:"investigating"};case"dynamicToolCall":return{message:`Running tool: ${$.tool}.`,phase:"investigating"};case"webSearch":return{message:`Searching: ${r($.query,96)}`,phase:"investigating"};default:return null}}function h0($){switch($.type){case"commandExecution":return{message:`Command ${$.status==="completed"?"completed":$.status}: ${r($.command,96)} (exit ${$.exitCode??"?"})`,phase:"running"};case"fileChange":return{message:`File changes ${$.status}.`,phase:"editing"};case"mcpToolCall":return{message:`Tool ${$.server}/${$.tool} ${$.status}.`,phase:"investigating"};case"dynamicToolCall":return{message:`Tool ${$.tool} ${$.status}.`,phase:"investigating"};default:return null}}function w0($,W={}){let Z,Q,V=new Promise((Y,K)=>{Z=Y,Q=K});return{threadId:$,rootThreadId:$,threadIds:new Set([$]),threadTurnIds:new Map,turnId:null,bufferedNotifications:[],completion:V,resolveCompletion:Z,rejectCompletion:Q,finalTurn:null,completed:!1,finalAnswerSeen:!1,pendingCollaborations:new Set,activeSubagentTurns:new Set,completionTimer:null,lastAgentMessage:"",reasoningSummary:[],error:null,fileChanges:[],commandExecutions:[],itemIndex:new Map,onProgress:W.onProgress??null}}function U$($){if($.completionTimer)clearTimeout($.completionTimer),$.completionTimer=null}function M$($,W=null){if($.completed)return;if(U$($),$.completed=!0,W){if($.finalTurn=W,!$.turnId)$.turnId=W.id}else if(!$.finalTurn)$.finalTurn={id:$.turnId??"inferred-turn",status:"completed"};$.resolveCompletion($)}function q$($){if($.completed||$.finalTurn||!$.finalAnswerSeen)return;if($.pendingCollaborations.size>0||$.activeSubagentTurns.size>0)return;U$($),$.completionTimer=setTimeout(()=>{if($.completionTimer=null,$.completed||$.finalTurn||!$.finalAnswerSeen)return;if($.pendingCollaborations.size>0||$.activeSubagentTurns.size>0)return;M$($,null)},250),$.completionTimer.unref?.()}function g$($,W){let Z=E0(W);if(!Z||!$.threadIds.has(Z))return!1;let Q=$.threadTurnIds.get(Z)??null,V=T0(W);return Q===null||V===null||V===Q}function m$($,W,Z,Q=null){if(W.id)$.itemIndex.set(W.id,W);if(W.type==="collabAgentToolCall"){if(!Q||Q===$.threadId){if(Z==="started"||W.status==="inProgress")$.pendingCollaborations.add(W.id);else if(Z==="completed")$.pendingCollaborations.delete(W.id),q$($)}for(let V of W.receiverThreadIds??[])$.threadIds.add(V)}if(W.type==="agentMessage"){if(W.text&&(!Q||Q===$.threadId)){if($.lastAgentMessage=W.text,Z==="completed"&&W.phase==="final_answer")$.finalAnswerSeen=!0,q$($)}return}if(W.type==="reasoning"&&Z==="completed"){$.reasoningSummary=I0($.reasoningSummary,I(W.summary));return}if(W.type==="fileChange"&&Z==="completed"){$.fileChanges.push(W);return}if(W.type==="commandExecution"&&Z==="completed")$.commandExecutions.push(W)}function z$($,W){switch(W.method){case"thread/started":$.threadIds.add(W.params.thread.id);break;case"turn/started":if($.threadIds.add(W.params.threadId),$.threadTurnIds.set(W.params.threadId,W.params.turn.id),(W.params.threadId??null)!==$.threadId)$.activeSubagentTurns.add(W.params.threadId);P($.onProgress,`Turn started (${W.params.turn.id}).`,"starting",{threadId:W.params.threadId??null,turnId:W.params.turn.id??null});break;case"item/started":m$($,W.params.item,"started",W.params.threadId??null);{let Z=j0(W.params.item);P($.onProgress,Z?.message,Z?.phase??null)}break;case"item/completed":m$($,W.params.item,"completed",W.params.threadId??null);{let Z=h0(W.params.item);P($.onProgress,Z?.message,Z?.phase??null)}break;case"error":$.error=W.params.error,P($.onProgress,`Codex error: ${W.params.error.message}`,"failed");break;case"turn/completed":if((W.params.threadId??null)!==$.threadId){$.activeSubagentTurns.delete(W.params.threadId),q$($);break}P($.onProgress,`Turn ${W.params.turn.status}.`,"finalizing"),M$($,W.params.turn);break;default:break}}async function v0($,W,Z,Q={}){let V=w0(W,Q),Y=$.notificationHandler;if($.setNotificationHandler((K)=>{if(!V.turnId){V.bufferedNotifications.push(K);return}if(K.method==="thread/started"){z$(V,K);return}if(!g$(V,K)){Y?.(K);return}z$(V,K)}),Q.onApprovalRequest)$.setServerRequestHandler((K,G)=>Q.onApprovalRequest(K,G,V));try{let K=await Z();if(V.turnId=K.turn?.id??null,V.turnId)V.threadTurnIds.set(V.threadId,V.turnId);for(let G of V.bufferedNotifications)if(g$(V,G))z$(V,G);else Y?.(G);if(V.bufferedNotifications.length=0,K.turn?.status&&K.turn.status!=="inProgress")M$(V,K.turn);return await V.completion}finally{U$(V),$.setNotificationHandler(Y??null),$.setServerRequestHandler(null)}}async function b0($,W){let Z=null;try{Z=await u.connect($);let Q=await W(Z);return await Z.close(),Q}catch(Q){let V=Z?.transport==="broker"||Boolean(process.env[i]),Y=Z?.transport==="broker"&&Q?.rpcCode===I$||V&&(Q?.code==="ENOENT"||Q?.code==="ECONNREFUSED");if(Z)await Z.close().catch(()=>{}),Z=null;if(!Y)throw Q;let K=await u.connect($,{disableBroker:!0});try{return await W(K)}finally{await K.close()}}}function c($){let W=K$("codex",["--version"],{cwd:$});if(!W.available)return W;let Z=K$("codex",["app-server","--help"],{cwd:$});if(!Z.available)return{available:!1,detail:`${W.detail}; app-server runtime unavailable: ${Z.detail}`};return{available:!0,detail:`${W.detail}; app-server runtime available`}}async function l$($){let W=c($);if(!W.available)return{available:!1,loggedIn:!1,detail:W.detail};let Z=null;try{Z=await u.connect($,{reuseExistingBroker:!0});let Q=await Z.request("account/read",{refreshToken:!1}),V=Q?.account??null;if(V?.type==="chatgpt")return{available:!0,loggedIn:!0,detail:V.email?`ChatGPT login active for ${V.email}`:"ChatGPT login active"};if(V?.type==="apiKey")return{available:!0,loggedIn:!0,detail:"API key configured"};if(Q?.requiresOpenaiAuth===!1)return{available:!0,loggedIn:!0,detail:"Active provider does not require OpenAI authentication"};return{available:!0,loggedIn:!1,detail:"Not authenticated. Run `codex login`."}}catch(Q){return{available:!0,loggedIn:!1,detail:Q instanceof Error?Q.message:String(Q)}}finally{await Z?.close().catch(()=>{})}}async function p$($,{threadId:W,turnId:Z}){if(!W||!Z)return{interrupted:!1,detail:"missing threadId or turnId"};let Q=null;try{return Q=await u.connect($,{reuseExistingBroker:!0}),await Q.request("turn/interrupt",{threadId:W,turnId:Z}),{interrupted:!0,detail:`Interrupted ${Z} on ${W}.`}}catch(V){return{interrupted:!1,detail:V instanceof Error?V.message:String(V)}}finally{await Q?.close().catch(()=>{})}}async function n$($,W={}){let Z=c($);if(!Z.available)throw Error(`Codex CLI is not available: ${Z.detail}`);let Q=W.prompt?.trim();if(!Q)throw Error("A prompt is required.");return b0($,async(V)=>{let Y;if(W.resumeThreadId)P(W.onProgress,`Resuming thread ${W.resumeThreadId}.`,"starting"),Y=(await V.request("thread/resume",{threadId:W.resumeThreadId,cwd:$,model:W.model??null,approvalPolicy:W.approvalPolicy??"never",sandbox:W.sandbox??"read-only"})).thread.id;else{P(W.onProgress,"Starting Codex task thread.","starting"),Y=(await V.request("thread/start",{cwd:$,model:W.model??null,approvalPolicy:W.approvalPolicy??"never",sandbox:W.sandbox??"read-only",serviceName:P0,ephemeral:W.ephemeral??!1})).thread.id;try{await V.request("thread/name/set",{threadId:Y,name:S0(Q)})}catch(X){let H=String(X?.message??X??"");if(!H.includes("unknown variant")&&!H.includes("unknown method"))throw X}}P(W.onProgress,`Thread ready (${Y}).`,"starting",{threadId:Y});let K=await v0(V,Y,()=>V.request("turn/start",{threadId:Y,input:[{type:"text",text:Q,text_elements:[]}],model:W.model??null,effort:W.effort??null,outputSchema:W.outputSchema??null}),{onProgress:W.onProgress,onApprovalRequest:W.onApprovalRequest});return{status:K.finalTurn?.status==="completed"?0:1,threadId:Y,turnId:K.turnId,finalMessage:K.lastAgentMessage,reasoningSummary:K.reasoningSummary,turn:K.finalTurn,error:K.error,stderr:D0(V.stderr),fileChanges:K.fileChanges,touchedFiles:u0(K.fileChanges),commandExecutions:K.commandExecutions}})}import{spawn as c0,spawnSync as l0}from"node:child_process";import{randomUUID as p0}from"node:crypto";import a from"node:fs";import B$ from"node:path";var N$={chain:["codex","claude"],agents:{codex:{model:"gpt-5.5",effort:"medium",permissions:"auto"},claude:{model:"opus",permissions:"auto"}},approvals:{escalationTimeoutMs:120000,allowedNetworkHosts:[]}},g0={spark:"gpt-5.3-codex-spark","5.5":"gpt-5.5"},L$=new Set(["low","medium","high"]);var J$=new Set(["low","medium","high"]),l={"read-only":{sandbox:"read-only",approvalPolicy:"never",approvalMode:null},"workspace-write":{sandbox:"workspace-write",approvalPolicy:"never",approvalMode:null},auto:{sandbox:"workspace-write",approvalPolicy:"on-request",approvalMode:"auto"}},m0=["ls","cat","head","tail","wc","grep","rg","find","fd","tree","file","stat","du","df","which","pwd","env","git status","git log","git diff","git show","git branch","git blame","git remote"].map(($)=>`Bash(${$}:*)`),x$={"read-only":["--permission-mode","dontAsk","--disallowedTools","Edit","Write","NotebookEdit","--allowedTools",...m0],"workspace-write":["--permission-mode","acceptEdits"],auto:["--permission-mode","auto"]};function d$($){if(!a.existsSync($))return null;try{return JSON.parse(a.readFileSync($,"utf8"))}catch(W){throw Error(`Invalid JSON in ${$}: ${W.message}`)}}function O$($,W){if(!W||typeof W!=="object"||Array.isArray(W))return W??$;let Z={...$};for(let[Q,V]of Object.entries(W))if(V&&typeof V==="object"&&!Array.isArray(V)&&$?.[Q]&&typeof $[Q]==="object")Z[Q]=O$($[Q],V);else Z[Q]=V;return Z}function t(){return B$.join($$(),"config.json")}function o($){let W=w($),Z=N$;return Z=O$(Z,d$(t())),Z=O$(Z,d$(B$.join(W,"coder.config.json"))),Z}function i$($){if(!$)return null;return g0[$]??$}function r$($){let W=t();return a.mkdirSync(B$.dirname(W),{recursive:!0}),a.writeFileSync(W,`${JSON.stringify($,null,2)}
13
+ `,"utf8"),W}function y$(){let $=l0("claude",["--version"],{encoding:"utf8"});if($.error||$.status!==0)return{available:!1,detail:"claude CLI not found on PATH (npm install -g @anthropic-ai/claude-code)"};return{available:!0,detail:String($.stdout||"").trim()}}async function a$($,W){let Z=W.resumeSessionId??p0(),Q=["-p",W.prompt,"--output-format","json"];if(Q.push(W.resumeSessionId?"--resume":"--session-id",Z),W.model)Q.push("--model",W.model);if(W.effort)Q.push("--effort",W.effort);return Q.push(...x$[W.permissions??"auto"]??x$.auto),W.onProgress?.({message:`claude turn started (session ${Z})`,threadId:Z}),new Promise((V,Y)=>{let K=c0("claude",Q,{cwd:$,stdio:["ignore","pipe","pipe"]}),G="",X="";K.stdout.on("data",(z)=>{G+=z}),K.stderr.on("data",(z)=>{X+=z});let H=()=>{K.kill("SIGTERM"),process.exit(143)};process.on("SIGTERM",H),K.on("error",(z)=>{process.removeListener("SIGTERM",H),Y(Error(`claude spawn failed: ${z.message}`))}),K.on("close",(z)=>{process.removeListener("SIGTERM",H);let U=null;try{U=JSON.parse(G)}catch{}let q=U?.result??G.trim(),O=z!==0||U?.is_error===!0;V({status:O?1:0,threadId:U?.session_id??Z,turnId:null,finalMessage:q,error:O?{message:q||X.trim()||`claude exited ${z}`}:null})})})}import f from"node:fs";import _ from"node:path";var n0="item/commandExecution/requestApproval",d0="item/fileChange/requestApproval",i0=new Set(["commit","checkout","switch","restore","stash","reset","rebase","merge","push","pull","cherry-pick","revert","clean","am","apply","tag","branch","worktree","filter-branch","filter-repo","gc","prune","reflog","remote","submodule","config"]),r0=[/\bsudo\b/,/\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\b.*\s(\/\*?|~\/?|\$HOME\/?)(\s|$)/,/\b(shutdown|reboot|halt|poweroff)\b/,/\bmkfs\b|\bdiskutil\s+erase/i,/\b(chmod|chown)\b.*-R.*\s\/(\s|$)/,/\bkill(all)?\s+-9\s+1\b/,/curl[^|;&]*\|\s*(ba|z|da)?sh\b/,/wget[^|;&]*\|\s*(ba|z|da)?sh\b/,/\blaunchctl\b|\bsystemctl\b/,/\bdefaults\s+write\b/,/>\s*\/dev\/(sd|disk)/],a0=new Set(["ls","cat","head","tail","wc","grep","rg","ugrep","find","fd","diff","file","stat","du","df","which","whoami","pwd","echo","printf","env","date","uname","sw_vers","jq","sort","uniq","tr","cut","basename","dirname","realpath","readlink","xxd","shasum","sha256sum","md5","tree","ps"]),t0=new Set(["status","log","diff","show","blame","rev-parse","ls-files","ls-tree","cat-file","describe","shortlog","grep","branch","stash"]),o0=new Map([["npm",new Set(["test","run","exec","ls","view","why","outdated","ping"])],["pnpm",new Set(["test","run","exec","ls","why","outdated"])],["yarn",new Set(["test","run","why","info"])],["bun",new Set(["test","run","x","pm"])],["npx",null],["bunx",null],["node",null],["python",null],["python3",null],["tsc",null],["eslint",null],["prettier",null],["vitest",null],["jest",null],["pytest",null],["cargo",new Set(["build","test","check","clippy","fmt","run","bench","doc","tree","metadata"])],["go",new Set(["build","test","vet","run","fmt","list","env","version"])],["make",null],["turbo",null]]);function s0($){let W=$.match(/^\s*(?:ba|z|da)?sh\s+(?:-[a-zA-Z]+\s+)*(?:-c\s+)?(["'])([\s\S]*)\1\s*$/);return W?W[2]:$}function e0($){return $.trim().split(/\s+/).filter(Boolean)}function $1($){let W=$.find((Z,Q)=>Q>0&&!Z.startsWith("-"));if(!W)return{decision:"accept",reason:"bare git"};if(W==="branch"&&$.some((Z)=>/^-(d|D|m|M|c|C|f|u)|^--(delete|move|copy|force|set-upstream)/.test(Z)))return{decision:"decline",reason:"git branch write operation (git is read-only for Coder)"};if(W==="stash"){let Z=$.find((Q,V)=>V>1&&!Q.startsWith("-"));if(Z==="list"||Z==="show")return{decision:"accept",reason:`git stash ${Z} is read-only`};return{decision:"decline",reason:"git stash write operation (git is read-only for Coder)"}}if(t0.has(W))return{decision:"accept",reason:`git ${W} is read-only`};if(i0.has(W))return{decision:"decline",reason:`git ${W} is a write operation (git is read-only for Coder)`};return{decision:"escalate",reason:`unrecognized git subcommand: ${W}`}}function o$($,W={}){let Z=s0(String($??""));if(!Z.trim())return{decision:"escalate",reason:"empty command"};for(let G of r0)if(G.test(Z))return{decision:"decline",reason:`matches hard-deny pattern ${G}`};if(/\$\(|`|<\(/.test(Z))return{decision:"escalate",reason:"contains command substitution"};let Q=Z.split(/&&|\|\||;|\||\r?\n/).map((G)=>G.trim()).filter(Boolean);if(Q.length>1){let G={decision:"accept",reason:"all segments safe"};for(let X of Q){let H=o$(X,W);if(H.decision==="decline")return H;if(H.decision==="escalate")G=H}return G}let V=e0(Z),Y=_.basename(V[0]??"");if(Y==="git")return $1(V);if(W.networkHost){if((W.allowedNetworkHosts??[]).some((X)=>W.networkHost===X||W.networkHost.endsWith(`.${X}`)))return{decision:"accept",reason:`network host ${W.networkHost} is allowlisted`};return{decision:"escalate",reason:`network access to ${W.networkHost}`}}if(a0.has(Y))return{decision:"accept",reason:`${Y} is on the safe-binaries list`};let K=o0.get(Y);if(K!==void 0){if(K===null)return{decision:"accept",reason:`${Y} is a trusted runner`};let G=V.find((X,H)=>H>0&&!X.startsWith("-"));if(G&&K.has(G))return{decision:"accept",reason:`${Y} ${G} is a trusted runner command`};return{decision:"escalate",reason:`${Y} ${G??""} is not on the trusted subcommand list`}}return{decision:"escalate",reason:`unknown binary: ${Y}`}}function W1($,W){if(!$.length)return{decision:"escalate",reason:"file change with no resolvable paths"};for(let Z of $){let Q=_.resolve(W,Z),V=_.relative(W,Q);if(V.startsWith("..")||_.isAbsolute(V))return{decision:"escalate",reason:`path outside workspace: ${Z}`};if(V===".git"||V.startsWith(`.git${_.sep}`))return{decision:"decline",reason:`direct .git modification: ${Z}`}}return{decision:"accept",reason:"all paths inside workspace"}}function f$($){return _.join($,"approvals")}function _$($){let W=f$($);if(!f.existsSync(W))return[];return f.readdirSync(W).filter((Z)=>Z.endsWith(".request.json")).map((Z)=>{let Q=Z.replace(/\.request\.json$/,""),V=JSON.parse(f.readFileSync(_.join(W,Z),"utf8")),Y=_.join(W,`${Q}.response.json`),K=f.existsSync(Y)?JSON.parse(f.readFileSync(Y,"utf8")):null;return{id:Q,...V,response:K}}).sort((Z,Q)=>String(Z.createdAt??"").localeCompare(String(Q.createdAt??"")))}function s$($,W,Z){let Q=f$($),V=_.join(Q,`${W}.request.json`);if(!f.existsSync(V))throw Error(`No pending approval "${W}" for this job.`);f.writeFileSync(_.join(Q,`${W}.response.json`),`${JSON.stringify({decision:Z,answeredAt:new Date().toISOString()},null,2)}
14
+ `,"utf8")}async function t$($,W,{timeoutMs:Z,onEvent:Q}){let V=f$($);f.mkdirSync(V,{recursive:!0});let Y=`apr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`;f.writeFileSync(_.join(V,`${Y}.request.json`),`${JSON.stringify({...W,createdAt:new Date().toISOString()},null,2)}
15
+ `,"utf8"),Q?.({kind:"approval-escalated",approvalId:Y,summary:W.summary,message:`Approval needed (${Y}): ${W.summary}. Answer with: coder approve <job> ${Y} [--deny]`});let K=_.join(V,`${Y}.response.json`),G=Date.now()+Z;while(Date.now()<G){if(f.existsSync(K))try{let H=JSON.parse(f.readFileSync(K,"utf8")).decision==="accept"?"accept":"decline";return Q?.({kind:"approval-answered",approvalId:Y,decision:H}),H}catch{}await new Promise((X)=>setTimeout(X,400))}return Q?.({kind:"approval-timeout",approvalId:Y,message:`Approval ${Y} timed out; declining.`}),"decline"}function e$({workspaceRoot:$,jobDir:W,mode:Z="auto",escalationTimeoutMs:Q=120000,allowedNetworkHosts:V=[],onEvent:Y=null}){return async(K,G,X)=>{if(K===n0){let H=G.command??X?.itemIndex?.get(G.itemId)?.command??"",z=`run command: ${H||"(unknown command)"}${G.reason?` — ${G.reason}`:""}`;if(Z==="deny")return Y?.({kind:"approval-decision",method:K,decision:"decline",reason:"deny mode",summary:z}),{decision:"decline"};let U=o$(H,{networkHost:G.networkApprovalContext?.host??null,allowedNetworkHosts:V});if(Y?.({kind:"approval-decision",method:K,decision:U.decision,reason:U.reason,summary:z}),U.decision==="escalate")return{decision:await t$(W,{method:K,summary:z,command:H,params:G},{timeoutMs:Q,onEvent:Y})};return{decision:U.decision}}if(K===d0){let z=(X?.itemIndex?.get(G.itemId)?.changes??[]).map((O)=>O.path).filter(Boolean),U=`apply file changes: ${z.join(", ")||"(unknown paths)"}${G.reason?` — ${G.reason}`:""}`;if(Z==="deny")return Y?.({kind:"approval-decision",method:K,decision:"decline",reason:"deny mode",summary:U}),{decision:"decline"};let q=W1(z,$);if(Y?.({kind:"approval-decision",method:K,decision:q.decision,reason:q.reason,summary:U}),q.decision==="escalate")return{decision:await t$(W,{method:K,summary:U,paths:z,params:G},{timeoutMs:Q,onEvent:Y})};return{decision:q.decision}}throw Y?.({kind:"approval-unsupported",method:K}),Error(`Coder does not handle server request ${K}; treating as denied.`)}}var Q1=s(import.meta.url),k$=/usage|quota|rate.?limit|429|401|unauthorized|not authenticated|login|insufficient|exhausted|not available|ENOENT/i;function L($,W=1){M.stderr.write(`${$}
16
+ `),M.exit(W)}function N($){M.stdout.write(`${JSON.stringify($,null,2)}
17
+ `)}function j($,W){let Z=Z$($,W);if(!Z)L(W?`No job found for "${W}".`:"No jobs found for this workspace.");return Z}function $0($,W,{echo:Z}){return(Q)=>{let V=typeof Q==="string"?{message:Q}:Q;if(v($,W,V),Z&&V.message)M.stderr.write(`[coder] ${V.message}
18
+ `)}}function V1($,W){let Z=$.agent??W.chain[0]??"codex",Q=W.agents[Z]??{},V=$.model??Q.model??null,Y=$.effort??Q.effort??null,K=$.permissions??Q.permissions??"auto";if(!(K in l))L(`Invalid --permissions "${K}". Use one of: ${Object.keys(l).join(", ")}`);if(Z==="codex"&&Y&&!L$.has(Y))L(`Invalid codex --effort "${Y}". Use one of: ${[...L$].join(", ")}`);if(Z==="claude"&&Y&&!J$.has(Y))L(`Invalid claude --effort "${Y}". Use one of: ${[...J$].join(", ")}`);return{agent:Z,model:V,effort:Y,permissions:K}}function p($,W,Z,Q=null){let V=$.agents.claude??{};return{agent:"claude",action:"spawn-claude-subagent",reason:Z,model:V.model??"opus",permissions:Q??V.permissions??"auto",note:"Spawn a general-purpose subagent with this model and forward the task verbatim.",task:W}}async function Y1($,W,{echo:Z}){let Q=w($),V=x($,W.id),Y=o($),K=$0($,W.id,{echo:Z}),G=l[W.permissions]??l.auto,X=G.approvalPolicy==="never"?void 0:e$({workspaceRoot:Q,jobDir:V,mode:G.approvalMode,escalationTimeoutMs:Y.approvals.escalationTimeoutMs,allowedNetworkHosts:Y.approvals.allowedNetworkHosts,onEvent:(U)=>{if(v($,W.id,U),Z&&U.message)M.stderr.write(`[coder] ${U.message}
19
+ `)}}),H=await n$($,{prompt:W.prompt,model:i$(W.model),effort:W.effort,sandbox:G.sandbox,approvalPolicy:G.approvalPolicy,onApprovalRequest:X,resumeThreadId:W.resumeThreadId??null,onProgress:(U)=>{K(U);let q=typeof U==="object"?U.threadId:null,O=typeof U==="object"?U.turnId:null;if(q||O)y($,W.id,{status:"running",...q?{threadId:q}:{},...O?{turnId:O}:{}})}}),z=H.status===0?"completed":"failed";return y($,W.id,{status:z,threadId:H.threadId,turnId:H.turnId,completedAt:new Date().toISOString()}),S.writeFileSync(J.join(V,"result.json"),`${JSON.stringify(H,null,2)}
20
+ `,"utf8"),H}async function G1($,W,{echo:Z}){let Q=x($,W.id),V=$0($,W.id,{echo:Z}),Y=await a$($,{prompt:W.prompt,model:W.model,effort:W.effort,permissions:W.permissions,resumeSessionId:W.resumeThreadId??null,onProgress:(K)=>{if(V(K),K.threadId)y($,W.id,{status:"running",threadId:K.threadId})}});return y($,W.id,{status:Y.status===0?"completed":"failed",threadId:Y.threadId,completedAt:new Date().toISOString()}),S.writeFileSync(J.join(Q,"result.json"),`${JSON.stringify(Y,null,2)}
21
+ `,"utf8"),Y}function W0($){return $.agent==="claude"?G1:Y1}async function Z0($){let{options:W,positionals:Z}=k($,{valueOptions:["agent","model","effort","permissions","resume","cwd","host"],booleanOptions:["background","wait","json"]}),Q=W.wait?!1:!0,V=W.cwd?J.resolve(W.cwd):M.cwd(),Y=Z.join(" ").trim();if(!Y)L('Usage: coder task [options] "<task text>"');let K=o(V),G=V1(W,K),X=W.host??"claude";if(G.agent==="claude"&&X==="claude")N(p(K,Y,"configured",G.permissions)),M.exit(3);if(G.agent==="codex"){let q=c(V);if(!q.available)N({error:`Codex unavailable: ${q.detail}`,fallback:p(K,Y,"codex-failed",G.permissions)}),M.exit(3)}else{let q=y$();if(!q.available)L(`Claude unavailable: ${q.detail}`)}let H=null;if(W.resume)H=Z$(V,W.resume)?.threadId??W.resume;let z=R$(),U=y(V,z,{status:"queued",kind:"task",agent:G.agent,host:X,prompt:Y,model:G.model,effort:G.effort,permissions:G.permissions,resumeThreadId:H,cwd:V,background:Q});if(Q){let q=x(V,z),O=S.openSync(J.join(q,"worker.log"),"a"),A=Z1(M.execPath,[Q1,"_worker",z,"--cwd",V],{cwd:V,detached:!0,stdio:["ignore",O,O]});A.unref(),S.closeSync(O),y(V,z,{pid:A.pid??null});let n=Date.now()+15000,F=U,e=null;while(Date.now()<(e??n)){if(F=E(V,z)??F,F.status==="failed"||F.status==="completed")break;if(F.threadId&&!e)e=Date.now()+5000;await new Promise((h)=>setTimeout(h,300))}if(F.status==="failed"){let h=Q$(V,z,5).map((Q0)=>Q0.message??"").join(`
22
+ `);if(G.agent==="codex"&&k$.test(h))N({error:`Codex failed to start: ${h}`,fallback:p(K,Y,"codex-failed",G.permissions)}),M.exit(3);N({jobId:z,status:"failed",detail:h}),M.exit(1)}N({jobId:z,status:F.status,threadId:F.threadId??null,startupCheck:F.threadId?"passed":"pending",commands:{status:`coder status ${z}`,result:`coder result ${z}`,steer:`coder steer ${z} "<follow-up>"`,stop:`coder stop ${z}`}});return}y(V,z,{status:"running",pid:M.pid});try{let q=E(V,z),O=await W0(q)(V,q,{echo:!W.json}),A=O.error?.message??"";if(G.agent==="codex"&&O.status!==0&&k$.test(A))N({error:`Codex failed to start: ${A}`,fallback:p(K,Y,"codex-failed",G.permissions)}),M.exit(3);if(W.json)N({jobId:z,status:O.status===0?"completed":"failed",threadId:O.threadId,finalMessage:O.finalMessage});else M.stdout.write(`${O.finalMessage||"(no final message)"}
23
+ `),M.stderr.write(`[coder] job=${z} thread=${O.threadId} status=${O.status===0?"completed":"failed"}
24
+ `);M.exit(O.status===0?0:1)}catch(q){let O=q instanceof Error?q.message:String(q);if(y(V,z,{status:"failed",error:O}),v(V,z,{kind:"error",message:O}),G.agent==="codex"&&k$.test(O))N({error:`Codex failed to start: ${O}`,fallback:p(K,Y,"codex-failed",G.permissions)}),M.exit(3);L(`${G.agent} run failed: ${O}`)}}async function K1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),V=Z[0],Y=E(Q,V);if(!Y)L(`Worker: job ${V} not found.`);y(Q,V,{status:"running",pid:M.pid});try{await W0(Y)(Q,Y,{echo:!1})}catch(K){let G=K instanceof Error?K.message:String(K);y(Q,V,{status:"failed",error:G}),v(Q,V,{kind:"error",message:G}),M.exit(1)}}async function H1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd"],booleanOptions:["json"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),V=j(Q,Z[0]),Y=_$(x(Q,V.id)).filter((G)=>!G.response),K=Q$(Q,V.id,6);N({jobId:V.id,status:V.status,agent:V.agent,threadId:V.threadId??null,turnId:V.turnId??null,createdAt:V.createdAt,updatedAt:V.updatedAt,pendingApprovals:Y.map((G)=>({id:G.id,summary:G.summary})),recentProgress:K.map((G)=>G.message??G.kind).filter(Boolean)})}async function X1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd"],booleanOptions:["json"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),V=j(Q,Z[0]),Y=J.join(x(Q,V.id),"result.json");if(!S.existsSync(Y))L(`Job ${V.id} is ${V.status}; no result yet. Check: coder status ${V.id}`);let K=JSON.parse(S.readFileSync(Y,"utf8"));if(W.json){N(K);return}if(M.stdout.write(`${K.finalMessage||"(no final message)"}
25
+ `),K.touchedFiles?.length)M.stderr.write(`[coder] touched files: ${K.touchedFiles.join(", ")}
26
+ `)}async function z1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd","model","effort","permissions"],booleanOptions:["background","wait"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),[V,...Y]=Z,K=Y.join(" ").trim();if(!V||!K)L('Usage: coder steer <job-id> "<follow-up instructions>" [--wait]');let G=j(Q,V);if(!G.threadId)L(`Job ${G.id} has no thread to steer yet (status: ${G.status}).`);if(G.status==="running")L(`Job ${G.id} is still running. Stop it first (coder stop ${G.id}) or wait, then steer.`);let X=[K,"--resume",G.id,"--cwd",Q,...G.agent?["--agent",G.agent]:[],...G.host&&G.host!=="claude"?["--host",G.host]:[],...W.model?["--model",W.model]:["--model",G.model].filter(()=>G.model),...W.effort?["--effort",W.effort]:G.effort?["--effort",G.effort]:[],...W.permissions?["--permissions",W.permissions]:G.permissions?["--permissions",G.permissions]:[],...W.wait?["--wait"]:[]];await Z0(X)}async function q1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),V=j(Q,Z[0]),Y=V.agent==="claude"?{detail:"claude worker terminated"}:await p$(Q,{threadId:V.threadId,turnId:V.turnId});if(V.pid&&V.status==="running")try{M.kill(V.pid,"SIGTERM")}catch{}y(Q,V.id,{status:"cancelled",completedAt:new Date().toISOString()}),N({jobId:V.id,status:"cancelled",interrupt:Y.detail})}async function U1($){let{options:W}=k($,{valueOptions:["cwd"]}),Z=W.cwd?J.resolve(W.cwd):M.cwd();N(W$(Z).map((Q)=>({jobId:Q.id,status:Q.status,agent:Q.agent,threadId:Q.threadId??null,prompt:String(Q.prompt??"").slice(0,80),updatedAt:Q.updatedAt})))}async function M1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),V=j(Q,Z[0]);N(_$(x(Q,V.id)).map((Y)=>({id:Y.id,summary:Y.summary,createdAt:Y.createdAt,answered:Y.response?.decision??null})))}async function O1($){let{options:W,positionals:Z}=k($,{valueOptions:["cwd"],booleanOptions:["deny"]}),Q=W.cwd?J.resolve(W.cwd):M.cwd(),[V,Y]=Z;if(!V||!Y)L("Usage: coder approve <job-id> <approval-id> [--deny]");let K=j(Q,V);s$(x(Q,K.id),Y,W.deny?"decline":"accept"),N({jobId:K.id,approvalId:Y,decision:W.deny?"decline":"accept"})}async function B1($){let{options:W}=k($,{valueOptions:["cwd"],booleanOptions:["codex","claude"]}),Z=W.cwd?J.resolve(W.cwd):M.cwd(),Q=c(Z),V=Q.available?await l$(Z):{loggedIn:!1,detail:Q.detail},Y=y$(),K=t();if(!S.existsSync(K))r$(N$);let G=null;if(W.codex){let H=s(new URL("../plugins",import.meta.url));D("codex",["plugin","remove","Coder"],{encoding:"utf8"}),D("codex",["plugin","marketplace","remove","wular-plugins"],{encoding:"utf8"});let z=D("codex",["plugin","marketplace","add",H],{encoding:"utf8"}),U=D("codex",["plugin","add","Coder@wular-plugins"],{encoding:"utf8"}),q=z.status===0&&U.status===0;G={marketplace:H,installed:q,note:q?"Plugin installed; restart any running codex session to load it.":`Automatic install failed (${(U.stderr||z.stderr||"codex not found").trim()}); run: codex plugin marketplace add "${H}" && codex plugin add Coder@wular-plugins`}}let X=null;if(W.claude){let H=s(new URL("../plugins",import.meta.url));D("claude",["plugin","marketplace","remove","wular-plugins"],{encoding:"utf8"});let z=D("claude",["plugin","marketplace","add",H],{encoding:"utf8"}),U=D("claude",["plugin","install","Coder@wular-plugins"],{encoding:"utf8"}),q=z.status===0&&U.status===0;X={marketplace:H,installed:q,note:q?"Plugin installed; restart any running Claude Code session to load it.":`Automatic install failed (${(U.stderr||z.stderr||"claude not found").trim()}); run: claude plugin marketplace add "${H}" && claude plugin install Coder@wular-plugins`}}N({codex:{available:Q.available,detail:Q.detail,auth:V.detail,loggedIn:V.loggedIn},claude:{available:Y.available,detail:Y.detail},configFile:K,runtime:s(new URL("../bin/coder.mjs",import.meta.url)),...G?{codexPlugin:G}:{},...X?{claudePlugin:X}:{},config:o(Z),ready:Q.available&&V.loggedIn})}var N1={task:Z0,_worker:K1,status:H1,result:X1,steer:z1,stop:q1,jobs:U1,approvals:M1,approve:O1,setup:B1};async function L1(){let[$,...W]=M.argv.slice(2),Z=N1[$];if(!Z){let Q=M.stderr.isTTY&&!M.env.NO_COLOR,V=(X)=>Q?`\x1B[1m${X}\x1B[0m`:X,Y=(X)=>Q?`\x1B[36m${X}\x1B[0m`:X,K=(X)=>Q?`\x1B[38;5;245m${X}\x1B[0m`:X,G=(X,H)=>` ${Y(X.padEnd(34))}${K(H)}`;L([`${V("Coder")}`,"",V("Usage:")," coder <command> [flags]","",V("Commands:"),G('task "<text>" [--wait]',"run a coding task (background; --wait blocks)"),G("status [job]","job status, progress, pending approvals"),G("result [job]","final message of a finished job"),G('steer <job> "<follow-up>"',"continue a job's thread with new instructions"),G("stop <job>","interrupt a running job"),G("jobs","list jobs for this workspace"),G("approvals <job>","list escalated approvals"),G("approve <job> <id> [--deny]","answer an escalated permission"),G("setup [--claude|--codex]","check engines, write config, install the host plugin"),"",V("Task flags:")+K(" (task and steer)"),G("--agent <codex|claude>","engine (default: first in configured chain)"),G("--model <alias|slug>","spark, 5.5 | opus, sonnet, fable | any raw slug"),G("--effort <low|medium|high>","reasoning effort"),G("--permissions <mode>","read-only | workspace-write | auto (default: auto)"),G("--resume <job>","continue that job's thread instead of starting fresh"),"",G("--cwd <dir>","workspace directory, any command (default: current)")].join(`
27
+ `))}await Z(W)}L1().catch(($)=>{M.stderr.write(`${$ instanceof Error?$.message:String($)}
28
+ `),M.exit(1)});
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import o from"node:fs";import At from"node:net";import T from"node:path";import W from"node:process";function p(t,u={}){let r=new Set(u.valueOptions??[]),n=new Set(u.booleanOptions??[]),h=u.aliasMap??{},f={},y=[],a=!1;for(let b=0;b<t.length;b+=1){let x=t[b];if(a){y.push(x);continue}if(x==="--"){a=!0;continue}if(!x.startsWith("-")||x==="-"){y.push(x);continue}if(x.startsWith("--")){let[O,G]=x.slice(2).split("=",2),R=h[O]??O;if(n.has(R)){f[R]=G===void 0?!0:G!=="false";continue}if(r.has(R)){let X=G??t[b+1];if(X===void 0)throw Error(`Missing value for --${O}`);if(f[R]=X,G===void 0)b+=1;continue}y.push(x);continue}let j=x.slice(1),J=h[j]??j;if(n.has(J)){f[J]=!0;continue}if(r.has(J)){let O=t[b+1];if(O===void 0)throw Error(`Missing value for -${j}`);f[J]=O,b+=1;continue}y.push(x)}return{options:f,positionals:y}}import Ut from"node:net";import Q from"node:process";import{spawn as vt}from"node:child_process";import Lt from"node:readline";import B from"node:path";import bt from"node:process";function $t(t){return String(t??"").replace(/[^A-Za-z0-9._-]/g,"-").replace(/^-+|-+$/g,"")}function F(t,u=bt.platform){if(u==="win32")return`pipe:\\\\.\\pipe\\${$t(`${B.win32.basename(t)}-codex-app-server`)}`;return`unix:${B.join(t,"broker.sock")}`}function z(t){if(typeof t!=="string"||t.length===0)throw Error("Missing broker endpoint.");if(t.startsWith("pipe:")){let u=t.slice(5);if(!u)throw Error("Broker pipe endpoint is missing its path.");return{kind:"pipe",path:u}}if(t.startsWith("unix:")){let u=t.slice(5);if(!u)throw Error("Broker Unix socket endpoint is missing its path.");return{kind:"unix",path:u}}throw Error(`Unsupported broker endpoint: ${t}`)}import l from"node:fs";import Wt from"node:net";import Nt from"node:os";import Z from"node:path";import A from"node:process";import{spawn as Rt}from"node:child_process";import{fileURLToPath as zt}from"node:url";import{createHash as lt}from"node:crypto";import{execFileSync as jt}from"node:child_process";import S from"node:fs";import Jt from"node:os";import H from"node:path";var Ot="CODER_HOME";function Mt(){return H.resolve(process.env[Ot]||H.join(Jt.homedir(),".coder"))}function Ht(t){try{return jt("git",["rev-parse","--show-toplevel"],{cwd:t,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return t}}function K(t){let u=Ht(t),r=u;try{r=S.realpathSync.native(u)}catch{r=u}let h=(H.basename(u)||"workspace").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"workspace",f=lt("sha256").update(r).digest("hex").slice(0,12);return H.join(Mt(),"state",`${h}-${f}`)}function g(t){return H.join(K(t),"jobs")}function U(t,u){return H.join(g(t),u)}function et(){let t=Math.random().toString(36).slice(2,8);return`coder-${Date.now().toString(36)}-${t}`}function st(t,u,r){let n=U(t,u);S.mkdirSync(n,{recursive:!0});let h=H.join(n,"job.json"),y={...d(t,u)??{id:u,createdAt:new Date().toISOString()},...r,updatedAt:new Date().toISOString()};return S.writeFileSync(h,`${JSON.stringify(y,null,2)}
3
+ `,"utf8"),y}function d(t,u){let r=H.join(U(t,u),"job.json");if(!S.existsSync(r))return null;try{return JSON.parse(S.readFileSync(r,"utf8"))}catch{return null}}function St(t){let u=g(t);if(!S.existsSync(u))return[];return S.readdirSync(u).map((r)=>d(t,r)).filter(Boolean).sort((r,n)=>String(n.updatedAt??"").localeCompare(String(r.updatedAt??"")))}function tu(t,u){let r=St(t);if(!u)return r[0]??null;let n=r.find((f)=>f.id===u);if(n)return n;let h=r.filter((f)=>f.id.startsWith(u));if(h.length===1)return h[0];if(h.length>1)throw Error(`Job reference "${u}" is ambiguous. Use a longer id.`);return null}function uu(t,u,r){let n=U(t,u);S.mkdirSync(n,{recursive:!0}),S.appendFileSync(H.join(n,"log.jsonl"),`${JSON.stringify({at:new Date().toISOString(),...r})}
4
+ `,"utf8")}function ru(t,u,r=40){let n=H.join(U(t,u),"log.jsonl");if(!S.existsSync(n))return[];return S.readFileSync(n,"utf8").split(/\r?\n/).filter(Boolean).slice(-r).map((f)=>{try{return JSON.parse(f)}catch{return{message:f}}})}var Gt="broker.json";function Vt(t="coder-"){return l.mkdtempSync(Z.join(Nt.tmpdir(),t))}function Zt(t){let u=z(t);return Wt.createConnection({path:u.path})}async function s(t,u=2000){let r=Date.now();while(Date.now()-r<u){if(await new Promise((h)=>{let f=Zt(t);f.on("connect",()=>{f.end(),h(!0)}),f.on("error",()=>h(!1))}))return!0;await new Promise((h)=>setTimeout(h,50))}return!1}function mt({scriptPath:t,cwd:u,endpoint:r,pidFile:n,logFile:h,env:f=A.env}){let y=l.openSync(h,"a"),a=Rt(A.execPath,[t,"serve","--endpoint",r,"--cwd",u,"--pid-file",n],{cwd:u,env:f,detached:!0,stdio:["ignore",y,y]});return a.unref(),l.closeSync(y),a}function ot(){let t=[new URL("./lib/broker.js",import.meta.url),new URL("./broker.js",import.meta.url)];for(let u of t){let r=zt(u);if(l.existsSync(r))return r}throw Error("Coder broker script not found next to the runtime. Rebuild with `bun run build`.")}function w(t){return Z.join(K(t),Gt)}function E(t){let u=w(t);if(!l.existsSync(u))return null;try{return JSON.parse(l.readFileSync(u,"utf8"))}catch{return null}}function qt(t,u){let r=K(t);l.mkdirSync(r,{recursive:!0}),l.writeFileSync(w(t),`${JSON.stringify(u,null,2)}
5
+ `,"utf8")}function Qt(t){let u=w(t);if(l.existsSync(u))l.unlinkSync(u)}async function Xt(t){if(!t)return!1;try{return await s(t,150)}catch{return!1}}async function tt(t,u={}){let r=E(t);if(r&&await Xt(r.endpoint))return r;if(r)e({endpoint:r.endpoint??null,pidFile:r.pidFile??null,logFile:r.logFile??null,sessionDir:r.sessionDir??null,pid:r.pid??null,killProcess:u.killProcess??null}),Qt(t);let n=Vt(),f=(u.createBrokerEndpoint??F)(n,u.platform),y=Z.join(n,"broker.pid"),a=Z.join(n,"broker.log"),b=u.scriptPath??ot(),x=mt({scriptPath:b,cwd:t,endpoint:f,pidFile:y,logFile:a,env:u.env??A.env});if(!await s(f,u.timeoutMs??2000))return e({endpoint:f,pidFile:y,logFile:a,sessionDir:n,pid:x.pid??null,killProcess:u.killProcess??null}),null;let J={endpoint:f,pidFile:y,logFile:a,sessionDir:n,pid:x.pid??null};return qt(t,J),J}function e({endpoint:t=null,pidFile:u,logFile:r,sessionDir:n=null,pid:h=null,killProcess:f=null}){if(Number.isFinite(h)&&f)try{f(h)}catch{}if(u&&l.existsSync(u))l.unlinkSync(u);if(r&&l.existsSync(r))l.unlinkSync(r);if(t)try{let a=z(t);if(a.kind==="unix"&&l.existsSync(a.path))l.unlinkSync(a.path)}catch{}let y=n??(u?Z.dirname(u):r?Z.dirname(r):null);if(y&&l.existsSync(y))try{l.rmdirSync(y)}catch{}}import{spawnSync as Yt}from"node:child_process";import q from"node:process";function ut(t,u=[],r={}){let n=Yt(t,u,{cwd:r.cwd,env:r.env,encoding:"utf8",input:r.input,maxBuffer:r.maxBuffer,stdio:r.stdio??"pipe",shell:q.platform==="win32"?q.env.SHELL||!0:!1,windowsHide:!0});return{command:t,args:u,status:n.status??0,signal:n.signal??null,stdout:n.stdout??"",stderr:n.stderr??"",error:n.error??null}}function Ou(t,u=["--version"],r={}){let n=ut(t,u,r);if(n.error&&n.error.code==="ENOENT")return{available:!1,detail:"not found"};if(n.error)return{available:!1,detail:n.error.message};if(n.status!==0)return{available:!1,detail:n.stderr.trim()||n.stdout.trim()||`exit ${n.status}`};return{available:!0,detail:n.stdout.trim()||n.stderr.trim()||"ok"}}function Ct(t){return/not found|no running instance|cannot find|does not exist|no such process/i.test(t)}function rt(t,u={}){if(!Number.isFinite(t))return{attempted:!1,delivered:!1,method:null};let r=u.platform??q.platform,n=u.runCommandImpl??ut,h=u.killImpl??q.kill.bind(q);if(r==="win32"){let f=n("taskkill",["/PID",String(t),"/T","/F"],{cwd:u.cwd,env:u.env});if(!f.error&&f.status===0)return{attempted:!0,delivered:!0,method:"taskkill",result:f};let y=`${f.stderr}
6
+ ${f.stdout}`.trim();if(!f.error&&Ct(y))return{attempted:!0,delivered:!1,method:"taskkill",result:f};if(f.error?.code==="ENOENT")try{return h(t),{attempted:!0,delivered:!0,method:"kill"}}catch(a){if(a?.code==="ESRCH")return{attempted:!0,delivered:!1,method:"kill"};throw a}if(f.error)throw f.error;throw Error(Kt(f))}try{return h(-t,"SIGTERM"),{attempted:!0,delivered:!0,method:"process-group"}}catch(f){if(f?.code!=="ESRCH")try{return h(t,"SIGTERM"),{attempted:!0,delivered:!0,method:"process"}}catch(y){if(y?.code==="ESRCH")return{attempted:!0,delivered:!1,method:"process"};throw y}return{attempted:!0,delivered:!1,method:"process-group"}}}function Kt(t){let u=[`${t.command} ${t.args.join(" ")}`.trim()];if(t.signal)u.push(`signal=${t.signal}`);else u.push(`exit=${t.status}`);let r=(t.stderr||"").trim(),n=(t.stdout||"").trim();if(r)u.push(r);else if(n)u.push(n);return u.join(": ")}var nt="CODER_APP_SERVER_ENDPOINT",ct=-32001,ht={title:"Coder",name:"coder",version:"0.1.0"},it={experimentalApi:!1,requestAttestation:!1,optOutNotificationMethods:["item/agentMessage/delta","item/reasoning/summaryTextDelta","item/reasoning/summaryPartAdded","item/reasoning/textDelta"]};function ft(t,u,r){return r===void 0?{code:t,message:u}:{code:t,message:u,data:r}}function P(t,u){let r=Error(t);if(r.data=u,u?.code!==void 0)r.rpcCode=u.code;return r}class _{cwd;options;pending;nextId;stderr;closed;exitError;notificationHandler;serverRequestHandler;lineBuffer;transport;exitPromise;exitResolved;resolveExit;handleServerRequest;constructor(t,u={}){this.cwd=t,this.options=u,this.pending=new Map,this.nextId=1,this.stderr="",this.closed=!1,this.exitError=null,this.notificationHandler=null,this.serverRequestHandler=null,this.lineBuffer="",this.transport="unknown",this.handleServerRequest=(r)=>this.defaultHandleServerRequest(r),this.exitPromise=new Promise((r)=>{this.resolveExit=r})}setNotificationHandler(t){this.notificationHandler=t}setServerRequestHandler(t){this.serverRequestHandler=t}request(t,u){if(this.closed)throw Error("codex app-server client is closed.");let r=this.nextId;return this.nextId+=1,new Promise((n,h)=>{this.pending.set(r,{resolve:n,reject:h,method:t}),this.sendMessage({id:r,method:t,params:u})})}notify(t,u={}){if(this.closed)return;this.sendMessage({method:t,params:u})}handleChunk(t){this.lineBuffer+=t;let u=this.lineBuffer.indexOf(`
7
+ `);while(u!==-1){let r=this.lineBuffer.slice(0,u);this.lineBuffer=this.lineBuffer.slice(u+1),this.handleLine(r),u=this.lineBuffer.indexOf(`
8
+ `)}}handleLine(t){if(!t.trim())return;let u;try{u=JSON.parse(t)}catch(r){this.handleExit(P(`Failed to parse codex app-server JSONL: ${r.message}`,{line:t}));return}if(u.id!==void 0&&u.method){this.handleServerRequest(u);return}if(u.id!==void 0){let r=this.pending.get(u.id);if(!r)return;if(this.pending.delete(u.id),u.error)r.reject(P(u.error.message??`codex app-server ${r.method} failed.`,u.error));else r.resolve(u.result??{});return}if(u.method&&this.notificationHandler)this.notificationHandler(u)}defaultHandleServerRequest(t){if(!this.serverRequestHandler){this.sendMessage({id:t.id,error:ft(-32601,`Unsupported server request: ${t.method}`)});return}Promise.resolve().then(()=>this.serverRequestHandler(t.method,t.params??{})).then((u)=>{this.sendMessage({id:t.id,result:u??{}})}).catch((u)=>{this.sendMessage({id:t.id,error:ft(-32000,u instanceof Error?u.message:String(u))})})}handleExit(t){if(this.exitResolved)return;this.exitResolved=!0,this.exitError=t??null;for(let u of this.pending.values())u.reject(this.exitError??Error("codex app-server connection closed."));this.pending.clear(),this.resolveExit(void 0)}sendMessage(t){throw Error("sendMessage must be implemented by subclasses.")}}class yt extends _{proc;readline;constructor(t,u={}){super(t,u);this.transport="direct"}async initialize(){this.proc=vt("codex",["app-server"],{cwd:this.cwd,env:this.options.env??Q.env,stdio:["pipe","pipe","pipe"],shell:Q.platform==="win32"?Q.env.SHELL||!0:!1,windowsHide:!0}),this.proc.stdout.setEncoding("utf8"),this.proc.stderr.setEncoding("utf8"),this.proc.stderr.on("data",(t)=>{this.stderr+=t}),this.proc.on("error",(t)=>{this.handleExit(t)}),this.proc.on("exit",(t,u)=>{let r=this.stderr.trim(),n=t===0?null:P(`codex app-server exited unexpectedly (${u?`signal ${u}`:`exit ${t}`}).${r?`
9
+ ${r}`:""}`);this.handleExit(n)}),this.readline=Lt.createInterface({input:this.proc.stdout}),this.readline.on("line",(t)=>{this.handleLine(t)}),await this.request("initialize",{clientInfo:this.options.clientInfo??ht,capabilities:this.options.capabilities??it}),this.notify("initialized",{})}async close(){if(this.closed){await this.exitPromise;return}if(this.closed=!0,this.readline)this.readline.close();if(this.proc&&!this.proc.killed)this.proc.stdin.end(),setTimeout(()=>{if(this.proc&&!this.proc.killed&&this.proc.exitCode===null)if(Q.platform==="win32")try{rt(this.proc.pid)}catch{}else this.proc.kill("SIGTERM")},50).unref?.();await this.exitPromise}sendMessage(t){let u=`${JSON.stringify(t)}
10
+ `,r=this.proc?.stdin;if(!r)throw Error("codex app-server stdin is not available.");r.write(u)}}class xt extends _{endpoint;socket;constructor(t,u={}){super(t,u);this.transport="broker",this.endpoint=u.brokerEndpoint}async initialize(){await new Promise((t,u)=>{let r=z(this.endpoint);this.socket=Ut.createConnection({path:r.path}),this.socket.setEncoding("utf8"),this.socket.on("connect",t),this.socket.on("data",(n)=>{this.handleChunk(n)}),this.socket.on("error",(n)=>{if(!this.exitResolved)u(n);this.handleExit(n)}),this.socket.on("close",()=>{this.handleExit(this.exitError)})}),await this.request("initialize",{clientInfo:this.options.clientInfo??ht,capabilities:this.options.capabilities??it}),this.notify("initialized",{})}async close(){if(this.closed){await this.exitPromise;return}if(this.closed=!0,this.socket)this.socket.end();await this.exitPromise}sendMessage(t){let u=`${JSON.stringify(t)}
11
+ `,r=this.socket;if(!r)throw Error("codex app-server broker connection is not connected.");r.write(u)}}class D{static async connect(t,u={}){let r=null;if(!u.disableBroker){if(r=u.brokerEndpoint??u.env?.[nt]??Q.env[nt]??null,!r&&u.reuseExistingBroker)r=E(t)?.endpoint??null;if(!r&&!u.reuseExistingBroker)r=(await tt(t,{env:u.env}))?.endpoint??null}let n=r?new xt(t,{...u,brokerEndpoint:r}):new yt(t,u);return await n.initialize(),n}}var wt=new Set(["turn/start","review/start","thread/compact/start"]);function at(t,u,r){let n=new Set;if(u?.threadId)n.add(u.threadId);if(t==="review/start"&&r?.reviewThreadId)n.add(r.reviewThreadId);return n}function m(t,u,r){return r===void 0?{code:t,message:u}:{code:t,message:u,data:r}}function N(t,u){if(t.destroyed)return;t.write(`${JSON.stringify(u)}
12
+ `)}function Et(t){return t?.method==="turn/interrupt"}function Pt(t){if(!t)return;o.mkdirSync(T.dirname(t),{recursive:!0}),o.writeFileSync(t,`${W.pid}
13
+ `,"utf8")}async function _t(){let[t,...u]=W.argv.slice(2);if(t!=="serve")throw Error("Usage: node src/lib/broker.mjs serve --endpoint <value> [--cwd <path>] [--pid-file <path>]");let{options:r}=p(u,{valueOptions:["cwd","pid-file","endpoint"]});if(!r.endpoint)throw Error("Missing required --endpoint.");let n=r.cwd?T.resolve(W.cwd(),r.cwd):W.cwd(),h=String(r.endpoint),f=z(h),y=r["pid-file"]?T.resolve(r["pid-file"]):null;Pt(y);let a=await D.connect(n,{disableBroker:!0}),b=null,x=null,j=null,J=new Set,O=new Map;function G(c){for(let[$,V]of O)if(V===c)O.delete($),a.sendMessage({id:$,error:m(-32000,"Broker client disconnected before answering the server request.")})}function R(c){if(G(c),b===c)b=null;if(x===c)x=null,j=null}function X(c){let $=b??x;if(!$)return;if(N($,c),c.method==="turn/completed"&&x===$){let V=c.params?.threadId??null;if(!V||!j||j.has(V)){if(x=null,j=null,b===$)b=null}}}async function v(c){for(let $ of J)$.end();if(await a.close().catch(()=>{}),await new Promise(($)=>c.close($)),f.kind==="unix"&&o.existsSync(f.path))o.unlinkSync(f.path);if(y&&o.existsSync(y))o.unlinkSync(y)}a.setNotificationHandler(X),a.handleServerRequest=(c)=>{let $=x??b;if(!$||$.destroyed){a.sendMessage({id:c.id,error:m(-32601,`No broker client available to answer ${c.method}.`)});return}O.set(c.id,$),N($,c)};let Y=At.createServer((c)=>{J.add(c),c.setEncoding("utf8");let $="";c.on("data",async(V)=>{$+=V;let C=$.indexOf(`
14
+ `);while(C!==-1){let I=$.slice(0,C);if($=$.slice(C+1),C=$.indexOf(`
15
+ `),!I.trim())continue;let i;try{i=JSON.parse(I)}catch(M){N(c,{id:null,error:m(-32700,`Invalid JSON: ${M.message}`)});continue}if(i.id!==void 0&&!i.method&&O.has(i.id)){O.delete(i.id),a.sendMessage(i);continue}if(i.id!==void 0&&i.method==="initialize"){N(c,{id:i.id,result:{userAgent:"coder-broker"}});continue}if(i.method==="initialized"&&i.id===void 0)continue;if(i.id!==void 0&&i.method==="broker/shutdown")N(c,{id:i.id,result:{}}),await v(Y),W.exit(0);if(i.id===void 0||!i.method)continue;let k=Et(i)&&x&&x!==c&&!b;if((b&&b!==c||x&&x!==c)&&!k){N(c,{id:i.id,error:m(ct,"Shared Codex broker is busy.")});continue}if(k){try{let M=await a.request(i.method,i.params??{});N(c,{id:i.id,result:M})}catch(M){N(c,{id:i.id,error:m(M.rpcCode??-32000,M.message)})}continue}let L=wt.has(i.method);if(b=c,L)x=c,j=at(i.method,i.params??{},null);try{let M=await a.request(i.method,i.params??{});if(N(c,{id:i.id,result:M}),L)j=at(i.method,i.params??{},M);if(b===c)b=null}catch(M){if(N(c,{id:i.id,error:m(M.rpcCode??-32000,M.message)}),b===c)b=null;if(x===c&&L)x=null,j=null}}}),c.on("close",()=>{J.delete(c),R(c)}),c.on("error",()=>{J.delete(c),R(c)})});W.on("SIGTERM",async()=>{await v(Y),W.exit(0)}),W.on("SIGINT",async()=>{await v(Y),W.exit(0)}),Y.listen(f.path)}_t().catch((t)=>{W.stderr.write(`${t instanceof Error?t.message:String(t)}
16
+ `),W.exit(1)});
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@wular/coder",
3
+ "version": "0.1.0",
4
+ "description": "Delegating coding-agent runtime",
5
+ "type": "module",
6
+ "bin": {
7
+ "coder": "./bin/coder.mjs"
8
+ },
9
+ "scripts": {
10
+ "build": "bun build src/cli.ts src/lib/broker.ts --outdir dist --root src --target node --format esm --minify",
11
+ "dev": "bun run build --watch",
12
+ "typecheck": "tsc -p tsconfig.json --noEmit",
13
+ "prepublishOnly": "bun run build && bun run typecheck"
14
+ },
15
+ "files": [
16
+ "bin",
17
+ "dist",
18
+ "plugins",
19
+ "README.md"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "engines": {
25
+ "node": ">=18"
26
+ }
27
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "wular-plugins",
3
+ "interface": {
4
+ "displayName": "Wular Plugins"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "Coder",
9
+ "source": {
10
+ "source": "local",
11
+ "path": "./codex"
12
+ },
13
+ "policy": {
14
+ "installation": "AVAILABLE"
15
+ }
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "wular-plugins",
3
+ "owner": {
4
+ "name": "wular"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "Coder",
9
+ "source": "./claude",
10
+ "description": "Delegate coding tasks to a Coder subagent. Codex first, Claude fallback; your main context stays clean."
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "Coder",
3
+ "version": "0.0.0",
4
+ "description": "Delegate coding tasks to a Coder subagent. Codex first, Claude fallback; your main context stays clean.",
5
+ "author": {
6
+ "name": "muzam1l"
7
+ }
8
+ }
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Set up the Coder runtime (install the CLI, verify Codex, write default config)
3
+ ---
4
+
5
+ Set up the Coder runtime for this machine.
6
+
7
+ 1. Check for the runtime: `command -v coder`. If missing, install it: `npm install -g @wular/coder` (in the monorepo checkout, `npm link` inside `packages/coder` instead).
8
+ 2. Run `coder setup` and show the JSON summary to the user.
9
+ 3. Setup writes the default config to `~/.coder/config.json` (chain codex -> claude, codex gpt-5.5/medium with auto permissions, claude fallback opus). Point the user at the config file for customization.
10
+ 4. If Codex is unavailable or not logged in, tell the user what to fix (`npm install -g @openai/codex`, `codex login`). The Coder agent still works via the Claude fallback in the meantime.
11
+ 5. Suggest trying it: "Use Coder to <some small task>".
12
+
13
+ $ARGUMENTS
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: coder
3
+ description: Use for any coding or implementation task - features, fixes, refactors, debugging, tests. Dispatches the task to the configured engine chain (Codex or Claude Code) in seconds. Give it one focused, self-contained goal with all context - overview, file paths, constraints e.g. read-only, git rules.
4
+ ---
5
+
6
+ # Coder dispatch
7
+
8
+ You dispatch coding tasks through the `coder` runtime instead of implementing them yourself. By default the runtime picks the engine from its configured chain; Flags like `--agent`/`--model`/`--effort` override it.
9
+
10
+ If `coder` is not on PATH, tell the user to set it up (`npm install -g @wular/coder`, then `/coder:setup`) and stop.
11
+
12
+ ## Dispatching a task
13
+
14
+ - Delegation is a hard gate for anything the engine can do itself: do not read source files, investigate, or write code first - no matter how simple the task. Fold into the task text whatever only you have - conversation context, results from tools the engine lacks.
15
+ - Compose one self-contained task text (goal, relevant paths, constraints) and run it as a **background Bash call** so you are notified on completion instead of blocking or polling:
16
+
17
+ ```bash
18
+ coder task --wait "<task text>" # Bash run_in_background: true
19
+ ```
20
+
21
+ - The harness re-invokes you when the command exits. Exit 0: relay the final message. Exit 1: the engine ran but the turn failed - report it, do not retry or fall back yourself.
22
+ - Engine, model, and effort come from config; add `--agent` / `--model` / `--effort` when the user asks or it is unambiguous from context (agents: `codex`, `claude`; codex models: `spark`, `5.5`; claude models: `opus`, `sonnet`, `fable`; efforts: `low|medium|high`).
23
+ - Permissions default to auto mode (workspace-write + policy-answered escalations). Pass `--permissions read-only` when the task is read-only; `--permissions workspace-write` to forbid any escalation beyond the project.
24
+ - For long jobs the user may want to manage by id, drop `--wait`: the runtime detaches a worker, does its own startup check, and prints a job id plus status/result/steer commands. Relay that and do not poll.
25
+
26
+ ## Claude engine (exit 3)
27
+
28
+ Exit code 3 means the runtime printed a `spawn-claude-subagent` payload instead of running Codex. Its `reason` field says why: `configured` (Claude is the selected engine) or `codex-failed` (Codex missing, auth, quota, rate limit - mention this to the user, including any limit-reset time).
29
+
30
+ Spawn one `general-purpose` subagent via the Agent tool, passing the payload's `model` as the Agent tool's model parameter. Its prompt is the original task text verbatim, prefixed with: "NEVER run git write operations (commit, checkout, stash, reset, push, etc.); leave changes uncommitted." Append the same for the payload's `permissions` if not the default `auto`: `read-only` means investigate and report without modifying anything; `workspace-write` means never touch anything outside the workspace.
31
+
32
+ Relay its output when it completes.
33
+
34
+ ## Controlling jobs
35
+
36
+ - Continue prior work ("keep going", "apply the top fix"): `coder steer <job-id> "<follow-up>"`.
37
+ - Inspect: `coder status <job-id>` / `result <job-id>` / `jobs`.
38
+ - Interrupt: `coder stop <job-id>`.
39
+ - If status shows `pendingApprovals`, surface them to the user; apply their decision with `coder approve <job-id> <approval-id> [--deny]`.
40
+
41
+ ## Hard rules
42
+
43
+ - Forward task text verbatim in `steer` and fallback prompts - never rewrite or summarize what the user asked for.
44
+ - NEVER run git write operations yourself (commit, checkout, stash, reset, rebase, merge, push, etc.); the runtime's approval policy enforces this for Codex.
45
+ - Honor any constraints in the user's request (read-only, scoped paths) over your own preferences.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "Coder",
3
+ "version": "0.0.0",
4
+ "description": "Delegate coding tasks to a Coder subagent. Your main context stays clean.",
5
+ "skills": "./skills/"
6
+ }
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: coder
3
+ description: Use for any coding or implementation task - features, fixes, refactors, debugging, tests. Dispatches the task to the configured engine chain (Codex or Claude). Give it one focused, self-contained goal with all context - overview, file paths, constraints e.g. read-only, git rules.
4
+ ---
5
+
6
+ # Coder dispatch (codex host)
7
+
8
+ You dispatch coding tasks through the `coder` runtime instead of implementing them yourself. By default the runtime picks the engine from its configured chain; `--agent`/`--model`/`--effort` override it. You never spawn agents yourself in this host.
9
+
10
+ If `coder` is not on PATH, tell the user to set it up (`npm install -g @wular/coder`, then `coder setup --codex`) and stop.
11
+
12
+ ## Dispatching a task
13
+
14
+ - Delegation is a hard gate for anything the engine can do itself: do not read source files, investigate, or write code first - no matter how simple the task. Fold into the task text whatever only you have - conversation context, results from tools the engine lacks.
15
+ - Compose one self-contained task text (goal, relevant paths, constraints) and run:
16
+
17
+ ```bash
18
+ coder task --host codex --wait "<task text>"
19
+ ```
20
+
21
+ - Always pass `--host codex` so the runtime executes Claude itself instead of asking the host to spawn a subagent.
22
+ - Exit 0: relay the final message. Exit 1: the engine ran but the turn failed - report it, do not retry.
23
+ - Exit 3: Codex could not start (auth, quota, rate limit - mention this to the user, including any limit-reset time). Rerun once with the Claude engine and relay its result:
24
+
25
+ ```bash
26
+ coder task --host codex --agent claude --wait "<same task text, verbatim>"
27
+ ```
28
+
29
+ - Engine, model, and effort come from config; add `--agent` / `--model` / `--effort` when the user asks or it is unambiguous from context (agents: `codex`, `claude`; codex models: `spark`, `5.5`; claude models: `opus`, `sonnet`, `fable`; efforts: `low|medium|high`).
30
+ - Permissions default to auto mode. Pass `--permissions read-only` when the task is read-only; `--permissions workspace-write` to forbid any escalation beyond the project.
31
+ - For long jobs the user may want to manage by id, drop `--wait`: the runtime detaches a worker and prints a job id plus status/result/steer commands. Relay that.
32
+
33
+ ## Controlling jobs
34
+
35
+ - Continue prior work ("keep going", "apply the top fix"): `coder steer <job-id> "<follow-up>"`.
36
+ - Inspect: `coder status <job-id>` / `result <job-id>` / `jobs`.
37
+ - Interrupt: `coder stop <job-id>`.
38
+ - If status shows `pendingApprovals`, surface them to the user; apply their decision with `coder approve <job-id> <approval-id> [--deny]`.
39
+
40
+ ## Hard rules
41
+
42
+ - Forward task text verbatim in `steer` and retries - never rewrite or summarize what the user asked for.
43
+ - NEVER run git write operations yourself (commit, checkout, stash, reset, rebase, merge, push, etc.); the runtime's permission model constrains the engines.
44
+ - Honor any constraints in the user's request (read-only, scoped paths) over your own preferences.