@wular/coder 0.1.22 → 0.1.24
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/dist/cli.js +121 -121
- package/dist/lib/broker.js +12 -12
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/handle/SKILL.md +6 -14
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/skills/handle/SKILL.md +3 -3
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/skills/handle/SKILL.md +2 -2
package/dist/lib/broker.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`,"utf8"),Q}function
|
|
4
|
-
`,"utf8")}function
|
|
5
|
-
`,"utf8")}function
|
|
6
|
-
${Q.stdout}`.trim();if(!Q.error&&
|
|
2
|
+
var T$=Object.defineProperty;var Q1=($,W)=>{for(var q in W)T$($,q,{get:W[q],enumerable:!0,configurable:!0,set:(Z)=>W[q]=()=>Z})};import u from"node:fs";import W1 from"node:net";import d from"node:path";import x from"node:process";function o($,W={}){let q=new Set(W.valueOptions??[]),Z=new Set(W.booleanOptions??[]),G=W.aliasMap??{},Y={},Q=[],y=!1;for(let X=0;X<$.length;X+=1){let H=$[X];if(y){Q.push(H);continue}if(H==="--"){y=!0;continue}if(!H.startsWith("-")||H==="-"){Q.push(H);continue}if(H.startsWith("--")){let[V,_]=H.slice(2).split("=",2),P=G[V]??V;if(Z.has(P)){Y[P]=_===void 0?!0:_!=="false";continue}if(q.has(P)){let h=_??$[X+1];if(h===void 0)throw Error(`Missing value for --${V}`);if(Y[P]=h,_===void 0)X+=1;continue}throw Error(`Unknown option --${V} (use -- to pass literal text starting with -)`)}let N=H.slice(1),J=G[N]??N;if(Z.has(J)){Y[J]=!0;continue}if(q.has(J)){let V=$[X+1];if(V===void 0)throw Error(`Missing value for -${N}`);Y[J]=V,X+=1;continue}throw Error(`Unknown option -${N} (use -- to pass literal text starting with -)`)}return{options:Y,positionals:Q}}import r$ from"node:net";import k from"node:process";import{spawn as s$}from"node:child_process";import o$ from"node:readline";import e from"node:path";import x$ from"node:process";function A$($){return String($??"").replace(/[^A-Za-z0-9._-]/g,"-").replace(/^-+|-+$/g,"")}function $$($,W=x$.platform){if(W==="win32")return`pipe:\\\\.\\pipe\\${A$(`${e.win32.basename($)}-codex-app-server`)}`;return`unix:${e.join($,"broker.sock")}`}function f($){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 M from"node:fs";import F$ from"node:net";import v$ from"node:os";import E from"node:path";import p from"node:process";import{spawn as j$}from"node:child_process";import{fileURLToPath as w$}from"node:url";import{createHash as P$}from"node:crypto";import{execFileSync as Z$}from"node:child_process";import O from"node:fs";import f$ from"node:os";import L from"node:path";var _$=["completed","failed","cancelled"],U1=["queued","running"];var R$="CODER_HOME";function B$(){return L.resolve(process.env[R$]||L.join(f$.homedir(),".coder"))}var W$=new Map;function E$($){let W=W$.get($);if(W!==void 0)return W;let q;try{q=Z$("git",["rev-parse","--show-toplevel"],{cwd:$,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{q=$}return W$.set($,q),q}var q$=new Map;function v($){let W=q$.get($);if(W!==void 0)return W;let q=E$($),Z=q;try{Z=O.realpathSync.native(q)}catch{Z=q}let Y=(L.basename(q)||"workspace").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"workspace",Q=P$("sha256").update(Z).digest("hex").slice(0,12),y=L.join(B$(),"state",`${Y}-${Q}`);return q$.set($,y),y}function G$($){return L.join(v($),"jobs")}function R($,W){return L.join(G$($),W)}function x1(){let $=Math.random().toString(36).slice(2,8);return`coder-${Date.now().toString(36)}-${$}`}function C$($,W,q){let Z=R($,W);O.mkdirSync(Z,{recursive:!0});let G=L.join(Z,"job.json"),Q={...K$($,W)??{id:W,createdAt:new Date().toISOString(),status:"queued"},...q,updatedAt:new Date().toISOString()};return O.writeFileSync(G,`${JSON.stringify(Q,null,2)}
|
|
3
|
+
`,"utf8"),Q}function K$($,W){let q=L.join(R($,W),"job.json");if(!O.existsSync(q))return null;try{return JSON.parse(O.readFileSync(q,"utf8"))}catch{return null}}function A1($,W){let q=R($,W);if(!O.existsSync(q))return!1;return O.rmSync(q,{recursive:!0,force:!0}),!0}function u$($){if(!$)return!1;try{return process.kill($,0),!0}catch(W){return W.code==="EPERM"}}function S$($){try{let W=Z$("ps",["-o","lstart=","-p",String($)],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim(),q=W?Date.parse(W):NaN;return Number.isFinite(q)?q:null}catch{return null}}function I$($,W){if(!u$($))return!1;let q=S$($),Z=Date.parse(W.createdAt??"");if(q===null||!Number.isFinite(Z))return!0;return q<=Z+60000}var k$=60000;function h$($,W){if(W.status!=="running"&&W.status!=="queued")return W;let q;if(W.pid)q=!I$(W.pid,W);else{let Z=Date.parse(W.updatedAt??W.createdAt??"");q=Number.isFinite(Z)&&Date.now()-Z>k$}if(q)return C$($,W.id,{status:"failed",error:"worker exited without finishing",completedAt:new Date().toISOString()});return W}function D$($){let W=G$($);if(!O.existsSync(W))return[];return O.readdirSync(W).map((q)=>K$($,q)).filter((q)=>q!==null).map((q)=>h$($,q)).sort((q,Z)=>String(Z.updatedAt??"").localeCompare(String(q.updatedAt??"")))}function _1($,W){let q=D$($);if(!W)return q[0]??null;let Z=q.find((Y)=>Y.id===W);if(Z)return Z;let G=q.filter((Y)=>Y.id.startsWith(W));if(G.length===1)return G[0];if(G.length>1)throw Error(`Job reference "${W}" is ambiguous. Use a longer id.`);return null}function P1($,W){let q=R($,W);try{O.mkdirSync(q,{recursive:!0}),O.writeFileSync(L.join(q,"heartbeat"),"","utf8")}catch{}}function f1($,W){let q=R($,W.id),Z=Date.parse(W.updatedAt??"")||0;for(let G of["log.jsonl","heartbeat"])try{Z=Math.max(Z,O.statSync(L.join(q,G)).mtimeMs)}catch{}return Z?new Date(Z).toISOString():void 0}function R1($,W,q){let Z=R($,W);O.mkdirSync(Z,{recursive:!0}),O.appendFileSync(L.join(Z,"log.jsonl"),`${JSON.stringify({at:new Date().toISOString(),...q})}
|
|
4
|
+
`,"utf8")}function B1($,W,q=40){let Z=L.join(R($,W),"log.jsonl");if(!O.existsSync(Z))return[];return O.readFileSync(Z,"utf8").split(/\r?\n/).filter(Boolean).slice(-q).map((Y)=>{try{return JSON.parse(Y)}catch{return{message:Y}}})}import g from"node:fs";import{fileURLToPath as m}from"node:url";var S1=m(import.meta.url);function Q$(){try{return JSON.parse(g.readFileSync(m(new URL("../package.json",import.meta.url)),"utf8")).version??"unknown"}catch{return"unknown"}}function I1($){try{return JSON.parse(g.readFileSync($,"utf8")).version??null}catch{return null}}function k1(){return m(new URL("..",import.meta.url))}var b$="broker.json";function g$($="coder-"){return M.mkdtempSync(E.join(v$.tmpdir(),$))}function m$($){let W=f($);return F$.createConnection({path:W.path})}async function z$($,W=2000){let q=Date.now();while(Date.now()-q<W){if(await new Promise((G)=>{let Y=m$($);Y.on("connect",()=>{Y.end(),G(!0)}),Y.on("error",()=>G(!1))}))return!0;await new Promise((G)=>setTimeout(G,50))}return!1}function p$({scriptPath:$,cwd:W,endpoint:q,pidFile:Z,logFile:G,env:Y=p.env}){let Q=M.openSync(G,"a"),y=j$(p.execPath,[$,"serve","--endpoint",q,"--cwd",W,"--pid-file",Z],{cwd:W,env:Y,detached:!0,stdio:["ignore",Q,Q]});return y.unref(),M.closeSync(Q),y}function n$(){let $=[new URL("./lib/broker.js",import.meta.url),new URL("./broker.js",import.meta.url)];for(let W of $){let q=w$(W);if(M.existsSync(q))return q}throw Error("Coder broker script not found next to the runtime. Rebuild with `bun run build`.")}function n($){return E.join(v($),b$)}function c($){let W=n($);if(!M.existsSync(W))return null;try{return JSON.parse(M.readFileSync(W,"utf8"))}catch{return null}}function c$($,W){let q=v($);M.mkdirSync(q,{recursive:!0}),M.writeFileSync(n($),`${JSON.stringify(W,null,2)}
|
|
5
|
+
`,"utf8")}function l$($){let W=n($);if(M.existsSync(W))M.unlinkSync(W)}async function a$($){if(!$)return!1;try{return await z$($,150)}catch{return!1}}async function X$($,W={}){let q=W.scriptPath??n$(),Z=Q$(),G=c($),Y=G?.version===Z&&G?.scriptPath===q;if(G&&Y&&await a$(G.endpoint))return G;if(G)Y$({endpoint:G.endpoint??null,pidFile:G.pidFile??null,logFile:G.logFile??null,sessionDir:G.sessionDir??null,pid:G.pid??null,killProcess:W.killProcess??null}),l$($);let Q=g$(),X=(W.createBrokerEndpoint??$$)(Q,W.platform),H=E.join(Q,"broker.pid"),N=E.join(Q,"broker.log"),J=p$({scriptPath:q,cwd:$,endpoint:X,pidFile:H,logFile:N,env:W.env??p.env});if(!await z$(X,W.timeoutMs??2000))return Y$({endpoint:X,pidFile:H,logFile:N,sessionDir:Q,pid:J.pid??null,killProcess:W.killProcess??null}),null;let _={endpoint:X,pidFile:H,logFile:N,sessionDir:Q,pid:J.pid??null,version:Z,scriptPath:q};return c$($,_),_}function Y$({endpoint:$=null,pidFile:W,logFile:q,sessionDir:Z=null,pid:G=null,killProcess:Y=null}){if(Number.isFinite(G)&&Y)try{Y(G)}catch{}if(W&&M.existsSync(W))M.unlinkSync(W);if(q&&M.existsSync(q))M.unlinkSync(q);if($)try{let y=f($);if(y.kind==="unix"&&M.existsSync(y.path))M.unlinkSync(y.path)}catch{}let Q=Z??(W?E.dirname(W):q?E.dirname(q):null);if(Q&&M.existsSync(Q))try{M.rmdirSync(Q)}catch{}}import{spawnSync as i$}from"node:child_process";import I from"node:process";function j($,W=[],q={}){let Z=i$($,W,{cwd:q.cwd,env:q.env,encoding:"utf8",input:q.input,maxBuffer:q.maxBuffer,stdio:q.stdio??"pipe",shell:I.platform==="win32"?I.env.SHELL||!0:!1,windowsHide:!0});return{command:$,args:W,status:Z.status??0,signal:Z.signal??null,stdout:Z.stdout??"",stderr:Z.stderr??"",error:Z.error??null}}function i1($,W=["--version"],q={}){let Z=j($,W,q);if(Z.error&&Z.error.code==="ENOENT")return{available:!1,detail:"not found"};if(Z.error)return{available:!1,detail:Z.error.message};if(Z.status!==0)return{available:!1,detail:Z.stderr.trim()||Z.stdout.trim()||`exit ${Z.status}`};return{available:!0,detail:Z.stdout.trim()||Z.stderr.trim()||"ok"}}function t$($){return/not found|no running instance|cannot find|does not exist|no such process/i.test($)}function y$($,W={}){if(!Number.isFinite($))return{attempted:!1,delivered:!1,method:null};let q=$,Z=W.platform??I.platform,G=W.runCommandImpl??j,Y=W.killImpl??I.kill.bind(I);if(Z==="win32"){let Q=G("taskkill",["/PID",String(q),"/T","/F"],{cwd:W.cwd,env:W.env});if(!Q.error&&Q.status===0)return{attempted:!0,delivered:!0,method:"taskkill",result:Q};let y=`${Q.stderr}
|
|
6
|
+
${Q.stdout}`.trim();if(!Q.error&&t$(y))return{attempted:!0,delivered:!1,method:"taskkill",result:Q};if(Q.error?.code==="ENOENT")try{return Y(q),{attempted:!0,delivered:!0,method:"kill"}}catch(X){if(X?.code==="ESRCH")return{attempted:!0,delivered:!1,method:"kill"};throw X}if(Q.error)throw Q.error;throw Error(d$(Q))}try{return Y(-q,"SIGTERM"),{attempted:!0,delivered:!0,method:"process-group"}}catch(Q){if(Q?.code!=="ESRCH")try{return Y(q,"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 d$($){let W=[`${$.command} ${$.args.join(" ")}`.trim()];if($.signal)W.push(`signal=${$.signal}`);else W.push(`exit=${$.status}`);let q=($.stderr||"").trim(),Z=($.stdout||"").trim();if(q)W.push(q);else if(Z)W.push(Z);return W.join(": ")}var H$="CODER_APP_SERVER_ENDPOINT",M$=-32001,e$="0.144.1",l;function $1(){if(l===void 0){let $=j("codex",["--version"]),W=/(\d+\.\d+\.\d+(?:-[\w.]+)?)/.exec(`${$.stdout} ${$.stderr}`);l=W?W[1]:e$}return l}function N$(){return{title:"Coder",name:"coder",version:$1()}}var O$={experimentalApi:!1,requestAttestation:!1,optOutNotificationMethods:["item/agentMessage/delta","item/reasoning/summaryTextDelta","item/reasoning/summaryPartAdded","item/reasoning/textDelta"]};function U$($,W,q){return q===void 0?{code:$,message:W}:{code:$,message:W,data:q}}function a($,W){let q=Error($);if(q.data=W,W?.code!==void 0)q.rpcCode=W.code;return q}class i{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=(q)=>this.defaultHandleServerRequest(q),this.exitPromise=new Promise((q)=>{this.resolveExit=q})}setNotificationHandler($){this.notificationHandler=$}setServerRequestHandler($){this.serverRequestHandler=$}request($,W){if(this.closed)throw Error("codex app-server client is closed.");let q=this.nextId;return this.nextId+=1,new Promise((Z,G)=>{this.pending.set(q,{resolve:Z,reject:G,method:$}),this.sendMessage({id:q,method:$,params:W})})}notify($,W={}){if(this.closed)return;this.sendMessage({method:$,params:W})}handleChunk($){this.lineBuffer+=$;let W=this.lineBuffer.indexOf(`
|
|
7
7
|
`);while(W!==-1){let q=this.lineBuffer.slice(0,W);this.lineBuffer=this.lineBuffer.slice(W+1),this.handleLine(q),W=this.lineBuffer.indexOf(`
|
|
8
|
-
`)}}handleLine($){if(!$.trim())return;let W;try{W=JSON.parse($)}catch(q){this.handleExit(
|
|
9
|
-
${G}`:""}`);this.handleExit(Y)}),this.readline=
|
|
10
|
-
`,q=this.proc?.stdin;if(!q)throw Error("codex app-server stdin is not available.");q.write(W)}}class
|
|
11
|
-
`,q=this.socket;if(!q)throw Error("codex app-server broker connection is not connected.");q.write(W)}}class
|
|
12
|
-
`)}function
|
|
13
|
-
`,"utf8")}async function
|
|
8
|
+
`)}}handleLine($){if(!$.trim())return;let W;try{W=JSON.parse($)}catch(q){this.handleExit(a(`Failed to parse codex app-server JSONL: ${q instanceof Error?q.message:String(q)}`,{line:$}));return}if(W.id!==void 0&&W.method){this.handleServerRequest(W);return}if(W.id!==void 0){let q=this.pending.get(W.id);if(!q)return;if(this.pending.delete(W.id),W.error)q.reject(a(W.error.message??`codex app-server ${q.method} failed.`,W.error));else q.resolve(W.result??{});return}if(W.method&&this.notificationHandler)this.notificationHandler(W)}defaultHandleServerRequest($){let W=this.serverRequestHandler;if(!W){this.sendMessage({id:$.id,error:U$(-32601,`Unsupported server request: ${$.method}`)});return}Promise.resolve().then(()=>W($.method,$.params??{})).then((q)=>{this.sendMessage({id:$.id,result:q??{}})}).catch((q)=>{this.sendMessage({id:$.id,error:U$(-32000,q instanceof Error?q.message:String(q))})})}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 V$ extends i{proc;readline;constructor($,W={}){super($,W);this.transport="direct"}async initialize(){this.proc=s$("codex",["-c","sandbox_workspace_write.network_access=false","app-server","--disable","plugins"],{cwd:this.cwd,env:this.options.env??k.env,stdio:["pipe","pipe","pipe"],shell:k.platform==="win32"?k.env.SHELL||!0:!1,windowsHide:!0});let $=this.proc.stdout,W=this.proc.stderr;if(!$||!W)throw Error("codex app-server did not expose stdio streams.");$.setEncoding("utf8"),W.setEncoding("utf8"),W.on("data",(q)=>{this.stderr+=q}),this.proc.on("error",(q)=>{this.handleExit(q)}),this.proc.on("exit",(q,Z)=>{let G=this.stderr.trim(),Y=q===0?null:a(`codex app-server exited unexpectedly (${Z?`signal ${Z}`:`exit ${q}`}).${G?`
|
|
9
|
+
${G}`:""}`);this.handleExit(Y)}),this.readline=o$.createInterface({input:$}),this.readline.on("line",(q)=>{this.handleLine(q)}),await this.request("initialize",{clientInfo:this.options.clientInfo??N$(),capabilities:this.options.capabilities??O$}),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(k.platform==="win32")try{y$(this.proc.pid)}catch{}else this.proc.kill("SIGTERM")},50).unref?.();await this.exitPromise}sendMessage($){let W=`${JSON.stringify($)}
|
|
10
|
+
`,q=this.proc?.stdin;if(!q)throw Error("codex app-server stdin is not available.");q.write(W)}}class L$ extends i{endpoint;socket;constructor($,W={}){super($,W);this.transport="broker",this.endpoint=W.brokerEndpoint}async initialize(){await new Promise(($,W)=>{let q=f(this.endpoint);this.socket=r$.createConnection({path:q.path}),this.socket.setEncoding("utf8"),this.socket.on("connect",$),this.socket.on("data",(Z)=>{this.handleChunk(Z)}),this.socket.on("error",(Z)=>{if(!this.exitResolved)W(Z);this.handleExit(Z)}),this.socket.on("close",()=>{this.handleExit(this.exitError)})}),await this.request("initialize",{clientInfo:this.options.clientInfo??N$(),capabilities:this.options.capabilities??O$}),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
|
+
`,q=this.socket;if(!q)throw Error("codex app-server broker connection is not connected.");q.write(W)}}class t{static async connect($,W={}){let q=null;if(!W.disableBroker){if(q=W.brokerEndpoint??W.env?.[H$]??k.env[H$]??null,!q&&W.reuseExistingBroker)q=c($)?.endpoint??null;if(!q&&!W.reuseExistingBroker)q=(await X$($,{env:W.env}))?.endpoint??null}let Z=q?new L$($,{...W,brokerEndpoint:q}):new V$($,W);return await Z.initialize(),Z}}var q1=new Set(["turn/start","review/start","thread/compact/start"]);function J$($,W,q){let Z=new Set;if(W?.threadId)Z.add(W.threadId);if($==="review/start"&&q?.reviewThreadId)Z.add(q.reviewThreadId);return Z}function C($,W,q){return q===void 0?{code:$,message:W}:{code:$,message:W,data:q}}function A($,W){if($.destroyed)return;$.write(`${JSON.stringify(W)}
|
|
12
|
+
`)}function Z1($){return $?.method==="turn/interrupt"}function G1($){if(!$)return;u.mkdirSync(d.dirname($),{recursive:!0}),u.writeFileSync($,`${x.pid}
|
|
13
|
+
`,"utf8")}async function K1(){let[$,...W]=x.argv.slice(2);if($!=="serve")throw Error("Usage: node src/lib/broker.mjs serve --endpoint <value> [--cwd <path>] [--pid-file <path>]");let{options:q}=o(W,{valueOptions:["cwd","pid-file","endpoint"]});if(!q.endpoint)throw Error("Missing required --endpoint.");let Z=q.cwd?d.resolve(x.cwd(),q.cwd):x.cwd(),G=String(q.endpoint),Y=f(G),Q=q["pid-file"]?d.resolve(q["pid-file"]):null;G1(Q);let y=await t.connect(Z,{disableBroker:!0}),X=null,H=null,N=null,J=new Set,V=new Map;function _(K){for(let[U,B]of V)if(B===K)V.delete(U),y.sendMessage({id:U,error:C(-32000,"Broker client disconnected before answering the server request.")})}function P(K){if(_(K),X===K)X=null;if(H===K)H=null,N=null}function h(K){let U=X??H;if(!U)return;if(A(U,K),K.method==="turn/completed"&&H===U){let B=K.params?.threadId??null;if(!B||!N||N.has(B)){if(H=null,N=null,X===U)X=null}}}async function w(K){for(let U of J)U.end();if(await y.close().catch(()=>{}),await new Promise((U)=>K.close(U)),Y.kind==="unix"&&u.existsSync(Y.path))u.unlinkSync(Y.path);if(Q&&u.existsSync(Q))u.unlinkSync(Q)}y.setNotificationHandler(h),y.handleServerRequest=(K)=>{let U=H??X;if(!U||U.destroyed){y.sendMessage({id:K.id,error:C(-32601,`No broker client available to answer ${K.method}.`)});return}V.set(K.id,U),A(U,K)};let D=W1.createServer((K)=>{J.add(K),K.setEncoding("utf8");let U="";K.on("data",async(B)=>{U+=B;let F=U.indexOf(`
|
|
14
14
|
`);while(F!==-1){let r=U.slice(0,F);if(U=U.slice(F+1),F=U.indexOf(`
|
|
15
|
-
`),!r.trim())continue;let z;try{z=JSON.parse(r)}catch(
|
|
15
|
+
`),!r.trim())continue;let z;try{z=JSON.parse(r)}catch(T){A(K,{id:null,error:C(-32700,`Invalid JSON: ${T instanceof Error?T.message:String(T)}`)});continue}if(z.id!==void 0&&!z.method&&V.has(z.id)){V.delete(z.id),y.sendMessage(z);continue}if(z.id!==void 0&&z.method==="initialize"){A(K,{id:z.id,result:{userAgent:"coder-broker"}});continue}if(z.method==="initialized"&&z.id===void 0)continue;if(z.id!==void 0&&z.method==="broker/shutdown")A(K,{id:z.id,result:{}}),await w(D),x.exit(0);if(z.id===void 0||!z.method)continue;let s=Z1(z)&&H&&H!==K&&!X;if((X&&X!==K||H&&H!==K)&&!s){A(K,{id:z.id,error:C(M$,"Shared Codex broker is busy.")});continue}if(s){try{let T=await y.request(z.method,z.params??{});A(K,{id:z.id,result:T})}catch(T){let S=T;A(K,{id:z.id,error:C(S.rpcCode??-32000,S.message)})}continue}let b=q1.has(z.method);if(X=K,b)H=K,N=J$(z.method,z.params??{},null);try{let T=await y.request(z.method,z.params??{});if(A(K,{id:z.id,result:T}),b)N=J$(z.method,z.params??{},T);if(X===K)X=null}catch(T){let S=T;if(A(K,{id:z.id,error:C(S.rpcCode??-32000,S.message)}),X===K)X=null;if(H===K&&b)H=null,N=null}}}),K.on("close",()=>{J.delete(K),P(K)}),K.on("error",()=>{J.delete(K),P(K)})});x.on("SIGTERM",async()=>{await w(D),x.exit(0)}),x.on("SIGINT",async()=>{await w(D),x.exit(0)}),D.listen(Y.path)}K1().catch(($)=>{x.stderr.write(`${$ instanceof Error?$.message:String($)}
|
|
16
16
|
`),x.exit(1)});
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ If `coder` is not on PATH, install it yourself without asking (`npm install -g @
|
|
|
16
16
|
- Compose one self-contained task text (goal, relevant paths, constraints) and dispatch it. It backgrounds by default: the runtime does a startup check and prints a task id (or errors / hands off, see exit codes):
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
coder task run
|
|
19
|
+
coder task run "<task text>"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Fetch the answer with `coder task result <task-id> --wait` - it blocks until the task finishes, then prints only the final answer (keeps your context clean). Run it as a **background Bash call** so it does not block you and you are re-invoked on completion:
|
|
@@ -29,23 +29,15 @@ If `coder` is not on PATH, install it yourself without asking (`npm install -g @
|
|
|
29
29
|
|
|
30
30
|
- A `--wait` (on `run` or `result`) exits **4** when the task is waiting on a permission approval: relay the approval to the user, apply their decision with `coder approve <task-id> <approval-id> [--deny]`, then re-fetch with `coder task result <task-id> --wait`. (Unanswered approvals auto-deny after 120s and the task moves on.)
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
- `coder task run` exits after its startup check. Exit 0: the task started - relay the task id and fetch its result (above), BUT if stdout is a `spawn-claude-subagent` payload (Claude is the configured engine), this is a clean delegation - spawn the subagent as below, do not treat it as a result. Exit 1: it failed to start - report it, do not retry or fall back yourself. Exit 3: Codex failed to start and handed off to Claude (see below).
|
|
32
|
+
- `coder task run` exits after its startup check. Exit 0: the task started - relay the task id and fetch its result (above). Exit 1: it failed to start - report it, do not retry or fall back yourself. Exit 3: no engine in the chain could start and stdout carries a `run-native-subagent` payload - see below. (The runtime runs every engine itself, claude included, and falls back through its configured chain on startup failures - never fall back by implementing the task yourself.)
|
|
34
33
|
- 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`, `luna`, `terra`, `sol`; claude models: `opus`, `sonnet`, `fable`; efforts: `low|medium|high`).
|
|
35
34
|
- `spark` is only for the very lightest tasks (formatting, renames, quick lookups) - very fast and very cheap. It runs on a separate quota, so reach for it when the others hit a usage limit; it may still run once they are exhausted.
|
|
36
35
|
- 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.
|
|
37
36
|
- A background dispatch prints a task id plus its result/steer/stop commands. Relay them and fetch the result (above) rather than polling.
|
|
38
37
|
|
|
39
|
-
##
|
|
38
|
+
## Last resort: run-native-subagent payload (exit 3)
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- `configured` (Claude is the selected engine): a planned delegation, so the runtime **exits 0** - present it as delegation, not an error.
|
|
44
|
-
- `codex-failed` (Codex missing, auth, quota, rate limit): the runtime **exits 3** and wraps the payload under a `fallback` key alongside an `error` string - mention the failure to the user, including any limit-reset time, then spawn the subagent.
|
|
45
|
-
|
|
46
|
-
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.
|
|
47
|
-
|
|
48
|
-
Relay its output when it completes.
|
|
40
|
+
When no coder engine can start (missing binaries, auth, quota across the whole chain), the runtime exits 3 with a JSON payload (`fallback.action: "run-native-subagent"`). Mention the failure to the user (including any limit-reset time), then run the task with your own subagent facility: spawn one `general-purpose` subagent via the Agent tool whose prompt is the payload's `task` verbatim, prefixed with: "NEVER run git write operations (commit, checkout, stash, reset, push, etc.); leave changes uncommitted." If the payload's `permissions` is not `auto`, append it: `read-only` means investigate and report without modifying anything; `workspace-write` means never touch anything outside the workspace. Relay its output when it completes.
|
|
49
41
|
|
|
50
42
|
## Controlling tasks
|
|
51
43
|
|
|
@@ -56,6 +48,6 @@ Relay its output when it completes.
|
|
|
56
48
|
|
|
57
49
|
## Hard rules
|
|
58
50
|
|
|
59
|
-
- Forward task text verbatim in `steer`
|
|
60
|
-
- Tell every coder to never run git write operations (commit, checkout, stash, reset, rebase, merge, push, etc.) and to leave changes uncommitted
|
|
51
|
+
- Forward task text verbatim in `steer` follow-ups - never rewrite or summarize what the user asked for.
|
|
52
|
+
- Tell every coder to never run git write operations (commit, checkout, stash, reset, rebase, merge, push, etc.) and to leave changes uncommitted.
|
|
61
53
|
- Pass the user's constraints (read-only, scoped paths) into each coder's task text, and honor them over your own preferences.
|
|
@@ -14,18 +14,18 @@ When you run setup, also check codex is new enough for the default GPT-5.6 model
|
|
|
14
14
|
## Dispatching a task
|
|
15
15
|
|
|
16
16
|
- Run every `coder` command with escalated permissions (outside your sandbox), justification: Coder is a supervisor - it spawns engines, keeps state in `~/.coder`, and enforces its own sandbox and approval policy on the task. Never retry a sandbox-blocked `coder` call sandboxed again or with HOME/state env workarounds.
|
|
17
|
-
- One coder per focused goal. When the work splits into independent parts, decompose it and dispatch each part as its own `coder task run
|
|
17
|
+
- One coder per focused goal. When the work splits into independent parts, decompose it and dispatch each part as its own `coder task run` call - fan out a wide web of coders, not one giant dispatch. Give each coder a self-contained goal with all context - overview, file paths, constraints (read-only, git rules); use `steer` to continue a single coder's thread.
|
|
18
18
|
- 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. The coder worker runs with plugins disabled (no skills, MCP, or connectors), so include any context only a plugin or MCP tool of yours produced.
|
|
19
19
|
- Compose one self-contained task text (goal, relevant paths, constraints) and dispatch it. It backgrounds by default: the runtime does a startup check and prints a task id (or errors / hands off, see exit codes):
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
coder task run
|
|
22
|
+
coder task run "<task text>"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Fetch the answer with `coder task result <task-id> --wait` - it blocks until the task finishes, then prints only the final result (keeps your context clean). Only use `--wait` when you can run it in a **background shell** (so it does not block you); if you can't use that, poll `coder task result <task-id>` (no `--wait`) until it is done. Or skip the two steps and block on the run itself with `--wait` on `coder task run`. A `--wait` exits **4** when the task is waiting on a permission approval: relay it to the user, apply their decision with `coder approve <task-id> <approval-id> [--deny]`, then re-fetch with `coder task result <task-id> --wait` (unanswered approvals auto-deny after 120s and the task moves on).
|
|
26
26
|
|
|
27
|
-
- Always pass `--host codex` to identify yourself.
|
|
28
27
|
- `coder task run` exits after its startup check. Exit 0: the task started - fetch its result (above). Exit 1: it failed to start / the turn failed - report it, do not retry.
|
|
28
|
+
- Exit 3: no engine in the chain could start; stdout carries a JSON payload (`fallback.action: "run-native-subagent"`). Mention the failure to the user (including any limit-reset time), then run the task with your own subagent facility: spawn one subagent whose prompt is the payload's `task` verbatim, prefixed with: "NEVER run git write operations (commit, checkout, stash, reset, push, etc.); leave changes uncommitted." If the payload's `permissions` is not `auto`, append it: `read-only` means investigate and report without modifying anything; `workspace-write` means never touch anything outside the workspace. Relay its output when it completes.
|
|
29
29
|
- When the first engine can't start (auth, quota, rate limit), the runtime automatically falls back to the next engine in the chain, runs the same task on it via CLI, and still exits 0 - so just fetch the result. If the failure mentions a usage-limit reset time, relay that to the user.
|
|
30
30
|
|
|
31
31
|
- 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`, `luna`, `terra`, `sol`; claude models: `opus`, `sonnet`, `fable`; efforts: `low|medium|high`).
|
|
@@ -19,13 +19,13 @@ When you run setup, also check codex is new enough for the default GPT-5.6 model
|
|
|
19
19
|
- Compose one self-contained task text (goal, relevant paths, constraints) and dispatch it. It backgrounds by default: the runtime does a startup check and prints a task id (or errors, see exit codes):
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
coder task run
|
|
22
|
+
coder task run "<task text>"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Fetch the answer with `coder task result <task-id> --wait` - it blocks until the task finishes, then prints only the final result (keeps your context clean). Run it in a **background terminal** so it does not block you; if you can't, poll `coder task result <task-id>` (no `--wait`) until it is done. Or skip the two steps and block on the run itself with `--wait` on `coder task run`. A `--wait` exits **4** when the task is waiting on a permission approval: relay it to the user, apply their decision with `coder task approve <task-id> <approval-id> [--deny]`, then re-fetch with `coder task result <task-id> --wait` (unanswered approvals auto-deny after 120s and the task moves on).
|
|
26
26
|
|
|
27
|
-
- Always pass `--host cursor` to identify yourself.
|
|
28
27
|
- `coder task run` exits after its startup check. Exit 0: the task started - fetch its result (above). Exit 1: it failed to start / the turn failed - report it, do not retry.
|
|
28
|
+
- Exit 3: no engine in the chain could start; stdout carries a JSON payload (`fallback.action: "run-native-subagent"`). Mention the failure to the user (including any limit-reset time), then run the task with your own subagent facility: spawn one subagent whose prompt is the payload's `task` verbatim, prefixed with: "NEVER run git write operations (commit, checkout, stash, reset, push, etc.); leave changes uncommitted." If the payload's `permissions` is not `auto`, append it: `read-only` means investigate and report without modifying anything; `workspace-write` means never touch anything outside the workspace. Relay its output when it completes.
|
|
29
29
|
- Codex startup failures (auth, quota, rate limit) fall back to the next engine in the chain automatically - the runtime reruns the same task on the claude engine via its CLI and still exits 0, so just fetch the result. If it mentions a usage-limit reset time, relay that to the user.
|
|
30
30
|
- 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`, `luna`, `terra`, `sol`; claude models: `opus`, `sonnet`, `fable`; efforts: `low|medium|high`).
|
|
31
31
|
- `spark` is only for the very lightest tasks (formatting, renames, quick lookups) - very fast and very cheap. It runs on a separate quota, so reach for it when the others hit a usage limit; it may still run once they are exhausted.
|