@routecraft/cli 0.7.2-canary-20261003210535 → 0.7.2
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/{acp-BQMPXMXQ.js → acp-JAOUSF3F.js} +3 -3
- package/dist/{acp-BQMPXMXQ.js.map → acp-JAOUSF3F.js.map} +1 -1
- package/dist/{chunk-5N7YMHE4.js → chunk-DJD3H3G5.js} +3 -3
- package/dist/{chunk-5N7YMHE4.js.map → chunk-DJD3H3G5.js.map} +1 -1
- package/dist/chunk-WCEUG34O.js +2 -0
- package/dist/chunk-WCEUG34O.js.map +1 -0
- package/dist/chunk-WYVHXXTI.js +4 -0
- package/dist/chunk-WYVHXXTI.js.map +1 -0
- package/dist/exec-LJZY4IXV.js +2 -0
- package/dist/{exec-3X7SDVNN.js.map → exec-LJZY4IXV.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/{ops-EJMP5OLL.js → ops-B6N4ATXZ.js} +2 -2
- package/dist/{ops-EJMP5OLL.js.map → ops-B6N4ATXZ.js.map} +1 -1
- package/dist/settings-XW6WNPDV.js +2 -0
- package/dist/{settings-TJKUAC35.js.map → settings-XW6WNPDV.js.map} +1 -1
- package/dist/tui/app.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-6CUX4CM4.js +0 -2
- package/dist/chunk-6CUX4CM4.js.map +0 -1
- package/dist/chunk-V56CL7XK.js +0 -4
- package/dist/chunk-V56CL7XK.js.map +0 -1
- package/dist/exec-3X7SDVNN.js +0 -2
- package/dist/settings-TJKUAC35.js +0 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {a as a$1,k as k$1}from'./chunk-
|
|
2
|
-
`))});return l.kind==="editor-closed"?{code:0}:l.kind==="editor-error"?{code:k$1.failed,error:`The editor's side of the pipe failed: ${b$2(l.error)}`}:{code:k$1.unreachable,error:`Could not reach ${h} (from the ${e(e$1.url)}): ${b$2(l.error)}`}}export{D as ACP_AGENT_HEADER,Q as acpCommand};//# sourceMappingURL=acp-
|
|
3
|
-
//# sourceMappingURL=acp-
|
|
1
|
+
import {a as a$1,k as k$1}from'./chunk-DJD3H3G5.js';import {d as d$1,b as b$1,e}from'./chunk-WYVHXXTI.js';import {b as b$2}from'./chunk-FFSMJDUF.js';import {Readable}from'stream';var T=[250,500,1e3,2e3,4e3,5e3],q=1e4,$=12,b=-32e3,A=-32002,P="craft-acp:",I=200,a={initialize:"initialize",newSession:"session/new",loadSession:"session/load",resumeSession:"session/resume",closeSession:"session/close",prompt:"session/prompt"};function E(r){return new g(r).run()}var g=class{constructor(e){this.options=e;this.editorWriter=e.editor.writable.getWriter(),this.delays=e.delays??T,this.handshakeTimeoutMs=e.handshakeTimeoutMs??q;}options;state="connected";everConnected=false;writer;editorWriter;editorChain=Promise.resolve();initialize;sessions=new Map;editorRequests=new Map;instanceRequests=new Set;ownRequests=new Map;queue=[];ownSequence=0;reconnecting=false;flushing=false;generation=0;lastFailureReason;delays;handshakeTimeoutMs;stopped=new AbortController;settle;settled=new Promise(e=>{this.settle=e;});async run(){try{this.attach(this.options.connect());}catch(e){return this.lost(e),this.settled}return this.readEditor(),this.settled}async readEditor(){try{await k(this.options.editor.readable,e=>this.fromEditor(e));}catch(e){this.editorClosed(true,e);return}this.editorClosed(false);}async fromEditor(e){if(this.state!=="done"){if(m(e)){let t=d(e.id);if(!this.instanceRequests.has(t))return;this.instanceRequests.delete(t),await this.toInstance(e);return}if(e.method===a.initialize&&u(e)&&(this.initialize=e),u(e)){let t=O(e);if(this.editorRequests.set(d(e.id),{id:e.id,method:e.method,...typeof t.sessionId=="string"?{sessionId:t.sessionId}:{},...typeof t.cwd=="string"?{cwd:t.cwd}:{}}),e.method===a.closeSession){let n=t.sessionId;typeof n=="string"&&this.sessions.delete(n);}}if(this.state!=="connected"||this.flushing){if(this.queue.length>=I){u(e)&&(this.editorRequests.delete(d(e.id)),this.toEditor({jsonrpc:"2.0",id:e.id,error:{code:b,message:`Lost the connection to the instance; too many messages queued during the outage, "${e.method}" was dropped rather than queued.`}}));return}this.queue.push(e);return}await this.toInstance(e);}}async toInstance(e){let t=this.writer;if(t===void 0){this.queue.unshift(e);return}try{await t.write(e);}catch(n){let i=u(e)?d(e.id):void 0;i!==void 0&&!this.editorRequests.has(i)||this.queue.unshift(e),this.lost(n);}}toEditor(e){this.editorChain=this.editorChain.then(()=>this.editorWriter.write(e)).catch(t=>this.editorClosed(true,t));}editorClosed(e,t){if(this.state!=="done"){this.state="done",this.stopped.abort();for(let n of this.ownRequests.values())n.reject(new Error("the editor closed"));this.ownRequests.clear(),this.writer?.close().catch(()=>{}),this.editorWriter.close().catch(()=>{}),this.settle(e?{kind:"editor-error",error:t}:{kind:"editor-closed"});}}attach(e){this.generation+=1;let t=this.generation;this.writer=e.writable.getWriter(),this.readInstance(e,t);}async readInstance(e,t){let n=new Error("the instance closed the connection");try{await k(e.readable,i=>{this.fromInstance(i,t);});}catch(i){n=i;}this.state==="done"||t!==this.generation||this.lost(n);}fromInstance(e,t){if(!(this.state==="done"||t!==this.generation)){if(this.everConnected=true,m(e)){let n=d(e.id),i=this.ownRequests.get(n);if(i!==void 0){this.ownRequests.delete(n),i.resolve(e);return}let s=this.editorRequests.get(n);this.editorRequests.delete(n),s!==void 0&&e.error===void 0&&this.noteAttached(s,e),this.toEditor(e);return}u(e)&&this.instanceRequests.add(d(e.id)),this.toEditor(e);}}noteAttached(e,t){if(e.cwd!==void 0){if(e.method===a.newSession){let n=t.result;typeof n?.sessionId=="string"&&this.sessions.set(n.sessionId,e.cwd);return}(e.method===a.loadSession||e.method===a.resumeSession)&&e.sessionId!==void 0&&this.sessions.set(e.sessionId,e.cwd);}}lost(e){if(this.state==="done"||this.state==="lost")return;if(!this.everConnected){this.state="done",this.stopped.abort(),this.editorWriter.close().catch(()=>{}),this.settle({kind:"unreachable",error:e});return}let t=this.state==="connecting";this.generation+=1,this.state="lost",this.writer?.abort().catch(()=>{}),this.writer=void 0;for(let n of this.ownRequests.values())n.reject(e);if(this.ownRequests.clear(),t){this.lastFailureReason=b$2(e);return}this.lastFailureReason=void 0,this.options.log(`Lost the connection to ${this.options.target}: ${b$2(e)}. Waiting for it to come back.`),this.failInFlight(),this.instanceRequests.clear(),this.reconnect();}failInFlight(){let e=new Set(this.queue.filter(u).map(t=>d(t.id)));for(let[t,n]of this.editorRequests)e.has(t)||(this.toEditor(n.method===a.prompt?{jsonrpc:"2.0",id:n.id,result:{stopReason:"cancelled"}}:{jsonrpc:"2.0",id:n.id,error:{code:b,message:`Lost the connection to the instance while "${n.method}" was in flight; the request was not answered.`}}),this.editorRequests.delete(t));}async reconnect(){if(this.reconnecting)return;this.reconnecting=true;let e=Date.now(),t=0;try{for(;this.state==="lost";){if(await C(this.delays[Math.min(t,this.delays.length-1)]??0,this.stopped.signal),this.state!=="lost")return;t+=1,t>1&&t%$===1&&this.options.log(`Still waiting for ${this.options.target} (attempt ${t}, ${S(e)})${this.lastFailureReason!==void 0?`: ${this.lastFailureReason}`:""}.`),this.state="connecting";try{let n=this.options.connect();this.attach(n),await _(this.handshake(),this.handshakeTimeoutMs);}catch(n){this.lost(n);continue}this.state==="connecting"&&(this.state="connected",this.options.log(`Reconnected to ${this.options.target} after ${t} attempt${t===1?"":"s"} (${S(e)}).`),await this.flush());}}finally{this.reconnecting=false;}}async handshake(){if(this.initialize===void 0)throw new Error("the editor never initialized");let e=await this.own(a.initialize,this.initialize.params);if(e.error!==void 0)throw new Error(`initialize was refused: ${e.error.message}`);for(let[t,n]of [...this.sessions]){let i=await this.own(a.resumeSession,{sessionId:t,cwd:n,mcpServers:[]});if(i.error!==void 0){if(i.error.code===A){this.sessions.delete(t),this.options.log(`Conversation ${t} is not on ${this.options.target} any more (${i.error.message}). Start a new one from the editor.`);continue}this.options.log(`Conversation ${t} could not be resumed on ${this.options.target} (${i.error.message}). It is kept, and the next reconnection tries again.`);}}}own(e,t){let n=this.writer;if(n===void 0)return Promise.reject(new Error("no transport"));this.ownSequence+=1;let i=`${P}${this.ownSequence}`,s=new Promise((h,p)=>{this.ownRequests.set(d(i),{resolve:h,reject:p});});return s.catch(()=>{}),n.write({jsonrpc:"2.0",id:i,method:e,params:t}).then(()=>s)}async flush(){this.flushing=true;try{for(;this.queue.length>0&&this.state==="connected";){let e=this.queue.shift();m(e)||await this.toInstance(e);}}finally{this.flushing=false;}}};async function k(r,e){let t=r.getReader();try{for(;;){let{done:n,value:i}=await t.read();if(n)return;await e(i);}}finally{t.releaseLock();}}function u(r){return typeof r.method=="string"&&r.id!==void 0&&r.id!==null}function m(r){return r.method===void 0&&r.id!==void 0&&r.id!==null&&("result"in r||"error"in r)}function d(r){return `${typeof r}:${String(r)}`}function O(r){return r.params!==null&&typeof r.params=="object"?r.params:{}}function S(r){return `${Math.round((Date.now()-r)/1e3)}s`}function C(r,e){return new Promise(t=>{if(e.aborted){t();return}let n=()=>{clearTimeout(i),e.removeEventListener("abort",n),t();},i=setTimeout(n,r);e.addEventListener("abort",n,{once:true});})}function _(r,e){return new Promise((t,n)=>{let i=setTimeout(()=>n(new Error(`the handshake took longer than ${e}ms`)),e);r.then(s=>{clearTimeout(i),t(s);},s=>{clearTimeout(i),n(s);});})}var W="/acp",D="Routecraft-Agent";function U(){return new WritableStream({write(r){return new Promise((e,t)=>{process.stdout.write(r,n=>n?t(n):e());})}})}function x(){return Readable.toWeb(process.stdin)}async function Q(r={}){let e$1;try{e$1=d$1(r);}catch(o){if(o instanceof b$1)return {code:2,error:o.message};throw o}let{ndJsonStream:t}=await import('@agentclientprotocol/sdk'),{createHttpStream:n,MemoryAcpCookieStore:i}=await import('@agentclientprotocol/sdk/experimental/http-client'),s={};if(e$1.token!==void 0){try{a$1(e$1);}catch(o){if(o instanceof b$1)return {code:2,error:o.message};throw o}s.Authorization=`Bearer ${e$1.token.value}`;}e$1.agent!==void 0&&(s[D]=e$1.agent.value);let h=`${e$1.url.value.replace(/\/+$/,"")}${W}`,p=new i,M=t(r.stdout??U(),r.stdin??x()),l=await E({editor:M,connect:()=>n(h,{headers:s,cookieStore:p}),target:h,log:r.stderr??(o=>process.stderr.write(`${o}
|
|
2
|
+
`))});return l.kind==="editor-closed"?{code:0}:l.kind==="editor-error"?{code:k$1.failed,error:`The editor's side of the pipe failed: ${b$2(l.error)}`}:{code:k$1.unreachable,error:`Could not reach ${h} (from the ${e(e$1.url)}): ${b$2(l.error)}`}}export{D as ACP_AGENT_HEADER,Q as acpCommand};//# sourceMappingURL=acp-JAOUSF3F.js.map
|
|
3
|
+
//# sourceMappingURL=acp-JAOUSF3F.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/acp-bridge.ts","../src/acp.ts"],"names":["DEFAULT_DELAYS_MS","DEFAULT_HANDSHAKE_TIMEOUT_MS","ATTEMPTS_PER_LOG","CONNECTION_LOST","RESOURCE_NOT_FOUND","OWN_ID_PREFIX","MAX_QUEUED","METHOD","runBridge","options","Bridge","resolve","error","readEach","message","isResponse","key","keyOf","isRequest","params","paramsOf","sessionId","writer","hadError","own","transport","generation","failure","request","response","result","wasConnecting","messageOf","stillQueued","startedAt","attempt","sleep","elapsed","withTimeout","initialized","cwd","resumed","method","id","reject","readable","each","reader","done","value","since","ms","signal","timer","promise","ACP_PATH","ACP_AGENT_HEADER","stdoutStream","chunk","stdinStream","Readable","acpCommand","settings","resolveSettings","SettingsError","ndJsonStream","createHttpStream","MemoryAcpCookieStore","headers","refuseClearTextBearer","url","cookieStore","editor","outcome","line","EXEC_EXIT","describeSource"],"mappings":"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","file":"acp-BQMPXMXQ.js","sourcesContent":["/**\n * The relay behind `craft acp`: a pipe that re-establishes itself.\n *\n * Every message crosses it verbatim while the instance is up. When the\n * instance goes away (a restart under an open editor window is the usual\n * case), the editor's side stays open and the relay reconnects: a fresh\n * transport, the editor's own `initialize` replayed, and `session/resume`\n * for every conversation the editor had attached, so the editor keeps\n * talking to the same conversation without knowing anything happened.\n *\n * The protocol does not replay what was in flight when the transport\n * died, so the relay answers for it: a `session/prompt` that was waiting\n * gets `cancelled` (the protocol's own word for a turn that ended with no\n * reply), any other request gets an error naming the outage, and an\n * answer the editor later gives to a request the dead connection made is\n * dropped rather than posted to a connection that never asked. Everything\n * the editor sends during the outage is queued and delivered once the\n * handshake on the new transport has completed.\n *\n * The first connection is different: an address nothing answers on is a\n * configuration mistake, and the relay reports it rather than waiting for\n * it to come right. Reconnection is for a connection that once worked.\n */\n\nimport { messageOf } from \"./util.js\";\n\n/** A JSON-RPC 2.0 message as it crosses the relay. */\nexport interface RpcMessage {\n readonly jsonrpc: \"2.0\";\n readonly id?: string | number | null;\n readonly method?: string;\n readonly params?: unknown;\n readonly result?: unknown;\n readonly error?: { code: number; message: string; data?: unknown };\n}\n\n/** One side of the relay: what it reads from and writes to. */\nexport interface BridgeTransport {\n readonly readable: ReadableStream<RpcMessage>;\n readonly writable: WritableStream<RpcMessage>;\n}\n\nexport interface BridgeOptions {\n /** The editor's side, open for the life of the relay. */\n readonly editor: BridgeTransport;\n /**\n * A fresh transport to the instance. Called once at the start and once\n * per reconnection attempt; a transport is never reused after it fails.\n */\n readonly connect: () => BridgeTransport;\n /** Where the instance is, for the diagnostics. */\n readonly target: string;\n /** One diagnostic line, to standard error in the command. */\n readonly log: (line: string) => void;\n /**\n * Delays between reconnection attempts, in milliseconds. The last one\n * repeats until the editor closes.\n */\n readonly delays?: readonly number[];\n /** How long one handshake may take before the attempt is abandoned. */\n readonly handshakeTimeoutMs?: number;\n}\n\n/** How the relay ended. */\nexport type BridgeOutcome =\n | { readonly kind: \"editor-closed\" }\n | { readonly kind: \"editor-error\"; readonly error: unknown }\n | { readonly kind: \"unreachable\"; readonly error: unknown };\n\n/** Doubling from a quarter second to five, which then repeats. */\nconst DEFAULT_DELAYS_MS: readonly number[] = [\n 250, 500, 1_000, 2_000, 4_000, 5_000,\n];\n\nconst DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;\n\n/** Attempts between \"still trying\" lines, so an outage does not fill stderr. */\nconst ATTEMPTS_PER_LOG = 12;\n\n/**\n * JSON-RPC's implementation-defined server error range starts here. The\n * protocol defines no code for a lost connection, and this is the one the\n * SDK's own `RequestError` family leaves free.\n */\nconst CONNECTION_LOST = -32000;\n\n/**\n * The protocol's resource-not-found code, which the instance answers a\n * `session/resume` with when it no longer holds the conversation.\n *\n * The one refusal the relay drops a conversation on. Any other means the\n * conversation may still exist on the instance and dropping it would lose\n * the person's work on a false premise, so it is kept and tried again on\n * the next reconnection.\n */\nconst RESOURCE_NOT_FOUND = -32002;\n\n/** The prefix on ids of requests the relay makes for itself. */\nconst OWN_ID_PREFIX = \"craft-acp:\";\n\n/** How many editor messages the relay holds while no transport can take them. */\nconst MAX_QUEUED = 200;\n\n/**\n * The methods the relay must understand to restore a connection after an\n * outage. Mirrors `@routecraft/ai`'s ACP mount; the CLI does not depend on\n * that package (see `ACP_AGENT_HEADER`'s own note in `acp.ts`), so this is\n * duplicated rather than shared, and is spelled once here rather than\n * scattered through the module.\n */\nconst METHOD = {\n initialize: \"initialize\",\n newSession: \"session/new\",\n loadSession: \"session/load\",\n resumeSession: \"session/resume\",\n closeSession: \"session/close\",\n prompt: \"session/prompt\",\n} as const;\n\ntype State = \"connected\" | \"connecting\" | \"lost\" | \"done\";\n\ninterface EditorRequest {\n readonly id: string | number;\n readonly method: string;\n readonly sessionId?: string;\n readonly cwd?: string;\n}\n\ninterface OwnRequest {\n resolve(message: RpcMessage): void;\n reject(error: unknown): void;\n}\n\n/** Run the relay until the editor closes its side, or the first connection fails. */\nexport function runBridge(options: BridgeOptions): Promise<BridgeOutcome> {\n return new Bridge(options).run();\n}\n\nclass Bridge {\n private state: State = \"connected\";\n private everConnected = false;\n private writer: WritableStreamDefaultWriter<RpcMessage> | undefined;\n private readonly editorWriter: WritableStreamDefaultWriter<RpcMessage>;\n private editorChain: Promise<void> = Promise.resolve();\n\n /** The editor's `initialize`, replayed on every reconnection. */\n private initialize: RpcMessage | undefined;\n /** Conversations the editor has attached, with the cwd it named. */\n private readonly sessions = new Map<string, string>();\n /** Editor requests the instance has not answered. */\n private readonly editorRequests = new Map<string, EditorRequest>();\n /** Instance requests the editor has not answered. */\n private readonly instanceRequests = new Set<string>();\n /** The relay's own handshake requests. */\n private readonly ownRequests = new Map<string, OwnRequest>();\n /** What the editor sent while no transport could take it. */\n private readonly queue: RpcMessage[] = [];\n private ownSequence = 0;\n private reconnecting = false;\n /**\n * True while {@link flush} is draining the outage queue. A message that\n * arrives from the editor during the drain must wait behind whatever is\n * still queued ahead of it, not overtake it by going straight to the\n * instance just because the state has already turned \"connected\".\n */\n private flushing = false;\n /**\n * Bumped on every `attach()` and on every connecting-phase failure. A\n * transport's read loop tags itself with the value current when it\n * started; if that value has moved on by the time the loop ends, this\n * transport has already been superseded and its failure must not be\n * acted on against whatever replaced it.\n */\n private generation = 0;\n /** The last reason a connecting-phase attempt failed, for the periodic line. */\n private lastFailureReason: string | undefined;\n\n private readonly delays: readonly number[];\n private readonly handshakeTimeoutMs: number;\n private readonly stopped = new AbortController();\n private settle!: (outcome: BridgeOutcome) => void;\n private readonly settled = new Promise<BridgeOutcome>((resolve) => {\n this.settle = resolve;\n });\n\n constructor(private readonly options: BridgeOptions) {\n this.editorWriter = options.editor.writable.getWriter();\n this.delays = options.delays ?? DEFAULT_DELAYS_MS;\n this.handshakeTimeoutMs =\n options.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS;\n }\n\n async run(): Promise<BridgeOutcome> {\n // A synchronous throw here (a malformed url, a transport already\n // locked) is the same \"address to check\" as a connection that never\n // answers, so it goes through the same `lost()` path rather than\n // rejecting this promise and leaving `acpCommand` without its\n // documented unreachable result.\n try {\n this.attach(this.options.connect());\n } catch (error: unknown) {\n this.lost(error);\n return this.settled;\n }\n void this.readEditor();\n return this.settled;\n }\n\n // ------------------------------------------------------------ the editor\n\n private async readEditor(): Promise<void> {\n try {\n await readEach(this.options.editor.readable, (message) =>\n this.fromEditor(message),\n );\n } catch (error: unknown) {\n // Reported once, by the caller building the command's result from\n // the `editor-error` outcome; a line here too would say the same\n // thing twice for a failure the process is exiting on regardless.\n this.editorClosed(true, error);\n return;\n }\n this.editorClosed(false);\n }\n\n private async fromEditor(message: RpcMessage): Promise<void> {\n if (this.state === \"done\") return;\n\n if (isResponse(message)) {\n // The editor answering the instance. An answer to a request a dead\n // connection made has nowhere to go: the connection that asked is\n // gone, and posting it to the new one would be posting an id it\n // never issued.\n const key = keyOf(message.id);\n if (!this.instanceRequests.has(key)) return;\n this.instanceRequests.delete(key);\n await this.toInstance(message);\n return;\n }\n\n if (message.method === METHOD.initialize && isRequest(message)) {\n this.initialize = message;\n }\n if (isRequest(message)) {\n const params = paramsOf(message);\n this.editorRequests.set(keyOf(message.id), {\n id: message.id,\n method: message.method,\n ...(typeof params[\"sessionId\"] === \"string\"\n ? { sessionId: params[\"sessionId\"] }\n : {}),\n ...(typeof params[\"cwd\"] === \"string\" ? { cwd: params[\"cwd\"] } : {}),\n });\n if (message.method === METHOD.closeSession) {\n const sessionId = params[\"sessionId\"];\n if (typeof sessionId === \"string\") this.sessions.delete(sessionId);\n }\n }\n\n if (this.state !== \"connected\" || this.flushing) {\n if (this.queue.length >= MAX_QUEUED) {\n if (isRequest(message)) {\n this.editorRequests.delete(keyOf(message.id));\n this.toEditor({\n jsonrpc: \"2.0\",\n id: message.id,\n error: {\n code: CONNECTION_LOST,\n message: `Lost the connection to the instance; too many messages queued during the outage, \"${message.method}\" was dropped rather than queued.`,\n },\n });\n }\n return;\n }\n this.queue.push(message);\n return;\n }\n await this.toInstance(message);\n }\n\n /**\n * Write to the current transport.\n *\n * A write that fails never reached the instance, so the message goes\n * back to the front of the queue for the next transport, and its\n * `editorRequests` entry is left exactly as it was: {@link failInFlight}\n * is what decides, from queue membership, which requests get an outage\n * answer, and this message is unshifted back before {@link lost} runs so\n * it is judged still-queued rather than in flight. The one exception is\n * a request the read side already found the same loss for first: its\n * entry is gone by the time this catch runs, and it must not be queued\n * again on top of the answer it already got.\n */\n private async toInstance(message: RpcMessage): Promise<void> {\n const writer = this.writer;\n if (writer === undefined) {\n this.queue.unshift(message);\n return;\n }\n try {\n await writer.write(message);\n } catch (error: unknown) {\n const key = isRequest(message) ? keyOf(message.id) : undefined;\n const alreadyAnswered =\n key !== undefined && !this.editorRequests.has(key);\n if (!alreadyAnswered) this.queue.unshift(message);\n this.lost(error);\n }\n }\n\n /**\n * A failed write means the editor's output pipe is broken; its input\n * side can stay open regardless, so nothing else would ever notice.\n * Settling here is what ends the relay instead of it running on,\n * silently dropping everything meant for an editor that can no longer\n * hear it. Already-settled is a safe no-op the guard in `editorClosed`\n * handles, including the ordinary case where this write lost a race\n * with the writer's own clean close.\n */\n private toEditor(message: RpcMessage): void {\n this.editorChain = this.editorChain\n .then(() => this.editorWriter.write(message))\n .catch((error: unknown) => this.editorClosed(true, error));\n }\n\n /**\n * The editor's side ended: cleanly (its stream closed, `hadError`\n * false) or not (its stream broke, `hadError` true). Either way there\n * is nothing left to relay to, so the teardown is the same; only the\n * outcome the command reports differs. `hadError` carries that,\n * separately from `error` itself: a rejection can be `undefined` (a\n * bare `controller.error()` is valid), so the error's own value can\n * never be what tells a genuine failure apart from a clean close.\n */\n private editorClosed(hadError: boolean, error?: unknown): void {\n if (this.state === \"done\") return;\n this.state = \"done\";\n this.stopped.abort();\n for (const own of this.ownRequests.values()) {\n own.reject(new Error(\"the editor closed\"));\n }\n this.ownRequests.clear();\n // Closing the transport's writable closes the transport, which ends\n // the readable the instance loop is reading, so both sides settle.\n void this.writer?.close().catch(() => undefined);\n void this.editorWriter.close().catch(() => undefined);\n this.settle(\n hadError ? { kind: \"editor-error\", error } : { kind: \"editor-closed\" },\n );\n }\n\n // ---------------------------------------------------------- the instance\n\n private attach(transport: BridgeTransport): void {\n this.generation += 1;\n const generation = this.generation;\n this.writer = transport.writable.getWriter();\n void this.readInstance(transport, generation);\n }\n\n private async readInstance(\n transport: BridgeTransport,\n generation: number,\n ): Promise<void> {\n let failure: unknown = new Error(\"the instance closed the connection\");\n try {\n await readEach(transport.readable, (message) => {\n this.fromInstance(message, generation);\n });\n } catch (error: unknown) {\n failure = error;\n }\n // A transport that has already been superseded by a newer `attach()`\n // (this one lost the race, or was explicitly aborted by `lost()`) must\n // not report its own end as a fresh loss against whatever replaced it.\n if (this.state === \"done\" || generation !== this.generation) return;\n this.lost(failure);\n }\n\n private fromInstance(message: RpcMessage, generation: number): void {\n if (this.state === \"done\" || generation !== this.generation) return;\n this.everConnected = true;\n\n if (isResponse(message)) {\n const key = keyOf(message.id);\n const own = this.ownRequests.get(key);\n if (own !== undefined) {\n this.ownRequests.delete(key);\n own.resolve(message);\n return;\n }\n const request = this.editorRequests.get(key);\n this.editorRequests.delete(key);\n if (request !== undefined && message.error === undefined) {\n this.noteAttached(request, message);\n }\n this.toEditor(message);\n return;\n }\n\n if (isRequest(message)) {\n this.instanceRequests.add(keyOf(message.id));\n }\n this.toEditor(message);\n }\n\n /** A conversation the editor now holds, from the request that attached it. */\n private noteAttached(request: EditorRequest, response: RpcMessage): void {\n if (request.cwd === undefined) return;\n if (request.method === METHOD.newSession) {\n const result = response.result as { sessionId?: unknown } | undefined;\n if (typeof result?.sessionId === \"string\") {\n this.sessions.set(result.sessionId, request.cwd);\n }\n return;\n }\n if (\n (request.method === METHOD.loadSession ||\n request.method === METHOD.resumeSession) &&\n request.sessionId !== undefined\n ) {\n this.sessions.set(request.sessionId, request.cwd);\n }\n }\n\n /**\n * The current transport is gone.\n *\n * Before the first message ever arrived, that is an address nothing\n * answers on, reported as such. During a handshake, it is one failed\n * attempt and the reconnection loop takes the next. Otherwise it is an\n * outage: what was in flight is answered for, and the loop starts.\n */\n private lost(error: unknown): void {\n if (this.state === \"done\" || this.state === \"lost\") return;\n\n if (!this.everConnected) {\n this.state = \"done\";\n this.stopped.abort();\n void this.editorWriter.close().catch(() => undefined);\n this.settle({ kind: \"unreachable\", error });\n return;\n }\n\n // Bumping the generation here, not only in `attach()`, means a\n // transport whose read loop is still unwinding when a *different*\n // failure (a write, or a handshake timeout) calls `lost()` first is\n // marked stale immediately, before its own `readInstance` callback\n // can run and act on a connection that is no longer current.\n const wasConnecting = this.state === \"connecting\";\n this.generation += 1;\n this.state = \"lost\";\n void this.writer?.abort().catch(() => undefined);\n this.writer = undefined;\n for (const own of this.ownRequests.values()) own.reject(error);\n this.ownRequests.clear();\n if (wasConnecting) {\n this.lastFailureReason = messageOf(error);\n return;\n }\n\n this.lastFailureReason = undefined;\n this.options.log(\n `Lost the connection to ${this.options.target}: ${messageOf(error)}. Waiting for it to come back.`,\n );\n this.failInFlight();\n this.instanceRequests.clear();\n void this.reconnect();\n }\n\n /**\n * Answer for every editor request the dead transport never will: one it\n * had already sent, or was in the middle of sending, when it died. A\n * request still waiting in the outage queue was never handed to this\n * transport at all; it stays registered and is answered fresh when a\n * later transport actually sends it, so it is left alone here rather\n * than answered twice.\n */\n private failInFlight(): void {\n const stillQueued = new Set(\n this.queue.filter(isRequest).map((message) => keyOf(message.id)),\n );\n for (const [key, request] of this.editorRequests) {\n if (stillQueued.has(key)) continue;\n this.toEditor(\n request.method === METHOD.prompt\n ? {\n jsonrpc: \"2.0\",\n id: request.id,\n result: { stopReason: \"cancelled\" },\n }\n : {\n jsonrpc: \"2.0\",\n id: request.id,\n error: {\n code: CONNECTION_LOST,\n message: `Lost the connection to the instance while \"${request.method}\" was in flight; the request was not answered.`,\n },\n },\n );\n this.editorRequests.delete(key);\n }\n }\n\n private async reconnect(): Promise<void> {\n if (this.reconnecting) return;\n this.reconnecting = true;\n const startedAt = Date.now();\n let attempt = 0;\n try {\n while (this.state === \"lost\") {\n await sleep(\n this.delays[Math.min(attempt, this.delays.length - 1)] ?? 0,\n this.stopped.signal,\n );\n if (this.state !== \"lost\") return;\n attempt += 1;\n if (attempt > 1 && attempt % ATTEMPTS_PER_LOG === 1) {\n this.options.log(\n `Still waiting for ${this.options.target} (attempt ${attempt}, ${elapsed(startedAt)})${this.lastFailureReason !== undefined ? `: ${this.lastFailureReason}` : \"\"}.`,\n );\n }\n this.state = \"connecting\";\n try {\n const transport = this.options.connect();\n this.attach(transport);\n await withTimeout(this.handshake(), this.handshakeTimeoutMs);\n } catch (error: unknown) {\n // Covers a `connect()`/`attach()` throw as well as a handshake\n // timeout; a transport failure surfaces through `readInstance`\n // instead and calls `lost()` itself, which this is a no-op\n // against once that has already moved the state on.\n this.lost(error);\n continue;\n }\n if (this.state !== \"connecting\") continue;\n this.state = \"connected\";\n this.options.log(\n `Reconnected to ${this.options.target} after ${attempt} attempt${attempt === 1 ? \"\" : \"s\"} (${elapsed(startedAt)}).`,\n );\n await this.flush();\n }\n } finally {\n this.reconnecting = false;\n }\n }\n\n /**\n * What a fresh transport needs before the editor's messages may cross\n * it: the editor's own `initialize`, then every conversation it had\n * attached, resumed rather than loaded so nothing is replayed onto a\n * screen that already shows it.\n */\n private async handshake(): Promise<void> {\n if (this.initialize === undefined) {\n throw new Error(\"the editor never initialized\");\n }\n const initialized = await this.own(\n METHOD.initialize,\n this.initialize.params,\n );\n if (initialized.error !== undefined) {\n throw new Error(`initialize was refused: ${initialized.error.message}`);\n }\n for (const [sessionId, cwd] of [...this.sessions]) {\n const resumed = await this.own(METHOD.resumeSession, {\n sessionId,\n cwd,\n mcpServers: [],\n });\n if (resumed.error === undefined) continue;\n if (resumed.error.code === RESOURCE_NOT_FOUND) {\n // The instance came back without it: a store that does not\n // outlive the process. Nothing to attach; the editor's next\n // message about it is refused by the instance, by name.\n this.sessions.delete(sessionId);\n this.options.log(\n `Conversation ${sessionId} is not on ${this.options.target} any more (${resumed.error.message}). Start a new one from the editor.`,\n );\n continue;\n }\n this.options.log(\n `Conversation ${sessionId} could not be resumed on ${this.options.target} (${resumed.error.message}). It is kept, and the next reconnection tries again.`,\n );\n }\n }\n\n private own(method: string, params: unknown): Promise<RpcMessage> {\n const writer = this.writer;\n if (writer === undefined) {\n return Promise.reject(new Error(\"no transport\"));\n }\n this.ownSequence += 1;\n const id = `${OWN_ID_PREFIX}${this.ownSequence}`;\n const response = new Promise<RpcMessage>((resolve, reject) => {\n this.ownRequests.set(keyOf(id), { resolve, reject });\n });\n // A transport that dies mid-write rejects the write and, through\n // `lost`, this response too; the caller sees the first and nobody\n // would otherwise see the second.\n response.catch(() => undefined);\n return writer\n .write({ jsonrpc: \"2.0\", id, method, params })\n .then(() => response);\n }\n\n /**\n * Deliver what the editor sent while no transport could take it, oldest\n * first. Writes straight to the transport rather than back through\n * {@link fromEditor}: each message was already registered when it was\n * first queued, and re-entering `fromEditor` would only requeue it,\n * since `flushing` holds every new arrival behind the drain.\n *\n * A response is the one shape dropped rather than replayed: it answers\n * an instance-initiated request, and `instanceRequests` is cleared on\n * every loss, so the new transport never made the request this would be\n * answering. Posting it anyway would be posting an id the new instance\n * never issued, the same case `fromEditor` itself already guards for a\n * response arriving live.\n */\n private async flush(): Promise<void> {\n this.flushing = true;\n try {\n while (this.queue.length > 0 && this.state === \"connected\") {\n const message = this.queue.shift() as RpcMessage;\n if (isResponse(message)) continue;\n await this.toInstance(message);\n }\n } finally {\n this.flushing = false;\n }\n }\n}\n\n// ------------------------------------------------------------------ helpers\n\n/** Every message a stream yields until it closes; a failed stream throws. */\nasync function readEach(\n readable: ReadableStream<RpcMessage>,\n each: (message: RpcMessage) => void | Promise<void>,\n): Promise<void> {\n const reader = readable.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) return;\n await each(value);\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction isRequest(\n message: RpcMessage,\n): message is RpcMessage & { id: string | number; method: string } {\n return (\n typeof message.method === \"string\" &&\n message.id !== undefined &&\n message.id !== null\n );\n}\n\nfunction isResponse(\n message: RpcMessage,\n): message is RpcMessage & { id: string | number } {\n return (\n message.method === undefined &&\n message.id !== undefined &&\n message.id !== null &&\n (\"result\" in message || \"error\" in message)\n );\n}\n\n/** The map key for a request or response id known not to be null. */\nfunction keyOf(id: string | number): string {\n return `${typeof id}:${String(id)}`;\n}\n\nfunction paramsOf(message: RpcMessage): Record<string, unknown> {\n return message.params !== null && typeof message.params === \"object\"\n ? (message.params as Record<string, unknown>)\n : {};\n}\n\nfunction elapsed(since: number): string {\n return `${Math.round((Date.now() - since) / 1000)}s`;\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const done = (): void => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const timer = setTimeout(done, ms);\n signal.addEventListener(\"abort\", done, { once: true });\n });\n}\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(`the handshake took longer than ${ms}ms`)),\n ms,\n );\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n","/**\n * `craft acp`: the pipe an editor runs.\n *\n * An editor starts one process and speaks the Agent Client Protocol to it\n * over its standard input and output. This is that process: newline-\n * delimited JSON on the editor side, Streamable HTTP on the instance side,\n * every message forwarded verbatim in both directions.\n *\n * It is a pipe that re-establishes itself. An editor keeps the process for\n * as long as its window is open and does not start another when the\n * instance behind it restarts, so the relay stays up through an outage\n * and reconnects on its own; what that involves is in `acp-bridge.ts`.\n *\n * It never starts an app. `craft start` owns running, which is what makes\n * one editor entry reach a laptop or a company instance by switching a\n * profile and changing nothing else.\n *\n * There is no login. A token in the profile authenticates, exactly as\n * `token:` does today for every other command, and an agent that\n * advertises no authentication method is conforming.\n */\n\nimport { Readable } from \"node:stream\";\n\nimport { runBridge, type BridgeTransport } from \"./acp-bridge.js\";\nimport {\n describeSource,\n resolveSettings,\n SettingsError,\n type SettingsOverrides,\n} from \"./settings.js\";\nimport { EXEC_EXIT } from \"./exec.js\";\nimport { refuseClearTextBearer } from \"./ops-client.js\";\nimport { messageOf } from \"./util.js\";\n\n/**\n * The path the protocol is mounted on.\n *\n * Not configurable from here on purpose: the mount's own `path` option is\n * the instance's to choose, and a person pointing an editor at an instance\n * that moved it says so with the full URL.\n */\nconst ACP_PATH = \"/acp\";\n\n/**\n * Request header naming the agent this connection wants to talk to.\n *\n * The protocol has no field for it, and inventing one in `_meta` would be\n * a private extension a conforming client could not be expected to send.\n * A header keeps the bridge a pipe: it adds one, exactly as it adds\n * `Authorization`.\n *\n * Spelled once here rather than shared with the mount, because the CLI\n * does not depend on `@routecraft/ai`: the mount exports the same constant\n * and a test pins the two together.\n */\nexport const ACP_AGENT_HEADER = \"Routecraft-Agent\";\n\n/** What `craft acp` answers with when it stops. */\nexport interface AcpResult {\n code: number;\n /** Written to standard error. Standard output belongs to the protocol. */\n error?: string;\n}\n\nexport interface AcpOptions extends SettingsOverrides {\n /** The editor's side of the pipe. Defaults to this process's streams. */\n stdin?: ReadableStream<Uint8Array>;\n stdout?: WritableStream<Uint8Array>;\n /**\n * Where a diagnostic line goes while the bridge runs: an outage, a\n * reconnection, a conversation the instance came back without. Standard\n * error by default; standard output belongs to the protocol.\n */\n stderr?: (line: string) => void;\n}\n\n/**\n * Standard output as a writable stream of bytes.\n *\n * Written through `process.stdout` rather than a Bun file handle: the\n * protocol's frames are small and frequent, and the node stream is what\n * flushes each one as it is written rather than when a buffer fills.\n */\nfunction stdoutStream(): WritableStream<Uint8Array> {\n return new WritableStream<Uint8Array>({\n write(chunk) {\n return new Promise<void>((resolve, reject) => {\n process.stdout.write(chunk, (error) =>\n error ? reject(error) : resolve(),\n );\n });\n },\n });\n}\n\n/**\n * Standard input as a readable stream of bytes.\n *\n * Through `node:stream` rather than a Bun-only global: the CLI runs on Bun\n * today, and there is no reason for the one line that reads a pipe to be\n * the thing that pins it there.\n */\nfunction stdinStream(): ReadableStream<Uint8Array> {\n // The node types describe a web stream of `any`; the bytes are what a\n // pipe carries either way, and the SDK reads them as bytes.\n return Readable.toWeb(process.stdin) as unknown as ReadableStream<Uint8Array>;\n}\n\n/**\n * The SDK types its transports by message shape rather than by\n * {@link BridgeTransport}; the relay reads only the JSON-RPC envelope, which\n * every shape carries. Routing both the editor pipe and the instance\n * connection through this one cast means an SDK shape change that drops\n * `readable`/`writable` fails to compile here, rather than silently at two\n * separate call sites.\n */\nfunction asBridgeTransport(transport: {\n readonly readable: ReadableStream<unknown>;\n readonly writable: WritableStream<never>;\n}): BridgeTransport {\n return transport as unknown as BridgeTransport;\n}\n\n/**\n * Run the bridge until the editor closes its side.\n *\n * Both halves are the SDK's own transports. The relay reads each message\n * only far enough to know what it is (a request, its answer, which\n * conversation it names), so a version of the protocol this build has\n * never heard of still passes through unchanged. An instance that drops\n * after the first exchange is waited for and reconnected to; one that\n * never answers at all is reported, because that is an address to check.\n */\nexport async function acpCommand(options: AcpOptions = {}): Promise<AcpResult> {\n let settings;\n try {\n settings = resolveSettings(options);\n } catch (error: unknown) {\n if (error instanceof SettingsError)\n return { code: 2, error: error.message };\n throw error;\n }\n\n const { ndJsonStream } = await import(\"@agentclientprotocol/sdk\");\n const { createHttpStream, MemoryAcpCookieStore } =\n await import(\"@agentclientprotocol/sdk/experimental/http-client\");\n\n const headers: Record<string, string> = {};\n if (settings.token !== undefined) {\n // The same refusal `craft exec` and `craft ops` make: a bearer does not\n // go over cleartext to anything but this machine. An editor holds this\n // connection open all day, so it is the longest-lived place a token\n // would be on the wire.\n try {\n refuseClearTextBearer(settings);\n } catch (error: unknown) {\n if (error instanceof SettingsError)\n return { code: 2, error: error.message };\n throw error;\n }\n headers[\"Authorization\"] = `Bearer ${settings.token.value}`;\n }\n if (settings.agent !== undefined) {\n headers[ACP_AGENT_HEADER] = settings.agent.value;\n }\n\n const url = `${settings.url.value.replace(/\\/+$/, \"\")}${ACP_PATH}`;\n // One cookie store for the process: the SDK uses it for routing\n // affinity, so a reconnect lands on the instance that holds the\n // conversation rather than on a sibling behind the same address.\n const cookieStore = new MemoryAcpCookieStore();\n const editor = ndJsonStream(\n options.stdout ?? stdoutStream(),\n options.stdin ?? stdinStream(),\n );\n\n const outcome = await runBridge({\n editor: asBridgeTransport(editor),\n connect: () =>\n asBridgeTransport(createHttpStream(url, { headers, cookieStore })),\n target: url,\n log: options.stderr ?? ((line) => process.stderr.write(`${line}\\n`)),\n });\n if (outcome.kind === \"editor-closed\") return { code: 0 };\n if (outcome.kind === \"editor-error\") {\n return {\n code: EXEC_EXIT.failed,\n error: `The editor's side of the pipe failed: ${messageOf(outcome.error)}`,\n };\n }\n // The family's code for an address nothing answered on, so a script\n // that already branches on `craft exec`'s exit codes reads this one\n // the same way.\n return {\n code: EXEC_EXIT.unreachable,\n error: `Could not reach ${url} (from the ${describeSource(settings.url)}): ${messageOf(outcome.error)}`,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/acp-bridge.ts","../src/acp.ts"],"names":["DEFAULT_DELAYS_MS","DEFAULT_HANDSHAKE_TIMEOUT_MS","ATTEMPTS_PER_LOG","CONNECTION_LOST","RESOURCE_NOT_FOUND","OWN_ID_PREFIX","MAX_QUEUED","METHOD","runBridge","options","Bridge","resolve","error","readEach","message","isResponse","key","keyOf","isRequest","params","paramsOf","sessionId","writer","hadError","own","transport","generation","failure","request","response","result","wasConnecting","messageOf","stillQueued","startedAt","attempt","sleep","elapsed","withTimeout","initialized","cwd","resumed","method","id","reject","readable","each","reader","done","value","since","ms","signal","timer","promise","ACP_PATH","ACP_AGENT_HEADER","stdoutStream","chunk","stdinStream","Readable","acpCommand","settings","resolveSettings","SettingsError","ndJsonStream","createHttpStream","MemoryAcpCookieStore","headers","refuseClearTextBearer","url","cookieStore","editor","outcome","line","EXEC_EXIT","describeSource"],"mappings":"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","file":"acp-JAOUSF3F.js","sourcesContent":["/**\n * The relay behind `craft acp`: a pipe that re-establishes itself.\n *\n * Every message crosses it verbatim while the instance is up. When the\n * instance goes away (a restart under an open editor window is the usual\n * case), the editor's side stays open and the relay reconnects: a fresh\n * transport, the editor's own `initialize` replayed, and `session/resume`\n * for every conversation the editor had attached, so the editor keeps\n * talking to the same conversation without knowing anything happened.\n *\n * The protocol does not replay what was in flight when the transport\n * died, so the relay answers for it: a `session/prompt` that was waiting\n * gets `cancelled` (the protocol's own word for a turn that ended with no\n * reply), any other request gets an error naming the outage, and an\n * answer the editor later gives to a request the dead connection made is\n * dropped rather than posted to a connection that never asked. Everything\n * the editor sends during the outage is queued and delivered once the\n * handshake on the new transport has completed.\n *\n * The first connection is different: an address nothing answers on is a\n * configuration mistake, and the relay reports it rather than waiting for\n * it to come right. Reconnection is for a connection that once worked.\n */\n\nimport { messageOf } from \"./util.js\";\n\n/** A JSON-RPC 2.0 message as it crosses the relay. */\nexport interface RpcMessage {\n readonly jsonrpc: \"2.0\";\n readonly id?: string | number | null;\n readonly method?: string;\n readonly params?: unknown;\n readonly result?: unknown;\n readonly error?: { code: number; message: string; data?: unknown };\n}\n\n/** One side of the relay: what it reads from and writes to. */\nexport interface BridgeTransport {\n readonly readable: ReadableStream<RpcMessage>;\n readonly writable: WritableStream<RpcMessage>;\n}\n\nexport interface BridgeOptions {\n /** The editor's side, open for the life of the relay. */\n readonly editor: BridgeTransport;\n /**\n * A fresh transport to the instance. Called once at the start and once\n * per reconnection attempt; a transport is never reused after it fails.\n */\n readonly connect: () => BridgeTransport;\n /** Where the instance is, for the diagnostics. */\n readonly target: string;\n /** One diagnostic line, to standard error in the command. */\n readonly log: (line: string) => void;\n /**\n * Delays between reconnection attempts, in milliseconds. The last one\n * repeats until the editor closes.\n */\n readonly delays?: readonly number[];\n /** How long one handshake may take before the attempt is abandoned. */\n readonly handshakeTimeoutMs?: number;\n}\n\n/** How the relay ended. */\nexport type BridgeOutcome =\n | { readonly kind: \"editor-closed\" }\n | { readonly kind: \"editor-error\"; readonly error: unknown }\n | { readonly kind: \"unreachable\"; readonly error: unknown };\n\n/** Doubling from a quarter second to five, which then repeats. */\nconst DEFAULT_DELAYS_MS: readonly number[] = [\n 250, 500, 1_000, 2_000, 4_000, 5_000,\n];\n\nconst DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;\n\n/** Attempts between \"still trying\" lines, so an outage does not fill stderr. */\nconst ATTEMPTS_PER_LOG = 12;\n\n/**\n * JSON-RPC's implementation-defined server error range starts here. The\n * protocol defines no code for a lost connection, and this is the one the\n * SDK's own `RequestError` family leaves free.\n */\nconst CONNECTION_LOST = -32000;\n\n/**\n * The protocol's resource-not-found code, which the instance answers a\n * `session/resume` with when it no longer holds the conversation.\n *\n * The one refusal the relay drops a conversation on. Any other means the\n * conversation may still exist on the instance and dropping it would lose\n * the person's work on a false premise, so it is kept and tried again on\n * the next reconnection.\n */\nconst RESOURCE_NOT_FOUND = -32002;\n\n/** The prefix on ids of requests the relay makes for itself. */\nconst OWN_ID_PREFIX = \"craft-acp:\";\n\n/** How many editor messages the relay holds while no transport can take them. */\nconst MAX_QUEUED = 200;\n\n/**\n * The methods the relay must understand to restore a connection after an\n * outage. Mirrors `@routecraft/ai`'s ACP mount; the CLI does not depend on\n * that package (see `ACP_AGENT_HEADER`'s own note in `acp.ts`), so this is\n * duplicated rather than shared, and is spelled once here rather than\n * scattered through the module.\n */\nconst METHOD = {\n initialize: \"initialize\",\n newSession: \"session/new\",\n loadSession: \"session/load\",\n resumeSession: \"session/resume\",\n closeSession: \"session/close\",\n prompt: \"session/prompt\",\n} as const;\n\ntype State = \"connected\" | \"connecting\" | \"lost\" | \"done\";\n\ninterface EditorRequest {\n readonly id: string | number;\n readonly method: string;\n readonly sessionId?: string;\n readonly cwd?: string;\n}\n\ninterface OwnRequest {\n resolve(message: RpcMessage): void;\n reject(error: unknown): void;\n}\n\n/** Run the relay until the editor closes its side, or the first connection fails. */\nexport function runBridge(options: BridgeOptions): Promise<BridgeOutcome> {\n return new Bridge(options).run();\n}\n\nclass Bridge {\n private state: State = \"connected\";\n private everConnected = false;\n private writer: WritableStreamDefaultWriter<RpcMessage> | undefined;\n private readonly editorWriter: WritableStreamDefaultWriter<RpcMessage>;\n private editorChain: Promise<void> = Promise.resolve();\n\n /** The editor's `initialize`, replayed on every reconnection. */\n private initialize: RpcMessage | undefined;\n /** Conversations the editor has attached, with the cwd it named. */\n private readonly sessions = new Map<string, string>();\n /** Editor requests the instance has not answered. */\n private readonly editorRequests = new Map<string, EditorRequest>();\n /** Instance requests the editor has not answered. */\n private readonly instanceRequests = new Set<string>();\n /** The relay's own handshake requests. */\n private readonly ownRequests = new Map<string, OwnRequest>();\n /** What the editor sent while no transport could take it. */\n private readonly queue: RpcMessage[] = [];\n private ownSequence = 0;\n private reconnecting = false;\n /**\n * True while {@link flush} is draining the outage queue. A message that\n * arrives from the editor during the drain must wait behind whatever is\n * still queued ahead of it, not overtake it by going straight to the\n * instance just because the state has already turned \"connected\".\n */\n private flushing = false;\n /**\n * Bumped on every `attach()` and on every connecting-phase failure. A\n * transport's read loop tags itself with the value current when it\n * started; if that value has moved on by the time the loop ends, this\n * transport has already been superseded and its failure must not be\n * acted on against whatever replaced it.\n */\n private generation = 0;\n /** The last reason a connecting-phase attempt failed, for the periodic line. */\n private lastFailureReason: string | undefined;\n\n private readonly delays: readonly number[];\n private readonly handshakeTimeoutMs: number;\n private readonly stopped = new AbortController();\n private settle!: (outcome: BridgeOutcome) => void;\n private readonly settled = new Promise<BridgeOutcome>((resolve) => {\n this.settle = resolve;\n });\n\n constructor(private readonly options: BridgeOptions) {\n this.editorWriter = options.editor.writable.getWriter();\n this.delays = options.delays ?? DEFAULT_DELAYS_MS;\n this.handshakeTimeoutMs =\n options.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS;\n }\n\n async run(): Promise<BridgeOutcome> {\n // A synchronous throw here (a malformed url, a transport already\n // locked) is the same \"address to check\" as a connection that never\n // answers, so it goes through the same `lost()` path rather than\n // rejecting this promise and leaving `acpCommand` without its\n // documented unreachable result.\n try {\n this.attach(this.options.connect());\n } catch (error: unknown) {\n this.lost(error);\n return this.settled;\n }\n void this.readEditor();\n return this.settled;\n }\n\n // ------------------------------------------------------------ the editor\n\n private async readEditor(): Promise<void> {\n try {\n await readEach(this.options.editor.readable, (message) =>\n this.fromEditor(message),\n );\n } catch (error: unknown) {\n // Reported once, by the caller building the command's result from\n // the `editor-error` outcome; a line here too would say the same\n // thing twice for a failure the process is exiting on regardless.\n this.editorClosed(true, error);\n return;\n }\n this.editorClosed(false);\n }\n\n private async fromEditor(message: RpcMessage): Promise<void> {\n if (this.state === \"done\") return;\n\n if (isResponse(message)) {\n // The editor answering the instance. An answer to a request a dead\n // connection made has nowhere to go: the connection that asked is\n // gone, and posting it to the new one would be posting an id it\n // never issued.\n const key = keyOf(message.id);\n if (!this.instanceRequests.has(key)) return;\n this.instanceRequests.delete(key);\n await this.toInstance(message);\n return;\n }\n\n if (message.method === METHOD.initialize && isRequest(message)) {\n this.initialize = message;\n }\n if (isRequest(message)) {\n const params = paramsOf(message);\n this.editorRequests.set(keyOf(message.id), {\n id: message.id,\n method: message.method,\n ...(typeof params[\"sessionId\"] === \"string\"\n ? { sessionId: params[\"sessionId\"] }\n : {}),\n ...(typeof params[\"cwd\"] === \"string\" ? { cwd: params[\"cwd\"] } : {}),\n });\n if (message.method === METHOD.closeSession) {\n const sessionId = params[\"sessionId\"];\n if (typeof sessionId === \"string\") this.sessions.delete(sessionId);\n }\n }\n\n if (this.state !== \"connected\" || this.flushing) {\n if (this.queue.length >= MAX_QUEUED) {\n if (isRequest(message)) {\n this.editorRequests.delete(keyOf(message.id));\n this.toEditor({\n jsonrpc: \"2.0\",\n id: message.id,\n error: {\n code: CONNECTION_LOST,\n message: `Lost the connection to the instance; too many messages queued during the outage, \"${message.method}\" was dropped rather than queued.`,\n },\n });\n }\n return;\n }\n this.queue.push(message);\n return;\n }\n await this.toInstance(message);\n }\n\n /**\n * Write to the current transport.\n *\n * A write that fails never reached the instance, so the message goes\n * back to the front of the queue for the next transport, and its\n * `editorRequests` entry is left exactly as it was: {@link failInFlight}\n * is what decides, from queue membership, which requests get an outage\n * answer, and this message is unshifted back before {@link lost} runs so\n * it is judged still-queued rather than in flight. The one exception is\n * a request the read side already found the same loss for first: its\n * entry is gone by the time this catch runs, and it must not be queued\n * again on top of the answer it already got.\n */\n private async toInstance(message: RpcMessage): Promise<void> {\n const writer = this.writer;\n if (writer === undefined) {\n this.queue.unshift(message);\n return;\n }\n try {\n await writer.write(message);\n } catch (error: unknown) {\n const key = isRequest(message) ? keyOf(message.id) : undefined;\n const alreadyAnswered =\n key !== undefined && !this.editorRequests.has(key);\n if (!alreadyAnswered) this.queue.unshift(message);\n this.lost(error);\n }\n }\n\n /**\n * A failed write means the editor's output pipe is broken; its input\n * side can stay open regardless, so nothing else would ever notice.\n * Settling here is what ends the relay instead of it running on,\n * silently dropping everything meant for an editor that can no longer\n * hear it. Already-settled is a safe no-op the guard in `editorClosed`\n * handles, including the ordinary case where this write lost a race\n * with the writer's own clean close.\n */\n private toEditor(message: RpcMessage): void {\n this.editorChain = this.editorChain\n .then(() => this.editorWriter.write(message))\n .catch((error: unknown) => this.editorClosed(true, error));\n }\n\n /**\n * The editor's side ended: cleanly (its stream closed, `hadError`\n * false) or not (its stream broke, `hadError` true). Either way there\n * is nothing left to relay to, so the teardown is the same; only the\n * outcome the command reports differs. `hadError` carries that,\n * separately from `error` itself: a rejection can be `undefined` (a\n * bare `controller.error()` is valid), so the error's own value can\n * never be what tells a genuine failure apart from a clean close.\n */\n private editorClosed(hadError: boolean, error?: unknown): void {\n if (this.state === \"done\") return;\n this.state = \"done\";\n this.stopped.abort();\n for (const own of this.ownRequests.values()) {\n own.reject(new Error(\"the editor closed\"));\n }\n this.ownRequests.clear();\n // Closing the transport's writable closes the transport, which ends\n // the readable the instance loop is reading, so both sides settle.\n void this.writer?.close().catch(() => undefined);\n void this.editorWriter.close().catch(() => undefined);\n this.settle(\n hadError ? { kind: \"editor-error\", error } : { kind: \"editor-closed\" },\n );\n }\n\n // ---------------------------------------------------------- the instance\n\n private attach(transport: BridgeTransport): void {\n this.generation += 1;\n const generation = this.generation;\n this.writer = transport.writable.getWriter();\n void this.readInstance(transport, generation);\n }\n\n private async readInstance(\n transport: BridgeTransport,\n generation: number,\n ): Promise<void> {\n let failure: unknown = new Error(\"the instance closed the connection\");\n try {\n await readEach(transport.readable, (message) => {\n this.fromInstance(message, generation);\n });\n } catch (error: unknown) {\n failure = error;\n }\n // A transport that has already been superseded by a newer `attach()`\n // (this one lost the race, or was explicitly aborted by `lost()`) must\n // not report its own end as a fresh loss against whatever replaced it.\n if (this.state === \"done\" || generation !== this.generation) return;\n this.lost(failure);\n }\n\n private fromInstance(message: RpcMessage, generation: number): void {\n if (this.state === \"done\" || generation !== this.generation) return;\n this.everConnected = true;\n\n if (isResponse(message)) {\n const key = keyOf(message.id);\n const own = this.ownRequests.get(key);\n if (own !== undefined) {\n this.ownRequests.delete(key);\n own.resolve(message);\n return;\n }\n const request = this.editorRequests.get(key);\n this.editorRequests.delete(key);\n if (request !== undefined && message.error === undefined) {\n this.noteAttached(request, message);\n }\n this.toEditor(message);\n return;\n }\n\n if (isRequest(message)) {\n this.instanceRequests.add(keyOf(message.id));\n }\n this.toEditor(message);\n }\n\n /** A conversation the editor now holds, from the request that attached it. */\n private noteAttached(request: EditorRequest, response: RpcMessage): void {\n if (request.cwd === undefined) return;\n if (request.method === METHOD.newSession) {\n const result = response.result as { sessionId?: unknown } | undefined;\n if (typeof result?.sessionId === \"string\") {\n this.sessions.set(result.sessionId, request.cwd);\n }\n return;\n }\n if (\n (request.method === METHOD.loadSession ||\n request.method === METHOD.resumeSession) &&\n request.sessionId !== undefined\n ) {\n this.sessions.set(request.sessionId, request.cwd);\n }\n }\n\n /**\n * The current transport is gone.\n *\n * Before the first message ever arrived, that is an address nothing\n * answers on, reported as such. During a handshake, it is one failed\n * attempt and the reconnection loop takes the next. Otherwise it is an\n * outage: what was in flight is answered for, and the loop starts.\n */\n private lost(error: unknown): void {\n if (this.state === \"done\" || this.state === \"lost\") return;\n\n if (!this.everConnected) {\n this.state = \"done\";\n this.stopped.abort();\n void this.editorWriter.close().catch(() => undefined);\n this.settle({ kind: \"unreachable\", error });\n return;\n }\n\n // Bumping the generation here, not only in `attach()`, means a\n // transport whose read loop is still unwinding when a *different*\n // failure (a write, or a handshake timeout) calls `lost()` first is\n // marked stale immediately, before its own `readInstance` callback\n // can run and act on a connection that is no longer current.\n const wasConnecting = this.state === \"connecting\";\n this.generation += 1;\n this.state = \"lost\";\n void this.writer?.abort().catch(() => undefined);\n this.writer = undefined;\n for (const own of this.ownRequests.values()) own.reject(error);\n this.ownRequests.clear();\n if (wasConnecting) {\n this.lastFailureReason = messageOf(error);\n return;\n }\n\n this.lastFailureReason = undefined;\n this.options.log(\n `Lost the connection to ${this.options.target}: ${messageOf(error)}. Waiting for it to come back.`,\n );\n this.failInFlight();\n this.instanceRequests.clear();\n void this.reconnect();\n }\n\n /**\n * Answer for every editor request the dead transport never will: one it\n * had already sent, or was in the middle of sending, when it died. A\n * request still waiting in the outage queue was never handed to this\n * transport at all; it stays registered and is answered fresh when a\n * later transport actually sends it, so it is left alone here rather\n * than answered twice.\n */\n private failInFlight(): void {\n const stillQueued = new Set(\n this.queue.filter(isRequest).map((message) => keyOf(message.id)),\n );\n for (const [key, request] of this.editorRequests) {\n if (stillQueued.has(key)) continue;\n this.toEditor(\n request.method === METHOD.prompt\n ? {\n jsonrpc: \"2.0\",\n id: request.id,\n result: { stopReason: \"cancelled\" },\n }\n : {\n jsonrpc: \"2.0\",\n id: request.id,\n error: {\n code: CONNECTION_LOST,\n message: `Lost the connection to the instance while \"${request.method}\" was in flight; the request was not answered.`,\n },\n },\n );\n this.editorRequests.delete(key);\n }\n }\n\n private async reconnect(): Promise<void> {\n if (this.reconnecting) return;\n this.reconnecting = true;\n const startedAt = Date.now();\n let attempt = 0;\n try {\n while (this.state === \"lost\") {\n await sleep(\n this.delays[Math.min(attempt, this.delays.length - 1)] ?? 0,\n this.stopped.signal,\n );\n if (this.state !== \"lost\") return;\n attempt += 1;\n if (attempt > 1 && attempt % ATTEMPTS_PER_LOG === 1) {\n this.options.log(\n `Still waiting for ${this.options.target} (attempt ${attempt}, ${elapsed(startedAt)})${this.lastFailureReason !== undefined ? `: ${this.lastFailureReason}` : \"\"}.`,\n );\n }\n this.state = \"connecting\";\n try {\n const transport = this.options.connect();\n this.attach(transport);\n await withTimeout(this.handshake(), this.handshakeTimeoutMs);\n } catch (error: unknown) {\n // Covers a `connect()`/`attach()` throw as well as a handshake\n // timeout; a transport failure surfaces through `readInstance`\n // instead and calls `lost()` itself, which this is a no-op\n // against once that has already moved the state on.\n this.lost(error);\n continue;\n }\n if (this.state !== \"connecting\") continue;\n this.state = \"connected\";\n this.options.log(\n `Reconnected to ${this.options.target} after ${attempt} attempt${attempt === 1 ? \"\" : \"s\"} (${elapsed(startedAt)}).`,\n );\n await this.flush();\n }\n } finally {\n this.reconnecting = false;\n }\n }\n\n /**\n * What a fresh transport needs before the editor's messages may cross\n * it: the editor's own `initialize`, then every conversation it had\n * attached, resumed rather than loaded so nothing is replayed onto a\n * screen that already shows it.\n */\n private async handshake(): Promise<void> {\n if (this.initialize === undefined) {\n throw new Error(\"the editor never initialized\");\n }\n const initialized = await this.own(\n METHOD.initialize,\n this.initialize.params,\n );\n if (initialized.error !== undefined) {\n throw new Error(`initialize was refused: ${initialized.error.message}`);\n }\n for (const [sessionId, cwd] of [...this.sessions]) {\n const resumed = await this.own(METHOD.resumeSession, {\n sessionId,\n cwd,\n mcpServers: [],\n });\n if (resumed.error === undefined) continue;\n if (resumed.error.code === RESOURCE_NOT_FOUND) {\n // The instance came back without it: a store that does not\n // outlive the process. Nothing to attach; the editor's next\n // message about it is refused by the instance, by name.\n this.sessions.delete(sessionId);\n this.options.log(\n `Conversation ${sessionId} is not on ${this.options.target} any more (${resumed.error.message}). Start a new one from the editor.`,\n );\n continue;\n }\n this.options.log(\n `Conversation ${sessionId} could not be resumed on ${this.options.target} (${resumed.error.message}). It is kept, and the next reconnection tries again.`,\n );\n }\n }\n\n private own(method: string, params: unknown): Promise<RpcMessage> {\n const writer = this.writer;\n if (writer === undefined) {\n return Promise.reject(new Error(\"no transport\"));\n }\n this.ownSequence += 1;\n const id = `${OWN_ID_PREFIX}${this.ownSequence}`;\n const response = new Promise<RpcMessage>((resolve, reject) => {\n this.ownRequests.set(keyOf(id), { resolve, reject });\n });\n // A transport that dies mid-write rejects the write and, through\n // `lost`, this response too; the caller sees the first and nobody\n // would otherwise see the second.\n response.catch(() => undefined);\n return writer\n .write({ jsonrpc: \"2.0\", id, method, params })\n .then(() => response);\n }\n\n /**\n * Deliver what the editor sent while no transport could take it, oldest\n * first. Writes straight to the transport rather than back through\n * {@link fromEditor}: each message was already registered when it was\n * first queued, and re-entering `fromEditor` would only requeue it,\n * since `flushing` holds every new arrival behind the drain.\n *\n * A response is the one shape dropped rather than replayed: it answers\n * an instance-initiated request, and `instanceRequests` is cleared on\n * every loss, so the new transport never made the request this would be\n * answering. Posting it anyway would be posting an id the new instance\n * never issued, the same case `fromEditor` itself already guards for a\n * response arriving live.\n */\n private async flush(): Promise<void> {\n this.flushing = true;\n try {\n while (this.queue.length > 0 && this.state === \"connected\") {\n const message = this.queue.shift() as RpcMessage;\n if (isResponse(message)) continue;\n await this.toInstance(message);\n }\n } finally {\n this.flushing = false;\n }\n }\n}\n\n// ------------------------------------------------------------------ helpers\n\n/** Every message a stream yields until it closes; a failed stream throws. */\nasync function readEach(\n readable: ReadableStream<RpcMessage>,\n each: (message: RpcMessage) => void | Promise<void>,\n): Promise<void> {\n const reader = readable.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) return;\n await each(value);\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction isRequest(\n message: RpcMessage,\n): message is RpcMessage & { id: string | number; method: string } {\n return (\n typeof message.method === \"string\" &&\n message.id !== undefined &&\n message.id !== null\n );\n}\n\nfunction isResponse(\n message: RpcMessage,\n): message is RpcMessage & { id: string | number } {\n return (\n message.method === undefined &&\n message.id !== undefined &&\n message.id !== null &&\n (\"result\" in message || \"error\" in message)\n );\n}\n\n/** The map key for a request or response id known not to be null. */\nfunction keyOf(id: string | number): string {\n return `${typeof id}:${String(id)}`;\n}\n\nfunction paramsOf(message: RpcMessage): Record<string, unknown> {\n return message.params !== null && typeof message.params === \"object\"\n ? (message.params as Record<string, unknown>)\n : {};\n}\n\nfunction elapsed(since: number): string {\n return `${Math.round((Date.now() - since) / 1000)}s`;\n}\n\nfunction sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const done = (): void => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const timer = setTimeout(done, ms);\n signal.addEventListener(\"abort\", done, { once: true });\n });\n}\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(`the handshake took longer than ${ms}ms`)),\n ms,\n );\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n","/**\n * `craft acp`: the pipe an editor runs.\n *\n * An editor starts one process and speaks the Agent Client Protocol to it\n * over its standard input and output. This is that process: newline-\n * delimited JSON on the editor side, Streamable HTTP on the instance side,\n * every message forwarded verbatim in both directions.\n *\n * It is a pipe that re-establishes itself. An editor keeps the process for\n * as long as its window is open and does not start another when the\n * instance behind it restarts, so the relay stays up through an outage\n * and reconnects on its own; what that involves is in `acp-bridge.ts`.\n *\n * It never starts an app. `craft start` owns running, which is what makes\n * one editor entry reach a laptop or a company instance by switching a\n * profile and changing nothing else.\n *\n * There is no login. A token in the profile authenticates, exactly as\n * `token:` does today for every other command, and an agent that\n * advertises no authentication method is conforming.\n */\n\nimport { Readable } from \"node:stream\";\n\nimport { runBridge, type BridgeTransport } from \"./acp-bridge.js\";\nimport {\n describeSource,\n resolveSettings,\n SettingsError,\n type SettingsOverrides,\n} from \"./settings.js\";\nimport { EXEC_EXIT } from \"./exec.js\";\nimport { refuseClearTextBearer } from \"./ops-client.js\";\nimport { messageOf } from \"./util.js\";\n\n/**\n * The path the protocol is mounted on.\n *\n * Not configurable from here on purpose: the mount's own `path` option is\n * the instance's to choose, and a person pointing an editor at an instance\n * that moved it says so with the full URL.\n */\nconst ACP_PATH = \"/acp\";\n\n/**\n * Request header naming the agent this connection wants to talk to.\n *\n * The protocol has no field for it, and inventing one in `_meta` would be\n * a private extension a conforming client could not be expected to send.\n * A header keeps the bridge a pipe: it adds one, exactly as it adds\n * `Authorization`.\n *\n * Spelled once here rather than shared with the mount, because the CLI\n * does not depend on `@routecraft/ai`: the mount exports the same constant\n * and a test pins the two together.\n */\nexport const ACP_AGENT_HEADER = \"Routecraft-Agent\";\n\n/** What `craft acp` answers with when it stops. */\nexport interface AcpResult {\n code: number;\n /** Written to standard error. Standard output belongs to the protocol. */\n error?: string;\n}\n\nexport interface AcpOptions extends SettingsOverrides {\n /** The editor's side of the pipe. Defaults to this process's streams. */\n stdin?: ReadableStream<Uint8Array>;\n stdout?: WritableStream<Uint8Array>;\n /**\n * Where a diagnostic line goes while the bridge runs: an outage, a\n * reconnection, a conversation the instance came back without. Standard\n * error by default; standard output belongs to the protocol.\n */\n stderr?: (line: string) => void;\n}\n\n/**\n * Standard output as a writable stream of bytes.\n *\n * Written through `process.stdout` rather than a Bun file handle: the\n * protocol's frames are small and frequent, and the node stream is what\n * flushes each one as it is written rather than when a buffer fills.\n */\nfunction stdoutStream(): WritableStream<Uint8Array> {\n return new WritableStream<Uint8Array>({\n write(chunk) {\n return new Promise<void>((resolve, reject) => {\n process.stdout.write(chunk, (error) =>\n error ? reject(error) : resolve(),\n );\n });\n },\n });\n}\n\n/**\n * Standard input as a readable stream of bytes.\n *\n * Through `node:stream` rather than a Bun-only global: the CLI runs on Bun\n * today, and there is no reason for the one line that reads a pipe to be\n * the thing that pins it there.\n */\nfunction stdinStream(): ReadableStream<Uint8Array> {\n // The node types describe a web stream of `any`; the bytes are what a\n // pipe carries either way, and the SDK reads them as bytes.\n return Readable.toWeb(process.stdin) as unknown as ReadableStream<Uint8Array>;\n}\n\n/**\n * The SDK types its transports by message shape rather than by\n * {@link BridgeTransport}; the relay reads only the JSON-RPC envelope, which\n * every shape carries. Routing both the editor pipe and the instance\n * connection through this one cast means an SDK shape change that drops\n * `readable`/`writable` fails to compile here, rather than silently at two\n * separate call sites.\n */\nfunction asBridgeTransport(transport: {\n readonly readable: ReadableStream<unknown>;\n readonly writable: WritableStream<never>;\n}): BridgeTransport {\n return transport as unknown as BridgeTransport;\n}\n\n/**\n * Run the bridge until the editor closes its side.\n *\n * Both halves are the SDK's own transports. The relay reads each message\n * only far enough to know what it is (a request, its answer, which\n * conversation it names), so a version of the protocol this build has\n * never heard of still passes through unchanged. An instance that drops\n * after the first exchange is waited for and reconnected to; one that\n * never answers at all is reported, because that is an address to check.\n */\nexport async function acpCommand(options: AcpOptions = {}): Promise<AcpResult> {\n let settings;\n try {\n settings = resolveSettings(options);\n } catch (error: unknown) {\n if (error instanceof SettingsError)\n return { code: 2, error: error.message };\n throw error;\n }\n\n const { ndJsonStream } = await import(\"@agentclientprotocol/sdk\");\n const { createHttpStream, MemoryAcpCookieStore } =\n await import(\"@agentclientprotocol/sdk/experimental/http-client\");\n\n const headers: Record<string, string> = {};\n if (settings.token !== undefined) {\n // The same refusal `craft exec` and `craft ops` make: a bearer does not\n // go over cleartext to anything but this machine. An editor holds this\n // connection open all day, so it is the longest-lived place a token\n // would be on the wire.\n try {\n refuseClearTextBearer(settings);\n } catch (error: unknown) {\n if (error instanceof SettingsError)\n return { code: 2, error: error.message };\n throw error;\n }\n headers[\"Authorization\"] = `Bearer ${settings.token.value}`;\n }\n if (settings.agent !== undefined) {\n headers[ACP_AGENT_HEADER] = settings.agent.value;\n }\n\n const url = `${settings.url.value.replace(/\\/+$/, \"\")}${ACP_PATH}`;\n // One cookie store for the process: the SDK uses it for routing\n // affinity, so a reconnect lands on the instance that holds the\n // conversation rather than on a sibling behind the same address.\n const cookieStore = new MemoryAcpCookieStore();\n const editor = ndJsonStream(\n options.stdout ?? stdoutStream(),\n options.stdin ?? stdinStream(),\n );\n\n const outcome = await runBridge({\n editor: asBridgeTransport(editor),\n connect: () =>\n asBridgeTransport(createHttpStream(url, { headers, cookieStore })),\n target: url,\n log: options.stderr ?? ((line) => process.stderr.write(`${line}\\n`)),\n });\n if (outcome.kind === \"editor-closed\") return { code: 0 };\n if (outcome.kind === \"editor-error\") {\n return {\n code: EXEC_EXIT.failed,\n error: `The editor's side of the pipe failed: ${messageOf(outcome.error)}`,\n };\n }\n // The family's code for an address nothing answered on, so a script\n // that already branches on `craft exec`'s exit codes reads this one\n // the same way.\n return {\n code: EXEC_EXIT.unreachable,\n error: `Could not reach ${url} (from the ${describeSource(settings.url)}): ${messageOf(outcome.error)}`,\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {b as b$1,e,d as d$1}from'./chunk-
|
|
1
|
+
import {b as b$1,e,d as d$1}from'./chunk-WYVHXXTI.js';import {b as b$2}from'./chunk-FFSMJDUF.js';import {isLoopbackHostname,OpsClientError,createOpsHttpClient}from'@routecraft/routecraft';export{OpsClientError as b}from'@routecraft/routecraft';function F(e$1){let t=new URL(e$1.url.value);if(!(t.protocol!=="http:"||isLoopbackHostname(t.hostname)))throw new b$1(`The instance URL from the ${e(e$1.url)} is ${e$1.url.value}, which is plain http, and the token from the ${e(e$1.token)} would travel over it as clear text. Use an https URL, or a loopback address (localhost, 127.0.0.1, ::1) for an instance on this machine.`)}function b(e$1){let t=e$1.token?.value;t!==void 0&&F(e$1);let n=e$1.url.value.replace(/\/+$/,"");return createOpsHttpClient({url:e$1.url.value,...t!==void 0?{token:t}:{},describeAddress:()=>`${n} (from the ${e(e$1.url)})`,advice:{missingCredential:"Put a token in .routecraft/settings.yaml, set CRAFT_TOKEN, or pass --token.",unreachable:"Start one with 'craft start', or point at another instance with --url."}})}function d(e){return JSON.stringify(e,null,2)}function w(e){return e==null?"":typeof e=="string"?e:JSON.stringify(e)}function R(e,t){if(t==="json")return d(e);if(t==="raw")return e.outcome==="completed"?w(e.body):e.outcome==="deferred"?d(e.deferral):w(e.message);if(e.outcome==="completed")return A(e.body);if(e.outcome==="dropped")return `Dropped: the route filtered this exchange out before it produced a result.
|
|
2
2
|
${e.message}`;let{deferral:n}=e,r=["Deferred: the route deferred this exchange and will finish it when it is resumed.",` deferral ${n.deferralId}`,` token ${n.token}`];return n.expiresAt!==void 0&&r.push(` expires ${n.expiresAt}`),r.push("","Resume it through the route the app exposes for that; the token is single-use and the framework mints no resume door of its own."),r.join(`
|
|
3
3
|
`)}function k(e,t){if(t==="json")return d({items:e});if(t==="raw")return e.map(i=>i.id).join(`
|
|
4
4
|
`);if(e.length===0)return "No routes.";let n=e.some(i=>i.enabled===false),r=e.map(i=>[i.id,i.dispatchable?"yes":"no",...n?[i.enabled?"yes":"no"]:[],i.sources.join(", "),i.title??i.description??""]);return S(["ROUTE","DISPATCHABLE",...n?["ENABLED"]:[],"SOURCES",""],r)}function B(e,t){if(t==="json")return d(e);if(t==="raw")return e.items.map(i=>i.id).join(`
|
|
@@ -23,5 +23,5 @@ Endpoint introspection was refused, so the list above is missing.
|
|
|
23
23
|
${I(r)}`}}}function I(e){return e.kind==="absent"?"The introspection tier is not enabled on this instance. Enable it in craft.config.ts with `ops: { tiers: { introspection: true } }`, or a scope string to gate it.":e.message}function E(e){return e.kind==="unreachable"?p.unreachable:e.kind==="refused"||e.kind==="absent"?p.refused:e.status===409?p.usage:p.failed}async function ne(e,t,n={}){let r=j(n);if(!r.ok)return {code:p.usage,error:r.error};let{client:i,format:s,settings:o}=r;if(e===void 0)return H(i,s);let u=n.stdin?.trim(),c;if(u!==void 0&&u.length>0){if(t.length>0)return {code:p.usage,error:"Route input arrived both on stdin and as --field=value arguments. Pass one or the other, so which one wins is never a question."};try{c=JSON.parse(u);}catch(a){return {code:p.usage,error:`Input on stdin must be JSON: ${b$2(a)}`}}}else {let a=T(t);if(a.error!==void 0)return {code:p.usage,error:a.error};c=a.body;}try{let a=await i.dispatch(e,c),y=R(a,s);return a.outcome==="dropped"?{code:p.failed,output:y}:{code:p.ok,output:y}}catch(a){if(!(a instanceof OpsClientError))throw a;return {code:E(a),error:P(a,e,o)}}}function P(e$1,t,n){let r=e$1.detail;return e$1.kind!=="absent"?C(e$1,r?.code,t,n):r?.code!==void 0?C(e$1,r.code,t,n):[`The instance at ${n.url.value} (from the ${e(n.url)}) did not accept a dispatch to "${t}".`,"","One of three things is true, and the instance deliberately does not say which:"," - the dispatch tier is not enabled: set `ops: { tiers: { dispatch: true } }` in craft.config.ts, or a scope string to gate it"," - a credential is needed to see the tier at all: pass --token, set CRAFT_TOKEN, or put one in .routecraft/settings.yaml",` - no route named "${t}" is registered here`,"","`craft ops routes` lists what this instance exposes, if introspection is enabled."].join(`
|
|
24
24
|
`)}function C(e$1,t,n,r){let i=e$1.message;return t===void 0?i:t==="RC5065"&&e$1.status===400?[i,"",`The payload did not match what "${n}" accepts. Its input schema:`,` craft ops routes ${n}`].join(`
|
|
25
25
|
`):[i,"",`The instance keeps the message in its own log: ${r.url.value} (from the ${e(r.url)}).`].join(`
|
|
26
|
-
`)}export{F as a,k as c,B as d,q as e,M as f,K as g,W as h,N as i,j,p as k,T as l,E as m,ne as n};//# sourceMappingURL=chunk-
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
26
|
+
`)}export{F as a,k as c,B as d,q as e,M as f,K as g,W as h,N as i,j,p as k,T as l,E as m,ne as n};//# sourceMappingURL=chunk-DJD3H3G5.js.map
|
|
27
|
+
//# sourceMappingURL=chunk-DJD3H3G5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ops-client.ts","../src/format.ts","../src/prepare.ts","../src/exec.ts"],"names":["refuseClearTextBearer","settings","url","isLoopbackHostname","SettingsError","describeSource","createOpsClient","token","base","createOpsHttpClient","asJson","value","asRaw","renderDispatch","outcome","format","prettyValue","deferral","lines","renderRoutes","routes","route","anyDisabled","rows","table","renderDeferrals","page","listing","renderDeferral","renderRouteDetail","health","view","combined","indent","renderComponent","viewNote","renderReport","report","componentLine","id","component","indicators","name","renderIndicators","entries","parts","details","key","text","line","headers","widths","header","column","row","render","cells","cell","prepare","overrides","client","resolveSettings","error","EXEC_EXIT","bodyFromArgs","args","body","index","arg","withoutDashes","equals","next","existing","introspect","staticHelp","OpsClientError","failureCode","introspectionBlame","execCommand","options","prepared","piped","messageOf","parsed","output","dispatchBlame","detail","withNextStep","code","message"],"mappings":"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","file":"chunk-5N7YMHE4.js","sourcesContent":["/**\n * The CLI's view of a running instance's ops server.\n *\n * The client itself lives in core (`createOpsHttpClient`), beside the\n * server it speaks to, because the remotes plugin drives the same wire\n * from inside a running context. What stays here is what only the CLI\n * knows: where the address and the token came from, and the remedies a\n * person at a terminal can act on.\n */\n\nimport {\n createOpsHttpClient,\n isLoopbackHostname,\n type OpsHttpClient,\n} from \"@routecraft/routecraft\";\nimport {\n describeSource,\n SettingsError,\n type ResolvedSettings,\n} from \"./settings.js\";\n\nexport {\n OpsClientError,\n type OpsFailureKind,\n type OpsHttpClient as OpsClient,\n} from \"@routecraft/routecraft\";\n\n/**\n * A bearer token over plain `http:` is readable by every hop between the\n * terminal and the instance, and the settings ladder makes it easy to pin\n * an `http://` address in a file and add a token from the environment\n * later without seeing the two together. Loopback is the exception: the\n * bytes never leave the machine, and it is the address `craft` defaults\n * to for an instance running here.\n */\nexport function refuseClearTextBearer(settings: ResolvedSettings): void {\n const url = new URL(settings.url.value);\n if (url.protocol !== \"http:\" || isLoopbackHostname(url.hostname)) return;\n throw new SettingsError(\n `The instance URL from the ${describeSource(settings.url)} is ${settings.url.value}, which is plain http, and the token from the ${describeSource(settings.token!)} would travel over it as clear text. ` +\n `Use an https URL, or a loopback address (localhost, 127.0.0.1, ::1) for an instance on this machine.`,\n );\n}\n\nexport function createOpsClient(settings: ResolvedSettings): OpsHttpClient {\n const token = settings.token?.value;\n if (token !== undefined) refuseClearTextBearer(settings);\n const base = settings.url.value.replace(/\\/+$/, \"\");\n return createOpsHttpClient({\n url: settings.url.value,\n ...(token !== undefined ? { token } : {}),\n describeAddress: () => `${base} (from the ${describeSource(settings.url)})`,\n advice: {\n missingCredential:\n \"Put a token in .routecraft/settings.yaml, set CRAFT_TOKEN, or pass --token.\",\n unreachable:\n \"Start one with 'craft start', or point at another instance with --url.\",\n },\n });\n}\n","/**\n * Rendering for `craft exec` and the `craft ops` family.\n *\n * Three formats, one meaning each. `pretty` is the default because the\n * commands are read by a person at a terminal far more often than they are\n * piped; `json` is the whole structured answer for a script; `raw` is the\n * bare payload with no envelope, so `craft exec greet --name=x | jq` works\n * without the caller unwrapping anything first.\n */\n\nimport type {\n HealthComponent,\n HealthReport,\n OpsDeferralSummary,\n OpsDispatchOutcome,\n OpsPage,\n OpsRouteDetail,\n OpsRouteSummary,\n} from \"@routecraft/routecraft\";\nimport type { OutputFormat } from \"./settings.js\";\n\n/** Serialise a whole structured answer. */\nexport function asJson(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n\n/**\n * The bare payload for `raw`.\n *\n * A string is printed as itself rather than as a quoted JSON string: the\n * point of `raw` is that the next program in the pipe reads the value, and\n * a shell reading `\"hello\"` with the quotes has to strip them.\n */\nexport function asRaw(value: unknown): string {\n if (value === undefined || value === null) return \"\";\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\n/** Render a dispatch outcome. */\nexport function renderDispatch(\n outcome: OpsDispatchOutcome,\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson(outcome);\n if (format === \"raw\") {\n if (outcome.outcome === \"completed\") return asRaw(outcome.body);\n if (outcome.outcome === \"deferred\") return asJson(outcome.deferral);\n return asRaw(outcome.message);\n }\n\n if (outcome.outcome === \"completed\") return prettyValue(outcome.body);\n if (outcome.outcome === \"dropped\") {\n // Named as a drop rather than a failure. A filter said no and there is\n // no response body, which is a different thing to go and look at from a\n // step that broke.\n return `Dropped: the route filtered this exchange out before it produced a result.\\n${outcome.message}`;\n }\n\n const { deferral } = outcome;\n const lines = [\n \"Deferred: the route deferred this exchange and will finish it when it is resumed.\",\n ` deferral ${deferral.deferralId}`,\n ` token ${deferral.token}`,\n ];\n if (deferral.expiresAt !== undefined) {\n lines.push(` expires ${deferral.expiresAt}`);\n }\n lines.push(\n \"\",\n \"Resume it through the route the app exposes for that; the token is single-use and the framework mints no resume door of its own.\",\n );\n return lines.join(\"\\n\");\n}\n\n/** Render the route collection. */\nexport function renderRoutes(\n routes: readonly OpsRouteSummary[],\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson({ items: routes });\n if (format === \"raw\") {\n return routes.map((route) => route.id).join(\"\\n\");\n }\n if (routes.length === 0) return \"No routes.\";\n\n // The ENABLED column only appears when something is actually disabled.\n // On the common all-enabled listing it would be a column of \"yes\".\n const anyDisabled = routes.some((route) => route.enabled === false);\n const rows = routes.map((route) => [\n route.id,\n route.dispatchable ? \"yes\" : \"no\",\n ...(anyDisabled ? [route.enabled ? \"yes\" : \"no\"] : []),\n route.sources.join(\", \"),\n route.title ?? route.description ?? \"\",\n ]);\n return table(\n [\n \"ROUTE\",\n \"DISPATCHABLE\",\n ...(anyDisabled ? [\"ENABLED\"] : []),\n \"SOURCES\",\n \"\",\n ],\n rows,\n );\n}\n\n/**\n * Render a page of deferrals.\n *\n * A page rather than a listing: an instance can hold every exchange it\n * ever deferred, so the answer says how to ask for the next screen\n * instead of pretending the first one is all of it.\n */\nexport function renderDeferrals(\n page: OpsPage<OpsDeferralSummary>,\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson(page);\n if (format === \"raw\") {\n return page.items.map((deferral) => deferral.id).join(\"\\n\");\n }\n // Phrased against the query rather than the instance: this page is\n // already narrowed by `--state` and `--route`, and \"nothing is deferred\"\n // would report an empty settled history as an idle instance.\n if (page.items.length === 0) return \"No deferrals match.\";\n\n const rows = page.items.map((deferral) => [\n deferral.id,\n deferral.routeId,\n // What it waits for, or what it was waiting for and how that ended.\n // One column because they are one question asked at two moments, and\n // splitting them would leave whichever half does not apply blank on\n // every row.\n deferral.state === \"waiting\"\n ? deferral.claimed\n ? `${deferral.waitingFor} (claimed)`\n : deferral.waitingFor\n : (deferral.outcome?.kind ?? \"settled\"),\n deferral.deferredAt,\n deferral.expiresAt ?? \"\",\n ]);\n const listing = table(\n [\"DEFERRAL\", \"ROUTE\", \"WAITING FOR\", \"DEFERRED\", \"EXPIRES\"],\n rows,\n );\n return page.nextCursor === undefined\n ? listing\n : `${listing}\\n\\nMore: --after ${page.nextCursor}`;\n}\n\n/** Render one deferral. */\nexport function renderDeferral(\n deferral: OpsDeferralSummary,\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson(deferral);\n // The whole record, not the id: describing one deferral is asked with the\n // id in hand, so answering with it back says nothing the caller did not\n // already type.\n if (format === \"raw\") return asRaw(deferral);\n const lines = [\n ` deferral ${deferral.id}`,\n ` route ${deferral.routeId}`,\n ` state ${deferral.state}`,\n ` waiting for ${deferral.waitingFor}`,\n ` claimed ${deferral.claimed ? \"yes\" : \"no\"}`,\n ` deferred ${deferral.deferredAt}`,\n ];\n if (deferral.expiresAt !== undefined) {\n lines.push(` expires ${deferral.expiresAt}`);\n }\n if (deferral.outcome !== undefined) {\n lines.push(\n ` outcome ${deferral.outcome.kind} at ${deferral.outcome.at}`,\n );\n if (deferral.outcome.reason !== undefined) {\n lines.push(` reason ${deferral.outcome.reason}`);\n }\n if (deferral.outcome.by !== undefined) {\n lines.push(` resumed by ${deferral.outcome.by.subject}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/** Render one route, optionally alongside its health component. */\nexport function renderRouteDetail(\n route: OpsRouteDetail | undefined,\n health: HealthComponent | undefined,\n format: OutputFormat,\n view: ViewNote,\n): string {\n const combined = {\n ...(route !== undefined ? { definition: route } : {}),\n ...(health !== undefined ? { health } : {}),\n };\n if (format === \"json\") return asJson(combined);\n if (format === \"raw\") return asJson(combined);\n\n const lines: string[] = [];\n if (route !== undefined) {\n lines.push(`Route ${route.id}`);\n if (route.title !== undefined) lines.push(`Title ${route.title}`);\n if (route.description !== undefined) {\n lines.push(`Description ${route.description}`);\n }\n lines.push(`Dispatchable ${route.dispatchable ? \"yes\" : \"no\"}`);\n lines.push(`Enabled ${route.enabled ? \"yes\" : \"no\"}`);\n lines.push(`Sources ${route.sources.join(\", \")}`);\n if (route.remote !== undefined) {\n lines.push(`Remote ${route.remote}`);\n }\n if (route.requiresPrincipal) {\n lines.push(`Authorize route entry requires a principal`);\n }\n if (route.tags !== undefined) {\n lines.push(`Tags ${route.tags.join(\", \")}`);\n }\n if (route.input?.body !== undefined) {\n lines.push(\"\", \"Input schema\", indent(asJson(route.input.body)));\n }\n }\n if (health !== undefined) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(\"Health\", indent(renderComponent(health)));\n }\n lines.push(\"\", viewNote(view));\n return lines.join(\"\\n\");\n}\n\n/** Render a health report. */\nexport function renderReport(\n report: HealthReport,\n format: OutputFormat,\n view: ViewNote,\n): string {\n if (format === \"json\") return asJson(report);\n if (format === \"raw\") return report.status;\n\n const lines = [\n `Status ${report.status}`,\n `View ${report.view}`,\n \"\",\n `context ${componentLine(report.context)}`,\n ];\n const routes = Object.entries(report.routes);\n if (routes.length > 0) {\n lines.push(\"\", \"Routes\");\n for (const [id, component] of routes) {\n lines.push(` ${id} ${componentLine(component)}`);\n }\n }\n const indicators = Object.entries(report.indicators);\n if (indicators.length > 0) {\n lines.push(\"\", \"Indicators\");\n for (const [name, component] of indicators) {\n lines.push(` ${name} ${componentLine(component)}`);\n }\n }\n lines.push(\"\", viewNote(view));\n return lines.join(\"\\n\");\n}\n\n/** Render the indicator map lifted off the aggregate report. */\nexport function renderIndicators(\n indicators: Record<string, HealthComponent>,\n format: OutputFormat,\n view: ViewNote,\n): string {\n if (format === \"json\") return asJson(indicators);\n if (format === \"raw\") {\n return Object.keys(indicators).join(\"\\n\");\n }\n const entries = Object.entries(indicators);\n if (entries.length === 0) return \"No indicators are registered.\";\n const lines = entries.map(\n ([name, component]) => `${name} ${componentLine(component)}`,\n );\n lines.push(\n \"\",\n \"Read from the indicators map on the health report; there is no separate indicator collection.\",\n viewNote(view),\n );\n return lines.join(\"\\n\");\n}\n\n/** Render one health component. */\nexport function renderComponent(\n component: HealthComponent,\n format: OutputFormat = \"pretty\",\n view?: ViewNote,\n): string {\n if (format === \"json\") return asJson(component);\n if (format === \"raw\") return component.status;\n const lines = [componentLine(component)];\n if (view !== undefined) lines.push(\"\", viewNote(view));\n return lines.join(\"\\n\");\n}\n\nfunction componentLine(component: HealthComponent): string {\n const parts = [component.status, `(${component.domain})`];\n if (component.ageMs !== undefined) {\n parts.push(`age ${String(component.ageMs)}ms`);\n }\n if (component.details !== undefined) {\n const details = Object.entries(component.details)\n .map(([key, value]) => `${key}=${String(value)}`)\n .join(\" \");\n if (details.length > 0) parts.push(details);\n }\n return parts.join(\" \");\n}\n\n/** Whether a credential was presented, so the reader knows which view this is. */\nexport type ViewNote = \"authenticated\" | \"anonymous\";\n\n/**\n * Say which view the reader is looking at.\n *\n * The unauthenticated answer is a thinner answer, not an error, and the\n * difference is invisible from the output alone: a component says\n * `degraded` either way, and only the authenticated view says `degraded`\n * because a breaker is open. Rendering a status with no reason and no note\n * would leave an operator believing they had the whole picture.\n */\nfunction viewNote(view: ViewNote): string {\n return view === \"authenticated\"\n ? \"Authenticated view: per-component details included where the instance exposes them.\"\n : \"Anonymous view: statuses only. Per-component details are withheld from callers with no credential; present one to see why a component is in the state it reports.\";\n}\n\nfunction prettyValue(value: unknown): string {\n if (value === undefined) return \"(no result body)\";\n return typeof value === \"string\" ? value : asJson(value);\n}\n\nfunction indent(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n}\n\n/** Fixed-width columns, padded to the widest cell. */\nfunction table(headers: string[], rows: string[][]): string {\n const widths = headers.map((header, column) =>\n Math.max(header.length, ...rows.map((row) => (row[column] ?? \"\").length)),\n );\n const render = (cells: string[]): string =>\n cells\n .map((cell, column) => cell.padEnd(widths[column] ?? 0))\n .join(\" \")\n .trimEnd();\n return [render(headers), ...rows.map(render)].join(\"\\n\");\n}\n","/**\n * The prelude every command that reaches an instance shares: resolve the\n * settings, name a settings problem, and build the client over them.\n *\n * One place rather than one per command because it is policy, not\n * boilerplate. A broken settings file must mean the same thing to `exec`\n * and to every `ops` command, and a failure that is not a settings problem\n * must surface as the crash it is on every one of them.\n */\n\nimport type { ViewNote } from \"./format.js\";\nimport { createOpsClient, type OpsClient } from \"./ops-client.js\";\nimport {\n resolveSettings,\n SettingsError,\n type OutputFormat,\n type ResolvedSettings,\n type SettingsOverrides,\n} from \"./settings.js\";\n\nexport type Prepared =\n | {\n ok: true;\n settings: ResolvedSettings;\n client: OpsClient;\n format: OutputFormat;\n /** Whether the reads will be authenticated, for the renderers that say so. */\n view: ViewNote;\n }\n | {\n /** A settings problem, in the operator's terms. Exit with the usage code. */\n ok: false;\n error: string;\n };\n\n/** Resolve the settings and build the client, or say why that failed. */\nexport function prepare(overrides: SettingsOverrides): Prepared {\n let settings: ResolvedSettings;\n let client: OpsClient;\n try {\n settings = resolveSettings(overrides);\n // The client refuses a settings combination the resolver accepts one\n // key at a time (a token over plain http); the same usage answer.\n client = createOpsClient(settings);\n } catch (error: unknown) {\n if (error instanceof SettingsError) {\n return { ok: false, error: error.message };\n }\n throw error;\n }\n return {\n ok: true,\n settings,\n client,\n format: settings.format.value,\n view: client.authenticated ? \"authenticated\" : \"anonymous\",\n };\n}\n","/**\n * `craft exec`: dispatch to a route on a running instance and print what\n * came back.\n *\n * The generic invoke-an-endpoint primitive, scriptable and composable. It\n * reaches an agent only through a route the app wrote, which is what gives\n * that path the app's own guardrails: the full pre-from chain runs, so\n * `.authorize()`, `.input()` and `.throttle()` all apply per message. That\n * is a property worth relying on rather than an accident of the plumbing.\n */\n\nimport { OpsClientError, type OpsClient } from \"./ops-client.js\";\nimport { renderDispatch, renderRoutes } from \"./format.js\";\nimport { prepare } from \"./prepare.js\";\nimport {\n describeSource,\n type ResolvedSettings,\n type OutputFormat,\n type SettingsOverrides,\n} from \"./settings.js\";\nimport { messageOf } from \"./util.js\";\n\n/**\n * Exit codes, which are the API for anything scripting this.\n *\n * A deferral shares `ok` with a completion because both mean the\n * instance accepted and answered; a deferral is an outcome, not a fault. A\n * refusal is separate from a failure because they need opposite actions:\n * one is about the credential, the other about the route.\n */\nexport const EXEC_EXIT = {\n ok: 0,\n /** The route failed, or dropped the exchange. */\n failed: 1,\n /** The command was used incorrectly. */\n usage: 2,\n /** No instance could be reached. */\n unreachable: 3,\n /** The door or the route's own `.authorize()` refused: missing, bad or expired credential, missing scope, an identity the route does not permit. */\n refused: 4,\n} as const;\n\nexport interface ExecResult {\n code: number;\n /** Written to stdout. */\n output?: string;\n /** Written to stderr. */\n error?: string;\n}\n\nexport interface ExecOptions extends SettingsOverrides {\n /** Body read from a pipe, when stdin was not a terminal. */\n stdin?: string;\n}\n\n/**\n * Turn trailing `--flag=value` arguments into a request body.\n *\n * Flat and predictable on purpose: a bare flag is `true`, a repeated flag\n * becomes an array, and everything else is the string the shell passed.\n * Nothing is coerced to a number, because a route that wants one declares\n * it with `.input()` and a CLI guessing types is a source of bugs that only\n * appear for values that look numeric.\n *\n * Anything richer than that goes in on stdin as JSON.\n */\nexport function bodyFromArgs(args: readonly string[]): {\n body: Record<string, unknown>;\n error?: string;\n} {\n const body: Record<string, unknown> = {};\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]!;\n if (!arg.startsWith(\"--\")) {\n return {\n body,\n error: `Unexpected argument \"${arg}\". Route input is passed as --field=value pairs, or as JSON on stdin.`,\n };\n }\n const withoutDashes = arg.slice(2);\n const equals = withoutDashes.indexOf(\"=\");\n let key: string;\n let value: unknown;\n if (equals >= 0) {\n key = withoutDashes.slice(0, equals);\n value = withoutDashes.slice(equals + 1);\n } else {\n key = withoutDashes;\n const next = args[index + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n value = next;\n index += 1;\n } else {\n value = true;\n }\n }\n if (key === \"\") {\n return { body, error: `\"${arg}\" is not a valid field name.` };\n }\n const existing = body[key];\n if (existing === undefined) {\n body[key] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n body[key] = [existing, value];\n }\n }\n return { body };\n}\n\n/**\n * List what this instance can be asked to run.\n *\n * A tier refusal is rendered as the refusal it is rather than swallowed\n * into an empty list: \"this instance exposes nothing\" and \"your credential\n * may not read the inventory\" call for different actions, and an empty list\n * posing as an answer would send the reader after the wrong one.\n */\nasync function introspect(\n client: OpsClient,\n format: OutputFormat,\n): Promise<ExecResult> {\n const staticHelp = [\n \"Usage: craft exec [options] <route> [--field=value ...]\",\n \"\",\n \"Options:\",\n \" --url <url> Instance ops server (default: the settings file, else http://127.0.0.1:8080)\",\n \" --token <token> Bearer credential presented at the management door\",\n \" --format <format> pretty (default), json, or raw\",\n \"\",\n \"Route input is passed as --field=value pairs after the route name, or as JSON on stdin.\",\n ].join(\"\\n\");\n\n try {\n const routes = await client.listRoutes({ dispatchable: true });\n return {\n code: EXEC_EXIT.ok,\n output: `${staticHelp}\\n\\nDispatchable routes on this instance:\\n\\n${renderRoutes(routes, format)}`,\n };\n } catch (error: unknown) {\n if (!(error instanceof OpsClientError)) throw error;\n return {\n code: failureCode(error),\n output: staticHelp,\n error: `\\nEndpoint introspection was refused, so the list above is missing.\\n${introspectionBlame(error)}`,\n };\n }\n}\n\nfunction introspectionBlame(error: OpsClientError): string {\n if (error.kind === \"absent\") {\n return \"The introspection tier is not enabled on this instance. Enable it in craft.config.ts with `ops: { tiers: { introspection: true } }`, or a scope string to gate it.\";\n }\n return error.message;\n}\n\nexport function failureCode(error: OpsClientError): number {\n if (error.kind === \"unreachable\") return EXEC_EXIT.unreachable;\n if (error.kind === \"refused\" || error.kind === \"absent\") {\n return EXEC_EXIT.refused;\n }\n // A route with no dispatch door is the command naming something that\n // cannot be asked, not a route that ran and failed. Nothing executed, so\n // reporting it as a failure would send a script's error path looking for\n // an exchange that never existed.\n if (error.status === 409) return EXEC_EXIT.usage;\n return EXEC_EXIT.failed;\n}\n\n/**\n * Run one dispatch.\n *\n * @param route - Route id to dispatch to, or undefined for `--help`\n * @param args - Trailing arguments that become the request body\n * @param options - Flags plus any piped stdin\n */\nexport async function execCommand(\n route: string | undefined,\n args: readonly string[],\n options: ExecOptions = {},\n): Promise<ExecResult> {\n const prepared = prepare(options);\n if (!prepared.ok) return { code: EXEC_EXIT.usage, error: prepared.error };\n const { client, format, settings } = prepared;\n\n if (route === undefined) return introspect(client, format);\n\n const piped = options.stdin?.trim();\n let body: unknown;\n if (piped !== undefined && piped.length > 0) {\n if (args.length > 0) {\n return {\n code: EXEC_EXIT.usage,\n error:\n \"Route input arrived both on stdin and as --field=value arguments. Pass one or the other, so which one wins is never a question.\",\n };\n }\n try {\n body = JSON.parse(piped);\n } catch (error: unknown) {\n return {\n code: EXEC_EXIT.usage,\n error: `Input on stdin must be JSON: ${messageOf(error)}`,\n };\n }\n } else {\n const parsed = bodyFromArgs(args);\n if (parsed.error !== undefined) {\n return { code: EXEC_EXIT.usage, error: parsed.error };\n }\n body = parsed.body;\n }\n\n try {\n const outcome = await client.dispatch(route, body);\n const output = renderDispatch(outcome, format);\n // A drop reached a terminal outcome without producing a result, so it\n // is reported as a non-zero exit alongside a failure while staying\n // visibly a different thing in the output.\n return outcome.outcome === \"dropped\"\n ? { code: EXEC_EXIT.failed, output }\n : { code: EXEC_EXIT.ok, output };\n } catch (error: unknown) {\n if (!(error instanceof OpsClientError)) throw error;\n return {\n code: failureCode(error),\n error: dispatchBlame(error, route, settings),\n };\n }\n}\n\n/**\n * Explain a dispatch that never ran.\n *\n * A 404 here is deliberately ambiguous on the wire: a disabled tier and an\n * unknown route answer alike so an unconfigured instance discloses nothing.\n * The client cannot resolve that, so it names every reading rather than\n * picking one and sending the reader down the wrong path.\n */\nfunction dispatchBlame(\n error: OpsClientError,\n route: string,\n settings: ResolvedSettings,\n): string {\n const detail = error.detail as\n { code?: string; message?: string } | undefined;\n if (error.kind !== \"absent\") {\n return withNextStep(error, detail?.code, route, settings);\n }\n if (detail?.code !== undefined) {\n return withNextStep(error, detail.code, route, settings);\n }\n return [\n `The instance at ${settings.url.value} (from the ${describeSource(settings.url)}) did not accept a dispatch to \"${route}\".`,\n \"\",\n \"One of three things is true, and the instance deliberately does not say which:\",\n \" - the dispatch tier is not enabled: set `ops: { tiers: { dispatch: true } }` in craft.config.ts, or a scope string to gate it\",\n \" - a credential is needed to see the tier at all: pass --token, set CRAFT_TOKEN, or put one in .routecraft/settings.yaml\",\n ` - no route named \"${route}\" is registered here`,\n \"\",\n \"`craft ops routes` lists what this instance exposes, if introspection is enabled.\",\n ].join(\"\\n\");\n}\n\n/**\n * Add the one line that turns a code into something to do next.\n *\n * The dispatch surface sends a framework error code and withholds the\n * message, deliberately: a route failure is whatever its steps threw, and\n * those messages carry hostnames, file paths and upstream response text.\n * That rule is sound and this does not touch it. What was missing is the\n * other half, because a reader handed `RC5031` and nothing else has no way\n * to know the sentence exists at all, let alone that it is in the\n * instance's log.\n *\n * `RC5065` answered with 400 gets a better answer than \"read the log\",\n * because the answer is local: the payload failed the route's own `.input()`\n * schema, and `craft ops routes <id>` prints that schema. An RC5065 answered\n * with 500 came from a route the dispatched one called, which the caller's\n * payload never reached, so it reads like any other route failure.\n */\nfunction withNextStep(\n error: OpsClientError,\n code: string | undefined,\n route: string,\n settings: ResolvedSettings,\n): string {\n const message = error.message;\n if (code === undefined) return message;\n if (code === \"RC5065\" && error.status === 400) {\n return [\n message,\n \"\",\n `The payload did not match what \"${route}\" accepts. Its input schema:`,\n ` craft ops routes ${route}`,\n ].join(\"\\n\");\n }\n return [\n message,\n \"\",\n `The instance keeps the message in its own log: ${settings.url.value} (from the ${describeSource(settings.url)}).`,\n ].join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ops-client.ts","../src/format.ts","../src/prepare.ts","../src/exec.ts"],"names":["refuseClearTextBearer","settings","url","isLoopbackHostname","SettingsError","describeSource","createOpsClient","token","base","createOpsHttpClient","asJson","value","asRaw","renderDispatch","outcome","format","prettyValue","deferral","lines","renderRoutes","routes","route","anyDisabled","rows","table","renderDeferrals","page","listing","renderDeferral","renderRouteDetail","health","view","combined","indent","renderComponent","viewNote","renderReport","report","componentLine","id","component","indicators","name","renderIndicators","entries","parts","details","key","text","line","headers","widths","header","column","row","render","cells","cell","prepare","overrides","client","resolveSettings","error","EXEC_EXIT","bodyFromArgs","args","body","index","arg","withoutDashes","equals","next","existing","introspect","staticHelp","OpsClientError","failureCode","introspectionBlame","execCommand","options","prepared","piped","messageOf","parsed","output","dispatchBlame","detail","withNextStep","code","message"],"mappings":"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","file":"chunk-DJD3H3G5.js","sourcesContent":["/**\n * The CLI's view of a running instance's ops server.\n *\n * The client itself lives in core (`createOpsHttpClient`), beside the\n * server it speaks to, because the remotes plugin drives the same wire\n * from inside a running context. What stays here is what only the CLI\n * knows: where the address and the token came from, and the remedies a\n * person at a terminal can act on.\n */\n\nimport {\n createOpsHttpClient,\n isLoopbackHostname,\n type OpsHttpClient,\n} from \"@routecraft/routecraft\";\nimport {\n describeSource,\n SettingsError,\n type ResolvedSettings,\n} from \"./settings.js\";\n\nexport {\n OpsClientError,\n type OpsFailureKind,\n type OpsHttpClient as OpsClient,\n} from \"@routecraft/routecraft\";\n\n/**\n * A bearer token over plain `http:` is readable by every hop between the\n * terminal and the instance, and the settings ladder makes it easy to pin\n * an `http://` address in a file and add a token from the environment\n * later without seeing the two together. Loopback is the exception: the\n * bytes never leave the machine, and it is the address `craft` defaults\n * to for an instance running here.\n */\nexport function refuseClearTextBearer(settings: ResolvedSettings): void {\n const url = new URL(settings.url.value);\n if (url.protocol !== \"http:\" || isLoopbackHostname(url.hostname)) return;\n throw new SettingsError(\n `The instance URL from the ${describeSource(settings.url)} is ${settings.url.value}, which is plain http, and the token from the ${describeSource(settings.token!)} would travel over it as clear text. ` +\n `Use an https URL, or a loopback address (localhost, 127.0.0.1, ::1) for an instance on this machine.`,\n );\n}\n\nexport function createOpsClient(settings: ResolvedSettings): OpsHttpClient {\n const token = settings.token?.value;\n if (token !== undefined) refuseClearTextBearer(settings);\n const base = settings.url.value.replace(/\\/+$/, \"\");\n return createOpsHttpClient({\n url: settings.url.value,\n ...(token !== undefined ? { token } : {}),\n describeAddress: () => `${base} (from the ${describeSource(settings.url)})`,\n advice: {\n missingCredential:\n \"Put a token in .routecraft/settings.yaml, set CRAFT_TOKEN, or pass --token.\",\n unreachable:\n \"Start one with 'craft start', or point at another instance with --url.\",\n },\n });\n}\n","/**\n * Rendering for `craft exec` and the `craft ops` family.\n *\n * Three formats, one meaning each. `pretty` is the default because the\n * commands are read by a person at a terminal far more often than they are\n * piped; `json` is the whole structured answer for a script; `raw` is the\n * bare payload with no envelope, so `craft exec greet --name=x | jq` works\n * without the caller unwrapping anything first.\n */\n\nimport type {\n HealthComponent,\n HealthReport,\n OpsDeferralSummary,\n OpsDispatchOutcome,\n OpsPage,\n OpsRouteDetail,\n OpsRouteSummary,\n} from \"@routecraft/routecraft\";\nimport type { OutputFormat } from \"./settings.js\";\n\n/** Serialise a whole structured answer. */\nexport function asJson(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n\n/**\n * The bare payload for `raw`.\n *\n * A string is printed as itself rather than as a quoted JSON string: the\n * point of `raw` is that the next program in the pipe reads the value, and\n * a shell reading `\"hello\"` with the quotes has to strip them.\n */\nexport function asRaw(value: unknown): string {\n if (value === undefined || value === null) return \"\";\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\n/** Render a dispatch outcome. */\nexport function renderDispatch(\n outcome: OpsDispatchOutcome,\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson(outcome);\n if (format === \"raw\") {\n if (outcome.outcome === \"completed\") return asRaw(outcome.body);\n if (outcome.outcome === \"deferred\") return asJson(outcome.deferral);\n return asRaw(outcome.message);\n }\n\n if (outcome.outcome === \"completed\") return prettyValue(outcome.body);\n if (outcome.outcome === \"dropped\") {\n // Named as a drop rather than a failure. A filter said no and there is\n // no response body, which is a different thing to go and look at from a\n // step that broke.\n return `Dropped: the route filtered this exchange out before it produced a result.\\n${outcome.message}`;\n }\n\n const { deferral } = outcome;\n const lines = [\n \"Deferred: the route deferred this exchange and will finish it when it is resumed.\",\n ` deferral ${deferral.deferralId}`,\n ` token ${deferral.token}`,\n ];\n if (deferral.expiresAt !== undefined) {\n lines.push(` expires ${deferral.expiresAt}`);\n }\n lines.push(\n \"\",\n \"Resume it through the route the app exposes for that; the token is single-use and the framework mints no resume door of its own.\",\n );\n return lines.join(\"\\n\");\n}\n\n/** Render the route collection. */\nexport function renderRoutes(\n routes: readonly OpsRouteSummary[],\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson({ items: routes });\n if (format === \"raw\") {\n return routes.map((route) => route.id).join(\"\\n\");\n }\n if (routes.length === 0) return \"No routes.\";\n\n // The ENABLED column only appears when something is actually disabled.\n // On the common all-enabled listing it would be a column of \"yes\".\n const anyDisabled = routes.some((route) => route.enabled === false);\n const rows = routes.map((route) => [\n route.id,\n route.dispatchable ? \"yes\" : \"no\",\n ...(anyDisabled ? [route.enabled ? \"yes\" : \"no\"] : []),\n route.sources.join(\", \"),\n route.title ?? route.description ?? \"\",\n ]);\n return table(\n [\n \"ROUTE\",\n \"DISPATCHABLE\",\n ...(anyDisabled ? [\"ENABLED\"] : []),\n \"SOURCES\",\n \"\",\n ],\n rows,\n );\n}\n\n/**\n * Render a page of deferrals.\n *\n * A page rather than a listing: an instance can hold every exchange it\n * ever deferred, so the answer says how to ask for the next screen\n * instead of pretending the first one is all of it.\n */\nexport function renderDeferrals(\n page: OpsPage<OpsDeferralSummary>,\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson(page);\n if (format === \"raw\") {\n return page.items.map((deferral) => deferral.id).join(\"\\n\");\n }\n // Phrased against the query rather than the instance: this page is\n // already narrowed by `--state` and `--route`, and \"nothing is deferred\"\n // would report an empty settled history as an idle instance.\n if (page.items.length === 0) return \"No deferrals match.\";\n\n const rows = page.items.map((deferral) => [\n deferral.id,\n deferral.routeId,\n // What it waits for, or what it was waiting for and how that ended.\n // One column because they are one question asked at two moments, and\n // splitting them would leave whichever half does not apply blank on\n // every row.\n deferral.state === \"waiting\"\n ? deferral.claimed\n ? `${deferral.waitingFor} (claimed)`\n : deferral.waitingFor\n : (deferral.outcome?.kind ?? \"settled\"),\n deferral.deferredAt,\n deferral.expiresAt ?? \"\",\n ]);\n const listing = table(\n [\"DEFERRAL\", \"ROUTE\", \"WAITING FOR\", \"DEFERRED\", \"EXPIRES\"],\n rows,\n );\n return page.nextCursor === undefined\n ? listing\n : `${listing}\\n\\nMore: --after ${page.nextCursor}`;\n}\n\n/** Render one deferral. */\nexport function renderDeferral(\n deferral: OpsDeferralSummary,\n format: OutputFormat,\n): string {\n if (format === \"json\") return asJson(deferral);\n // The whole record, not the id: describing one deferral is asked with the\n // id in hand, so answering with it back says nothing the caller did not\n // already type.\n if (format === \"raw\") return asRaw(deferral);\n const lines = [\n ` deferral ${deferral.id}`,\n ` route ${deferral.routeId}`,\n ` state ${deferral.state}`,\n ` waiting for ${deferral.waitingFor}`,\n ` claimed ${deferral.claimed ? \"yes\" : \"no\"}`,\n ` deferred ${deferral.deferredAt}`,\n ];\n if (deferral.expiresAt !== undefined) {\n lines.push(` expires ${deferral.expiresAt}`);\n }\n if (deferral.outcome !== undefined) {\n lines.push(\n ` outcome ${deferral.outcome.kind} at ${deferral.outcome.at}`,\n );\n if (deferral.outcome.reason !== undefined) {\n lines.push(` reason ${deferral.outcome.reason}`);\n }\n if (deferral.outcome.by !== undefined) {\n lines.push(` resumed by ${deferral.outcome.by.subject}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/** Render one route, optionally alongside its health component. */\nexport function renderRouteDetail(\n route: OpsRouteDetail | undefined,\n health: HealthComponent | undefined,\n format: OutputFormat,\n view: ViewNote,\n): string {\n const combined = {\n ...(route !== undefined ? { definition: route } : {}),\n ...(health !== undefined ? { health } : {}),\n };\n if (format === \"json\") return asJson(combined);\n if (format === \"raw\") return asJson(combined);\n\n const lines: string[] = [];\n if (route !== undefined) {\n lines.push(`Route ${route.id}`);\n if (route.title !== undefined) lines.push(`Title ${route.title}`);\n if (route.description !== undefined) {\n lines.push(`Description ${route.description}`);\n }\n lines.push(`Dispatchable ${route.dispatchable ? \"yes\" : \"no\"}`);\n lines.push(`Enabled ${route.enabled ? \"yes\" : \"no\"}`);\n lines.push(`Sources ${route.sources.join(\", \")}`);\n if (route.remote !== undefined) {\n lines.push(`Remote ${route.remote}`);\n }\n if (route.requiresPrincipal) {\n lines.push(`Authorize route entry requires a principal`);\n }\n if (route.tags !== undefined) {\n lines.push(`Tags ${route.tags.join(\", \")}`);\n }\n if (route.input?.body !== undefined) {\n lines.push(\"\", \"Input schema\", indent(asJson(route.input.body)));\n }\n }\n if (health !== undefined) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(\"Health\", indent(renderComponent(health)));\n }\n lines.push(\"\", viewNote(view));\n return lines.join(\"\\n\");\n}\n\n/** Render a health report. */\nexport function renderReport(\n report: HealthReport,\n format: OutputFormat,\n view: ViewNote,\n): string {\n if (format === \"json\") return asJson(report);\n if (format === \"raw\") return report.status;\n\n const lines = [\n `Status ${report.status}`,\n `View ${report.view}`,\n \"\",\n `context ${componentLine(report.context)}`,\n ];\n const routes = Object.entries(report.routes);\n if (routes.length > 0) {\n lines.push(\"\", \"Routes\");\n for (const [id, component] of routes) {\n lines.push(` ${id} ${componentLine(component)}`);\n }\n }\n const indicators = Object.entries(report.indicators);\n if (indicators.length > 0) {\n lines.push(\"\", \"Indicators\");\n for (const [name, component] of indicators) {\n lines.push(` ${name} ${componentLine(component)}`);\n }\n }\n lines.push(\"\", viewNote(view));\n return lines.join(\"\\n\");\n}\n\n/** Render the indicator map lifted off the aggregate report. */\nexport function renderIndicators(\n indicators: Record<string, HealthComponent>,\n format: OutputFormat,\n view: ViewNote,\n): string {\n if (format === \"json\") return asJson(indicators);\n if (format === \"raw\") {\n return Object.keys(indicators).join(\"\\n\");\n }\n const entries = Object.entries(indicators);\n if (entries.length === 0) return \"No indicators are registered.\";\n const lines = entries.map(\n ([name, component]) => `${name} ${componentLine(component)}`,\n );\n lines.push(\n \"\",\n \"Read from the indicators map on the health report; there is no separate indicator collection.\",\n viewNote(view),\n );\n return lines.join(\"\\n\");\n}\n\n/** Render one health component. */\nexport function renderComponent(\n component: HealthComponent,\n format: OutputFormat = \"pretty\",\n view?: ViewNote,\n): string {\n if (format === \"json\") return asJson(component);\n if (format === \"raw\") return component.status;\n const lines = [componentLine(component)];\n if (view !== undefined) lines.push(\"\", viewNote(view));\n return lines.join(\"\\n\");\n}\n\nfunction componentLine(component: HealthComponent): string {\n const parts = [component.status, `(${component.domain})`];\n if (component.ageMs !== undefined) {\n parts.push(`age ${String(component.ageMs)}ms`);\n }\n if (component.details !== undefined) {\n const details = Object.entries(component.details)\n .map(([key, value]) => `${key}=${String(value)}`)\n .join(\" \");\n if (details.length > 0) parts.push(details);\n }\n return parts.join(\" \");\n}\n\n/** Whether a credential was presented, so the reader knows which view this is. */\nexport type ViewNote = \"authenticated\" | \"anonymous\";\n\n/**\n * Say which view the reader is looking at.\n *\n * The unauthenticated answer is a thinner answer, not an error, and the\n * difference is invisible from the output alone: a component says\n * `degraded` either way, and only the authenticated view says `degraded`\n * because a breaker is open. Rendering a status with no reason and no note\n * would leave an operator believing they had the whole picture.\n */\nfunction viewNote(view: ViewNote): string {\n return view === \"authenticated\"\n ? \"Authenticated view: per-component details included where the instance exposes them.\"\n : \"Anonymous view: statuses only. Per-component details are withheld from callers with no credential; present one to see why a component is in the state it reports.\";\n}\n\nfunction prettyValue(value: unknown): string {\n if (value === undefined) return \"(no result body)\";\n return typeof value === \"string\" ? value : asJson(value);\n}\n\nfunction indent(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n}\n\n/** Fixed-width columns, padded to the widest cell. */\nfunction table(headers: string[], rows: string[][]): string {\n const widths = headers.map((header, column) =>\n Math.max(header.length, ...rows.map((row) => (row[column] ?? \"\").length)),\n );\n const render = (cells: string[]): string =>\n cells\n .map((cell, column) => cell.padEnd(widths[column] ?? 0))\n .join(\" \")\n .trimEnd();\n return [render(headers), ...rows.map(render)].join(\"\\n\");\n}\n","/**\n * The prelude every command that reaches an instance shares: resolve the\n * settings, name a settings problem, and build the client over them.\n *\n * One place rather than one per command because it is policy, not\n * boilerplate. A broken settings file must mean the same thing to `exec`\n * and to every `ops` command, and a failure that is not a settings problem\n * must surface as the crash it is on every one of them.\n */\n\nimport type { ViewNote } from \"./format.js\";\nimport { createOpsClient, type OpsClient } from \"./ops-client.js\";\nimport {\n resolveSettings,\n SettingsError,\n type OutputFormat,\n type ResolvedSettings,\n type SettingsOverrides,\n} from \"./settings.js\";\n\nexport type Prepared =\n | {\n ok: true;\n settings: ResolvedSettings;\n client: OpsClient;\n format: OutputFormat;\n /** Whether the reads will be authenticated, for the renderers that say so. */\n view: ViewNote;\n }\n | {\n /** A settings problem, in the operator's terms. Exit with the usage code. */\n ok: false;\n error: string;\n };\n\n/** Resolve the settings and build the client, or say why that failed. */\nexport function prepare(overrides: SettingsOverrides): Prepared {\n let settings: ResolvedSettings;\n let client: OpsClient;\n try {\n settings = resolveSettings(overrides);\n // The client refuses a settings combination the resolver accepts one\n // key at a time (a token over plain http); the same usage answer.\n client = createOpsClient(settings);\n } catch (error: unknown) {\n if (error instanceof SettingsError) {\n return { ok: false, error: error.message };\n }\n throw error;\n }\n return {\n ok: true,\n settings,\n client,\n format: settings.format.value,\n view: client.authenticated ? \"authenticated\" : \"anonymous\",\n };\n}\n","/**\n * `craft exec`: dispatch to a route on a running instance and print what\n * came back.\n *\n * The generic invoke-an-endpoint primitive, scriptable and composable. It\n * reaches an agent only through a route the app wrote, which is what gives\n * that path the app's own guardrails: the full pre-from chain runs, so\n * `.authorize()`, `.input()` and `.throttle()` all apply per message. That\n * is a property worth relying on rather than an accident of the plumbing.\n */\n\nimport { OpsClientError, type OpsClient } from \"./ops-client.js\";\nimport { renderDispatch, renderRoutes } from \"./format.js\";\nimport { prepare } from \"./prepare.js\";\nimport {\n describeSource,\n type ResolvedSettings,\n type OutputFormat,\n type SettingsOverrides,\n} from \"./settings.js\";\nimport { messageOf } from \"./util.js\";\n\n/**\n * Exit codes, which are the API for anything scripting this.\n *\n * A deferral shares `ok` with a completion because both mean the\n * instance accepted and answered; a deferral is an outcome, not a fault. A\n * refusal is separate from a failure because they need opposite actions:\n * one is about the credential, the other about the route.\n */\nexport const EXEC_EXIT = {\n ok: 0,\n /** The route failed, or dropped the exchange. */\n failed: 1,\n /** The command was used incorrectly. */\n usage: 2,\n /** No instance could be reached. */\n unreachable: 3,\n /** The door or the route's own `.authorize()` refused: missing, bad or expired credential, missing scope, an identity the route does not permit. */\n refused: 4,\n} as const;\n\nexport interface ExecResult {\n code: number;\n /** Written to stdout. */\n output?: string;\n /** Written to stderr. */\n error?: string;\n}\n\nexport interface ExecOptions extends SettingsOverrides {\n /** Body read from a pipe, when stdin was not a terminal. */\n stdin?: string;\n}\n\n/**\n * Turn trailing `--flag=value` arguments into a request body.\n *\n * Flat and predictable on purpose: a bare flag is `true`, a repeated flag\n * becomes an array, and everything else is the string the shell passed.\n * Nothing is coerced to a number, because a route that wants one declares\n * it with `.input()` and a CLI guessing types is a source of bugs that only\n * appear for values that look numeric.\n *\n * Anything richer than that goes in on stdin as JSON.\n */\nexport function bodyFromArgs(args: readonly string[]): {\n body: Record<string, unknown>;\n error?: string;\n} {\n const body: Record<string, unknown> = {};\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]!;\n if (!arg.startsWith(\"--\")) {\n return {\n body,\n error: `Unexpected argument \"${arg}\". Route input is passed as --field=value pairs, or as JSON on stdin.`,\n };\n }\n const withoutDashes = arg.slice(2);\n const equals = withoutDashes.indexOf(\"=\");\n let key: string;\n let value: unknown;\n if (equals >= 0) {\n key = withoutDashes.slice(0, equals);\n value = withoutDashes.slice(equals + 1);\n } else {\n key = withoutDashes;\n const next = args[index + 1];\n if (next !== undefined && !next.startsWith(\"--\")) {\n value = next;\n index += 1;\n } else {\n value = true;\n }\n }\n if (key === \"\") {\n return { body, error: `\"${arg}\" is not a valid field name.` };\n }\n const existing = body[key];\n if (existing === undefined) {\n body[key] = value;\n } else if (Array.isArray(existing)) {\n existing.push(value);\n } else {\n body[key] = [existing, value];\n }\n }\n return { body };\n}\n\n/**\n * List what this instance can be asked to run.\n *\n * A tier refusal is rendered as the refusal it is rather than swallowed\n * into an empty list: \"this instance exposes nothing\" and \"your credential\n * may not read the inventory\" call for different actions, and an empty list\n * posing as an answer would send the reader after the wrong one.\n */\nasync function introspect(\n client: OpsClient,\n format: OutputFormat,\n): Promise<ExecResult> {\n const staticHelp = [\n \"Usage: craft exec [options] <route> [--field=value ...]\",\n \"\",\n \"Options:\",\n \" --url <url> Instance ops server (default: the settings file, else http://127.0.0.1:8080)\",\n \" --token <token> Bearer credential presented at the management door\",\n \" --format <format> pretty (default), json, or raw\",\n \"\",\n \"Route input is passed as --field=value pairs after the route name, or as JSON on stdin.\",\n ].join(\"\\n\");\n\n try {\n const routes = await client.listRoutes({ dispatchable: true });\n return {\n code: EXEC_EXIT.ok,\n output: `${staticHelp}\\n\\nDispatchable routes on this instance:\\n\\n${renderRoutes(routes, format)}`,\n };\n } catch (error: unknown) {\n if (!(error instanceof OpsClientError)) throw error;\n return {\n code: failureCode(error),\n output: staticHelp,\n error: `\\nEndpoint introspection was refused, so the list above is missing.\\n${introspectionBlame(error)}`,\n };\n }\n}\n\nfunction introspectionBlame(error: OpsClientError): string {\n if (error.kind === \"absent\") {\n return \"The introspection tier is not enabled on this instance. Enable it in craft.config.ts with `ops: { tiers: { introspection: true } }`, or a scope string to gate it.\";\n }\n return error.message;\n}\n\nexport function failureCode(error: OpsClientError): number {\n if (error.kind === \"unreachable\") return EXEC_EXIT.unreachable;\n if (error.kind === \"refused\" || error.kind === \"absent\") {\n return EXEC_EXIT.refused;\n }\n // A route with no dispatch door is the command naming something that\n // cannot be asked, not a route that ran and failed. Nothing executed, so\n // reporting it as a failure would send a script's error path looking for\n // an exchange that never existed.\n if (error.status === 409) return EXEC_EXIT.usage;\n return EXEC_EXIT.failed;\n}\n\n/**\n * Run one dispatch.\n *\n * @param route - Route id to dispatch to, or undefined for `--help`\n * @param args - Trailing arguments that become the request body\n * @param options - Flags plus any piped stdin\n */\nexport async function execCommand(\n route: string | undefined,\n args: readonly string[],\n options: ExecOptions = {},\n): Promise<ExecResult> {\n const prepared = prepare(options);\n if (!prepared.ok) return { code: EXEC_EXIT.usage, error: prepared.error };\n const { client, format, settings } = prepared;\n\n if (route === undefined) return introspect(client, format);\n\n const piped = options.stdin?.trim();\n let body: unknown;\n if (piped !== undefined && piped.length > 0) {\n if (args.length > 0) {\n return {\n code: EXEC_EXIT.usage,\n error:\n \"Route input arrived both on stdin and as --field=value arguments. Pass one or the other, so which one wins is never a question.\",\n };\n }\n try {\n body = JSON.parse(piped);\n } catch (error: unknown) {\n return {\n code: EXEC_EXIT.usage,\n error: `Input on stdin must be JSON: ${messageOf(error)}`,\n };\n }\n } else {\n const parsed = bodyFromArgs(args);\n if (parsed.error !== undefined) {\n return { code: EXEC_EXIT.usage, error: parsed.error };\n }\n body = parsed.body;\n }\n\n try {\n const outcome = await client.dispatch(route, body);\n const output = renderDispatch(outcome, format);\n // A drop reached a terminal outcome without producing a result, so it\n // is reported as a non-zero exit alongside a failure while staying\n // visibly a different thing in the output.\n return outcome.outcome === \"dropped\"\n ? { code: EXEC_EXIT.failed, output }\n : { code: EXEC_EXIT.ok, output };\n } catch (error: unknown) {\n if (!(error instanceof OpsClientError)) throw error;\n return {\n code: failureCode(error),\n error: dispatchBlame(error, route, settings),\n };\n }\n}\n\n/**\n * Explain a dispatch that never ran.\n *\n * A 404 here is deliberately ambiguous on the wire: a disabled tier and an\n * unknown route answer alike so an unconfigured instance discloses nothing.\n * The client cannot resolve that, so it names every reading rather than\n * picking one and sending the reader down the wrong path.\n */\nfunction dispatchBlame(\n error: OpsClientError,\n route: string,\n settings: ResolvedSettings,\n): string {\n const detail = error.detail as\n { code?: string; message?: string } | undefined;\n if (error.kind !== \"absent\") {\n return withNextStep(error, detail?.code, route, settings);\n }\n if (detail?.code !== undefined) {\n return withNextStep(error, detail.code, route, settings);\n }\n return [\n `The instance at ${settings.url.value} (from the ${describeSource(settings.url)}) did not accept a dispatch to \"${route}\".`,\n \"\",\n \"One of three things is true, and the instance deliberately does not say which:\",\n \" - the dispatch tier is not enabled: set `ops: { tiers: { dispatch: true } }` in craft.config.ts, or a scope string to gate it\",\n \" - a credential is needed to see the tier at all: pass --token, set CRAFT_TOKEN, or put one in .routecraft/settings.yaml\",\n ` - no route named \"${route}\" is registered here`,\n \"\",\n \"`craft ops routes` lists what this instance exposes, if introspection is enabled.\",\n ].join(\"\\n\");\n}\n\n/**\n * Add the one line that turns a code into something to do next.\n *\n * The dispatch surface sends a framework error code and withholds the\n * message, deliberately: a route failure is whatever its steps threw, and\n * those messages carry hostnames, file paths and upstream response text.\n * That rule is sound and this does not touch it. What was missing is the\n * other half, because a reader handed `RC5031` and nothing else has no way\n * to know the sentence exists at all, let alone that it is in the\n * instance's log.\n *\n * `RC5065` answered with 400 gets a better answer than \"read the log\",\n * because the answer is local: the payload failed the route's own `.input()`\n * schema, and `craft ops routes <id>` prints that schema. An RC5065 answered\n * with 500 came from a route the dispatched one called, which the caller's\n * payload never reached, so it reads like any other route failure.\n */\nfunction withNextStep(\n error: OpsClientError,\n code: string | undefined,\n route: string,\n settings: ResolvedSettings,\n): string {\n const message = error.message;\n if (code === undefined) return message;\n if (code === \"RC5065\" && error.status === 400) {\n return [\n message,\n \"\",\n `The payload did not match what \"${route}\" accepts. Its input schema:`,\n ` craft ops routes ${route}`,\n ].join(\"\\n\");\n }\n return [\n message,\n \"\",\n `The instance keeps the message in its own log: ${settings.url.value} (from the ${describeSource(settings.url)}).`,\n ].join(\"\\n\");\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../package.json"],"names":["version"],"mappings":"AAEE,IAAAA,CAAAA,CAAW","file":"chunk-WCEUG34O.js","sourcesContent":["{\n \"name\": \"@routecraft/cli\",\n \"version\": \"0.7.2\",\n \"description\": \"CLI for running Routecraft capabilities\",\n \"private\": false,\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup src/index.ts src/tui/app.tsx src/tui/db.ts --format esm --dts --external react --external ink --external bun:sqlite\",\n \"test\": \"vitest\",\n \"test:coverage\": \"vitest --coverage\",\n \"prepublishOnly\": \"bun run build\"\n },\n \"dependencies\": {\n \"@agentclientprotocol/sdk\": \"^1.4.0\",\n \"@routecraft/routecraft\": \"^0.7.2\",\n \"commander\": \"^14.0.3\",\n \"dotenv\": \"^17.4.2\",\n \"ink\": \"^7.1.1\",\n \"pino-pretty\": \"^13.1.3\",\n \"react\": \"^19.2.0\",\n \"yaml\": \"^2.9.1\"\n },\n \"devDependencies\": {\n \"@types/mailparser\": \"^3.4.6\",\n \"@types/react\": \"^19.2.18\",\n \"ink-testing-library\": \"^4.0.0\",\n \"jose\": \"^6.2.12\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": \"https://github.com/routecraftjs/routecraft\",\n \"bin\": {\n \"craft\": \"./dist/index.js\"\n },\n \"engines\": {\n \"bun\": \">=1.1.0\"\n },\n \"keywords\": [\n \"routecraft\",\n \"cli\",\n \"routes\",\n \"automation\",\n \"typescript\"\n ],\n \"author\": \"routecraftjs\",\n \"license\": \"Apache-2.0\",\n \"homepage\": \"https://routecraft.dev\"\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import {b as b$1}from'./chunk-FFSMJDUF.js';import {homedir}from'os';import {join,resolve}from'path';import {statSync,existsSync,readFileSync}from'fs';import {parse}from'yaml';var I="http://127.0.0.1:8080",D=[join(".routecraft","settings.yaml"),join(".routecraft","settings.yml")];function T(e){let n=D.map(e),t=n.filter(r=>existsSync(r));if(t.length>1)throw new o(`Both ${t[0]} and ${t[1]} exist. Settings are read from exactly one file per location; keep one and remove the other.`);return t[0]??n[0]}var b=["pretty","json","raw"],G="CRAFT_URL",M="CRAFT_TOKEN",J="CRAFT_FORMAT",z="CRAFT_AGENT",B="CRAFT_PROFILE",o=class extends Error{};function c(e){let n=e?.trim();return n===void 0||n===""?void 0:n}function k(e){let n;try{n=readFileSync(e,"utf8");}catch(s){let i=s?.code;if(i==="ENOENT"||i==="ENOTDIR")return;throw new o(`${e} exists but could not be read: ${b$1(s)}`)}let t;try{t=parse(n);}catch(s){let i=b$1(s).split(`
|
|
2
|
+
`,1)[0];throw new o(`${e} is not valid YAML: ${i}`)}if(t==null)return {};if(typeof t!="object"||Array.isArray(t))throw new o(`${e} must contain a mapping of settings (for example \`url: http://127.0.0.1:8080\`).`);let r=t.profiles;if(r===null&&delete t.profiles,r!=null){if(!C(r))throw new o(`The "profiles" in ${e} must be a map of profile names to their settings.`);for(let[s,i]of Object.entries(r))if(i!=null&&!C(i))throw new o(`The profile "${s}" in ${e} must be a map of settings, not a single value.`)}return t}function Y(e,n){if(b.includes(e))return e;throw new o(`${n} must be one of ${b.join(", ")}; received "${e}".`)}function q(e){if(e===void 0)return;let n=resolve(process.cwd(),e),t=false;try{t=statSync(n,{throwIfNoEntry:!1})?.isDirectory()??!1;}catch{}if(!t)throw new o(`--project "${e}" is not a directory (resolved to ${n}).`);return n}function re(e={}){let n=e.cwd??q(e.project)??process.cwd(),t=e.env??process.env,r=T(g=>resolve(n,g)),s=e.home??homedir(),i=T(g=>join(s,g)),f=k(r),p=r===i?void 0:k(i),y=H(e.profile,t[B],{settings:f,path:r},{settings:p,path:i}),h=(g,L,j)=>{let S=c(L),$=c(j);return S!==void 0?{value:S,source:"flag"}:$!==void 0?{value:$,source:"environment"}:x(g,O(y,f,r,p,i))},a=h("url",e.url,t[G])??{value:I,source:"default"};if(typeof a.value!="string"||a.value.trim()==="")throw new o(`The instance URL from the ${u(a)} is empty. Give a full base URL, for example http://127.0.0.1:8080.`);let m;try{m=new URL(a.value);}catch{throw new o(`The instance URL from the ${u(a)} is not a URL: "${a.value}". Give a full base URL, for example http://127.0.0.1:8080.`)}if(m.protocol!=="http:"&&m.protocol!=="https:")throw new o(`The instance URL from the ${u(a)} uses "${m.protocol}"; the ops server is reached over http or https.`);let l=h("token",e.token,t[M]);if(l!==void 0&&typeof l.value!="string")throw new o(`The token from the ${l.source} must be a string.`);if(l!==void 0&&l.value.trim()==="")throw new o(`The token from the ${u(l)} is empty. Put a credential there, or remove the key.`);let v=h("format",e.format,t[J]),F=v===void 0?{value:"pretty",source:"default"}:{...v,value:Y(String(v.value),`The output format from the ${v.source}`)},d=h("agent",e.agent,t[z]);if(d!==void 0&&typeof d.value!="string")throw new o(`The agent name from the ${u(d)} must be a string.`);if(d!==void 0&&d.value.trim()==="")throw new o(`The agent name from the ${u(d)} is empty. Name an agent, or remove the key.`);let A=Q(y,f,r,p,i);return {url:a,token:l,format:F,agent:d,profile:y,env:A}}function H(e,n,t,r){let s=new Set([...Object.keys(t.settings?.profiles??{}),...Object.keys(r.settings?.profiles??{})]),i=()=>s.size===0?"no profiles are defined in either settings file":`defined profiles: ${[...s].sort().join(", ")}`,f=c(e)!==void 0?{value:c(e),source:"flag"}:c(n)!==void 0?{value:c(n),source:"environment"}:t.settings?.profile!==void 0?{value:t.settings.profile,source:"project file",path:t.path}:r.settings?.profile!==void 0?{value:r.settings.profile,source:"global file",path:r.path}:void 0;if(f!==void 0){if(typeof f.value!="string"||f.value.trim()==="")throw new o(`The profile from the ${u(f)} is not a name; ${i()}.`);if(!s.has(f.value))throw new o(`No profile "${f.value}" (named by the ${u(f)}); ${i()}.`);return f}}function N(e,n){if(e===void 0||n===void 0)return;let t=e.profiles;if(t!==void 0)return Object.prototype.hasOwnProperty.call(t,n)?t[n]:void 0}function Q(e,n,t,r,s){let i=x("env",O(e,n,t,r,s));if(i===void 0)return;let{value:f}=i,p=typeof f=="string"&&f.trim()==="";if(typeof f!="string"&&!W(f)||p)throw new o(`The "env" from the ${u(i)} must be a path to an env file or a map of names to string values.`);return i}function x(e,n){for(let t of n){let r=t.values?.[e];if(r!==void 0)return {value:r,source:t.source,path:t.path}}}function O(e,n,t,r,s){return [{values:N(n,e?.value),source:"project profile",path:t},{values:n,source:"project file",path:t},{values:N(r,e?.value),source:"global profile",path:s},{values:r,source:"global file",path:s}]}function C(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function W(e){return e===null||typeof e!="object"||Array.isArray(e)?false:Object.values(e).every(n=>typeof n=="string")}function u(e){return e.path===void 0?e.source:`${e.source} ${e.path}`}
|
|
3
|
+
export{I as a,o as b,c,re as d,u as e};//# sourceMappingURL=chunk-WYVHXXTI.js.map
|
|
4
|
+
//# sourceMappingURL=chunk-WYVHXXTI.js.map
|