@wular/coder 0.2.12 → 0.3.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 CHANGED
@@ -14,6 +14,7 @@
14
14
  - **Fast dispatch.** Handoffs are instant, and light enough to spin up a large number of subagents at once.
15
15
  - **Steerable tasks.** Full visibility into every task, from live progress to mid-run course corrections.
16
16
  - **Unified permissions.** Three modes across all engines: _Read-only_, _Workspace-write_, and _Auto_.
17
+ - **Flows and SDK.** Orchestrate whole waves of tasks from a plain TypeScript file, or drive everything from your own code.
17
18
 
18
19
  ## Get started
19
20
 
@@ -50,7 +51,7 @@ npm install -g @openai/codex && codex login
50
51
  npm install -g @anthropic-ai/claude-code && claude auth login
51
52
  ```
52
53
 
53
- Or a local/provider model of your own (see [Bring your own model](docs/custom-models.md)). `coder setup-host` checks what's ready.
54
+ Or a local/provider model of your own (see [Models](docs/models.md)). `coder setup-host` checks what's ready.
54
55
 
55
56
  **3. Ask your host to use it:**
56
57
 
@@ -62,24 +63,6 @@ Or make it the default in AGENTS.md/CLAUDE.md:
62
63
 
63
64
  Recommended setup: Claude Code as host (fable low/medium) and Codex (terra) as engine - best for performance and cost distribution.
64
65
 
65
- ## Configuration
66
-
67
- Machine defaults live in `~/.coder/config.json`; a `coder.config.json` in a repo overrides per project.
68
-
69
- ```json
70
- {
71
- "chain": ["codex", "claude", "custom"],
72
- "agents": {
73
- "codex": { "model": "terra", "effort": "high", "permissions": "auto" },
74
- "claude": { "model": "opus", "effort": "medium", "permissions": "auto" },
75
- "custom": { "model": "qwen" }
76
- },
77
- "models": {
78
- "qwen": { "baseUrl": "http://localhost:11434/v1", "model": "qwen2.5-coder:32b" }
79
- }
80
- }
81
- ```
82
-
83
66
  ## Staying up to date
84
67
 
85
68
  ```bash
@@ -88,6 +71,40 @@ coder upgrade
88
71
 
89
72
  Updates the CLI through whichever package manager installed it and refreshes the host plugins to match. See `coder upgrade --help` for narrowing flags. Set `CODER_NO_UPDATE_CHECK=1` to silence the update notice.
90
73
 
74
+ ## Flows
75
+
76
+ For well-defined workflows, ask your host to write a flow. A Coder flow is a TypeScript file that fans out coders at scale with built-in support for deterministic verification gates, crash/resume handling, etc. Ask like:
77
+
78
+ > Create a Coder flow that fixes every failing test file: one task per file, gated on its tests passing.
79
+
80
+ The harness authors the flow, you can review or tweak it, and it runs with live status and mid-run steering like any coder task. A generated flow looks like this:
81
+
82
+ ```ts
83
+ // .coder/flows/first.ts
84
+ import { z } from 'zod';
85
+ import { task, gate, pipeline } from '@wular/coder/flow';
86
+
87
+ const failing = await task(
88
+ 'Run `bun test` and list the failing test files.',
89
+ {
90
+ name: 'Find failing tests',
91
+ returns: z.object({ files: z.array(z.string()) }),
92
+ },
93
+ );
94
+
95
+ export default await pipeline(
96
+ failing.data.files,
97
+ file => task(`Fix the failing tests in ${file}.`, { name: file }),
98
+ (r, file) => gate(`bun test ${file}`),
99
+ );
100
+ ```
101
+
102
+ See [Flows](docs/flows.md), or the [SDK](docs/sdk.md) to drive everything from your own code.
103
+
104
+ ## Configuration
105
+
106
+ Machine defaults live in `~/.coder/config.json`; a `coder.config.json` in a repo overrides per project. See [Configuration](docs/config.md).
107
+
91
108
  ## Advanced usage
92
109
 
93
110
  You rarely need these - the host agent drives tasks itself - but everything is scriptable:
@@ -100,6 +117,16 @@ coder task steer <task-id> "<text>" # continue a task with new instructions
100
117
  coder task stop <task-id> # interrupt it
101
118
  coder task stream [task-id] # watch the live progress log
102
119
  coder task approve <task-id> <appr-id> # answer an escalated permission (--deny)
120
+
121
+ coder flow run <name|path> --wait # run a flow (--dry-run to preview prompts)
122
+ coder flow list # recent flow runs
123
+ coder flow discover # flows runnable here (workspace + global)
124
+ coder flow result [run-id] # progress and result across the whole wave
125
+ coder flow stream [run-id] # watch the wave live (replay + follow)
126
+ coder flow stop [run-id] # stop a run and its tasks
127
+ coder flow resume [run-id] # continue a crashed or edited run
128
+
129
+ coder docs [topic] # print bundled docs (flows, sdk, config, models)
103
130
  ```
104
131
 
105
132
  Any `[task-id]` defaults to the most recent task.
@@ -0,0 +1,3 @@
1
+ import e from"node:process";import t from"node:fs";import n from"node:path";import{execFileSync as r,spawn as i,spawnSync as a}from"node:child_process";import o from"node:os";import{fileURLToPath as s}from"node:url";import*as c from"zod/mini";import l from"node:net";import u from"node:readline";function d(e){try{let t=i(`codex`,[`archive`,e],{detached:!0,stdio:`ignore`});t.on(`error`,()=>{}),t.unref()}catch{}}function f(e){let r=n.join(process.env.CODEX_HOME||n.join(o.homedir(),`.codex`),`archived_sessions`);try{return t.readdirSync(r).some(t=>t.endsWith(`-${e}.jsonl`))}catch{return!1}}function p(e){return new Promise(t=>{try{let n=i(`codex`,[`unarchive`,e],{stdio:`ignore`});n.on(`error`,()=>t(!1)),n.on(`close`,e=>t(e===0))}catch{t(!1)}})}function ee(e){return a(`codex`,[`delete`,`--force`,e],{encoding:`utf8`}).status===0}const m=[`completed`,`failed`,`cancelled`],te=[`queued`,`running`],ne=/^[a-z0-9][a-z0-9_-]*$/i;function h(e){return ne.test(e)}function g(e,t){if(!h(e))throw Error(`Invalid ${t} "${e}".`)}function _(){return n.resolve(process.env.CODER_HOME||n.join(o.homedir(),`.coder`))}const v=new Map;function re(e){let t=v.get(e);if(t!==void 0)return t;let n;try{n=r(`git`,[`rev-parse`,`--show-toplevel`],{cwd:e,encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]}).trim()}catch{n=e}return v.set(e,n),n}const y=`global`;function b(e){return n.join(_(),`state`,y)}let x=null;function S(){if(x)return x;let e=n.join(_(),`state`),r=[];try{r=t.readdirSync(e,{withFileTypes:!0})}catch{}return x=r.filter(e=>e.isDirectory()&&e.name!==y).map(t=>n.join(e,t.name)),x}function C(e){return n.join(b(e),`jobs`)}const ie=10*6e4;function w(e){return n.join(b(e),`archive`)}function T(e,r){g(r,`task id`);let i=n.join(C(e),r);if(t.existsSync(n.join(i,`job.json`)))return i;let a=n.join(w(e),r);if(t.existsSync(n.join(a,`job.json`)))return a;for(let e of S()){let i=n.join(e,`jobs`,r);if(t.existsSync(n.join(i,`job.json`)))return i}return i}function E(e,t){return t.archived?t:O(e,t.id,{archived:!0,archivedAt:new Date().toISOString()})}function D(e,r){let i=E(e,r);r.engine===`codex`&&r.threadId&&d(r.threadId);let a=T(e,r.id),o=n.join(w(e),r.id);if(a!==o)try{t.mkdirSync(w(e),{recursive:!0}),t.renameSync(a,o)}catch{}return i}function ae(){let e=Math.random().toString(36).slice(2,8);return`task-${Date.now().toString(36)}-${e}`}function O(e,r,i){let a=T(e,r);t.mkdirSync(a,{recursive:!0});let o=n.join(a,`job.json`),s={...A(e,r)??{id:r,createdAt:new Date().toISOString(),status:`queued`},...i,updatedAt:new Date().toISOString()};return t.writeFileSync(o,`${JSON.stringify(s,null,2)}\n`,`utf8`),s}function k(e){return!e.engine&&(e.agent===`codex`||e.agent===`claude`)&&(e.engine=e.agent),e}function A(e,r){if(!h(r))return null;let i=n.join(T(e,r),`job.json`);if(!t.existsSync(i))return null;try{return k(JSON.parse(t.readFileSync(i,`utf8`)))}catch{return null}}function oe(e,n){let r=T(e,n);return t.existsSync(r)?(t.rmSync(r,{recursive:!0,force:!0}),!0):!1}function se(e){if(!e)return!1;try{return process.kill(e,0),!0}catch(e){return e.code===`EPERM`}}const j=new Map;function M(e){let t=j.get(e);if(t&&Date.now()-t.at<5e3)return t.value;let n=ce(e);return j.set(e,{at:Date.now(),value:n}),n}function ce(e){try{let t=r(`ps`,[`-o`,`lstart=`,`-p`,String(e)],{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`],env:{...process.env,TZ:Intl.DateTimeFormat().resolvedOptions().timeZone}}).trim(),n=t?Date.parse(t):NaN;return Number.isFinite(n)?n:null}catch{return null}}function le(e,t){if(!se(e))return!1;let n=M(e),r=Date.parse(t.createdAt??``);return n===null||!Number.isFinite(r)||n<=r+6e4}function N(e,t){if(t.status!==`running`&&t.status!==`queued`)return t;let n;if(t.pid)n=!le(t.pid,t);else{let e=Date.parse(t.updatedAt??t.createdAt??``);n=Number.isFinite(e)&&Date.now()-e>6e4}return n?O(e,t.id,{status:`failed`,error:`worker exited without finishing`,completedAt:new Date().toISOString()}):t}function P(e,r){let i=[];for(let a of e){let e=[];try{e=t.readdirSync(a)}catch{continue}for(let o of e){if(r.has(o))continue;let e=n.join(a,o,`job.json`);try{let n=k(JSON.parse(t.readFileSync(e,`utf8`)));r.add(o),i.push(n)}catch{}}}return i}const F=(e,t)=>String(t.updatedAt??``).localeCompare(String(e.updatedAt??``));function ue(e){let t=[C(e),...S().map(e=>n.join(e,`jobs`))],r=[];for(let n of P(t,new Set))n.archived?D(e,n):r.push(N(e,n));return r.sort(F)}function I(e,t){let r=t?.migrate??!0,i=new Set,a=P([w(e)],i),o=[C(e),...S().map(e=>n.join(e,`jobs`))];for(let t of P(o,i))t.archived&&a.push(r?D(e,t):t);return a.sort(F)}function de(e,t){let n=[...ue(e),...I(e)].sort(F);if(!t)return n[0]??null;let r=n.find(e=>e.id===t);if(r)return r;let i=n.filter(e=>e.id.startsWith(t));if(i.length===1)return i[0];if(i.length>1)throw Error(`Job reference "${t}" is ambiguous. Use a longer id.`);return null}function fe(e,r){let i=T(e,r);try{t.mkdirSync(i,{recursive:!0}),t.writeFileSync(n.join(i,`heartbeat`),``,`utf8`)}catch{}}function pe(e,r){let i=T(e,r.id),a=Date.parse(r.updatedAt??``)||0;for(let e of[`log.jsonl`,`heartbeat`])try{a=Math.max(a,t.statSync(n.join(i,e)).mtimeMs)}catch{}return a?new Date(a).toISOString():void 0}async function me(e,t,n=400){let r=t;for(;!m.includes(r.status);)await new Promise(e=>setTimeout(e,n)),r=N(e,A(e,r.id)??r);return r}const L=`steer-queue.jsonl`;function he(e,r,i){let a=T(e,r);t.mkdirSync(a,{recursive:!0}),t.appendFileSync(n.join(a,L),`${JSON.stringify({at:new Date().toISOString(),text:i})}\n`,`utf8`)}function ge(e,r){let i=T(e,r),a=n.join(i,L),o=n.join(i,`steer-claim-${process.pid}-${Date.now()}.tmp`);try{t.renameSync(a,o)}catch{return[]}try{return t.readFileSync(o,`utf8`).split(/\r?\n/).filter(Boolean).map(e=>{try{return String(JSON.parse(e).text??``)}catch{return``}}).filter(Boolean)}finally{try{t.unlinkSync(o)}catch{}}}function _e(e,r,i){let a=T(e,r);t.mkdirSync(a,{recursive:!0}),t.appendFileSync(n.join(a,`log.jsonl`),`${JSON.stringify({at:new Date().toISOString(),...i})}\n`,`utf8`)}function ve(e,r,i=40){let a=n.join(T(e,r),`log.jsonl`);return t.existsSync(a)?t.readFileSync(a,`utf8`).split(/\r?\n/).filter(Boolean).slice(-i).map(e=>{try{return JSON.parse(e)}catch{return{message:e}}}):[]}function ye(){let e=n.dirname(s(import.meta.url));for(let r=0;r<10;r++){let r=n.join(e,`package.json`);try{if(JSON.parse(t.readFileSync(r,`utf8`)).name===`@wular/coder`)return e}catch{}let i=n.dirname(e);if(i===e)break;e=i}return n.resolve(n.dirname(s(import.meta.url)),`..`)}const R=ye(),be=t.existsSync(n.join(R,`dist/cli.js`))?n.join(R,`dist/cli.js`):n.join(R,`src/cli.ts`);function z(){try{return JSON.parse(t.readFileSync(n.join(R,`package.json`),`utf8`)).version??`unknown`}catch{return`unknown`}}function xe(e){try{return JSON.parse(t.readFileSync(e,`utf8`)).version??null}catch{return null}}function Se(){return R}const B=c.optional(c.boolean()),V=c.optional(c.string()),Ce={cwd:V,json:B},we=c.optional(c.union([c.literal(`all`),c.coerce.number().check(c.int(),c.positive())],{error:`expected a positive integer or "all"`})),Te=c.optional(c.union([c.literal(`all`),c.coerce.number().check(c.int(),c.nonnegative())],{error:`expected a number or "all"`}));function H(e){let t=e.def;for(;t;){if(t.type===`boolean`)return!0;let e=t.innerType??t.in;t=e?e.def:void 0}return!1}function Ee(e,t){let n=t.shape,r={},i=[],a=!1,o=(e,t,i,a)=>{let o=n[t],s=a?`-`:`--`;if(!o)throw Error(`Unknown option ${s}${e} (use -- to pass literal text starting with -)`);if(H(o))return r[t]=i===void 0||i!==`false`,0;if(i===void 0)throw Error(`Missing value for ${s}${e}`);return r[t]=i,1};for(let t=0;t<e.length;t+=1){let r=e[t];if(a){i.push(r);continue}if(r===`--`){a=!0;continue}if(!r.startsWith(`-`)||r===`-`){i.push(r);continue}if(r.startsWith(`--`)){let[i,a]=r.slice(2).split(`=`,2);a===void 0&&n[i]&&!H(n[i])?t+=o(i,i,e[t+1],!1):o(i,i,a,!1);continue}let s=r.slice(1);n[s]&&!H(n[s])?t+=o(s,s,e[t+1],!0):o(s,s,void 0,!0)}let s=t.safeParse(r);if(!s.success){let e=s.error.issues[0],t=String(e.path[0]??``),n=r[t];throw Error(n===void 0?`Missing required --${t}.`:`Invalid --${t} value ${JSON.stringify(n)}: ${e.message}`)}return{options:s.data,positionals:i}}function De(e){return String(e??``).replace(/[^A-Za-z0-9._-]/g,`-`).replace(/^-+|-+$/g,``)}function Oe(t,r=e.platform){return r===`win32`?`pipe:\\\\.\\pipe\\${De(`${n.win32.basename(t)}-codex-app-server`)}`:`unix:${n.join(t,`broker.sock`)}`}function U(e){if(typeof e!=`string`||e.length===0)throw Error(`Missing broker endpoint.`);if(e.startsWith(`pipe:`)){let t=e.slice(5);if(!t)throw Error(`Broker pipe endpoint is missing its path.`);return{kind:`pipe`,path:t}}if(e.startsWith(`unix:`)){let t=e.slice(5);if(!t)throw Error(`Broker Unix socket endpoint is missing its path.`);return{kind:`unix`,path:t}}throw Error(`Unsupported broker endpoint: ${e}`)}function ke(e=`coder-`){return t.mkdtempSync(n.join(o.tmpdir(),e))}function Ae(e){let t=U(e);return l.createConnection({path:t.path})}async function W(e,t=2e3){let n=Date.now();for(;Date.now()-n<t;){if(await new Promise(t=>{let n=Ae(e);n.on(`connect`,()=>{n.end(),t(!0)}),n.on(`error`,()=>t(!1))}))return!0;await new Promise(e=>setTimeout(e,50))}return!1}function je({scriptPath:n,cwd:r,endpoint:a,pidFile:o,logFile:s,env:c=e.env}){let l=t.openSync(s,`a`),u=i(e.execPath,[n,`serve`,`--endpoint`,a,`--cwd`,r,`--pid-file`,o],{cwd:r,env:c,detached:!0,stdio:[`ignore`,l,l]});return u.unref(),t.closeSync(l),u}function Me(){let e=[new URL(`./lib/broker.js`,import.meta.url),new URL(`./broker.js`,import.meta.url)];for(let n of e){let e=s(n);if(t.existsSync(e))return e}throw Error("Coder broker script not found next to the runtime. Rebuild with `bun run build`.")}function G(e){return n.join(b(e),`broker.json`)}function K(e){let n=G(e);if(!t.existsSync(n))return null;try{return JSON.parse(t.readFileSync(n,`utf8`))}catch{return null}}function Ne(e,n){let r=b(e);t.mkdirSync(r,{recursive:!0}),t.writeFileSync(G(e),`${JSON.stringify(n,null,2)}\n`,`utf8`)}function Pe(e){let n=G(e);t.existsSync(n)&&t.unlinkSync(n)}async function Fe(e){if(!e)return!1;try{return await W(e,150)}catch{return!1}}async function Ie(t,r={}){let i=r.scriptPath??Me(),a=z(),o=K(t),s=o?.version===a&&o?.scriptPath===i;if(o&&s&&await Fe(o.endpoint))return o;o&&(q({endpoint:o.endpoint??null,pidFile:o.pidFile??null,logFile:o.logFile??null,sessionDir:o.sessionDir??null,pid:o.pid??null,killProcess:r.killProcess??null}),Pe(t));let c=ke(),l=(r.createBrokerEndpoint??Oe)(c,r.platform),u=n.join(c,`broker.pid`),d=n.join(c,`broker.log`),f=je({scriptPath:i,cwd:t,endpoint:l,pidFile:u,logFile:d,env:r.env??e.env});if(!await W(l,r.timeoutMs??2e3))return q({endpoint:l,pidFile:u,logFile:d,sessionDir:c,pid:f.pid??null,killProcess:r.killProcess??null}),null;let p={endpoint:l,pidFile:u,logFile:d,sessionDir:c,pid:f.pid??null,version:a,scriptPath:i};return Ne(t,p),p}function q({endpoint:e=null,pidFile:r,logFile:i,sessionDir:a=null,pid:o=null,killProcess:s=null}){if(Number.isFinite(o)&&s)try{s(o)}catch{}if(r&&t.existsSync(r)&&t.unlinkSync(r),i&&t.existsSync(i)&&t.unlinkSync(i),e)try{let n=U(e);n.kind===`unix`&&t.existsSync(n.path)&&t.unlinkSync(n.path)}catch{}let c=a??(r?n.dirname(r):i?n.dirname(i):null);if(c&&t.existsSync(c))try{t.rmdirSync(c)}catch{}}function J(t,n=[],r={}){let i=a(t,n,{cwd:r.cwd,env:r.env,encoding:`utf8`,input:r.input,maxBuffer:r.maxBuffer,stdio:r.stdio??`pipe`,shell:e.platform===`win32`?e.env.SHELL||!0:!1,windowsHide:!0});return{command:t,args:n,status:i.status??0,signal:i.signal??null,stdout:i.stdout??``,stderr:i.stderr??``,error:i.error??null}}function Le(e,t=[`--version`],n={}){let r=J(e,t,n);return r.error&&r.error.code===`ENOENT`?{available:!1,detail:`not found`}:r.error?{available:!1,detail:r.error.message}:r.status===0?{available:!0,detail:r.stdout.trim()||r.stderr.trim()||`ok`}:{available:!1,detail:r.stderr.trim()||r.stdout.trim()||`exit ${r.status}`}}function Re(e){return/not found|no running instance|cannot find|does not exist|no such process/i.test(e)}function ze(t,n={}){if(!Number.isFinite(t))return{attempted:!1,delivered:!1,method:null};let r=t,i=n.platform??e.platform,a=n.runCommandImpl??J,o=n.killImpl??e.kill.bind(e);if(i===`win32`){let e=a(`taskkill`,[`/PID`,String(r),`/T`,`/F`],{cwd:n.cwd,env:n.env});if(!e.error&&e.status===0)return{attempted:!0,delivered:!0,method:`taskkill`,result:e};let t=`${e.stderr}\n${e.stdout}`.trim();if(!e.error&&Re(t))return{attempted:!0,delivered:!1,method:`taskkill`,result:e};if(e.error?.code===`ENOENT`)try{return o(r),{attempted:!0,delivered:!0,method:`kill`}}catch(e){if(e?.code===`ESRCH`)return{attempted:!0,delivered:!1,method:`kill`};throw e}throw e.error?e.error:Error(Be(e))}try{return o(-r,`SIGTERM`),{attempted:!0,delivered:!0,method:`process-group`}}catch(e){if(e?.code!==`ESRCH`)try{return o(r,`SIGTERM`),{attempted:!0,delivered:!0,method:`process`}}catch(e){if(e?.code===`ESRCH`)return{attempted:!0,delivered:!1,method:`process`};throw e}return{attempted:!0,delivered:!1,method:`process-group`}}}function Be(e){let t=[`${e.command} ${e.args.join(` `)}`.trim()];e.signal?t.push(`signal=${e.signal}`):t.push(`exit=${e.status}`);let n=(e.stderr||``).trim(),r=(e.stdout||``).trim();return n?t.push(n):r&&t.push(r),t.join(`: `)}const Ve=`CODER_APP_SERVER_ENDPOINT`,He=-32001;let Y;function Ue(){if(Y===void 0){let e=J(`codex`,[`--version`]),t=/(\d+\.\d+\.\d+(?:-[\w.]+)?)/.exec(`${e.stdout} ${e.stderr}`);Y=t?t[1]:`0.144.1`}return Y}function X(){return{title:`Coder`,name:`coder`,version:Ue()}}const Z={experimentalApi:!1,requestAttestation:!1,optOutNotificationMethods:[`item/agentMessage/delta`,`item/reasoning/summaryTextDelta`,`item/reasoning/summaryPartAdded`,`item/reasoning/textDelta`]};function Q(e,t,n){return n===void 0?{code:e,message:t}:{code:e,message:t,data:n}}function $(e,t){let n=Error(e);return n.data=t,t?.code!==void 0&&(n.rpcCode=t.code),n}var We=class{cwd;options;pending;nextId;stderr;closed;exitError;notificationHandler;serverRequestHandler;lineBuffer;transport;exitPromise;exitResolved;resolveExit;handleServerRequest;constructor(e,t={}){this.cwd=e,this.options=t,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=e=>this.defaultHandleServerRequest(e),this.exitPromise=new Promise(e=>{this.resolveExit=e})}setNotificationHandler(e){this.notificationHandler=e}setServerRequestHandler(e){this.serverRequestHandler=e}request(e,t){if(this.closed)throw Error(`codex app-server client is closed.`);let n=this.nextId;return this.nextId+=1,new Promise((r,i)=>{this.pending.set(n,{resolve:r,reject:i,method:e}),this.sendMessage({id:n,method:e,params:t})})}notify(e,t={}){this.closed||this.sendMessage({method:e,params:t})}handleChunk(e){this.lineBuffer+=e;let t=this.lineBuffer.indexOf(`
2
+ `);for(;t!==-1;){let e=this.lineBuffer.slice(0,t);this.lineBuffer=this.lineBuffer.slice(t+1),this.handleLine(e),t=this.lineBuffer.indexOf(`
3
+ `)}}handleLine(e){if(!e.trim())return;let t;try{t=JSON.parse(e)}catch(t){this.handleExit($(`Failed to parse codex app-server JSONL: ${t instanceof Error?t.message:String(t)}`,{line:e}));return}if(t.id!==void 0&&t.method){this.handleServerRequest(t);return}if(t.id!==void 0){let e=this.pending.get(t.id);if(!e)return;this.pending.delete(t.id),t.error?e.reject($(t.error.message??`codex app-server ${e.method} failed.`,t.error)):e.resolve(t.result??{});return}t.method&&this.notificationHandler&&this.notificationHandler(t)}defaultHandleServerRequest(e){let t=this.serverRequestHandler;if(!t){this.sendMessage({id:e.id,error:Q(-32601,`Unsupported server request: ${e.method}`)});return}Promise.resolve().then(()=>t(e.method,e.params??{})).then(t=>{this.sendMessage({id:e.id,result:t??{}})}).catch(t=>{this.sendMessage({id:e.id,error:Q(-32e3,t instanceof Error?t.message:String(t))})})}handleExit(e){if(!this.exitResolved){this.exitResolved=!0,this.exitError=e??null;for(let e of this.pending.values())e.reject(this.exitError??Error(`codex app-server connection closed.`));this.pending.clear(),this.resolveExit(void 0)}}sendMessage(e){throw Error(`sendMessage must be implemented by subclasses.`)}},Ge=class extends We{proc;readline;constructor(e,t={}){super(e,t),this.transport=`direct`}async initialize(){this.proc=i(`codex`,[`-c`,`sandbox_workspace_write.network_access=false`,`app-server`,`--disable`,`plugins`],{cwd:this.cwd,env:this.options.env??e.env,stdio:[`pipe`,`pipe`,`pipe`],shell:e.platform===`win32`?e.env.SHELL||!0:!1,windowsHide:!0});let t=this.proc.stdout,n=this.proc.stderr;if(!t||!n)throw Error(`codex app-server did not expose stdio streams.`);t.setEncoding(`utf8`),n.setEncoding(`utf8`),n.on(`data`,e=>{this.stderr+=e}),this.proc.on(`error`,e=>{this.handleExit(e)}),this.proc.on(`exit`,(e,t)=>{let n=this.stderr.trim(),r=e===0?null:$(`codex app-server exited unexpectedly (${t?`signal ${t}`:`exit ${e}`}).${n?`\n${n}`:``}`);this.handleExit(r)}),this.readline=u.createInterface({input:t}),this.readline.on(`line`,e=>{this.handleLine(e)}),await this.request(`initialize`,{clientInfo:this.options.clientInfo??X(),capabilities:this.options.capabilities??Z}),this.notify(`initialized`,{})}async close(){if(this.closed){await this.exitPromise;return}this.closed=!0,this.readline&&this.readline.close(),this.proc&&!this.proc.killed&&(this.proc.stdin?.end(),setTimeout(()=>{if(this.proc&&!this.proc.killed&&this.proc.exitCode===null)if(e.platform===`win32`)try{ze(this.proc.pid)}catch{}else this.proc.kill(`SIGTERM`)},50).unref?.()),await this.exitPromise}sendMessage(e){let t=`${JSON.stringify(e)}\n`,n=this.proc?.stdin;if(!n)throw Error(`codex app-server stdin is not available.`);n.write(t)}},Ke=class extends We{endpoint;socket;constructor(e,t={}){super(e,t),this.transport=`broker`,this.endpoint=t.brokerEndpoint}async initialize(){await new Promise((e,t)=>{let n=U(this.endpoint);this.socket=l.createConnection({path:n.path}),this.socket.setEncoding(`utf8`),this.socket.on(`connect`,e),this.socket.on(`data`,e=>{this.handleChunk(e)}),this.socket.on(`error`,e=>{this.exitResolved||t(e),this.handleExit(e)}),this.socket.on(`close`,()=>{this.handleExit(this.exitError)})}),await this.request(`initialize`,{clientInfo:this.options.clientInfo??X(),capabilities:this.options.capabilities??Z}),this.notify(`initialized`,{})}async close(){if(this.closed){await this.exitPromise;return}this.closed=!0,this.socket&&this.socket.end(),await this.exitPromise}sendMessage(e){let t=`${JSON.stringify(e)}\n`,n=this.socket;if(!n)throw Error(`codex app-server broker connection is not connected.`);n.write(t)}},qe=class{static async connect(t,n={}){let r=null;n.disableBroker||(r=n.brokerEndpoint??n.env?.CODER_APP_SERVER_ENDPOINT??e.env.CODER_APP_SERVER_ENDPOINT??null,!r&&n.reuseExistingBroker&&(r=K(t)?.endpoint??null),!r&&!n.reuseExistingBroker&&(r=(await Ie(t,{env:n.env}))?.endpoint??null));let i=r?new Ke(t,{...n,brokerEndpoint:r}):new Ge(t,n);return await i.initialize(),i}};export{M as A,te as B,de as C,I as D,pe as E,T as F,ee as H,re as I,fe as L,ve as M,N,ue as O,_ as P,me as R,he as S,h as T,f as U,m as V,p as W,_e as _,U as a,ge as b,we as c,Te as d,be as f,ie as g,Se as h,Le as i,A as j,E as k,Ee as l,z as m,Ve as n,Ce as o,xe as p,qe as r,B as s,He as t,V as u,D as v,ae as w,oe as x,g as y,O as z};
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};