@north-light/cli 0.3.575 → 0.3.577

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/builtin-memory/crouter-sdk/release-notes.md +8 -0
  4. package/dist/clients/attach/viewer.js +212 -212
  5. package/dist/core/canvas/executions.d.ts +4 -4
  6. package/dist/core/canvas/executions.js +1 -1
  7. package/dist/core/command-plugins/bundle.d.ts +6 -3
  8. package/dist/core/command-plugins/bundle.js +1 -1
  9. package/dist/core/io.js +4 -4
  10. package/dist/core/llm/run.js +2 -2
  11. package/dist/core/model-routes.d.ts +11 -0
  12. package/dist/core/model-routes.js +1 -1
  13. package/dist/core/peer/envelope.d.ts +2 -0
  14. package/dist/core/peer/envelope.js +1 -1
  15. package/dist/core/provider-error-recognition.d.ts +3 -0
  16. package/dist/core/provider-error-recognition.js +1 -1
  17. package/dist/core/providers/bundles.js +2 -2
  18. package/dist/core/providers/call.js +1 -1
  19. package/dist/core/providers/roots.d.ts +4 -0
  20. package/dist/core/providers/roots.js +1 -1
  21. package/dist/core/runtime/broker/daemon-ops.d.ts +4 -0
  22. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  23. package/dist/core/runtime/broker-request.js +1 -1
  24. package/dist/core/runtime/broker.js +1 -1
  25. package/dist/core/runtime/revive.d.ts +6 -0
  26. package/dist/core/runtime/revive.js +2 -2
  27. package/dist/core/runtime/run-command.js +1 -1
  28. package/dist/core/runtime/runner/blaxel/client.d.ts +2 -0
  29. package/dist/core/runtime/runner/blaxel/client.js +1 -1
  30. package/dist/core/runtime/runner/blaxel/runner.js +2 -2
  31. package/dist/core/runtime/spawn.js +2 -2
  32. package/dist/core/runtime/view-socket.d.ts +1 -1
  33. package/dist/core/subscription-state.d.ts +4 -0
  34. package/dist/core/subscription-state.js +1 -1
  35. package/dist/daemon/api/handlers/messages.js +2 -2
  36. package/dist/daemon/api/handlers/nodes.js +1 -1
  37. package/dist/daemon/api/handlers/providers.js +1 -1
  38. package/dist/daemon/crtrd.js +11 -11
  39. package/dist/daemon/peer/forward.js +1 -1
  40. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  41. package/dist/daemon/reconcilers/live-obligation.d.ts +11 -0
  42. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  43. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  44. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  45. package/dist/pi-extensions/canvas-stophook.js +1 -1
  46. package/docs/sdk/release-notes.md +8 -0
  47. package/package.json +2 -2
  48. package/packages/crouter-identity/package.json +1 -1
  49. package/runtime.lock.json +13 -13
  50. package/scripts/sandbox-start.mjs +8 -1
@@ -106,8 +106,8 @@ export declare function markProfilePauseStop(nodeId: string): Promise<void>;
106
106
  export declare function hasProfilePauseStop(nodeId: string): Promise<boolean>;
107
107
  /** Consumes the pause stop once: true for the single dead-row pass that must absorb it. */
108
108
  export declare function absorbProfilePauseStop(nodeId: string): Promise<boolean>;
109
- /** Whether the latest execution's profile-pause stop was absorbed by a policy pass, the exit it caused already forgiven. */
110
- export declare function hasAbsorbedProfilePauseStop(nodeId: string): Promise<boolean>;
109
+ /** Whether the execution's profile-pause stop was absorbed by a policy pass, the exit it caused already forgiven. */
110
+ export declare function hasAbsorbedProfilePauseStop(execution: Execution): boolean;
111
111
  /** Records a child's observed exit status on its execution without ending it; the next dead-row pass reads it once. The first recorded reason stands. */
112
112
  export declare function recordExecutionExit(executionId: string, status: {
113
113
  code: number | null;
@@ -119,7 +119,7 @@ export interface ObservedExit {
119
119
  signal: NodeJS.Signals | null;
120
120
  oomKilled?: boolean;
121
121
  }
122
- /** Whether the latest execution holds an observed exit no dead-row pass has consumed yet. */
123
- export declare function hasObservedExit(nodeId: string): Promise<boolean>;
122
+ /** Whether the execution holds an observed exit no dead-row pass has consumed yet. */
123
+ export declare function hasObservedExit(execution: Execution): boolean;
124
124
  /** Reads and consumes the latest execution's unhandled observed exit, once. */
125
125
  export declare function takeObservedExit(nodeId: string): Promise<ObservedExit | undefined>;
@@ -1 +1 @@
1
- var T=Object.defineProperty;var a=(e,t)=>T(e,"name",{value:t,configurable:!0});import{randomUUID as g}from"node:crypto";import{openDb as r,withCanvasWrite as _}from"./db.js";import{capturePidIdentities as R,captureLivenessSnapshot as L,identitiesMatch as D,isBootScopedIdentity as y,isLegacyTicksIdentity as C}from"./pid.js";import{existsSync as O,readFileSync as P}from"node:fs";import{LAUNCHER_SOCKET as U}from"../../launcher/spec.js";import{revokeNodeTokens as x}from"./node-tokens.js";import{clearPendingParkIn as l}from"../../daemon/park-pending.js";function d(){return new Date().toISOString()}a(d,"now");function A(e){return{executionId:e.execution_id,nodeId:e.node_id,daemonEpoch:e.daemon_epoch??null,pid:e.pid??null,processIdentity:e.process_identity??null,state:e.state,startedAt:e.started_at,endedAt:e.ended_at??null,exitReason:e.exit_reason??null,runner:e.runner,sandboxName:e.sandbox_name??null,attempt:Number(e.attempt),release:e.release??null,heartbeatAt:e.heartbeat_at??null,awakeSince:e.awake_since??null,idleAt:e.idle_at??null,exitedAt:e.exited_at??null,launchJson:e.launch_json??null,stopRequestedAt:e.stop_requested_at??null,stopAfterTurn:Number(e.stop_after_turn)===1,stopReason:e.stop_reason??null}}a(A,"hydrate");async function X(e){const t=await(await r()).prepare("SELECT * FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:A(t)}a(X,"liveExecutionForNode");class W extends Error{static{a(this,"ExecutionCapReached")}cap;live;constructor(t,n){super(`no broker slot is free \u2014 ${n} live executions at the cap of ${t}.`),this.cap=t,this.live=n,this.name="ExecutionCapReached"}}class H extends Error{static{a(this,"ExecutionAlreadyLive")}nodeId;executionId;constructor(t,n){super(`${t} already has a live execution ${n}.`),this.nodeId=t,this.executionId=n,this.name="ExecutionAlreadyLive"}}const b=7340001;async function Y(e,t){return await _(async n=>{const i=d();if(!await n.prepare("SELECT node_id FROM nodes WHERE node_id = ? FOR UPDATE").get(e))throw new Error(`cannot begin execution: missing node ${e}`);if(t.cap!==void 0){await n.prepare(`SELECT pg_advisory_xact_lock(${b})`).get();const u=Number((await n.prepare("SELECT COUNT(*) AS n FROM executions WHERE state != 'exited' AND node_id != ?").get(e)).n);if(u>=t.cap)throw new W(t.cap,u)}const s=(await n.prepare("SELECT execution_id, runner, daemon_epoch, state FROM executions WHERE node_id = ? AND state != 'exited'").all(e)).find(u=>u.state==="launching"?u.runner!=="process"||t.daemonEpoch!==null&&u.daemon_epoch===t.daemonEpoch:u.runner!=="process");if(s!==void 0)throw new H(e,s.execution_id);(await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = 'superseded' WHERE node_id = ? AND state != 'exited'").run(i,i,e)).changes>0&&await l(n,e),await x(n,"node_id",e,i);const p=g(),E=t.attempt??Number((await n.prepare("SELECT COALESCE(MAX(attempt), 0) + 1 AS next FROM executions WHERE node_id = ?").get(e)).next);return await n.prepare("INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason, runner, sandbox_name, attempt, release, launch_json) VALUES (?, ?, ?, NULL, NULL, 'launching', ?, NULL, NULL, ?, ?, ?, ?, ?)").run(p,e,t.daemonEpoch,i,t.runner??"process",t.sandboxName??null,E,t.release??null,t.launchJson??null),p})}a(Y,"beginExecution");async function j(e,t){const n=await(await r()).prepare("UPDATE executions SET launch_json = ? WHERE execution_id = ? AND state = 'launching'").run(t,e);if(Number(n.changes)!==1)throw new Error(`cannot set launch recipe for execution ${e}: no launching execution`)}a(j,"setExecutionLaunchJson");async function J(e){const t=await(await r()).prepare("UPDATE executions SET idle_at = ? WHERE execution_id = ? AND state = 'running' AND idle_at IS NULL").run(d(),e);return Number(t.changes)===1}a(J,"recordExecutionIdle");async function G(e){await(await r()).prepare("UPDATE executions SET awake_since = ?, idle_at = NULL WHERE execution_id = ? AND state = 'running'").run(d(),e)}a(G,"recordExecutionAwake");async function V(e,t,n){const i=R([n])?.get(n)??null;return await _(async o=>{if((await o.prepare("UPDATE executions SET pid = ?, process_identity = ?, state = 'running', awake_since = ? WHERE execution_id = ? AND node_id = ? AND state = 'launching'").run(n,i,d(),e,t)).changes>0)return"recorded";const s=await o.prepare("SELECT pid FROM executions WHERE execution_id = ? AND node_id = ? AND state = 'running'").get(e,t);if(s?.pid===n)return"recorded";if(process.platform==="linux"&&O(U)&&s?.pid!=null){const S=L(),p=[s.pid];for(let E=0;E<p.length;E++)p.push(...S?.childrenOf.get(p[E])??[]);if(p.includes(n))return"recorded";for(const E of p)try{const u=P(`/proc/${E}/status`,"utf8"),m=/^NSpid:\s*(.+)$/m.exec(u)?.[1]?.trim().split(/\s+/).map(Number)??[];if(m.length>1&&m.at(-1)===n)return"recorded"}catch{}}return"shutdown"})}a(V,"recordExecutionPid");async function F(e,t){return await _(async n=>{const i=d(),o=await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = COALESCE(exit_reason, ?) WHERE execution_id = ? AND state != 'exited' RETURNING node_id").get(i,i,t.reason,e);return await x(n,"broker_process_id",e,i),o!==void 0&&await l(n,o.node_id),o!==void 0})}a(F,"endExecution");async function z(e,t){return await _(async n=>{const i=d(),o=await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = COALESCE(exit_reason, ?) WHERE execution_id = ? AND state = 'launching' RETURNING node_id").get(i,i,t.reason,e);return o===void 0?!1:(await x(n,"broker_process_id",e,i),await l(n,o.node_id),!0)})}a(z,"endLaunchingExecution");async function Q(e,t){return await F(e,t)}a(Q,"abortExecution");async function Z(e,t){return await _(async n=>{const i=d(),o=await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = COALESCE(exit_reason, ?) WHERE node_id = ? AND state != 'exited'").run(i,i,t.reason,e);return await x(n,"node_id",e,i),Number(o.changes)>0&&await l(n,e),Number(o.changes)===1})}a(Z,"endLiveExecution");async function ee(e=t=>R([t])?.get(t)){const t=await r(),n=await t.prepare("SELECT execution_id, pid, process_identity FROM executions WHERE state = 'running' AND pid IS NOT NULL AND process_identity IS NOT NULL").all(),i=t.prepare("UPDATE executions SET process_identity = ? WHERE execution_id = ? AND pid = ? AND process_identity = ? AND state = 'running'"),o=[];for(const c of n){if(!C(c.process_identity))continue;const s=e(c.pid);s===void 0||!y(s)||!D(s,c.process_identity)||(await i.run(s,c.execution_id,c.pid,c.process_identity)).changes>0&&o.push(c.execution_id)}return{migrated:o}}a(ee,"migrateLegacyExecutionIdentities");async function te(e,t){const n=await(await r()).prepare("UPDATE executions SET stop_requested_at = COALESCE(stop_requested_at, ?), stop_after_turn = CASE WHEN stop_requested_at IS NULL THEN ? ELSE stop_after_turn END, stop_reason = COALESCE(stop_reason, ?) WHERE node_id = ? AND state != 'exited'").run(d(),t.afterTurn?1:0,t.reason,e);return Number(n.changes)>0}a(te,"requestExecutionStop");async function ne(e){return(await(await r()).prepare("SELECT * FROM executions WHERE state != 'exited' AND stop_requested_at IS NOT NULL AND stop_requested_at <= ? ORDER BY stop_requested_at").all(e)).map(A)}a(ne,"executionsWithStopRequestedBefore");async function f(e){const t=await(await r()).prepare("SELECT * FROM executions WHERE node_id = ? ORDER BY started_at DESC, attempt DESC LIMIT 1").get(e);return t===void 0?null:A(t)}a(f,"latestExecutionForNode");async function ae(e){return await(await r()).prepare("SELECT 1 AS x FROM executions WHERE node_id = ? AND state = 'running' AND runner != 'process'").get(e)!==void 0}a(ae,"hasRunningRemoteExecution");async function ie(e,t){return await(await r()).prepare("SELECT 1 AS x FROM executions WHERE node_id = ? AND state = 'launching' AND (runner != 'process' OR daemon_epoch = ?)").get(e,t)!==void 0}a(ie,"hasLaunchingExecution");async function oe(e){const t=await(await r()).prepare("SELECT node_id FROM executions WHERE state != 'exited' AND runner = 'process' AND daemon_epoch = ?").all(e);return new Set(t.map(n=>n.node_id))}a(oe,"liveProcessExecutionNodeIds");async function re(){const e=await(await r()).prepare("SELECT node_id FROM executions WHERE state != 'exited'").all();return new Set(e.map(t=>t.node_id))}a(re,"liveExecutionNodeIds");const h="profile_pause",w="profile_pause_absorbed";async function se(e){await(await r()).prepare("UPDATE executions SET stop_requested_at = COALESCE(stop_requested_at, ?), stop_reason = ? WHERE node_id = ? AND state != 'exited'").run(d(),h,e)}a(se,"markProfilePauseStop");async function ce(e){return(await f(e))?.stopReason===h}a(ce,"hasProfilePauseStop");async function ue(e){const t=await(await r()).prepare("UPDATE executions SET stop_reason = ? WHERE execution_id = (SELECT execution_id FROM executions WHERE node_id = ? ORDER BY started_at DESC, attempt DESC LIMIT 1) AND stop_reason = ?").run(w,e,h);return Number(t.changes)>0}a(ue,"absorbProfilePauseStop");async function de(e){return(await f(e))?.stopReason===w}a(de,"hasAbsorbedProfilePauseStop");const N="exited:";async function pe(e,t){const n=t.oomKilled===!0?"out_of_memory":`${N}${t.code??""}:${t.signal??""}`;await(await r()).prepare("UPDATE executions SET exit_reason = ? WHERE execution_id = ? AND exit_reason IS NULL").run(n,e)}a(pe,"recordExecutionExit");async function Ee(e){const t=(await f(e))?.exitReason;return t!=null&&(t==="out_of_memory"||t.startsWith(N))}a(Ee,"hasObservedExit");async function _e(e){const t=await f(e),n=t?.exitReason;if(t===null||n==null)return;const i=n==="out_of_memory";if(!i&&!n.startsWith(N))return;const o=await(await r()).prepare("UPDATE executions SET exit_reason = 'exit_handled' WHERE execution_id = ? AND exit_reason = ?").run(t.executionId,n);if(Number(o.changes)===0)return;if(i)return{code:null,signal:null,oomKilled:!0};const[c,s]=n.slice(N.length).split(":");return{code:c===""?null:Number(c),signal:s===""?null:s}}a(_e,"takeObservedExit");export{H as ExecutionAlreadyLive,W as ExecutionCapReached,h as PROFILE_PAUSE_STOP_REASON,Q as abortExecution,ue as absorbProfilePauseStop,Y as beginExecution,F as endExecution,z as endLaunchingExecution,Z as endLiveExecution,ne as executionsWithStopRequestedBefore,de as hasAbsorbedProfilePauseStop,ie as hasLaunchingExecution,Ee as hasObservedExit,ce as hasProfilePauseStop,ae as hasRunningRemoteExecution,f as latestExecutionForNode,X as liveExecutionForNode,re as liveExecutionNodeIds,oe as liveProcessExecutionNodeIds,se as markProfilePauseStop,ee as migrateLegacyExecutionIdentities,G as recordExecutionAwake,pe as recordExecutionExit,J as recordExecutionIdle,V as recordExecutionPid,te as requestExecutionStop,j as setExecutionLaunchJson,_e as takeObservedExit};
1
+ var T=Object.defineProperty;var a=(e,t)=>T(e,"name",{value:t,configurable:!0});import{randomUUID as g}from"node:crypto";import{openDb as r,withCanvasWrite as _}from"./db.js";import{capturePidIdentities as m,captureLivenessSnapshot as L,identitiesMatch as D,isBootScopedIdentity as y,isLegacyTicksIdentity as C}from"./pid.js";import{existsSync as O,readFileSync as P}from"node:fs";import{LAUNCHER_SOCKET as U}from"../../launcher/spec.js";import{revokeNodeTokens as x}from"./node-tokens.js";import{clearPendingParkIn as l}from"../../daemon/park-pending.js";function d(){return new Date().toISOString()}a(d,"now");function N(e){return{executionId:e.execution_id,nodeId:e.node_id,daemonEpoch:e.daemon_epoch??null,pid:e.pid??null,processIdentity:e.process_identity??null,state:e.state,startedAt:e.started_at,endedAt:e.ended_at??null,exitReason:e.exit_reason??null,runner:e.runner,sandboxName:e.sandbox_name??null,attempt:Number(e.attempt),release:e.release??null,heartbeatAt:e.heartbeat_at??null,awakeSince:e.awake_since??null,idleAt:e.idle_at??null,exitedAt:e.exited_at??null,launchJson:e.launch_json??null,stopRequestedAt:e.stop_requested_at??null,stopAfterTurn:Number(e.stop_after_turn)===1,stopReason:e.stop_reason??null}}a(N,"hydrate");async function X(e){const t=await(await r()).prepare("SELECT * FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:N(t)}a(X,"liveExecutionForNode");class W extends Error{static{a(this,"ExecutionCapReached")}cap;live;constructor(t,n){super(`no broker slot is free \u2014 ${n} live executions at the cap of ${t}.`),this.cap=t,this.live=n,this.name="ExecutionCapReached"}}class H extends Error{static{a(this,"ExecutionAlreadyLive")}nodeId;executionId;constructor(t,n){super(`${t} already has a live execution ${n}.`),this.nodeId=t,this.executionId=n,this.name="ExecutionAlreadyLive"}}const b=7340001;async function Y(e,t){return await _(async n=>{const i=d();if(!await n.prepare("SELECT node_id FROM nodes WHERE node_id = ? FOR UPDATE").get(e))throw new Error(`cannot begin execution: missing node ${e}`);if(t.cap!==void 0){await n.prepare(`SELECT pg_advisory_xact_lock(${b})`).get();const u=Number((await n.prepare("SELECT COUNT(*) AS n FROM executions WHERE state != 'exited' AND node_id != ?").get(e)).n);if(u>=t.cap)throw new W(t.cap,u)}const s=(await n.prepare("SELECT execution_id, runner, daemon_epoch, state FROM executions WHERE node_id = ? AND state != 'exited'").all(e)).find(u=>u.state==="launching"?u.runner!=="process"||t.daemonEpoch!==null&&u.daemon_epoch===t.daemonEpoch:u.runner!=="process");if(s!==void 0)throw new H(e,s.execution_id);(await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = 'superseded' WHERE node_id = ? AND state != 'exited'").run(i,i,e)).changes>0&&await l(n,e),await x(n,"node_id",e,i);const p=g(),E=t.attempt??Number((await n.prepare("SELECT COALESCE(MAX(attempt), 0) + 1 AS next FROM executions WHERE node_id = ?").get(e)).next);return await n.prepare("INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason, runner, sandbox_name, attempt, release, launch_json) VALUES (?, ?, ?, NULL, NULL, 'launching', ?, NULL, NULL, ?, ?, ?, ?, ?)").run(p,e,t.daemonEpoch,i,t.runner??"process",t.sandboxName??null,E,t.release??null,t.launchJson??null),p})}a(Y,"beginExecution");async function j(e,t){const n=await(await r()).prepare("UPDATE executions SET launch_json = ? WHERE execution_id = ? AND state = 'launching'").run(t,e);if(Number(n.changes)!==1)throw new Error(`cannot set launch recipe for execution ${e}: no launching execution`)}a(j,"setExecutionLaunchJson");async function J(e){const t=await(await r()).prepare("UPDATE executions SET idle_at = ? WHERE execution_id = ? AND state = 'running' AND idle_at IS NULL").run(d(),e);return Number(t.changes)===1}a(J,"recordExecutionIdle");async function G(e){await(await r()).prepare("UPDATE executions SET awake_since = ?, idle_at = NULL WHERE execution_id = ? AND state = 'running'").run(d(),e)}a(G,"recordExecutionAwake");async function V(e,t,n){const i=m([n])?.get(n)??null;return await _(async o=>{if((await o.prepare("UPDATE executions SET pid = ?, process_identity = ?, state = 'running', awake_since = ? WHERE execution_id = ? AND node_id = ? AND state = 'launching'").run(n,i,d(),e,t)).changes>0)return"recorded";const s=await o.prepare("SELECT pid FROM executions WHERE execution_id = ? AND node_id = ? AND state = 'running'").get(e,t);if(s?.pid===n)return"recorded";if(process.platform==="linux"&&O(U)&&s?.pid!=null){const h=L(),p=[s.pid];for(let E=0;E<p.length;E++)p.push(...h?.childrenOf.get(p[E])??[]);if(p.includes(n))return"recorded";for(const E of p)try{const u=P(`/proc/${E}/status`,"utf8"),S=/^NSpid:\s*(.+)$/m.exec(u)?.[1]?.trim().split(/\s+/).map(Number)??[];if(S.length>1&&S.at(-1)===n)return"recorded"}catch{}}return"shutdown"})}a(V,"recordExecutionPid");async function F(e,t){return await _(async n=>{const i=d(),o=await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = COALESCE(exit_reason, ?) WHERE execution_id = ? AND state != 'exited' RETURNING node_id").get(i,i,t.reason,e);return await x(n,"broker_process_id",e,i),o!==void 0&&await l(n,o.node_id),o!==void 0})}a(F,"endExecution");async function z(e,t){return await _(async n=>{const i=d(),o=await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = COALESCE(exit_reason, ?) WHERE execution_id = ? AND state = 'launching' RETURNING node_id").get(i,i,t.reason,e);return o===void 0?!1:(await x(n,"broker_process_id",e,i),await l(n,o.node_id),!0)})}a(z,"endLaunchingExecution");async function Q(e,t){return await F(e,t)}a(Q,"abortExecution");async function Z(e,t){return await _(async n=>{const i=d(),o=await n.prepare("UPDATE executions SET state = 'exited', ended_at = ?, exited_at = ?, exit_reason = COALESCE(exit_reason, ?) WHERE node_id = ? AND state != 'exited'").run(i,i,t.reason,e);return await x(n,"node_id",e,i),Number(o.changes)>0&&await l(n,e),Number(o.changes)===1})}a(Z,"endLiveExecution");async function ee(e=t=>m([t])?.get(t)){const t=await r(),n=await t.prepare("SELECT execution_id, pid, process_identity FROM executions WHERE state = 'running' AND pid IS NOT NULL AND process_identity IS NOT NULL").all(),i=t.prepare("UPDATE executions SET process_identity = ? WHERE execution_id = ? AND pid = ? AND process_identity = ? AND state = 'running'"),o=[];for(const c of n){if(!C(c.process_identity))continue;const s=e(c.pid);s===void 0||!y(s)||!D(s,c.process_identity)||(await i.run(s,c.execution_id,c.pid,c.process_identity)).changes>0&&o.push(c.execution_id)}return{migrated:o}}a(ee,"migrateLegacyExecutionIdentities");async function te(e,t){const n=await(await r()).prepare("UPDATE executions SET stop_requested_at = COALESCE(stop_requested_at, ?), stop_after_turn = CASE WHEN stop_requested_at IS NULL THEN ? ELSE stop_after_turn END, stop_reason = COALESCE(stop_reason, ?) WHERE node_id = ? AND state != 'exited'").run(d(),t.afterTurn?1:0,t.reason,e);return Number(n.changes)>0}a(te,"requestExecutionStop");async function ne(e){return(await(await r()).prepare("SELECT * FROM executions WHERE state != 'exited' AND stop_requested_at IS NOT NULL AND stop_requested_at <= ? ORDER BY stop_requested_at").all(e)).map(N)}a(ne,"executionsWithStopRequestedBefore");async function R(e){const t=await(await r()).prepare("SELECT * FROM executions WHERE node_id = ? ORDER BY started_at DESC, attempt DESC LIMIT 1").get(e);return t===void 0?null:N(t)}a(R,"latestExecutionForNode");async function ae(e){return await(await r()).prepare("SELECT 1 AS x FROM executions WHERE node_id = ? AND state = 'running' AND runner != 'process'").get(e)!==void 0}a(ae,"hasRunningRemoteExecution");async function ie(e,t){return await(await r()).prepare("SELECT 1 AS x FROM executions WHERE node_id = ? AND state = 'launching' AND (runner != 'process' OR daemon_epoch = ?)").get(e,t)!==void 0}a(ie,"hasLaunchingExecution");async function oe(e){const t=await(await r()).prepare("SELECT node_id FROM executions WHERE state != 'exited' AND runner = 'process' AND daemon_epoch = ?").all(e);return new Set(t.map(n=>n.node_id))}a(oe,"liveProcessExecutionNodeIds");async function re(){const e=await(await r()).prepare("SELECT node_id FROM executions WHERE state != 'exited'").all();return new Set(e.map(t=>t.node_id))}a(re,"liveExecutionNodeIds");const A="profile_pause",w="profile_pause_absorbed";async function se(e){await(await r()).prepare("UPDATE executions SET stop_requested_at = COALESCE(stop_requested_at, ?), stop_reason = ? WHERE node_id = ? AND state != 'exited'").run(d(),A,e)}a(se,"markProfilePauseStop");async function ce(e){return(await R(e))?.stopReason===A}a(ce,"hasProfilePauseStop");async function ue(e){const t=await(await r()).prepare("UPDATE executions SET stop_reason = ? WHERE execution_id = (SELECT execution_id FROM executions WHERE node_id = ? ORDER BY started_at DESC, attempt DESC LIMIT 1) AND stop_reason = ?").run(w,e,A);return Number(t.changes)>0}a(ue,"absorbProfilePauseStop");function de(e){return e.stopReason===w}a(de,"hasAbsorbedProfilePauseStop");const f="exited:";async function pe(e,t){const n=t.oomKilled===!0?"out_of_memory":`${f}${t.code??""}:${t.signal??""}`;await(await r()).prepare("UPDATE executions SET exit_reason = ? WHERE execution_id = ? AND exit_reason IS NULL").run(n,e)}a(pe,"recordExecutionExit");function Ee(e){const t=e.exitReason;return t!=null&&(t==="out_of_memory"||t.startsWith(f))}a(Ee,"hasObservedExit");async function _e(e){const t=await R(e),n=t?.exitReason;if(t===null||n==null)return;const i=n==="out_of_memory";if(!i&&!n.startsWith(f))return;const o=await(await r()).prepare("UPDATE executions SET exit_reason = 'exit_handled' WHERE execution_id = ? AND exit_reason = ?").run(t.executionId,n);if(Number(o.changes)===0)return;if(i)return{code:null,signal:null,oomKilled:!0};const[c,s]=n.slice(f.length).split(":");return{code:c===""?null:Number(c),signal:s===""?null:s}}a(_e,"takeObservedExit");export{H as ExecutionAlreadyLive,W as ExecutionCapReached,A as PROFILE_PAUSE_STOP_REASON,Q as abortExecution,ue as absorbProfilePauseStop,Y as beginExecution,F as endExecution,z as endLaunchingExecution,Z as endLiveExecution,ne as executionsWithStopRequestedBefore,de as hasAbsorbedProfilePauseStop,ie as hasLaunchingExecution,Ee as hasObservedExit,ce as hasProfilePauseStop,ae as hasRunningRemoteExecution,R as latestExecutionForNode,X as liveExecutionForNode,re as liveExecutionNodeIds,oe as liveProcessExecutionNodeIds,se as markProfilePauseStop,ee as migrateLegacyExecutionIdentities,G as recordExecutionAwake,pe as recordExecutionExit,J as recordExecutionIdle,V as recordExecutionPid,te as requestExecutionStop,j as setExecutionLaunchJson,_e as takeObservedExit};
@@ -131,6 +131,9 @@ export interface InstallBundleArchiveOptions<M> {
131
131
  /** Checked against the validated archive before anything is written; a
132
132
  * returned message refuses the archive (a provider bundle naming another id). */
133
133
  accept?: (bundle: ValidatedBundle) => string | undefined;
134
+ /** Default true. False leaves the config version and state validator unwritten: the caller keeps the install record itself
135
+ * (a cloud role's deployment-wide provider bundle has no person whose settings could hold it). */
136
+ record?: boolean;
134
137
  }
135
138
  export interface InstalledBundleArchive<M> {
136
139
  manifest: PluginManifest;
@@ -140,9 +143,9 @@ export interface InstalledBundleArchive<M> {
140
143
  staged: M;
141
144
  }
142
145
  /**
143
- * Validate `raw`, stage it beside the package root, swap it in, then record
144
- * the config version and the state validator — in that order, never the
145
- * reverse. A failure before the swap leaves the previous package untouched.
146
+ * Validate `raw`, stage it beside the package root, swap it in, then (unless
147
+ * `record` is false) record the config version and state validator. A failure
148
+ * before the swap leaves the previous package untouched.
146
149
  * The caller holds the plugin's swap lock (`bundleSwapLockPath`).
147
150
  */
148
151
  export declare function installBundleArchive<M>(name: string, raw: Uint8Array, options: InstallBundleArchiveOptions<M>): Promise<InstalledBundleArchive<M>>;
@@ -1 +1 @@
1
- var P=Object.defineProperty;var c=(e,o)=>P(e,"name",{value:o,configurable:!0});import{Parser as J}from"tar";import{createHash as I}from"node:crypto";import{chmodSync as W,renameSync as k}from"node:fs";import{dirname as $,join as w,resolve as V,sep as z}from"node:path";import{validateCommandManifest as B}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as R,invalidPluginKindsReasons as F,readConfig as M,updateConfig as D,updateState as T}from"../config.js";import{general as x}from"../errors.js";import{atomicWriteJson as C,atomicWriteText as q,ensureDir as j,nowIso as E,pathExists as _,removePath as S,syncDirectory as H,syncTree as U}from"../fs-utils.js";import{invalidPageComponentsReasons as K,normalizePageComponents as G}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as X}from"../plugin-extensions.js";import{objectBlocksShapeReasons as Z}from"../plugin-objects-manifest.js";import{validateBundleTransport as L}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,Y=new Set(["id","major","groups"]),Q=new Set(["group","consent_line","rate","who","allowlist"]),ee=new Set(["own","user","allowlist","any"]),ne=/^team:[a-z0-9]+$/,oe=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider","object_kinds","scopes","groups"]);function re(e,o,n,r,s,d){return{code:e,message:o,received:n,expected:r,next:s,...d!==void 0?{path:d}:{}}}c(re,"issue");function u(e,o,n,r,s){return re("plugin_bundle_invalid",e,o,n,r,s)}c(u,"bundleInvalid");function se(e){if(e[0]===31&&e[1]===139)return u("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return u("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(o=>o===0))return u("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}c(se,"archiveEnvelopeIssue");function te(e,o){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return u("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(o==="file"&&e.endsWith("/"))return u("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((o==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(r=>r===""||r==="."||r===".."))return u("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}c(te,"validateMemberName");function ie(e,o){if(o==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:u("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return u("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}c(ie,"validateMemberShape");function ae(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}c(ae,"materializedPath");function de(e){const o=new Map;for(const n of e){const r=ae(n),s=o.get(r);if(s!==void 0)return u(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${r} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",r);o.set(r,n.type)}for(const[n,r]of o)if(r==="file"){for(const s of o.keys())if(s.startsWith(`${n}/`))return u("bundle nests a member beneath a regular-file path",`${s} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",s)}}c(de,"validateMemberTree");async function ue(e){const o=[],n=new Set;let r;return await new Promise((s,d)=>{const p=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:c(t=>{const a=t.path,f=t.type==="File"||t.type==="OldFile"?"file":t.type==="Directory"?"directory":void 0;if(f===void 0||t.unsupported||t.ignore||t.linkpath!==void 0){r??=u("bundle member must be a regular file or directory without a link target",`${a} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",a),t.resume();return}r??=te(a,f),r??=ie(a,f),n.has(a)&&(r??=u("bundle contains a duplicate member",a,"each member path exactly once","Emit each archive member only once.",a)),n.add(a);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{r===void 0&&o.push({path:a,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});p.on("ignoredEntry",t=>{const a=t.path;r??=u("bundle contains an ignored or unsupported archive member",`${a} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",a),t.resume()}),p.once("error",d),p.once("end",s);try{p.end(Buffer.from(e))}catch(t){d(t)}}).catch(s=>{r??=u("bundle archive could not be parsed",s instanceof Error?s.message:String(s),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),r===void 0?{members:o}:{issue:r}}c(ue,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}c(N,"isPositiveInt");function le(e){const o=c((t,a,f,i)=>({issue:u(t,a,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return o("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,r=Object.keys(n).filter(t=>!Y.has(t));if(r.length>0)return o("bundle.json provider has unknown keys",r.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return o("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!N(n.major))return o("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const s=n.groups;if(!Array.isArray(s)||s.length===0)return o("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const d=[],p=new Set;for(let t=0;t<s.length;t++){const a=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return o("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",a);const i=f,b=Object.keys(i).filter(v=>!Q.has(v));if(b.length>0)return o("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",a);if(typeof i.group!="string"||!O.test(i.group))return o("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${a}.group`);if(p.has(i.group))return o("provider group ids must be unique",i.group,"each group declared once",`${a}.group`);if(p.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return o("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${a}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return o("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${a}.rate`);if(typeof i.who!="string"||!(ee.has(i.who)||ne.test(i.who)))return o("provider group who must be own|user|allowlist|any|team:<id>",JSON.stringify(i.who),"own | user | allowlist | any | team:<id> (id of lowercase letters and digits)",`${a}.who`);const m=i.allowlist;if(!Array.isArray(m)||!m.every(v=>typeof v=="string"&&v.length>0))return o("provider group allowlist must be an array of non-empty strings",JSON.stringify(m),"an array of app principals ([] unless who is allowlist)",`${a}.allowlist`);if(m.length>0&&i.who!=="allowlist")return o("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${a}.allowlist`);d.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:m})}return{provider:{id:n.id,major:n.major,groups:d}}}c(le,"parseProviderBlock");function pe(e){const o=[],n=c((r,s)=>{const d=[...s,r.name];if(r.kind==="leaf")o.push({path:d.join(" "),leaf:r});else for(const p of r.children)n(p,d)},"walk");for(const r of e.roots)n(r,[]);for(const r of e.coreMounts)n(r.node,r.parent);return o}c(pe,"collectLeaves");function ce(e,o){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return u("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.kind==="http"&&e.transport.authEnv!==void 0)return u("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const r=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:d}of pe(o)){const p="group"in d?d.group:void 0;if(p===void 0)return u(`provider leaf "${s}" has no group`,s,"a group on every leaf of a provider bundle",`Add a group to leaf "${s}".`,`commands.json:${s}.group`);if(!r.has(p))return u(`provider leaf "${s}" names undeclared group "${p}"`,p,`one of: ${[...r].join(", ")}`,`Declare group "${p}" in the provider block, or move leaf "${s}" to a declared group.`,`commands.json:${s}.group`);if("rest"in d&&d.rest.streaming===!0)return u(`provider leaf "${s}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${s}" return one response.`,`commands.json:${s}.rest.streaming`)}}c(ce,"providerBundleIssue");function me(e){let o;try{o=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:u("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof o!="object"||o===null||Array.isArray(o))return{issue:u("bundle.json must be a JSON object",JSON.stringify(o),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=o,r=Object.keys(n).filter(a=>!oe.has(a));if(n.bundleVersion!==1||r.length>0)return{issue:u("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, memory_extensions, object_kinds, scopes, and groups",JSON.stringify(o),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, memory_extensions, object_kinds, scopes, and groups",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const a=F(n.kinds);if(a.length>0)return{issue:u("bundle.json kinds block is invalid",a.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const a=K(n.page_components);if(a.length>0)return{issue:u("bundle.json page_components block is invalid",a.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}const s=Z(n);if(n.provider!==void 0&&n.scopes!==void 0&&s.push("scopes is for local plugins; a provider gets its scopes from directory consent"),s.length>0)return{issue:u("bundle.json object_kinds or scopes block is invalid",s.join("; "),"object_kinds keyed by kind name with description, events and optional read.command; scopes an array of crtr:objects:<verb>:<target> strings (local plugins only)","Fix the object_kinds and scopes blocks in bundle.json.","bundle.json")};let d;if(n.memory_extensions!==void 0)try{d=X(n.memory_extensions)}catch(a){return{issue:u("bundle.json memory_extensions block is invalid",a instanceof Error?a.message:String(a),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!N(n.version))return{issue:u("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let p;if(n.transport!==void 0){const a=L(n.transport);if(a.transport===void 0)return{issue:u("bundle.json transport is invalid",a.errors.join("; "),'{ kind: "http", endpoint, authEnv? } or { kind: "machine" }',"Fix transport in bundle.json.","bundle.json.transport")};p=a.transport}let t;if(n.provider!==void 0){const a=le(n.provider);if(a.issue!==void 0)return{issue:a.issue};if(t=a.provider,n.version!==void 0&&n.version!==t.major)return{issue:u("bundle.json provider major must equal version",`version ${String(n.version)}, major ${t.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...p!==void 0?{transport:p}:{},...t!==void 0?{provider:t}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...d!==void 0?{memory_extensions:d}:{},...n.object_kinds!==void 0?{object_kinds:n.object_kinds}:{},...n.scopes!==void 0?{scopes:n.scopes}:{}}}c(me,"parseBundleMetadata");function fe(e,o,n,r=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[u("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const d=B(s,{transport:"http",reservedCoreNames:o,extensibleCoreBranches:r,...n!==void 0?{coreCommandPaths:n}:{}});return{...d.manifest!==void 0?{commands:d.manifest}:{},issues:d.issues}}c(fe,"parseCommands");async function he(e,o){const n=se(e);if(n!==void 0)return{issues:[n]};const r=await ue(e);if(r.issue!==void 0||r.members===void 0)return{issues:[r.issue]};const s=de(r.members);if(s!==void 0)return{issues:[s]};const d=r.members.find(l=>l.path==="bundle.json"&&l.type==="file"),p=r.members.find(l=>l.path==="commands.json"&&l.type==="file");if(d===void 0||p===void 0)return{issues:[u("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${d!==void 0}, commands.json=${p!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=r.members.find(l=>l.path==="hooks.json"&&l.type==="file"),a=r.members.filter(l=>l.type==="file"&&l.path.startsWith("hooks/"));if((t!==void 0||r.members.some(l=>l.path==="hooks"||l.path.startsWith("hooks/")))&&(t===void 0||!a.some(l=>l.path==="hooks/dispatch")))return{issues:[u("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${a.some(l=>l.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=me(d.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=fe(p.bytes,o.reservedCoreNames,o.coreCommandPaths,o.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const h=b.commands.version;if(i.version!==void 0&&h!==void 0&&i.version!==h)return{issues:[u("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${h}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const m=i.version??h;if(i.provider!==void 0&&m===void 0)return{issues:[u("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&m!==i.provider.major)return{issues:[u("bundle provider major must equal the plugin version",`version ${m}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=ce(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...m!==void 0?{version:m}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},...i.object_kinds!==void 0?{object_kinds:i.object_kinds}:{},...i.scopes!==void 0?{scopes:i.scopes}:{},commands:b.commands,commandsBytes:p.bytes,directories:r.members.filter(l=>l.type==="directory").map(l=>({path:l.path})),memory:r.members.filter(l=>l.type==="file"&&l.path.startsWith("memory/")).map(l=>({path:l.path,bytes:l.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:a.map(l=>({path:l.path,bytes:l.bytes}))}}:{}},issues:[]}}c(he,"validatePluginBundle");function be(e){return I("sha256").update(e).digest("hex").slice(0,12)}c(be,"bundleVersion");function Je(e,o,n,r){return M(o).plugins[e]?.version!==r?!1:_(w(n,"commands.json"))}c(Je,"isInstalledGeneration");async function ve(e,o,n,r){await T(e,s=>{s.plugins[o]===void 0&&(s.plugins[o]={}),r.swapped&&(s.plugins[o].last_updated=E()),s.plugins[o].checked_at=E(),n===void 0?delete s.plugins[o].etag:s.plugins[o].etag=n})}c(ve,"recordCheckedGeneration");function g(e,o){const n=V(e,o);if(n===e||!n.startsWith(e+z))throw x(`bundle member path escapes the staging directory: ${o}`);return n}c(g,"stagedPath");function ge(e,o,n){const r=c((s,d,p=420)=>q(s,d,{mode:p,durable:!0}),"write");C(g(e,".crouter-plugin/plugin.json"),o,{durable:!0});for(const s of n.directories)j(g(e,s.path));r(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)r(g(e,s.path),s.bytes);if(n.hooks!==void 0){r(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)r(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}U(e)}c(ge,"writeStagedBundle");async function Ie(e,o,n){const{scope:r,scopeRootPath:s,root:d}=n,p=await he(o,n.validation);if(p.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,p.issues);const t=p.bundle,a=n.accept?.(t);if(a!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[u(a,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??be(o),i=t.provider!==void 0&&t.transport!==void 0?t.transport:n.transport,b={name:e,version:f,commands:"commands.json",transport:i,bundle:n.bundle,...t.kinds!==void 0?{kinds:t.kinds}:{},...t.page_components!==void 0?{page_components:t.page_components}:{},...t.memory_extensions!==void 0?{memory_extensions:t.memory_extensions}:{},...t.object_kinds!==void 0?{object_kinds:t.object_kinds}:{},...t.scopes!==void 0?{scopes:t.scopes}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),m=w(h,`${e}.${process.pid}`),v=`${m}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(d)),S(m);let l;try{j(m),ge(m,b,t),l=await n.validateStaged(m,b);let y=!1;_(d)&&(k(d,v),y=!0);try{k(m,d)}catch(A){throw y&&k(v,d),A}H($(d)),y&&S(v)}finally{S(m)}return await R(r,s),await D(r,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),await ve(r,e,n.etag,{swapped:!0}),{manifest:b,validated:t,version:f,root:d,staged:l}}c(Ie,"installBundleArchive");export{be as bundleVersion,Ie as installBundleArchive,Je as isInstalledGeneration,ve as recordCheckedGeneration,he as validatePluginBundle,ge as writeStagedBundle};
1
+ var P=Object.defineProperty;var c=(e,o)=>P(e,"name",{value:o,configurable:!0});import{Parser as J}from"tar";import{createHash as I}from"node:crypto";import{chmodSync as W,renameSync as k}from"node:fs";import{dirname as $,join as w,resolve as V,sep as z}from"node:path";import{validateCommandManifest as B}from"../../api/command-manifest/manifest.js";import{ensureScopeInitialized as R,invalidPluginKindsReasons as F,readConfig as M,updateConfig as D,updateState as T}from"../config.js";import{general as x}from"../errors.js";import{atomicWriteJson as C,atomicWriteText as q,ensureDir as j,nowIso as E,pathExists as _,removePath as S,syncDirectory as H,syncTree as U}from"../fs-utils.js";import{invalidPageComponentsReasons as K,normalizePageComponents as G}from"../human/page-catalog.js";import{validateMemoryExtensionDeclarations as X}from"../plugin-extensions.js";import{objectBlocksShapeReasons as Z}from"../plugin-objects-manifest.js";import{validateBundleTransport as L}from"./endpoint.js";const O=/^[A-Za-z0-9_-]{1,64}$/,Y=new Set(["id","major","groups"]),Q=new Set(["group","consent_line","rate","who","allowlist"]),ee=new Set(["own","user","allowlist","any"]),ne=/^team:[a-z0-9]+$/,oe=new Set(["bundleVersion","kinds","page_components","memory_extensions","version","transport","provider","object_kinds","scopes","groups"]);function re(e,o,n,r,s,d){return{code:e,message:o,received:n,expected:r,next:s,...d!==void 0?{path:d}:{}}}c(re,"issue");function u(e,o,n,r,s){return re("plugin_bundle_invalid",e,o,n,r,s)}c(u,"bundleInvalid");function se(e){if(e[0]===31&&e[1]===139)return u("bundle archive must be uncompressed tar data","gzip-compressed archive","an uncompressed tar archive","Serve the archive without compression.");if(e.length===0||e.length%512!==0)return u("bundle archive must be a non-empty whole number of 512-byte tar blocks",`${e.length} bytes`,"a non-empty tar stream with a 512-byte block length","Serve one complete uncompressed tar archive.");if(e.length<1024||!e.subarray(-1024).every(o=>o===0))return u("bundle archive is missing the required two zero-block terminator","archive tail is not 1024 zero bytes","a complete tar stream ending in two 512-byte zero blocks","Serve the complete uncompressed tar archive.")}c(se,"archiveEnvelopeIssue");function te(e,o){if(e.length===0||e.includes("\0")||e.includes("\\")||e.startsWith("/")||/^[A-Za-z]:($|\/)/.test(e))return u("bundle member path must be a safe relative POSIX path",e,"a relative path without absolute, drive-letter, backslash, or NUL forms","Use a safe relative POSIX member path.",e);if(o==="file"&&e.endsWith("/"))return u("only directory members may have a trailing slash",e,"a regular-file path without a trailing slash","Emit this member as a regular file path.",e);if((o==="directory"&&e.endsWith("/")?e.slice(0,-1):e).split("/").some(r=>r===""||r==="."||r===".."))return u("bundle member path contains an unsafe segment",e,"non-empty segments other than . or ..","Use a safe relative POSIX member path.",e)}c(te,"validateMemberName");function ie(e,o){if(o==="file")return e==="bundle.json"||e==="commands.json"||e==="hooks.json"||e.startsWith("memory/")&&e.endsWith(".md")||e.startsWith("hooks/")?void 0:u("bundle contains an unsupported file member",e,"bundle.json, commands.json, hooks.json, memory/**/*.md, or hooks/**","Remove unsupported members from the archive.",e);if(!(e==="memory"||e.startsWith("memory/")||e==="hooks"||e.startsWith("hooks/")))return u("bundle contains an unsupported directory member",e,"memory or hooks, or a directory below either subtree","Remove unsupported directory members from the archive.",e)}c(ie,"validateMemberShape");function ae(e){return e.type==="directory"&&e.path.endsWith("/")?e.path.slice(0,-1):e.path}c(ae,"materializedPath");function de(e){const o=new Map;for(const n of e){const r=ae(n),s=o.get(r);if(s!==void 0)return u(s===n.type?"bundle contains a duplicate member":"bundle claims one path as both a file and a directory",`${r} (${s} and ${n.type})`,"each materialized path exactly once, with one type","Emit each archive path once, as a single member type.",r);o.set(r,n.type)}for(const[n,r]of o)if(r==="file"){for(const s of o.keys())if(s.startsWith(`${n}/`))return u("bundle nests a member beneath a regular-file path",`${s} beneath file ${n}`,"members nested only beneath directory paths","Emit the parent path as a directory or remove the nested member.",s)}}c(de,"validateMemberTree");async function ue(e){const o=[],n=new Set;let r;return await new Promise((s,d)=>{const p=new J({strict:!0,maxMetaEntrySize:Number.MAX_SAFE_INTEGER,gzip:!1,brotli:!1,zstd:!1,onReadEntry:c(t=>{const a=t.path,f=t.type==="File"||t.type==="OldFile"?"file":t.type==="Directory"?"directory":void 0;if(f===void 0||t.unsupported||t.ignore||t.linkpath!==void 0){r??=u("bundle member must be a regular file or directory without a link target",`${a} (${t.type})`,"a regular file or directory member without link metadata","Remove links and unsupported member types from the archive.",a),t.resume();return}r??=te(a,f),r??=ie(a,f),n.has(a)&&(r??=u("bundle contains a duplicate member",a,"each member path exactly once","Emit each archive member only once.",a)),n.add(a);const i=[];t.on("data",b=>i.push(b)),t.on("end",()=>{r===void 0&&o.push({path:a,type:f,bytes:new Uint8Array(Buffer.concat(i))})})},"onReadEntry")});p.on("ignoredEntry",t=>{const a=t.path;r??=u("bundle contains an ignored or unsupported archive member",`${a} (${t.type})`,"only supported regular file or directory members","Remove ignored or unsupported members from the archive.",a),t.resume()}),p.once("error",d),p.once("end",s);try{p.end(Buffer.from(e))}catch(t){d(t)}}).catch(s=>{r??=u("bundle archive could not be parsed",s instanceof Error?s.message:String(s),"a valid complete uncompressed tar archive","Fix the archive format and serve it again.")}),r===void 0?{members:o}:{issue:r}}c(ue,"parseArchive");function N(e){return typeof e=="number"&&Number.isInteger(e)&&e>=1}c(N,"isPositiveInt");function le(e){const o=c((t,a,f,i)=>({issue:u(t,a,f,"Fix the provider block in bundle.json.",`bundle.json.${i}`)}),"bad");if(typeof e!="object"||e===null||Array.isArray(e))return o("bundle.json provider must be an object",JSON.stringify(e),"{ id, major, groups }","provider");const n=e,r=Object.keys(n).filter(t=>!Y.has(t));if(r.length>0)return o("bundle.json provider has unknown keys",r.join(", "),"only: id, major, groups","provider");if(typeof n.id!="string"||!O.test(n.id))return o("provider id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(n.id),'a provider id of 1-64 letters, digits, "_" or "-"',"provider.id");if(!N(n.major))return o("provider major must be an integer of at least 1",JSON.stringify(n.major),"the served major as an integer >= 1","provider.major");const s=n.groups;if(!Array.isArray(s)||s.length===0)return o("provider groups must be a non-empty array",JSON.stringify(s),"at least one { group, consent_line, rate?, who, allowlist }","provider.groups");const d=[],p=new Set;for(let t=0;t<s.length;t++){const a=`provider.groups[${t}]`,f=s[t];if(typeof f!="object"||f===null||Array.isArray(f))return o("provider group must be an object",JSON.stringify(f),"{ group, consent_line, rate?, who, allowlist }",a);const i=f,b=Object.keys(i).filter(v=>!Q.has(v));if(b.length>0)return o("provider group has unknown keys",b.join(", "),"only: group, consent_line, rate, who, allowlist",a);if(typeof i.group!="string"||!O.test(i.group))return o("provider group id must match [A-Za-z0-9_-]{1,64}",JSON.stringify(i.group),'a group id of 1-64 letters, digits, "_" or "-"',`${a}.group`);if(p.has(i.group))return o("provider group ids must be unique",i.group,"each group declared once",`${a}.group`);if(p.add(i.group),typeof i.consent_line!="string"||i.consent_line.trim().length===0)return o("provider group consent_line must be a non-empty string",JSON.stringify(i.consent_line),"the consent line shown for this group",`${a}.consent_line`);const h=i.rate;if(h!=null&&(typeof h!="number"||!Number.isFinite(h)||h<0))return o("provider group rate must be a non-negative number or null",JSON.stringify(h),"a finite number >= 0, null, or omitted",`${a}.rate`);if(typeof i.who!="string"||!(ee.has(i.who)||ne.test(i.who)))return o("provider group who must be own|user|allowlist|any|team:<id>",JSON.stringify(i.who),"own | user | allowlist | any | team:<id> (id of lowercase letters and digits)",`${a}.who`);const m=i.allowlist;if(!Array.isArray(m)||!m.every(v=>typeof v=="string"&&v.length>0))return o("provider group allowlist must be an array of non-empty strings",JSON.stringify(m),"an array of app principals ([] unless who is allowlist)",`${a}.allowlist`);if(m.length>0&&i.who!=="allowlist")return o("provider group allowlist must be empty unless who is allowlist",`who ${i.who}`,'who: "allowlist", or allowlist: []',`${a}.allowlist`);d.push({group:i.group,consent_line:i.consent_line,...h!==void 0?{rate:h}:{},who:i.who,allowlist:m})}return{provider:{id:n.id,major:n.major,groups:d}}}c(le,"parseProviderBlock");function pe(e){const o=[],n=c((r,s)=>{const d=[...s,r.name];if(r.kind==="leaf")o.push({path:d.join(" "),leaf:r});else for(const p of r.children)n(p,d)},"walk");for(const r of e.roots)n(r,[]);for(const r of e.coreMounts)n(r.node,r.parent);return o}c(pe,"collectLeaves");function ce(e,o){const n=e.provider;if(n===void 0)return;if(e.transport===void 0)return u("a provider bundle must declare its transport","(missing)",'transport { kind: "http", endpoint } in bundle.json',"Add the provider endpoint as bundle.json transport.","bundle.json.transport");if(e.transport.kind==="http"&&e.transport.authEnv!==void 0)return u("a provider bundle must not declare authEnv",e.transport.authEnv,"no authEnv: the runtime presents its own outbound token","Remove transport.authEnv from bundle.json.","bundle.json.transport.authEnv");const r=new Set(n.groups.map(s=>s.group));for(const{path:s,leaf:d}of pe(o)){const p="group"in d?d.group:void 0;if(p===void 0)return u(`provider leaf "${s}" has no group`,s,"a group on every leaf of a provider bundle",`Add a group to leaf "${s}".`,`commands.json:${s}.group`);if(!r.has(p))return u(`provider leaf "${s}" names undeclared group "${p}"`,p,`one of: ${[...r].join(", ")}`,`Declare group "${p}" in the provider block, or move leaf "${s}" to a declared group.`,`commands.json:${s}.group`);if("rest"in d&&d.rest.streaming===!0)return u(`provider leaf "${s}" streams`,"rest.streaming: true","no streaming leaves in a provider bundle",`Make leaf "${s}" return one response.`,`commands.json:${s}.rest.streaming`)}}c(ce,"providerBundleIssue");function me(e){let o;try{o=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issue:u("bundle.json is not valid JSON","invalid JSON",'{"bundleVersion":1}',"Write valid bundle metadata.","bundle.json")}}if(typeof o!="object"||o===null||Array.isArray(o))return{issue:u("bundle.json must be a JSON object",JSON.stringify(o),"an object with bundleVersion set to 1 and optionally kinds",'Set bundle.json to {"bundleVersion":1}.',"bundle.json")};const n=o,r=Object.keys(n).filter(a=>!oe.has(a));if(n.bundleVersion!==1||r.length>0)return{issue:u("bundle.json must contain bundleVersion 1 and at most version, transport, provider, kinds, page_components, memory_extensions, object_kinds, scopes, and groups",JSON.stringify(o),"an object with bundleVersion set to 1 and optional version, transport, provider, kinds, page_components, memory_extensions, object_kinds, scopes, and groups",'Set bundle.json to {"bundleVersion":1} plus optional contribution blocks.',"bundle.json")};if(n.kinds!==void 0){const a=F(n.kinds);if(a.length>0)return{issue:u("bundle.json kinds block is invalid",a.join("; "),"each entry a full KindConfig (whenToUse required) or a sparse patch of launch fields","Fix the kinds entries in bundle.json.","bundle.json")}}if(n.page_components!==void 0){const a=K(n.page_components);if(a.length>0)return{issue:u("bundle.json page_components block is invalid",a.join("; "),"an array of page component registrations, each a kind string or {kind, description?, useWhen?, doc?, display?}","Fix the page_components entries in bundle.json.","bundle.json")}}const s=Z(n);if(n.provider!==void 0&&n.scopes!==void 0&&s.push("scopes is for local plugins; a provider gets its scopes from directory consent"),s.length>0)return{issue:u("bundle.json object_kinds or scopes block is invalid",s.join("; "),"object_kinds keyed by kind name with description, events and optional read.command; scopes an array of crtr:objects:<verb>:<target> strings (local plugins only)","Fix the object_kinds and scopes blocks in bundle.json.","bundle.json")};let d;if(n.memory_extensions!==void 0)try{d=X(n.memory_extensions)}catch(a){return{issue:u("bundle.json memory_extensions block is invalid",a instanceof Error?a.message:String(a),"a valid memory_extensions declaration object","Fix the memory_extensions declarations in bundle.json.","bundle.json")}}if(n.version!==void 0&&!N(n.version))return{issue:u("bundle.json version must be an integer of at least 1",JSON.stringify(n.version),"the plugin major as an integer >= 1, or omitted","Fix version in bundle.json.","bundle.json.version")};let p;if(n.transport!==void 0){const a=L(n.transport);if(a.transport===void 0)return{issue:u("bundle.json transport is invalid",a.errors.join("; "),'{ kind: "http", endpoint, authEnv? } or { kind: "machine" }',"Fix transport in bundle.json.","bundle.json.transport")};p=a.transport}let t;if(n.provider!==void 0){const a=le(n.provider);if(a.issue!==void 0)return{issue:a.issue};if(t=a.provider,n.version!==void 0&&n.version!==t.major)return{issue:u("bundle.json provider major must equal version",`version ${String(n.version)}, major ${t.major}`,"one major","Set provider.major and version to the same integer.","bundle.json.provider.major")}}return{...n.version!==void 0?{version:n.version}:{},...p!==void 0?{transport:p}:{},...t!==void 0?{provider:t}:{},...n.kinds!==void 0?{kinds:n.kinds}:{},...n.page_components!==void 0?{page_components:G(n.page_components)}:{},...d!==void 0?{memory_extensions:d}:{},...n.object_kinds!==void 0?{object_kinds:n.object_kinds}:{},...n.scopes!==void 0?{scopes:n.scopes}:{}}}c(me,"parseBundleMetadata");function fe(e,o,n,r=new Set){let s;try{s=JSON.parse(Buffer.from(e).toString("utf8"))}catch{return{issues:[u("commands.json is not valid JSON","invalid JSON","a valid command-manifest JSON object","Regenerate commands.json.","commands.json")]}}const d=B(s,{transport:"http",reservedCoreNames:o,extensibleCoreBranches:r,...n!==void 0?{coreCommandPaths:n}:{}});return{...d.manifest!==void 0?{commands:d.manifest}:{},issues:d.issues}}c(fe,"parseCommands");async function he(e,o){const n=se(e);if(n!==void 0)return{issues:[n]};const r=await ue(e);if(r.issue!==void 0||r.members===void 0)return{issues:[r.issue]};const s=de(r.members);if(s!==void 0)return{issues:[s]};const d=r.members.find(l=>l.path==="bundle.json"&&l.type==="file"),p=r.members.find(l=>l.path==="commands.json"&&l.type==="file");if(d===void 0||p===void 0)return{issues:[u("bundle must contain exactly one bundle.json and one commands.json regular file",`bundle.json=${d!==void 0}, commands.json=${p!==void 0}`,"one regular bundle.json and one regular commands.json member","Add the required metadata members to the archive.")]};const t=r.members.find(l=>l.path==="hooks.json"&&l.type==="file"),a=r.members.filter(l=>l.type==="file"&&l.path.startsWith("hooks/"));if((t!==void 0||r.members.some(l=>l.path==="hooks"||l.path.startsWith("hooks/")))&&(t===void 0||!a.some(l=>l.path==="hooks/dispatch")))return{issues:[u("bundle hook payload must contain both hooks.json and hooks/dispatch regular files",`hooks.json=${t!==void 0}, hooks/dispatch=${a.some(l=>l.path==="hooks/dispatch")}`,"both hooks.json and hooks/dispatch regular files","Add both required hook members or remove the hook payload.")]};const i=me(d.bytes);if(i.issue!==void 0)return{issues:[i.issue]};const b=fe(p.bytes,o.reservedCoreNames,o.coreCommandPaths,o.extensibleCoreBranches);if(b.commands===void 0)return{issues:b.issues};const h=b.commands.version;if(i.version!==void 0&&h!==void 0&&i.version!==h)return{issues:[u("bundle.json version and commands.json version differ",`bundle.json ${i.version}, commands.json ${h}`,"one plugin major","Emit the same version in bundle.json and commands.json.","bundle.json.version")]};const m=i.version??h;if(i.provider!==void 0&&m===void 0)return{issues:[u("a provider bundle requires a plugin version","no version in bundle.json or commands.json",`version ${i.provider.major}, the provider major`,"Emit the provider major as the plugin version.","bundle.json.version")]};if(i.provider!==void 0&&m!==i.provider.major)return{issues:[u("bundle provider major must equal the plugin version",`version ${m}, major ${i.provider.major}`,"one major","Emit the provider major as the plugin version.","bundle.json.provider.major")]};const v=ce(i,b.commands);return v!==void 0?{issues:[v]}:{bundle:{bundleVersion:1,...m!==void 0?{version:m}:{},...i.transport!==void 0?{transport:i.transport}:{},...i.provider!==void 0?{provider:i.provider}:{},...i.kinds!==void 0?{kinds:i.kinds}:{},...i.page_components!==void 0?{page_components:i.page_components}:{},...i.memory_extensions!==void 0?{memory_extensions:i.memory_extensions}:{},...i.object_kinds!==void 0?{object_kinds:i.object_kinds}:{},...i.scopes!==void 0?{scopes:i.scopes}:{},commands:b.commands,commandsBytes:p.bytes,directories:r.members.filter(l=>l.type==="directory").map(l=>({path:l.path})),memory:r.members.filter(l=>l.type==="file"&&l.path.startsWith("memory/")).map(l=>({path:l.path,bytes:l.bytes})),...t!==void 0?{hooks:{manifest:t.bytes,files:a.map(l=>({path:l.path,bytes:l.bytes}))}}:{}},issues:[]}}c(he,"validatePluginBundle");function be(e){return I("sha256").update(e).digest("hex").slice(0,12)}c(be,"bundleVersion");function Je(e,o,n,r){return M(o).plugins[e]?.version!==r?!1:_(w(n,"commands.json"))}c(Je,"isInstalledGeneration");async function ve(e,o,n,r){await T(e,s=>{s.plugins[o]===void 0&&(s.plugins[o]={}),r.swapped&&(s.plugins[o].last_updated=E()),s.plugins[o].checked_at=E(),n===void 0?delete s.plugins[o].etag:s.plugins[o].etag=n})}c(ve,"recordCheckedGeneration");function g(e,o){const n=V(e,o);if(n===e||!n.startsWith(e+z))throw x(`bundle member path escapes the staging directory: ${o}`);return n}c(g,"stagedPath");function ge(e,o,n){const r=c((s,d,p=420)=>q(s,d,{mode:p,durable:!0}),"write");C(g(e,".crouter-plugin/plugin.json"),o,{durable:!0});for(const s of n.directories)j(g(e,s.path));r(g(e,"commands.json"),n.commandsBytes);for(const s of n.memory)r(g(e,s.path),s.bytes);if(n.hooks!==void 0){r(g(e,"hooks.json"),n.hooks.manifest);for(const s of n.hooks.files)r(g(e,s.path),s.bytes);W(g(e,"hooks/dispatch"),493)}U(e)}c(ge,"writeStagedBundle");async function Ie(e,o,n){const{scope:r,scopeRootPath:s,root:d}=n,p=await he(o,n.validation);if(p.bundle===void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,p.issues);const t=p.bundle,a=n.accept?.(t);if(a!==void 0)throw n.invalid(`HTTP plugin bundle for "${e}" is invalid`,[u(a,e,"the bundle this install asked for","Serve the requested bundle.")]);const f=n.version??be(o),i=t.provider!==void 0&&t.transport!==void 0?t.transport:n.transport,b={name:e,version:f,commands:"commands.json",transport:i,bundle:n.bundle,...t.kinds!==void 0?{kinds:t.kinds}:{},...t.page_components!==void 0?{page_components:t.page_components}:{},...t.memory_extensions!==void 0?{memory_extensions:t.memory_extensions}:{},...t.object_kinds!==void 0?{object_kinds:t.object_kinds}:{},...t.scopes!==void 0?{scopes:t.scopes}:{},...t.hooks!==void 0?{hooks:"hooks.json",hookExecutable:"hooks/dispatch"}:{},...t.provider!==void 0?{provider:t.provider}:{}},h=w(s,"tmp"),m=w(h,`${e}.${process.pid}`),v=`${m}.prev`;if(_(v))throw x(`cannot replace plugin "${e}": prior interrupted replacement remains at ${v}`);j(h),j($(d)),S(m);let l;try{j(m),ge(m,b,t),l=await n.validateStaged(m,b);let y=!1;_(d)&&(k(d,v),y=!0);try{k(m,d)}catch(A){throw y&&k(v,d),A}H($(d)),y&&S(v)}finally{S(m)}return n.record!==!1&&(await R(r,s),await D(r,y=>{y.plugins[e]={enabled:n.enable?!0:y.plugins[e]?.enabled??!0,version:f}}),await ve(r,e,n.etag,{swapped:!0})),{manifest:b,validated:t,version:f,root:d,staged:l}}c(Ie,"installBundleArchive");export{be as bundleVersion,Ie as installBundleArchive,Je as isInstalledGeneration,ve as recordCheckedGeneration,he as validatePluginBundle,ge as writeStagedBundle};
package/dist/core/io.js CHANGED
@@ -1,7 +1,7 @@
1
- var _=Object.defineProperty;var i=(e,t)=>_(e,"name",{value:t,configurable:!0});import{appendFileSync as b,statSync as w}from"node:fs";import{CrtrError as c}from"./errors.js";import{ExitCode as a}from"../types.js";import{PREVIEW_RESULT_PATH_ENV as h}from"./preview-result-path.js";import{renderError as A}from"./render.js";import{ApiError as E}from"../api/index.js";let p=!1,f;const v=256*1024;function q(e){const t=process.env[h];f=t===void 0||t===""?void 0:{path:e,jsonl:[]}}i(q,"beginPreview");function y(e){const t=process.env[h];if(t===void 0||t==="")return;const s=`${JSON.stringify({path:f?.path??"",...e})}
2
- `;try{if((w(t,{throwIfNoEntry:!1})?.size??0)+Buffer.byteLength(s,"utf8")>v)return;b(t,s,"utf8")}catch{}}i(y,"publishPreview");function B(e){f!==void 0&&(e!=null?y({result:e}):(f.jsonl.length>0||f.error!==void 0)&&y({...f.jsonl.length>0?{jsonl:f.jsonl}:{},...f.error===void 0?{}:{error:f.error}}))}i(B,"publishPreviewResult");function g(e){f?.jsonl.push(e)}i(g,"recordPreviewLine");function G(e){try{const t=JSON.parse(e);t!==null&&typeof t=="object"&&!Array.isArray(t)&&g(t)}catch{}}i(G,"recordPreviewJsonLine");function F(e){f!==void 0&&(f.error=e)}i(F,"recordPreviewError");function K(e){p=e}i(K,"setJsonOutput");function z(){return p}i(z,"isJsonOutput");class m extends c{static{i(this,"InputError")}payload;constructor(t,s=a.USAGE){super(t.error,t.message,s,{...t}),this.name="InputError",this.payload=t}}async function D(){if(process.stdin.isTTY)return"";const e=[];for await(const t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf8")}i(D,"readStdinRaw");async function V(e=200){return process.stdin.isTTY?"":await new Promise(t=>{const s=[];let n=!1;const r=i(()=>{n||(n=!0,clearTimeout(u),process.stdin.removeListener("data",o),process.stdin.removeListener("end",l),process.stdin.removeListener("error",d),process.stdin.pause(),t(Buffer.concat(s).toString("utf8")))},"finish"),o=i(x=>s.push(x),"onData"),l=i(()=>r(),"onEnd"),d=i(()=>r(),"onError"),u=setTimeout(r,e);process.stdin.on("data",o),process.stdin.on("end",l),process.stdin.on("error",d),process.stdin.resume()})}i(V,"peekStdinRaw");function Y(e){process.stdout.write(JSON.stringify(e,null,2)+`
1
+ var _=Object.defineProperty;var i=(e,t)=>_(e,"name",{value:t,configurable:!0});import{appendFileSync as b,statSync as w}from"node:fs";import{CrtrError as c}from"./errors.js";import{ExitCode as u}from"../types.js";import{PREVIEW_RESULT_PATH_ENV as h}from"./preview-result-path.js";import{renderError as A}from"./render.js";import{ApiError as E}from"../api/index.js";let p=!1,f;const v=256*1024;function q(e){const t=process.env[h];f=t===void 0||t===""?void 0:{path:e,jsonl:[]}}i(q,"beginPreview");function y(e){const t=process.env[h];if(t===void 0||t==="")return;const s=`${JSON.stringify({path:f?.path??"",...e})}
2
+ `;try{if((w(t,{throwIfNoEntry:!1})?.size??0)+Buffer.byteLength(s,"utf8")>v)return;b(t,s,"utf8")}catch{}}i(y,"publishPreview");function B(e){f!==void 0&&(e!=null?y({result:e}):(f.jsonl.length>0||f.error!==void 0)&&y({...f.jsonl.length>0?{jsonl:f.jsonl}:{},...f.error===void 0?{}:{error:f.error}}))}i(B,"publishPreviewResult");function g(e){f?.jsonl.push(e)}i(g,"recordPreviewLine");function G(e){try{const t=JSON.parse(e);t!==null&&typeof t=="object"&&!Array.isArray(t)&&g(t)}catch{}}i(G,"recordPreviewJsonLine");function F(e){f!==void 0&&(f.error=e)}i(F,"recordPreviewError");function K(e){p=e}i(K,"setJsonOutput");function z(){return p}i(z,"isJsonOutput");class m extends c{static{i(this,"InputError")}payload;constructor(t,s=u.USAGE){super(t.error,t.message,s,{...t}),this.name="InputError",this.payload=t}}async function D(){if(process.stdin.isTTY)return"";const e=[];for await(const t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf8")}i(D,"readStdinRaw");async function V(e=200){return process.stdin.isTTY?"":await new Promise(t=>{const s=[];let n=!1;const r=i(()=>{n||(n=!0,clearTimeout(a),process.stdin.removeListener("data",o),process.stdin.removeListener("end",l),process.stdin.removeListener("error",d),process.stdin.pause(),t(Buffer.concat(s).toString("utf8")))},"finish"),o=i(x=>s.push(x),"onData"),l=i(()=>r(),"onEnd"),d=i(()=>r(),"onError"),a=setTimeout(r,e);process.stdin.on("data",o),process.stdin.on("end",l),process.stdin.on("error",d),process.stdin.resume()})}i(V,"peekStdinRaw");function Y(e){process.stdout.write(JSON.stringify(e,null,2)+`
3
3
  `)}i(Y,"emit");function H(e){g(e),process.stdout.write(JSON.stringify(e)+`
4
4
  `)}i(H,"emitLine");function M(e){return new Promise(t=>{let s=!1;const n=i(o=>{o.code==="EPIPE"&&r(!1)},"onErr"),r=i(o=>{s||(s=!0,process.stdout.off("error",n),t(o))},"finish");process.stdout.on("error",n);try{process.stdout.write(e,o=>r(o==null))}catch{r(!1)}})}i(M,"writeStdout");function X(e){process.stderr.write(e+`
5
- `)}i(X,"diag");function S(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}i(S,"isRecord");function T(e){if(e instanceof m)return e.payload;const t=e.details!==void 0?e.details:{},s=t.primaryCompleted===!0&&t.result!==null&&typeof t.result=="object"&&!Array.isArray(t.result)?O(t.result):void 0,n=t.next!==void 0?t.next:"Inspect the error and adjust the call. See -h for the schema.",r={error:e.code,message:e.message,received:t.received,field:t.field,...typeof t.http_status=="number"?{http_status:t.http_status}:{},...typeof t.retry_after_s=="number"?{retry_after_s:t.retry_after_s}:{},...S(t.details)?{details:t.details}:{}};return s===void 0?{...r,next:n}:{...r,primaryCompleted:!0,result:s,next:n}}i(T,"payloadOf");function O(e){const t=new WeakSet;try{const s=JSON.stringify(e,(n,r)=>{if(typeof r=="bigint")return{$crtrType:"bigint",value:r.toString()};if(typeof r=="number"&&!Number.isFinite(r))return{$crtrType:"number",value:String(r)};if(typeof r>"u"||typeof r=="function"||typeof r=="symbol")return{$crtrType:typeof r};if(r!==null&&typeof r=="object"){if(t.has(r))return{$crtrType:"circular"};t.add(r)}return r});if(s!==void 0){const n=JSON.parse(s);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}}catch{}return{$crtrType:"unavailable",message:"The completed primary result could not be represented as JSON."}}i(O,"jsonSafeResult");const N={400:"invalid_request",404:"not_found",409:"conflict",422:"unprocessable"};function R(e,t,s={}){if(e.code==="daemon_restarting")return new c("daemon_restarting",e.message,a.NETWORK,{next:"crtrd is now on the new generation \u2014 retry the command."});if(e.code==="layout_migration_required")return new c("layout_migration_required",e.message,a.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(e.code==="daemon_unavailable")return new c("daemon_unavailable",e.message,a.NETWORK,{next:"Start it with `crtr sys daemon start`, then retry."});if(e.code==="model_sign_in_required"){const d=e.details?.url;return new m({error:e.code,message:e.message,next:typeof d=="string"?`Ask the person to sign in at ${d}, then retry.`:"Ask the person to sign in to an AI subscription on their Northlight account, then retry.",...typeof d=="string"?{details:{url:d}}:{}},a.USAGE)}const n=e.details,r=$(e,n,s.providerCall===!0);if(e.status===503||s.providerCall===!0&&e.status===504&&r.code!==void 0){const d=r.code??e.code,u={next:r.next??t??"Retry the command; if it persists, inspect the unavailable service named in the error."};return r.details!==void 0&&(u.details=r.details),r.code!==void 0&&(r.field!==void 0&&(u.field=r.field),r.received!==void 0&&(u.received=r.received),r.retryAfterS!==void 0&&(u.retry_after_s=r.retryAfterS)),new c(d,e.message,a.NETWORK,u)}const o=Array.isArray(n?.violations)?n.violations.map(d=>`${String(d.field)} ${String(d.problem)}${d.received!==void 0?` (received ${JSON.stringify(d.received)})`:""}`):[],l=n&&typeof n.error=="string"&&typeof n.message=="string"&&typeof n.next=="string"?n:{error:r.code??N[e.status]??e.code,...r.field!==void 0?{field:r.field}:{},...r.received!==void 0?{received:r.received}:{},...r.retryAfterS!==void 0?{retry_after_s:r.retryAfterS}:{},...r.details!==void 0?{details:r.details}:{},message:o.length>0?`${e.message}: ${o.join("; ")}`:e.message.trim()!==""?e.message:`request refused (HTTP ${e.status}, ${e.code}) with no message`,next:r.next??t??(r.retryAfterS!==void 0?`Wait ${r.retryAfterS}s, then retry; do not retry sooner.`:e.status>=500?"This is a crtr bug, not a bad call. Retry; if it persists, report it (the daemon log holds the full cause).":e.status===404?"List nodes with `crtr node inspect list`.":"Inspect the error and adjust the call. See -h for the schema.")};return new m(l,j(l.error,e.status))}i(R,"apiErrorToCliError");function $(e,t,s){const n=i(o=>typeof o=="string"&&/^[a-z][a-z0-9_]*$/.test(o),"snake");if(s&&e.origin!==void 0&&e.origin!=="provider"&&n(e.code)){const o={code:e.code};return(e.status===503||e.status===504)&&(o.next=e.retryable===!0?"Retry the command; if it persists, the machine or service named in the error is unavailable.":"Read the error before retrying: the action may already have run."),S(e.details)&&(o.details=e.details),e.param!==void 0&&(o.field=e.param),typeof e.retryAfterS=="number"&&(o.retryAfterS=e.retryAfterS),o}if(e.origin!=="provider"||t===void 0||t===null||typeof t!="object")return{};const r={};return typeof t.status=="number"&&n(t.code)&&(r.code=t.code),typeof t.next=="string"&&t.next.trim()!==""&&(r.next=t.next),typeof t.field=="string"&&t.field!==""?r.field=t.field:e.param!==void 0&&(r.field=e.param),t.received!==void 0&&t.received!==null&&(r.received=t.received),typeof e.retryAfterS=="number"&&(r.retryAfterS=e.retryAfterS),r}i($,"providerSupplied");function j(e,t){return e==="not_found"||t===404?a.NOT_FOUND:e==="ambiguous"?a.AMBIGUOUS:a.USAGE}i(j,"exitForCliCode");function P(e){if(e instanceof E){P(R(e));return}if(e instanceof c){const n=T(e);f!==void 0&&y({error:n});const r=p?JSON.stringify(n,null,2):A(n);process.stdout.write(r+`
5
+ `)}i(X,"diag");function S(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}i(S,"isRecord");function T(e){if(e instanceof m)return e.payload;const t=e.details!==void 0?e.details:{},s=t.primaryCompleted===!0&&t.result!==null&&typeof t.result=="object"&&!Array.isArray(t.result)?O(t.result):void 0,n=t.next!==void 0?t.next:"Inspect the error and adjust the call. See -h for the schema.",r={error:e.code,message:e.message,received:t.received,field:t.field,...typeof t.http_status=="number"?{http_status:t.http_status}:{},...typeof t.retry_after_s=="number"?{retry_after_s:t.retry_after_s}:{},...S(t.details)?{details:t.details}:{}};return s===void 0?{...r,next:n}:{...r,primaryCompleted:!0,result:s,next:n}}i(T,"payloadOf");function O(e){const t=new WeakSet;try{const s=JSON.stringify(e,(n,r)=>{if(typeof r=="bigint")return{$crtrType:"bigint",value:r.toString()};if(typeof r=="number"&&!Number.isFinite(r))return{$crtrType:"number",value:String(r)};if(typeof r>"u"||typeof r=="function"||typeof r=="symbol")return{$crtrType:typeof r};if(r!==null&&typeof r=="object"){if(t.has(r))return{$crtrType:"circular"};t.add(r)}return r});if(s!==void 0){const n=JSON.parse(s);if(n!==null&&typeof n=="object"&&!Array.isArray(n))return n}}catch{}return{$crtrType:"unavailable",message:"The completed primary result could not be represented as JSON."}}i(O,"jsonSafeResult");const N={400:"invalid_request",404:"not_found",409:"conflict",422:"unprocessable"};function R(e,t,s={}){if(e.code==="daemon_restarting")return new c("daemon_restarting",e.message,u.NETWORK,{next:"crtrd is now on the new generation \u2014 retry the command."});if(e.code==="layout_migration_required")return new c("layout_migration_required",e.message,u.NETWORK,{next:"Run `crtr sys migrate` while the runtime is offline, then restart it."});if(e.code==="daemon_unavailable")return new c("daemon_unavailable",e.message,u.NETWORK,{next:"Start it with `crtr sys daemon start`, then retry."});if(e.code==="model_sign_in_required"){const d=e.details?.url;return new m({error:e.code,message:e.message,next:typeof d=="string"?`Ask the person to sign in at ${d}, then retry.`:"Ask the person to sign in to an AI subscription on their Northlight account, then retry.",...typeof d=="string"?{details:{url:d}}:{}},u.USAGE)}const n=e.details,r=$(e,n,s.providerCall===!0);if(e.status===503||s.providerCall===!0&&e.status===504&&r.code!==void 0){const d=r.code??e.code,a={next:r.next??t??"Retry the command; if it persists, inspect the unavailable service named in the error."};return r.details!==void 0&&(a.details=r.details),r.code!==void 0&&(r.field!==void 0&&(a.field=r.field),r.received!==void 0&&(a.received=r.received),r.retryAfterS!==void 0&&(a.retry_after_s=r.retryAfterS)),new c(d,e.message,u.NETWORK,a)}const o=Array.isArray(n?.violations)?n.violations.map(d=>`${String(d.field)} ${String(d.problem)}${d.received!==void 0?` (received ${JSON.stringify(d.received)})`:""}`):[],l=n&&typeof n.error=="string"&&typeof n.message=="string"&&typeof n.next=="string"?n:{error:r.code??N[e.status]??e.code,...r.field!==void 0?{field:r.field}:{},...r.received!==void 0?{received:r.received}:{},...r.retryAfterS!==void 0?{retry_after_s:r.retryAfterS}:{},...r.details!==void 0?{details:r.details}:{},message:o.length>0?`${e.message}: ${o.join("; ")}`:e.message.trim()!==""?e.message:`request refused (HTTP ${e.status}, ${e.code}) with no message`,next:r.next??t??(r.retryAfterS!==void 0?`Wait ${r.retryAfterS}s, then retry; do not retry sooner.`:e.status>=500?"This is a crtr bug, not a bad call. Retry; if it persists, report it (the daemon log holds the full cause).":e.status===404&&r.code===void 0?"List nodes with `crtr node inspect list`.":"Inspect the error and adjust the call. See -h for the schema.")};return new m(l,j(l.error,e.status))}i(R,"apiErrorToCliError");function $(e,t,s){const n=i(o=>typeof o=="string"&&/^[a-z][a-z0-9_]*$/.test(o),"snake");if(s&&e.origin!==void 0&&e.origin!=="provider"&&n(e.code)){const o={code:e.code};return(e.status===503||e.status===504)&&(o.next=e.retryable===!0?"Retry the command; if it persists, the machine or service named in the error is unavailable.":"Read the error before retrying: the action may already have run."),S(e.details)&&(o.details=e.details),e.param!==void 0&&(o.field=e.param),typeof e.retryAfterS=="number"&&(o.retryAfterS=e.retryAfterS),o}if(e.origin!=="provider"||t===void 0||t===null||typeof t!="object")return{};const r={};return typeof t.status=="number"&&n(t.code)&&(r.code=t.code),typeof t.next=="string"&&t.next.trim()!==""&&(r.next=t.next),typeof t.field=="string"&&t.field!==""?r.field=t.field:e.param!==void 0&&(r.field=e.param),t.received!==void 0&&t.received!==null&&(r.received=t.received),typeof e.retryAfterS=="number"&&(r.retryAfterS=e.retryAfterS),r}i($,"providerSupplied");function j(e,t){return e==="not_found"||t===404?u.NOT_FOUND:e==="ambiguous"?u.AMBIGUOUS:u.USAGE}i(j,"exitForCliCode");function P(e){if(e instanceof E){P(R(e));return}if(e instanceof c){const n=T(e);f!==void 0&&y({error:n});const r=p?JSON.stringify(n,null,2):A(n);process.stdout.write(r+`
6
6
  `),process.exitCode=e.exitCode;return}const t=e,s=t!=null&&typeof t.message=="string"?t.message:String(e);process.stderr.write(JSON.stringify({error:"internal",message:s,next:"This is a crtr bug, not a bad call. Retry; if it persists, report it."},null,2)+`
7
- `),process.exitCode=a.GENERAL}i(P,"handle");export{m as InputError,v as PREVIEW_RESULT_MAX_BYTES,R as apiErrorToCliError,q as beginPreview,X as diag,Y as emit,H as emitLine,P as handle,z as isJsonOutput,V as peekStdinRaw,B as publishPreviewResult,D as readStdinRaw,F as recordPreviewError,G as recordPreviewJsonLine,g as recordPreviewLine,K as setJsonOutput,M as writeStdout};
7
+ `),process.exitCode=u.GENERAL}i(P,"handle");export{m as InputError,v as PREVIEW_RESULT_MAX_BYTES,R as apiErrorToCliError,q as beginPreview,X as diag,Y as emit,H as emitLine,P as handle,z as isJsonOutput,V as peekStdinRaw,B as publishPreviewResult,D as readStdinRaw,F as recordPreviewError,G as recordPreviewJsonLine,g as recordPreviewLine,K as setJsonOutput,M as writeStdout};
@@ -1,3 +1,3 @@
1
- var Oe=Object.defineProperty;var c=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{randomUUID as Se}from"node:crypto";import{streamSimple as Le}from"@earendil-works/pi-ai/compat";import{probeRouteAvailability as Ee}from"../model-routes-pool.js";import{APP_FAMILY as Fe,expandModelCandidates as _e,parseModelSpec as ye}from"../model-routes.js";import{readMergedLaunchConfig as ge}from"../config.js";import{openModelRegistry as Ne}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as we,coolingUntilFor as be,isManagedProvider as Y,markSubscriptionRateLimited as De,markSubscriptionSuccess as Pe,readSubscriptionPool as Te,retryAfterMsFromHeaders as Z,retryAfterMsFromError as Je,refreshSubscriptionCredential as Ke,subscriptionNeedsSuccessWrite as We}from"../subscription-state.js";import{classifyRateLimit as Ue,describeRateLimitScope as ve,rateLimitHeaders as Be}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as le}from"../provider-error-recognition.js";import{isSafetyRefusal as He}from"./refusal.js";import{findRefusal as ze,payerOf as ke,refusalError as Xe,refusedMark as Ge,writeCharge as xe}from"../ledger/index.js";import{storedApiKey as Ye}from"../auth-file.js";import{personIdAsync as Ze}from"../canvas/person.js";import{getNode as qe}from"../canvas/canvas.js";import{outboundToken as Qe}from"../providers/mint.js";import{discardFile as Ve}from"../providers/token-cache.js";import{ProviderError as et}from"../providers/errors.js";import{appCandidates as tt}from"./app-rungs.js";function nt(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}c(nt,"validContent");function Ae(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,o,i]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:o,data:i}}):e??""}c(Ae,"imageContent");function ot(e,t){const o=(e instanceof Error?e.message:String(e)).split("; stack=",1)[0],i=le({message:o}).kind;return i!=="connection"&&i!=="connection-only"?e:new s(503,"upstream_model_unavailable",o.slice(0,500),void 0,{served_model:t})}c(ot,"refreshFailure");function Ot(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(o=>o&&typeof o=="object"&&["system","user","assistant","tool"].includes(o.role)&&nt(o.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(o=>["system","assistant"].includes(o.role)&&typeof o.content!="string"&&o.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(o=>o?.type==="function"&&typeof o.function?.name=="string"&&o.function.name.length>0&&(o.function.description===void 0||typeof o.function.description=="string")&&(o.function.parameters===void 0||o.function.parameters!==null&&typeof o.function.parameters=="object"&&!Array.isArray(o.function.parameters)&&o.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(o=>o.role==="assistant"&&o.tool_calls!==void 0&&(!Array.isArray(o.tool_calls)||!o.tool_calls.every(i=>i&&typeof i.id=="string"&&typeof i.function?.name=="string"&&typeof i.function.arguments=="string"&&rt(i.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(o=>typeof o=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[o,i]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(i!==void 0&&(typeof i!="number"||!Number.isFinite(i)||o==="temperature"&&(i<0||i>2)||o==="top_p"&&(i<0||i>1)||o==="max_tokens"&&(!Number.isSafeInteger(i)||i<1)))throw new s(400,"invalid_request",`invalid ${o}`,o);if(t.response_format!==void 0){const o=t.response_format;if(!o||typeof o!="object"||Array.isArray(o)||!["text","json_object","json_schema"].includes(o.type)||o.type==="json_schema"&&(typeof o.json_schema?.name!="string"||!o.json_schema.name||!o.json_schema.schema||typeof o.json_schema.schema!="object"||Array.isArray(o.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}c(Ot,"parseLlmRequest");function rt(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}c(rt,"isJsonObject");function it(e){const t=Date.now(),o=e.messages.map(r=>r.role==="system"?{role:"system",content:r.content??"",timestamp:t}:r.role==="tool"?{role:"toolResult",toolCallId:r.tool_call_id??"",toolName:"",content:typeof r.content=="string"||r.content===null?[{type:"text",text:r.content??""}]:Ae(r.content),isError:!1,timestamp:t}:r.role==="assistant"?{role:"assistant",content:[...r.content?[{type:"text",text:r.content}]:[],...(r.tool_calls??[]).map(h=>({type:"toolCall",id:h.id,name:h.function.name,arguments:JSON.parse(h.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:Ae(r.content),timestamp:t}),i=o.findIndex(r=>r.role!=="system"),l=o.slice(0,i<0?o.length:i).map(r=>r.content),g=l.join(`
1
+ var Oe=Object.defineProperty;var c=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{randomUUID as Se}from"node:crypto";import{streamSimple as Le}from"@earendil-works/pi-ai/compat";import{probeRouteAvailability as Ee}from"../model-routes-pool.js";import{APP_FAMILY as Fe,expandModelCandidates as _e,parseModelSpec as ye}from"../model-routes.js";import{readMergedLaunchConfig as ge}from"../config.js";import{openModelRegistry as Ne}from"../runtime/model-registry.js";import{MAX_RATE_LIMIT_COOLDOWN_MS as we,coolingUntilFor as be,isManagedProvider as Y,markSubscriptionRateLimited as De,markSubscriptionSuccess as Pe,readSubscriptionPool as Te,retryAfterMsFromHeaders as Z,retryAfterMsFromError as Je,refreshSubscriptionCredential as Ke,subscriptionNeedsSuccessWrite as We,RefreshBusyError as Ue,RefreshTimeoutError as Be}from"../subscription-state.js";import{classifyRateLimit as He,describeRateLimitScope as ve,rateLimitHeaders as ze}from"../rate-limit-scope.js";import{RunError as s}from"../runs/operations.js";import{recognizeProviderError as le}from"../provider-error-recognition.js";import{isSafetyRefusal as Xe}from"./refusal.js";import{findRefusal as Ge,payerOf as ke,refusalError as Ye,refusedMark as Ze,writeCharge as xe}from"../ledger/index.js";import{storedApiKey as Qe}from"../auth-file.js";import{personIdAsync as Ve}from"../canvas/person.js";import{getNode as qe}from"../canvas/canvas.js";import{outboundToken as et}from"../providers/mint.js";import{discardFile as tt}from"../providers/token-cache.js";import{ProviderError as nt}from"../providers/errors.js";import{appCandidates as ot}from"./app-rungs.js";function rt(e){return typeof e=="string"||e===null||Array.isArray(e)&&e.every(t=>t&&typeof t=="object"&&(t.type==="text"&&typeof t.text=="string"||t.type==="image_url"&&typeof t.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(t.image_url.url)))}c(rt,"validContent");function Ae(e){return Array.isArray(e)?e.map(t=>{if(t.type==="text")return t;const[,o,i]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(t.image_url.url);return{type:"image",mimeType:o,data:i}}):e??""}c(Ae,"imageContent");function it(e,t){const o=(e instanceof Error?e.message:String(e)).split("; stack=",1)[0],i=le({message:o}).kind;return i!=="connection"&&i!=="connection-only"?e:new s(503,"upstream_model_unavailable",o.slice(0,500),void 0,{served_model:t})}c(it,"refreshFailure");function Lt(e){if(!e||typeof e!="object"||Array.isArray(e))throw new s(400,"invalid_request","body must be an object");const t=e;if(!Array.isArray(t.messages)||!t.messages.every(o=>o&&typeof o=="object"&&["system","user","assistant","tool"].includes(o.role)&&rt(o.content)))throw new s(400,"invalid_request","messages must be chat messages","messages");if(t.messages.some(o=>["system","assistant"].includes(o.role)&&typeof o.content!="string"&&o.content!==null))throw new s(400,"invalid_request","system and assistant content must be text","messages");if(t.model!==void 0&&typeof t.model!="string")throw new s(400,"invalid_request","model must be a string","model");if(t.model_policy!==void 0&&!["exact","substitute"].includes(t.model_policy))throw new s(400,"invalid_request","invalid model_policy","model_policy");if(t.payer!==void 0&&!["user","app"].includes(t.payer))throw new s(400,"invalid_request","invalid payer","payer");if(t.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(t.reasoning_effort))throw new s(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(t.stream!==void 0&&typeof t.stream!="boolean")throw new s(400,"invalid_request","stream must be boolean","stream");if(t.tools!==void 0&&(!Array.isArray(t.tools)||!t.tools.every(o=>o?.type==="function"&&typeof o.function?.name=="string"&&o.function.name.length>0&&(o.function.description===void 0||typeof o.function.description=="string")&&(o.function.parameters===void 0||o.function.parameters!==null&&typeof o.function.parameters=="object"&&!Array.isArray(o.function.parameters)&&o.function.parameters.type==="object"))))throw new s(400,"invalid_request","invalid tools","tools");if(t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&t.tool_choice!=="required"&&(typeof t.tool_choice!="object"||t.tool_choice===null||t.tool_choice.type!=="function"||typeof t.tool_choice.function?.name!="string"))throw new s(400,"invalid_request","invalid tool_choice","tool_choice");if(t.messages.some(o=>o.role==="assistant"&&o.tool_calls!==void 0&&(!Array.isArray(o.tool_calls)||!o.tool_calls.every(i=>i&&typeof i.id=="string"&&typeof i.function?.name=="string"&&typeof i.function.arguments=="string"&&st(i.function.arguments)))))throw new s(400,"invalid_request","invalid assistant tool_calls","messages");if(t.stop!==void 0&&!(typeof t.stop=="string"||Array.isArray(t.stop)&&t.stop.every(o=>typeof o=="string")))throw new s(400,"invalid_request","invalid stop","stop");if(t.seed!==void 0&&!Number.isSafeInteger(t.seed))throw new s(400,"invalid_request","invalid seed","seed");for(const[o,i]of[["temperature",t.temperature],["top_p",t.top_p],["max_tokens",t.max_tokens]])if(i!==void 0&&(typeof i!="number"||!Number.isFinite(i)||o==="temperature"&&(i<0||i>2)||o==="top_p"&&(i<0||i>1)||o==="max_tokens"&&(!Number.isSafeInteger(i)||i<1)))throw new s(400,"invalid_request",`invalid ${o}`,o);if(t.response_format!==void 0){const o=t.response_format;if(!o||typeof o!="object"||Array.isArray(o)||!["text","json_object","json_schema"].includes(o.type)||o.type==="json_schema"&&(typeof o.json_schema?.name!="string"||!o.json_schema.name||!o.json_schema.schema||typeof o.json_schema.schema!="object"||Array.isArray(o.json_schema.schema)))throw new s(400,"invalid_request","invalid response_format","response_format")}if(t.metadata!==void 0&&Buffer.byteLength(JSON.stringify(t.metadata)??"")>4096)throw new s(400,"invalid_request","metadata exceeds 4 KB","metadata");return t}c(Lt,"parseLlmRequest");function st(e){try{const t=JSON.parse(e);return t!==null&&typeof t=="object"&&!Array.isArray(t)}catch{return!1}}c(st,"isJsonObject");function at(e){const t=Date.now(),o=e.messages.map(r=>r.role==="system"?{role:"system",content:r.content??"",timestamp:t}:r.role==="tool"?{role:"toolResult",toolCallId:r.tool_call_id??"",toolName:"",content:typeof r.content=="string"||r.content===null?[{type:"text",text:r.content??""}]:Ae(r.content),isError:!1,timestamp:t}:r.role==="assistant"?{role:"assistant",content:[...r.content?[{type:"text",text:r.content}]:[],...(r.tool_calls??[]).map(_=>({type:"toolCall",id:_.id,name:_.function.name,arguments:JSON.parse(_.function.arguments)}))],api:"openai-completions",provider:"openai",model:e.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:t}:{role:"user",content:Ae(r.content),timestamp:t}),i=o.findIndex(r=>r.role!=="system"),l=o.slice(0,i<0?o.length:i).map(r=>r.content),w=l.join(`
2
2
 
3
- `),p=e.tools?.map(({function:r})=>({name:r.name,description:r.description??"",parameters:r.parameters??{type:"object",properties:{}}}));return{...g?{systemPrompt:g}:{},messages:o.slice(l.length),...p?{tools:p}:{}}}c(it,"contextOf");function Me(e,t,o,i){const l=c(()=>{const p=ge(o,i),r=Object.values({...p.modelRoutes??{},...p.legacyModelRoutes??{}});return r.length?[p.modelLadders.defaultProvider,...p.modelRouting?.familyOrder??[],...r.map(h=>h.family)].find(h=>r.some($=>$.family===h)):p.modelLadders.defaultProvider??"anthropic"},"ownerFamily");if(!e){const p=l();return{kind:"logical",family:p,strength:"strong",spec:`${p}/strong`}}const g=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);if(g){if(g[1]===Fe){const p=l();return{kind:"logical",family:p,strength:g[2],spec:`${p}/${g[2]}`}}return{kind:"logical",family:g[1],strength:g[2],spec:e}}return{kind:t==="exact"?"exact":"concrete",spec:e}}c(Me,"requestFor");function st(e,t){const{input:o,output:i,cacheRead:l,cacheWrite:g}=e.cost;return o===0&&i===0&&l===0&&g===0?null:Number.isFinite(t)&&t>=0?t:null}c(st,"usdEquivalent");function at(e,t){return e.compat?.allowedFallbackModels?.find(i=>i.provider===e.provider&&i.model===t)}c(at,"allowedFallback");function St(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}c(St,"llmCallerOf");function dt(e){return e instanceof et?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}c(dt,"mintFailure");const lt=/\bmodel is not supported when using Codex with a ChatGPT account\b/i;function ct(e,t,o){if(!(e!=="openai-codex"||t===void 0||!lt.test(t)))return new s(404,"model_unavailable",t,void 0,{served_model:o})}c(ct,"codexAccountModelRejection");function ut(e){return e.get("request-id")??e.get("x-request-id")}c(ut,"vendorRequestId");async function Lt(e,t,o,i,l,g,p){const r=g?.payer==="app"?g:void 0;if(e.payer==="app"&&!r)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&r)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(r&&!r.inference_endpoint)throw new s(404,"model_unavailable",`${r.grantee} is app-paid but its grant carries no inference endpoint`);const h=r?.inference_endpoint??void 0,$=l===void 0?null:r?.sub??l.sub??await Ze(),N=await Ne(),ce=c(u=>u.routeId!=="concrete"&&u.routeId!=="exact","configured"),ue=c(u=>{const _=N.find(u.providerId,u.modelId);return _!==void 0&&(!e.reasoning_effort||_.reasoning)&&(e.seed===void 0&&e.response_format===void 0||_.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(_.api))},"matchesFeatures"),je=c(async()=>{const u=Me(e.model,e.model_policy,i?.cwd,i?.profileId),_=(await _e(u.kind==="exact"?{kind:"concrete",spec:u.spec}:u,i?.cwd,i?.profileId)).filter(ce);let a;if(u.kind==="logical")a=e.model_policy==="exact"?_.filter(f=>f.family===u.family&&f.strength===u.strength).slice(0,1):_;else{const f=ye(u.spec),L=_.filter(v=>v.providerId===f?.providerId&&v.modelId===f.modelId&&(e.model_policy!=="exact"||v.thinkingLevel===f.thinkingLevel)&&ue(v));if(e.model_policy==="exact")a=L.slice(0,1);else if(L.length)a=L;else{const v=ge(i?.cwd,i?.profileId),M=[...Object.values(v.modelRoutes??{}).flatMap(x=>Object.entries(x.models)),...[v.modelLadders.anthropic,v.modelLadders.openai].flatMap(x=>Object.entries(x))].find(([,x])=>x===u.spec)?.[0],D=Me(void 0,"substitute",i?.cwd,i?.profileId);a=M&&D.kind==="logical"?(await _e({...D,strength:M,spec:u.spec},i?.cwd,i?.profileId)).filter(x=>ce(x)&&x.strength===M):[]}}return a},"ownerCandidates");let O;if(h?O=tt(h,e.model,e.model_policy==="exact").flatMap((u,_)=>{const a=ye(u.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:u.strength,...a,rank:_}]:[]}):O=await je(),O=O.filter(ue),O.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const J=`chatcmpl-${Se()}`;let S=1/0,b,Q,V;const fe=c(async()=>p??(l?.node?(await qe(l.node))?.caused_by??null:null)??(l?.run?(await qe(l.run))?.caused_by??null:null),"causeOf");for(const u of O){if(!r&&!(await Ee(u,N)).authenticated)continue;const _=N.find(u.providerId,u.modelId);if(!_)continue;const a=h?{..._,baseUrl:`${h.base_url}/${_.provider}`}:_,f=u.providerId,L=!r&&Y(f)?await Te(f):[],v=L.filter(j=>!j.authFailure),ee=v.filter(j=>be(j,a.id)<=Date.now()),M=L.length?await Ye(f):void 0;if(v.length&&!ee.length&&M===void 0){const j=Math.min(...v.map(I=>be(I,a.id)));j<S&&(S=j,Q=`${a.provider}/${a.id}`);continue}const D=r?[void 0,void 0]:v.length||M!==void 0?[...ee,...M!==void 0?["key"]:[]]:[void 0];let x=!1;for(const[j,I]of D.entries()){if(x&&I!=="key")continue;const y=I==="key"?void 0:I,me=I==="key",K=Date.now();let pe=y?.access;if(y&&y.expires<=Date.now()&&Y(f)){const n=N.getProvider(f)?.auth.oauth;if(!n)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${f}`,void 0,{served_model:`${a.provider}/${a.id}`});pe=(await Ke(f,y.label,async(k,he)=>{const G=await n.refresh({type:"oauth",refresh:k,access:"",expires:0,...he?{clientId:he}:{}},t??new AbortController().signal).catch(Ce=>{throw ot(Ce,`${a.provider}/${a.id}`)}),de=G.clientId;return{refresh:G.refresh,access:G.access,expires:G.expires,...typeof de=="string"&&de?{clientId:de}:{}}})).access}const Re=it(e),E=y||r||me?void 0:await N.getApiKeyAndHeaders(a);if(E&&!E.ok)continue;const m=`${a.provider}/${a.id}`,F=e.model??m,R=r?ke({call:"model",route:"endpoint",grantee:r.grantee}):$===null?null:ke({call:"model",route:y?"subscription":"key",sub:$});if(l!==void 0&&R!==null){const n=await ze({scope:"call",payer:R.payer,funding:R.funding,sub:$,grantee:l.grantee,provider:f});if(n!==null){if(await xe({request:l.request??J,kind:"model",what:m,usd:null,payer:R.payer,funding:R.funding,sub:$,grantee:l.grantee,node:l.node??null,run:l.run??null,provider:f,causedBy:await fe(),refused:Ge(n)}),V??=n,I!=="key"&&D.includes("key")){x=!0;continue}break}}let W;if(r&&h)try{W=await Qe({audience:h.base_url,sub:r.sub,grantee:r.grantee,initiating_app:r.grantee,scope:["crtr:llm"]},{signal:t})}catch(n){throw dt(n)}let A=!1,P,te=null,q=3e4,C;const ne=[],oe=[],U=[];let d,B;const H=c((n,w=null)=>({id:J,object:"chat.completion.chunk",created:Math.floor(K/1e3),model:m,served_model:m,requested_model:F,substituted:F!==m&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(F),choices:[{index:0,delta:n,finish_reason:w}]}),"chunk"),z=c(n=>{ne.push(n),o?.(n)},"emit"),$e=Le(a,Re,{apiKey:W?.access_token??pe??(me?M:void 0)??(E?.ok?E.apiKey:void 0),...E?.ok?{headers:E.headers}:{},signal:t,reasoning:e.reasoning_effort??u.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:c(n=>{if(!n||typeof n!="object")return;const w=e.tool_choice,k=w==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof w=="object"&&w!==null?a.api==="anthropic-messages"?{type:"tool",name:w.function.name}:w:void 0;return{...n,...k?{tool_choice:k}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:c(async(n,w)=>{const k=await fetch(n,w);return P=k.status,te=ut(k.headers)??te,k.status===429&&(A=!0,C=k.headers,q=Z(k.headers)??15e3),k},"fetch"),onResponse:c(n=>{n.status===429&&(A=!0,C=n.headers,q=Z(n.headers)??15e3)},"onResponse")});try{for await(const n of $e)n.type==="text_delta"&&(oe.push(n.delta),z(H({content:n.delta}))),n.type==="thinking_delta"&&(U.push(n.delta),z(H({reasoning_content:n.delta}))),n.type==="done"&&(d=n.message),n.type==="error"&&(d=n.error)}catch(n){const w=Je(n);C===void 0&&n&&typeof n=="object"&&"headers"in n&&Object.keys(Be(n.headers)).length&&(C=n.headers);const k=le({status:n&&typeof n=="object"&&"status"in n?n.status:void 0,message:n instanceof Error?n.message:String(n)});if(r)B=n;else{if(!A&&k.kind!=="rate-limit")throw ne.length?new s(503,"upstream_model_unavailable",n instanceof Error?n.message:String(n),void 0,{served_model:m}):n;A=!0,q=w??q}}if(r){if(P===401){if(W&&Ve(W.key),j===0)continue;throw new s(404,"model_unavailable",`${h.base_url} refuses this runtime's tokens`,void 0,{served_model:m})}if(A&&Z(C)===void 0)throw new s(503,"upstream_model_unavailable",`${m} was rate-limited without a retry-after`,void 0,{served_model:m});if(!A&&(B!==void 0||P!==void 0&&P>=500))throw new s(503,"upstream_model_unavailable",B instanceof Error?B.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:m})}if(!A&&d?.stopReason==="error"&&le({message:d.errorMessage}).kind==="rate-limit"&&(A=!0,q=15e3),A){const n=Ue(C,a.id);if(Z(C)===void 0&&n.resetAt!==void 0&&(q=Math.max(q,n.resetAt-Date.now())),b={provider:f,model:a.id,...y?{account:y.label}:{},observation:n},console.error(`[llm] 429 from ${m}${y?` account "${y.label}"`:""}: scope=${ve(n.scope)} retry_after_ms=${q} headers=${JSON.stringify(n.headers)}`),y&&Y(f)&&await De(f,y.label,q,K,Date.now(),n.scope),Date.now()+Math.min(q,we)<S&&(S=Date.now()+Math.min(q,we),Q=m),oe.length||U.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:m});if(r)break;continue}const X=He(d);if(P===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:m});if(d?.stopReason==="error"){const n=ct(a.provider,d.errorMessage,m);if(n)throw n}if(!d||d.stopReason==="error"&&!X||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:m});y&&Y(f)&&We(y,a.id)&&await Pe(f,y.label,K,a.id);const re=d.content.filter(n=>n.type==="toolCall").map(n=>({id:n.id,type:"function",function:{name:n.name,arguments:JSON.stringify(n.arguments)}})),ie=X?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",se=d.responseModel?at(a,d.responseModel):void 0,T=se?`${a.provider}/${se.model}`:m,ae={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:st(se??a,d.usage.cost.total),served_model:T};l!==void 0&&R!==null&&await xe({request:l.request??J,kind:"model",what:T,units:{input:d.usage.input,output:d.usage.output,cache_read:d.usage.cacheRead,cache_write:d.usage.cacheWrite},usd:ae.usd_equivalent,payer:R.payer,funding:R.funding,sub:$,grantee:l.grantee,node:l.node??null,run:l.run??null,provider:f,causedBy:await fe(),vendorRef:te});const Ie={id:J,object:"chat.completion",created:Math.floor(K/1e3),model:T,served_model:T,requested_model:F,substituted:F!==T&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(F),choices:[{index:0,message:{role:"assistant",content:oe.join("")||null,...U.length?{reasoning_content:U.join("")}:{},...re.length?{tool_calls:re}:{},...X?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:ie}],usage:ae,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[n,w]of re.entries())z(H({tool_calls:[{index:n,...w}]}));return z({...H({},ie),...X?{choices:[{index:0,delta:{},finish_reason:ie,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:ae}),{completion:Ie,chunks:ne}}}throw V!==void 0?Xe(V):S!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((S-Date.now())/1e3)),served_model:b?`${b.provider}/${b.model}`:Q,...b?{rate_limit:{provider:b.provider,model:b.model,...b.account?{account:b.account}:{},scope:ve(b.observation.scope),...b.observation.claim?{claim:b.observation.claim}:{},headers:b.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}c(Lt,"runLlm");export{St as llmCallerOf,Ot as parseLlmRequest,Lt as runLlm};
3
+ `),h=e.tools?.map(({function:r})=>({name:r.name,description:r.description??"",parameters:r.parameters??{type:"object",properties:{}}}));return{...w?{systemPrompt:w}:{},messages:o.slice(l.length),...h?{tools:h}:{}}}c(at,"contextOf");function Me(e,t,o,i){const l=c(()=>{const h=ge(o,i),r=Object.values({...h.modelRoutes??{},...h.legacyModelRoutes??{}});return r.length?[h.modelLadders.defaultProvider,...h.modelRouting?.familyOrder??[],...r.map(_=>_.family)].find(_=>r.some($=>$.family===_)):h.modelLadders.defaultProvider??"anthropic"},"ownerFamily");if(!e){const h=l();return{kind:"logical",family:h,strength:"strong",spec:`${h}/strong`}}const w=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(e);if(w){if(w[1]===Fe){const h=l();return{kind:"logical",family:h,strength:w[2],spec:`${h}/${w[2]}`}}return{kind:"logical",family:w[1],strength:w[2],spec:e}}return{kind:t==="exact"?"exact":"concrete",spec:e}}c(Me,"requestFor");function dt(e,t){const{input:o,output:i,cacheRead:l,cacheWrite:w}=e.cost;return o===0&&i===0&&l===0&&w===0?null:Number.isFinite(t)&&t>=0?t:null}c(dt,"usdEquivalent");function lt(e,t){return e.compat?.allowedFallbackModels?.find(i=>i.provider===e.provider&&i.model===t)}c(lt,"allowedFallback");function Et(e){return e?{payer:e.payer??"user",sub:e.sub,grantee:e.grantee,inference_endpoint:e.inference_endpoint??null}:void 0}c(Et,"llmCallerOf");function ct(e){return e instanceof nt?e.code==="server_error"?new s(503,"upstream_model_unavailable",e.message):new s(404,"model_unavailable",e.message):e}c(ct,"mintFailure");const ut=/\bmodel is not supported when using Codex with a ChatGPT account\b/i;function ft(e,t,o){if(!(e!=="openai-codex"||t===void 0||!ut.test(t)))return new s(404,"model_unavailable",t,void 0,{served_model:o})}c(ft,"codexAccountModelRejection");function mt(e){return e.get("request-id")??e.get("x-request-id")}c(mt,"vendorRequestId");async function Ft(e,t,o,i,l,w,h){const r=w?.payer==="app"?w:void 0;if(e.payer==="app"&&!r)throw new s(403,"payer_not_app","this grant is not app-paid");if(e.payer==="user"&&r)throw new s(400,"invalid_request","this grant is app-paid; payer cannot be user","payer");if(r&&!r.inference_endpoint)throw new s(404,"model_unavailable",`${r.grantee} is app-paid but its grant carries no inference endpoint`);const _=r?.inference_endpoint??void 0,$=l===void 0?null:r?.sub??l.sub??await Ve(),N=await Ne(),ce=c(u=>u.unrouted!==!0,"configured"),ue=c(u=>{const y=N.find(u.providerId,u.modelId);return y!==void 0&&(!e.reasoning_effort||y.reasoning)&&(e.seed===void 0&&e.response_format===void 0||y.api==="openai-completions")&&(e.stop===void 0||["openai-completions","anthropic-messages"].includes(y.api))},"matchesFeatures"),je=c(async()=>{const u=Me(e.model,e.model_policy,i?.cwd,i?.profileId),y=(await _e(u.kind==="exact"?{kind:"concrete",spec:u.spec}:u,i?.cwd,i?.profileId)).filter(ce);let a;if(u.kind==="logical")a=e.model_policy==="exact"?y.filter(f=>f.family===u.family&&f.strength===u.strength).slice(0,1):y;else{const f=ye(u.spec),L=y.filter(k=>k.providerId===f?.providerId&&k.modelId===f.modelId&&(e.model_policy!=="exact"||k.thinkingLevel===f.thinkingLevel)&&ue(k));if(e.model_policy==="exact")a=L.slice(0,1);else if(L.length)a=L;else{const k=ge(i?.cwd,i?.profileId),M=[...Object.values(k.modelRoutes??{}).flatMap(x=>Object.entries(x.models)),...[k.modelLadders.anthropic,k.modelLadders.openai].flatMap(x=>Object.entries(x))].find(([,x])=>x===u.spec)?.[0],D=Me(void 0,"substitute",i?.cwd,i?.profileId);a=M&&D.kind==="logical"?(await _e({...D,strength:M,spec:u.spec},i?.cwd,i?.profileId)).filter(x=>ce(x)&&x.strength===M):[]}}return a},"ownerCandidates");let O;if(_?O=ot(_,e.model,e.model_policy==="exact").flatMap((u,y)=>{const a=ye(u.model);return a?[{routeId:"app",credentialSource:"app",family:"app",strength:u.strength,...a,rank:y}]:[]}):O=await je(),O=O.filter(ue),O.length===0)throw e.model_policy==="exact"?new s(400,"substitution_refused","no eligible model route is configured"):new s(404,"model_unavailable","no eligible model route is configured");const J=`chatcmpl-${Se()}`;let S=1/0,v,Q,V;const fe=c(async()=>h??(l?.node?(await qe(l.node))?.caused_by??null:null)??(l?.run?(await qe(l.run))?.caused_by??null:null),"causeOf");for(const u of O){if(!r&&!(await Ee(u,N)).authenticated)continue;const y=N.find(u.providerId,u.modelId);if(!y)continue;const a=_?{...y,baseUrl:`${_.base_url}/${y.provider}`}:y,f=u.providerId,L=!r&&Y(f)?await Te(f):[],k=L.filter(j=>!j.authFailure),ee=k.filter(j=>be(j,a.id)<=Date.now()),M=L.length?await Qe(f):void 0;if(k.length&&!ee.length&&M===void 0){const j=Math.min(...k.map(I=>be(I,a.id)));j<S&&(S=j,Q=`${a.provider}/${a.id}`);continue}const D=r?[void 0,void 0]:k.length||M!==void 0?[...ee,...M!==void 0?["key"]:[]]:[void 0];let x=!1;for(const[j,I]of D.entries()){if(x&&I!=="key")continue;const g=I==="key"?void 0:I,me=I==="key",K=Date.now();let pe=g?.access;if(g&&g.expires<=Date.now()&&Y(f)){const n=N.getProvider(f)?.auth.oauth;if(!n)throw new s(503,"upstream_model_unavailable",`no OAuth refresh for ${f}`,void 0,{served_model:`${a.provider}/${a.id}`});pe=(await Ke(f,g.label,async(m,he)=>{const G=await n.refresh({type:"oauth",refresh:m,access:"",expires:0,...he?{clientId:he}:{}},t??new AbortController().signal).catch(Ce=>{throw it(Ce,`${a.provider}/${a.id}`)}),de=G.clientId;return{refresh:G.refresh,access:G.access,expires:G.expires,...typeof de=="string"&&de?{clientId:de}:{}}}).catch(m=>{throw m instanceof Ue||m instanceof Be?new s(503,"upstream_model_unavailable",m.message,void 0,{served_model:`${a.provider}/${a.id}`}):m})).access}const Re=at(e),E=g||r||me?void 0:await N.getApiKeyAndHeaders(a);if(E&&!E.ok)continue;const p=`${a.provider}/${a.id}`,F=e.model??p,R=r?ke({call:"model",route:"endpoint",grantee:r.grantee}):$===null?null:ke({call:"model",route:g?"subscription":"key",sub:$});if(l!==void 0&&R!==null){const n=await Ge({scope:"call",payer:R.payer,funding:R.funding,sub:$,grantee:l.grantee,provider:f});if(n!==null){if(await xe({request:l.request??J,kind:"model",what:p,usd:null,payer:R.payer,funding:R.funding,sub:$,grantee:l.grantee,node:l.node??null,run:l.run??null,provider:f,causedBy:await fe(),refused:Ze(n)}),V??=n,I!=="key"&&D.includes("key")){x=!0;continue}break}}let W;if(r&&_)try{W=await et({audience:_.base_url,sub:r.sub,grantee:r.grantee,initiating_app:r.grantee,scope:["crtr:llm"]},{signal:t})}catch(n){throw ct(n)}let A=!1,P,te=null,q=3e4,C;const ne=[],oe=[],U=[];let d,B;const H=c((n,b=null)=>({id:J,object:"chat.completion.chunk",created:Math.floor(K/1e3),model:p,served_model:p,requested_model:F,substituted:F!==p&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(F),choices:[{index:0,delta:n,finish_reason:b}]}),"chunk"),z=c(n=>{ne.push(n),o?.(n)},"emit"),$e=Le(a,Re,{apiKey:W?.access_token??pe??(me?M:void 0)??(E?.ok?E.apiKey:void 0),...E?.ok?{headers:E.headers}:{},signal:t,reasoning:e.reasoning_effort??u.thinkingLevel,...e.temperature!==void 0?{temperature:e.temperature}:{},...e.max_tokens!==void 0?{maxTokens:e.max_tokens}:{},...e.top_p!==void 0?{samplingParams:{top_p:e.top_p}}:{},...e.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:c(n=>{if(!n||typeof n!="object")return;const b=e.tool_choice,m=b==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof b=="object"&&b!==null?a.api==="anthropic-messages"?{type:"tool",name:b.function.name}:b:void 0;return{...n,...m?{tool_choice:m}:{},...e.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof e.stop=="string"?[e.stop]:e.stop}:{},...e.seed!==void 0?{seed:e.seed}:{},...e.response_format!==void 0?{response_format:e.response_format}:{}}},"onPayload"),fetch:c(async(n,b)=>{const m=await fetch(n,b);return P=m.status,te=mt(m.headers)??te,m.status===429&&(A=!0,C=m.headers,q=Z(m.headers)??15e3),m},"fetch"),onResponse:c(n=>{n.status===429&&(A=!0,C=n.headers,q=Z(n.headers)??15e3)},"onResponse")});try{for await(const n of $e)n.type==="text_delta"&&(oe.push(n.delta),z(H({content:n.delta}))),n.type==="thinking_delta"&&(U.push(n.delta),z(H({reasoning_content:n.delta}))),n.type==="done"&&(d=n.message),n.type==="error"&&(d=n.error)}catch(n){const b=Je(n);C===void 0&&n&&typeof n=="object"&&"headers"in n&&Object.keys(ze(n.headers)).length&&(C=n.headers);const m=le({status:n&&typeof n=="object"&&"status"in n?n.status:void 0,message:n instanceof Error?n.message:String(n)});if(r)B=n;else{if(!A&&m.kind!=="rate-limit")throw ne.length?new s(503,"upstream_model_unavailable",n instanceof Error?n.message:String(n),void 0,{served_model:p}):n;A=!0,q=b??q}}if(r){if(P===401){if(W&&tt(W.key),j===0)continue;throw new s(404,"model_unavailable",`${_.base_url} refuses this runtime's tokens`,void 0,{served_model:p})}if(A&&Z(C)===void 0)throw new s(503,"upstream_model_unavailable",`${p} was rate-limited without a retry-after`,void 0,{served_model:p});if(!A&&(B!==void 0||P!==void 0&&P>=500))throw new s(503,"upstream_model_unavailable",B instanceof Error?B.message:d?.errorMessage??"inference endpoint failed",void 0,{served_model:p})}if(!A&&d?.stopReason==="error"&&le({message:d.errorMessage}).kind==="rate-limit"&&(A=!0,q=15e3),A){const n=He(C,a.id);if(Z(C)===void 0&&n.resetAt!==void 0&&(q=Math.max(q,n.resetAt-Date.now())),v={provider:f,model:a.id,...g?{account:g.label}:{},observation:n},console.error(`[llm] 429 from ${p}${g?` account "${g.label}"`:""}: scope=${ve(n.scope)} retry_after_ms=${q} headers=${JSON.stringify(n.headers)}`),g&&Y(f)&&await De(f,g.label,q,K,Date.now(),n.scope),Date.now()+Math.min(q,we)<S&&(S=Date.now()+Math.min(q,we),Q=p),oe.length||U.length)throw new s(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:p});if(r)break;continue}const X=Xe(d);if(P===400&&e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new s(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:p});if(d?.stopReason==="error"){const n=ft(a.provider,d.errorMessage,p);if(n)throw n}if(!d||d.stopReason==="error"&&!X||d.stopReason==="aborted")throw new s(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:p});g&&Y(f)&&We(g,a.id)&&await Pe(f,g.label,K,a.id);const re=d.content.filter(n=>n.type==="toolCall").map(n=>({id:n.id,type:"function",function:{name:n.name,arguments:JSON.stringify(n.arguments)}})),ie=X?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",se=d.responseModel?lt(a,d.responseModel):void 0,T=se?`${a.provider}/${se.model}`:p,ae={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:dt(se??a,d.usage.cost.total),served_model:T};l!==void 0&&R!==null&&await xe({request:l.request??J,kind:"model",what:T,units:{input:d.usage.input,output:d.usage.output,cache_read:d.usage.cacheRead,cache_write:d.usage.cacheWrite},usd:ae.usd_equivalent,payer:R.payer,funding:R.funding,sub:$,grantee:l.grantee,node:l.node??null,run:l.run??null,provider:f,causedBy:await fe(),vendorRef:te});const Ie={id:J,object:"chat.completion",created:Math.floor(K/1e3),model:T,served_model:T,requested_model:F,substituted:F!==T&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test(F),choices:[{index:0,message:{role:"assistant",content:oe.join("")||null,...U.length?{reasoning_content:U.join("")}:{},...re.length?{tool_calls:re}:{},...X?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:ie}],usage:ae,...e.metadata!==void 0?{metadata:e.metadata}:{}};for(const[n,b]of re.entries())z(H({tool_calls:[{index:n,...b}]}));return z({...H({},ie),...X?{choices:[{index:0,delta:{},finish_reason:ie,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:ae}),{completion:Ie,chunks:ne}}}throw V!==void 0?Ye(V):S!==1/0?new s(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((S-Date.now())/1e3)),served_model:v?`${v.provider}/${v.model}`:Q,...v?{rate_limit:{provider:v.provider,model:v.model,...v.account?{account:v.account}:{},scope:ve(v.observation.scope),...v.observation.claim?{claim:v.observation.claim}:{},headers:v.observation.headers}}:{}},void 0,!0):e.model_policy==="exact"?new s(400,"substitution_refused","the requested model is unavailable"):new s(404,"model_unavailable","no usable model route is available")}c(Ft,"runLlm");export{Et as llmCallerOf,Lt as parseLlmRequest,Ft as runLlm};
@@ -21,6 +21,8 @@ export type RoutedCandidate = {
21
21
  modelId: string;
22
22
  thinkingLevel?: string;
23
23
  rank: number;
24
+ /** Set on a candidate no configured route offers: an exact request, or a concrete request's fallback to its own model. */
25
+ unrouted?: true;
24
26
  };
25
27
  export type RouteAvailability = {
26
28
  authenticated: boolean;
@@ -81,6 +83,15 @@ export declare function unregisteredConcreteModel(request: ModelRequest | undefi
81
83
  cwd?: string;
82
84
  profileId?: string | null;
83
85
  }): Promise<UnregisteredConcreteModel | undefined>;
86
+ /** A concrete request whose model is registered but offered by no configured route. The daemon serves a turn only
87
+ * from configured routes, so such a recipe is accepted and then faults every turn with "no eligible model route".
88
+ * An exact request is served without any route, so it is never unroutable. */
89
+ export declare function unroutableConcreteModel(request: ModelRequest | undefined, options?: {
90
+ cwd?: string;
91
+ profileId?: string | null;
92
+ }): Promise<Extract<ModelRequest, {
93
+ kind: 'concrete';
94
+ }> | undefined>;
84
95
  /** The availability facts of one route given the managed pool already read for its provider (undefined for a provider that has no pool). */
85
96
  export declare function routeAvailability(candidate: RoutedCandidate, registry: ModelRegistryLike, pool: readonly SubscriptionCredential[] | undefined, now?: number): RouteAvailability;
86
97
  export declare function modelRequestFromConfig(model: string | undefined, modelIntent: string | undefined, exact: boolean): ModelRequest | undefined;
@@ -1 +1 @@
1
- var E=Object.defineProperty;var s=(e,t)=>E(e,"name",{value:t,configurable:!0});import{readMergedLaunchConfig as U}from"./config.js";import{brokerConfigSnapshot as G}from"./config.js";import{coolingUntilFor as L}from"./managed-providers.js";const I="app",y=["ultra","strong","medium","light"],M=new Set(["off","minimal","low","medium","high","xhigh","max"]);function p(e){const t=e.indexOf("/");if(t<=0||t===e.length-1)return;const o=e.slice(0,t),r=e.slice(t+1),d=r.lastIndexOf(":"),a=d>0?r.slice(d+1).toLowerCase():void 0;return{providerId:o,modelId:a!==void 0&&M.has(a)?r.slice(0,d):r,...a!==void 0&&M.has(a)&&a!=="off"?{thinkingLevel:a}:{}}}s(p,"parseModelSpec");function H(e){const t=s(o=>{const r=e[o].strong.slice(0,e[o].strong.indexOf("/"));return{family:o,credentialSource:"default",providerId:r,models:e[o]}},"route");return{anthropic:t("anthropic"),openai:t("openai")}}s(H,"synthesizeRoutes");function A(e,t){return[e,...t.filter(o=>o!==e)]}s(A,"ordered");function P(e,t){return A(e,t).filter((o,r,d)=>d.indexOf(o)===r)}s(P,"strengths");let v;function Y(e){v=e}s(Y,"registerRotationReader");async function _(){const e=G();return e?e.rotation:v?v():{}}s(_,"rotationConfig");async function z(e,t,o){if(e.kind==="exact"){const n=p(e.spec);return n?[{routeId:"exact",credentialSource:"exact",family:n.providerId,strength:"strong",...n,rank:0}]:[]}if(e.kind==="logical"&&e.family===I){const n=p(e.spec);return n?[{routeId:I,credentialSource:I,family:I,strength:e.strength,...n,rank:0}]:[]}const r=U(t,o??null),d={...r.modelRoutes??{},...r.legacyModelRoutes??{}},a=Object.keys(d).length>0?d:H(r.modelLadders),u=Object.entries(a),g=r.modelRouting?.strengthFallback??[(await _()).defaultFallbackStrength??"strong"],N=u.map(([,n])=>n.family).filter((n,i,c)=>c.indexOf(n)===i),O=u.map(([,n])=>n.credentialSource).filter((n,i,c)=>c.indexOf(n)===i),f=e.kind==="concrete"?p(e.spec):void 0,R=e.kind==="logical"?e:void 0,C=s((n,i)=>{const c=p(n);return c!==void 0&&f!==void 0&&c.providerId===f.providerId&&c.modelId===f.modelId&&(!i||c.thinkingLevel===f.thinkingLevel)},"matchesConcrete"),F=f===void 0?void 0:u.find(([,n])=>Object.values(n.models).some(i=>C(i,!1))),x=R?.strength??(()=>{if(!f)return"strong";for(const n of[!0,!1])for(const i of n?y:[...y].reverse())if(u.some(([,c])=>{const m=c.models[i];return m!==void 0&&C(m,n)}))return i;return"strong"})(),k=R?.family??F?.[1].family??f?.providerId??"anthropic",T=f?O:r.modelRouting?.credentialSourceOrder??O,j=f?[k]:r.modelRouting?.familyOrder??N,S=[],b=new Set,w=s((n,i,c,m)=>{const l=p(m);if(!l||f&&l.modelId!==f.modelId)return;const h=JSON.stringify([n,l.providerId,l.modelId,l.thinkingLevel]);b.has(h)||(b.add(h),S.push({routeId:n,credentialSource:i.credentialSource,family:i.family,strength:c,...l,rank:S.length}))},"append");if(f){const[n,i]=F??["concrete",{family:k,credentialSource:"default"}];w(n,i,x,e.spec)}for(const n of T)for(const i of f?[x]:P(x,g))for(const c of A(k,j))for(const[m,l]of u){if(l.credentialSource!==n||l.family!==c)continue;const h=l.models[i];h&&w(m,l,i,h)}return S.map((n,i)=>({...n,rank:i}))}s(z,"expandModelCandidates");async function B(e,t,o={}){if(e===void 0||e.kind==="logical")return;const r=(await z(e,o.cwd,o.profileId))[0];if(!(r===void 0||t.find(r.providerId,r.modelId)!==void 0))return{request:e,candidate:r,reason:"unregistered"}}s(B,"unregisteredConcreteModel");function Q(e,t,o,r=Date.now()){const d=t.find(e.providerId,e.modelId);if(d===void 0)return{registered:!1,authenticated:!1};if(o===void 0)return{registered:!0,authenticated:t.hasConfiguredAuth(d)};const a=o.filter(g=>g.authFailure===void 0),u=a.length>0&&a.every(g=>L(g,e.modelId)>r);return{registered:!0,authenticated:a.length>0||t.hasConfiguredAuth(d),...u?{coolingUntil:Math.min(...a.map(g=>L(g,e.modelId)))}:{}}}s(Q,"routeAvailability");function V(e,t,o){if(e){if(o)return{kind:"exact",spec:e};if(t){const r=t.lastIndexOf("/"),d=r>0?t.slice(r+1):"";if(y.includes(d))return{kind:"logical",family:t.slice(0,r),strength:d,spec:e}}return{kind:"concrete",spec:e}}}s(V,"modelRequestFromConfig");export{I as APP_FAMILY,z as expandModelCandidates,V as modelRequestFromConfig,p as parseModelSpec,Y as registerRotationReader,Q as routeAvailability,B as unregisteredConcreteModel};
1
+ var G=Object.defineProperty;var c=(e,t)=>G(e,"name",{value:t,configurable:!0});import{readMergedLaunchConfig as H}from"./config.js";import{brokerConfigSnapshot as P}from"./config.js";import{coolingUntilFor as M}from"./managed-providers.js";const I="app",v=["ultra","strong","medium","light"],A=new Set(["off","minimal","low","medium","high","xhigh","max"]);function p(e){const t=e.indexOf("/");if(t<=0||t===e.length-1)return;const o=e.slice(0,t),r=e.slice(t+1),d=r.lastIndexOf(":"),a=d>0?r.slice(d+1).toLowerCase():void 0;return{providerId:o,modelId:a!==void 0&&A.has(a)?r.slice(0,d):r,...a!==void 0&&A.has(a)&&a!=="off"?{thinkingLevel:a}:{}}}c(p,"parseModelSpec");function _(e){const t=c(o=>{const r=e[o].strong.slice(0,e[o].strong.indexOf("/"));return{family:o,credentialSource:"default",providerId:r,models:e[o]}},"route");return{anthropic:t("anthropic"),openai:t("openai")}}c(_,"synthesizeRoutes");function N(e,t){return[e,...t.filter(o=>o!==e)]}c(N,"ordered");function z(e,t){return N(e,t).filter((o,r,d)=>d.indexOf(o)===r)}c(z,"strengths");let O;function B(e){O=e}c(B,"registerRotationReader");async function D(){const e=P();return e?e.rotation:O?O():{}}c(D,"rotationConfig");async function T(e,t,o){if(e.kind==="exact"){const n=p(e.spec);return n?[{routeId:"exact",credentialSource:"exact",family:n.providerId,strength:"strong",...n,rank:0,unrouted:!0}]:[]}if(e.kind==="logical"&&e.family===I){const n=p(e.spec);return n?[{routeId:I,credentialSource:I,family:I,strength:e.strength,...n,rank:0}]:[]}const r=H(t,o??null),d={...r.modelRoutes??{},...r.legacyModelRoutes??{}},a=Object.keys(d).length>0?d:_(r.modelLadders),l=Object.entries(a),g=r.modelRouting?.strengthFallback??[(await D()).defaultFallbackStrength??"strong"],j=l.map(([,n])=>n.family).filter((n,i,s)=>s.indexOf(n)===i),R=l.map(([,n])=>n.credentialSource).filter((n,i,s)=>s.indexOf(n)===i),f=e.kind==="concrete"?p(e.spec):void 0,w=e.kind==="logical"?e:void 0,C=c((n,i)=>{const s=p(n);return s!==void 0&&f!==void 0&&s.providerId===f.providerId&&s.modelId===f.modelId&&(!i||s.thinkingLevel===f.thinkingLevel)},"matchesConcrete"),x=f===void 0?void 0:l.find(([,n])=>Object.values(n.models).some(i=>C(i,!1))),k=w?.strength??(()=>{if(!f)return"strong";for(const n of[!0,!1])for(const i of n?v:[...v].reverse())if(l.some(([,s])=>{const m=s.models[i];return m!==void 0&&C(m,n)}))return i;return"strong"})(),S=w?.family??x?.[1].family??f?.providerId??"anthropic",E=f?R:r.modelRouting?.credentialSourceOrder??R,U=f?[S]:r.modelRouting?.familyOrder??j,y=[],b=new Set,F=c((n,i,s,m,h=!1)=>{const u=p(m);if(!u||f&&u.modelId!==f.modelId)return;const L=JSON.stringify([n,u.providerId,u.modelId,u.thinkingLevel]);b.has(L)||(b.add(L),y.push({routeId:n,credentialSource:i.credentialSource,family:i.family,strength:s,...u,rank:y.length,...h?{unrouted:!0}:{}}))},"append");if(f){const[n,i]=x??["concrete",{family:S,credentialSource:"default"}];F(n,i,k,e.spec,x===void 0)}for(const n of E)for(const i of f?[k]:z(k,g))for(const s of N(S,U))for(const[m,h]of l){if(h.credentialSource!==n||h.family!==s)continue;const u=h.models[i];u&&F(m,h,i,u)}return y.map((n,i)=>({...n,rank:i}))}c(T,"expandModelCandidates");async function Q(e,t,o={}){if(e===void 0||e.kind==="logical")return;const r=(await T(e,o.cwd,o.profileId))[0];if(!(r===void 0||t.find(r.providerId,r.modelId)!==void 0))return{request:e,candidate:r,reason:"unregistered"}}c(Q,"unregisteredConcreteModel");async function V(e,t={}){if(e?.kind!=="concrete")return;const o=(await T(e,t.cwd,t.profileId))[0];return o===void 0||o.unrouted?e:void 0}c(V,"unroutableConcreteModel");function W(e,t,o,r=Date.now()){const d=t.find(e.providerId,e.modelId);if(d===void 0)return{registered:!1,authenticated:!1};if(o===void 0)return{registered:!0,authenticated:t.hasConfiguredAuth(d)};const a=o.filter(g=>g.authFailure===void 0),l=a.length>0&&a.every(g=>M(g,e.modelId)>r);return{registered:!0,authenticated:a.length>0||t.hasConfiguredAuth(d),...l?{coolingUntil:Math.min(...a.map(g=>M(g,e.modelId)))}:{}}}c(W,"routeAvailability");function Z(e,t,o){if(e){if(o)return{kind:"exact",spec:e};if(t){const r=t.lastIndexOf("/"),d=r>0?t.slice(r+1):"";if(v.includes(d))return{kind:"logical",family:t.slice(0,r),strength:d,spec:e}}return{kind:"concrete",spec:e}}}c(Z,"modelRequestFromConfig");export{I as APP_FAMILY,T as expandModelCandidates,Z as modelRequestFromConfig,p as parseModelSpec,B as registerRotationReader,W as routeAvailability,Q as unregisteredConcreteModel,V as unroutableConcreteModel};
@@ -4,6 +4,8 @@ import { type RunPlace } from '../permissions/ceiling.js';
4
4
  export declare const ENVELOPE_HEADER = "Crtr-Envelope";
5
5
  /** A header larger than this is refused unread. */
6
6
  export declare const MAX_ENVELOPE_BYTES: number;
7
+ /** The largest header a sender builds. Node's HTTP parser answers 431 once a request's headers together pass 16 KiB, so this leaves 4 KiB for the bearer token and the other headers. */
8
+ export declare const MAX_SENT_ENVELOPE_BYTES: number;
7
9
  /** The app (and its node) a call is made for. `node` is null for a cron with no anchor node. */
8
10
  export interface EnvelopeApp {
9
11
  sub: string;
@@ -1 +1 @@
1
- var b=Object.defineProperty;var c=(e,o)=>b(e,"name",{value:o,configurable:!0});import{joinedOwner as w}from"../directory/join.js";import{nodePlace as d}from"../permissions/ceiling.js";import{openDb as f}from"../canvas/db.js";import{ownMachineId as p}from"./directory.js";const g="Crtr-Envelope",h=16*1024;class r extends Error{static{c(this,"EnvelopeError")}constructor(o){super(o),this.name="EnvelopeError"}}async function S(e,o={}){const n=o.via??p(),s=e.class==="person"?"":e.sub,t=o.owner??(s.startsWith("user:")?s:w());if(t==null)throw new r("this machine has no account owner to make a peer call for");if(e.class==="person")return{sub:t,act:{sub:n}};if(e.class==="broker"){const a=await(o.placeOf??(async u=>await d(await f(),u)))(e.node);return{sub:t,act:{sub:n,act:{sub:e.app,node:e.node,node_scopes:e.scopes===null?null:[...e.scopes],place:a}}}}return{sub:t,act:{sub:n,act:{sub:e.app,node:e.anchor,node_scopes:e.scopes===null?null:[...e.scopes],place:{run:e.origin_run??"",isolated:e.isolated}}}}}c(S,"envelopeFor");function O(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}c(O,"encodeEnvelope");const l=c(e=>typeof e=="string"&&e!=="","isString"),i=c(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isRecord");function A(e){if(e.length>h)throw new r("the envelope is too large");if(!/^[A-Za-z0-9_-]+$/.test(e))throw new r("the envelope is not base64url");let o;try{o=JSON.parse(Buffer.from(e,"base64url").toString("utf8"))}catch{throw new r("the envelope is not JSON")}if(!i(o)||!l(o.sub)||!o.sub.startsWith("user:"))throw new r("the envelope names no user");const n=o.act;if(!i(n)||!l(n.sub)||!n.sub.startsWith("runtime:"))throw new r("the envelope names no calling machine");const s=n.act;if(s===void 0)return{sub:o.sub,act:{sub:n.sub}};if(!i(s)||!l(s.sub)||!s.sub.startsWith("app:"))throw new r("the envelope names no calling app");const{node:t,node_scopes:a,place:u}=s;if(t!==null&&!l(t))throw new r("the envelope node is not a node id");if(a!==null&&!(Array.isArray(a)&&a.every(l)))throw new r("the envelope node_scopes is not a list of scopes");if(u!==null&&!(i(u)&&typeof u.run=="string"&&typeof u.isolated=="boolean"))throw new r("the envelope place is malformed");return{sub:o.sub,act:{sub:n.sub,act:{sub:s.sub,node:t,node_scopes:a,place:u===null?null:{run:u.run,isolated:u.isolated}}}}}c(A,"decodeEnvelope");async function x(e,o,n={}){const s=n.via??p();if(e.class==="person"||e.grantee===null)return{sub:o,act:{sub:s}};const t=e.node_id===null?null:await(n.placeOf??(async a=>await d(await f(),a)))(e.node_id);return{sub:o,act:{sub:s,act:{sub:e.grantee,node:e.node_id,node_scopes:e.nodeScopes===null?null:[...e.nodeScopes],place:t}}}}c(x,"envelopeForProviderCaller");export{g as ENVELOPE_HEADER,r as EnvelopeError,h as MAX_ENVELOPE_BYTES,A as decodeEnvelope,O as encodeEnvelope,S as envelopeFor,x as envelopeForProviderCaller};
1
+ var w=Object.defineProperty;var a=(e,o)=>w(e,"name",{value:o,configurable:!0});import{joinedOwner as m}from"../directory/join.js";import{nodePlace as d}from"../permissions/ceiling.js";import{openDb as f}from"../canvas/db.js";import{ownMachineId as p}from"./directory.js";const N="Crtr-Envelope",E=16*1024,h=12*1024;class r extends Error{static{a(this,"EnvelopeError")}constructor(o){super(o),this.name="EnvelopeError"}}async function x(e,o={}){return b(await v(e,o))}a(x,"envelopeFor");async function v(e,o){const n=o.via??p(),t=e.class==="person"?"":e.sub,s=o.owner??(t.startsWith("user:")?t:m());if(s==null)throw new r("this machine has no account owner to make a peer call for");if(e.class==="person")return{sub:s,act:{sub:n}};if(e.class==="broker"){const c=await(o.placeOf??(async u=>await d(await f(),u)))(e.node);return{sub:s,act:{sub:n,act:{sub:e.app,node:e.node,node_scopes:e.scopes===null?null:[...e.scopes],place:c}}}}return{sub:s,act:{sub:n,act:{sub:e.app,node:e.anchor,node_scopes:e.scopes===null?null:[...e.scopes],place:{run:e.origin_run??"",isolated:e.isolated}}}}}a(v,"buildEnvelope");function b(e){const o=y(e).length;if(o>h){const n=e.act.act?.node_scopes?.length;throw new r(`the call's identity is ${o} bytes, over the ${h}-byte limit for a call to another machine${n===void 0?"":` (the node holds ${n} scopes)`}; narrow the node's scopes`)}return e}a(b,"fits");function y(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}a(y,"encodeEnvelope");const i=a(e=>typeof e=="string"&&e!=="","isString"),l=a(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isRecord");function P(e){if(e.length>E)throw new r("the envelope is too large");if(!/^[A-Za-z0-9_-]+$/.test(e))throw new r("the envelope is not base64url");let o;try{o=JSON.parse(Buffer.from(e,"base64url").toString("utf8"))}catch{throw new r("the envelope is not JSON")}if(!l(o)||!i(o.sub)||!o.sub.startsWith("user:"))throw new r("the envelope names no user");const n=o.act;if(!l(n)||!i(n.sub)||!n.sub.startsWith("runtime:"))throw new r("the envelope names no calling machine");const t=n.act;if(t===void 0)return{sub:o.sub,act:{sub:n.sub}};if(!l(t)||!i(t.sub)||!t.sub.startsWith("app:"))throw new r("the envelope names no calling app");const{node:s,node_scopes:c,place:u}=t;if(s!==null&&!i(s))throw new r("the envelope node is not a node id");if(c!==null&&!(Array.isArray(c)&&c.every(i)))throw new r("the envelope node_scopes is not a list of scopes");if(u!==null&&!(l(u)&&typeof u.run=="string"&&typeof u.isolated=="boolean"))throw new r("the envelope place is malformed");return{sub:o.sub,act:{sub:n.sub,act:{sub:t.sub,node:s,node_scopes:c,place:u===null?null:{run:u.run,isolated:u.isolated}}}}}a(P,"decodeEnvelope");async function $(e,o,n={}){const t=n.via??p();if(e.class==="person"||e.grantee===null)return{sub:o,act:{sub:t}};const s=e.node_id===null?null:await(n.placeOf??(async c=>await d(await f(),c)))(e.node_id);return b({sub:o,act:{sub:t,act:{sub:e.grantee,node:e.node_id,node_scopes:e.nodeScopes===null?null:[...e.nodeScopes],place:s}}})}a($,"envelopeForProviderCaller");export{N as ENVELOPE_HEADER,r as EnvelopeError,E as MAX_ENVELOPE_BYTES,h as MAX_SENT_ENVELOPE_BYTES,P as decodeEnvelope,y as encodeEnvelope,x as envelopeFor,$ as envelopeForProviderCaller};
@@ -23,5 +23,8 @@ export interface ProviderErrorRecognitionInput {
23
23
  wsCloseReason?: unknown;
24
24
  message?: string;
25
25
  }
26
+ /** The wording of the two subscription-refresh errors (`RefreshBusyError`, `RefreshTimeoutError` in `subscription-state.ts`), which build their messages from these phrases so the patterns below cannot drift from them. */
27
+ export declare const REFRESH_BUSY_PHRASE = "is being refreshed by another process";
28
+ export declare const REFRESH_TIMEOUT_PHRASE = "refresh did not answer within";
26
29
  /** Recognizes provider failure facts. Consumers apply their own retry policy. */
27
30
  export declare function recognizeProviderError(input: ProviderErrorRecognitionInput): ProviderErrorSignal;
@@ -1 +1 @@
1
- var b=Object.defineProperty;var a=(t,e)=>b(t,"name",{value:e,configurable:!0});function d(t){return typeof t=="string"?t:""}a(d,"text");function n(t,e,o={}){return{kind:t,matchedOn:e,...o}}a(n,"signal");function i(t,e){return e.matchedOn==="text"?{...t,textKind:e.kind,textMatch:e.match,textMatches:e.textMatches}:t}a(i,"withTextMatch");const h=[["protocol",/workspace api usage limits?/i],["rate-limit",/rate.?limit|\b429\b|too many requests|usage[ _]limit|usage_not_included|usagelimiterror|insufficient_quota|quota exceeded/i],["protocol",/\b413\b|payload too large|entity too large|request too large|entity\.too\.large|body exceeds/i],["auth",/invalid_grant|refresh token|unauthori[sz]ed|invalid.?api.?key|authentication failed|\b401\b|\b403\b/i],["context-overflow",/context overflow recovery failed/i],["protocol",/websocket.*(?:closed.*\b(?:1002|1003|1007|1008|1009|1010)\b|protocol error|policy violation|unsupported data|invalid (?:frame|payload)|message too big|mandatory extension)/i],["overloaded-only",/lock file is already being held/i],["overloaded",/overloaded|temporarily unavailable/i],["service-unavailable",/service.?unavailable/i],["overloaded-only",/internal server error|api.?error|\b5\d\d\b|capacity|server.{0,3}busy/i],["connection",/connection|econnreset|etimedout|enotfound|econnrefused|network|fetch failed|socket hang|timed? out|timeout|websocket\s+(?:was\s+)?closed|websocket stream closed before response\.completed|websocket\s+error|websocket.*interrupt/i],["socket",/socket/i],["connection-only",/eai_again|epipe|ehostunreach|enetunreach|\bterminated\b|other side closed|premature close/i]];function f(t){const e=h.flatMap(([c,r])=>{const s=t.match(r);return s?[{kind:c,match:s[0]}]:[]}),o=e[0];return o===void 0?n("other","none"):{...n(o.kind,"text",{match:o.match}),textMatches:e}}a(f,"recognizeText");function E(t){const e=t.retryAfterMs,o=f(t.message??""),c=d(t.code)||d(t.errno);if(/^(ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOENT|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH)$/i.test(c))return i(n("connection","errno",{retryAfterMs:e}),o);if(/^ELOCKED$/i.test(c))return i(n("overloaded","errno",{retryAfterMs:e}),o);const r=typeof t.status=="number"?t.status:void 0;if(r!==void 0){if(r===401||r===403)return i(n("auth","status",{status:r,retryAfterMs:e}),o);if(r===429)return i(n("rate-limit","status",{status:r,retryAfterMs:e}),o);if(r===500||r===502||r===503||r===504)return i(n("transient-http","status",{status:r,retryAfterMs:e}),o);if(r>=500&&r<=599)return i(n("overloaded","status",{status:r,retryAfterMs:e}),o);if(r>=400&&r<=499)return i(n("protocol","status",{status:r,retryAfterMs:e}),o)}const s=typeof t.wsCloseCode=="number"?t.wsCloseCode:void 0;if(s===1008)return i(n("protocol","websocket-close",{retryAfterMs:e}),o);if(s===1006)return i(n("connection","websocket-close",{retryAfterMs:e}),o);if(s===1011){const l=d(t.wsCloseReason),u=f(l);return/no running broker/i.test(l)||u.kind==="connection"||u.kind==="connection-only"?i(n("connection","websocket-close",{retryAfterMs:e}),o):i(n("other","websocket-close",{retryAfterMs:e}),o)}return e===void 0?o:{...o,retryAfterMs:e}}a(E,"recognizeProviderError");export{E as recognizeProviderError};
1
+ var b=Object.defineProperty;var a=(t,e)=>b(t,"name",{value:e,configurable:!0});function d(t){return typeof t=="string"?t:""}a(d,"text");function n(t,e,o={}){return{kind:t,matchedOn:e,...o}}a(n,"signal");function i(t,e){return e.matchedOn==="text"?{...t,textKind:e.kind,textMatch:e.match,textMatches:e.textMatches}:t}a(i,"withTextMatch");const h="is being refreshed by another process",E="refresh did not answer within",m=[["protocol",/workspace api usage limits?/i],["rate-limit",/rate.?limit|\b429\b|too many requests|usage[ _]limit|usage_not_included|usagelimiterror|insufficient_quota|quota exceeded/i],["protocol",/\b413\b|payload too large|entity too large|request too large|entity\.too\.large|body exceeds/i],["auth",/invalid_grant|refresh token|unauthori[sz]ed|invalid.?api.?key|authentication failed|\b401\b|\b403\b/i],["context-overflow",/context overflow recovery failed/i],["protocol",/websocket.*(?:closed.*\b(?:1002|1003|1007|1008|1009|1010)\b|protocol error|policy violation|unsupported data|invalid (?:frame|payload)|message too big|mandatory extension)/i],["overloaded-only",/lock file is already being held/i],["overloaded",new RegExp(h,"i")],["connection",new RegExp(E,"i")],["overloaded",/overloaded|temporarily unavailable/i],["service-unavailable",/service.?unavailable/i],["overloaded-only",/internal server error|api.?error|\b5\d\d\b|capacity|server.{0,3}busy/i],["connection",/connection|econnreset|etimedout|enotfound|econnrefused|network|fetch failed|socket hang|timed? out|timeout|websocket\s+(?:was\s+)?closed|websocket stream closed before response\.completed|websocket\s+error|websocket.*interrupt/i],["socket",/socket/i],["connection-only",/eai_again|epipe|ehostunreach|enetunreach|\bterminated\b|other side closed|premature close/i]];function f(t){const e=m.flatMap(([c,r])=>{const s=t.match(r);return s?[{kind:c,match:s[0]}]:[]}),o=e[0];return o===void 0?n("other","none"):{...n(o.kind,"text",{match:o.match}),textMatches:e}}a(f,"recognizeText");function y(t){const e=t.retryAfterMs,o=f(t.message??""),c=d(t.code)||d(t.errno);if(/^(ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOENT|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH)$/i.test(c))return i(n("connection","errno",{retryAfterMs:e}),o);if(/^ELOCKED$/i.test(c))return i(n("overloaded","errno",{retryAfterMs:e}),o);const r=typeof t.status=="number"?t.status:void 0;if(r!==void 0){if(r===401||r===403)return i(n("auth","status",{status:r,retryAfterMs:e}),o);if(r===429)return i(n("rate-limit","status",{status:r,retryAfterMs:e}),o);if(r===500||r===502||r===503||r===504)return i(n("transient-http","status",{status:r,retryAfterMs:e}),o);if(r>=500&&r<=599)return i(n("overloaded","status",{status:r,retryAfterMs:e}),o);if(r>=400&&r<=499)return i(n("protocol","status",{status:r,retryAfterMs:e}),o)}const s=typeof t.wsCloseCode=="number"?t.wsCloseCode:void 0;if(s===1008)return i(n("protocol","websocket-close",{retryAfterMs:e}),o);if(s===1006)return i(n("connection","websocket-close",{retryAfterMs:e}),o);if(s===1011){const l=d(t.wsCloseReason),u=f(l);return/no running broker/i.test(l)||u.kind==="connection"||u.kind==="connection-only"?i(n("connection","websocket-close",{retryAfterMs:e}),o):i(n("other","websocket-close",{retryAfterMs:e}),o)}return e===void 0?o:{...o,retryAfterMs:e}}a(y,"recognizeProviderError");export{h as REFRESH_BUSY_PHRASE,E as REFRESH_TIMEOUT_PHRASE,y as recognizeProviderError};