@north-light/cli 0.3.575 → 0.3.576
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
- package/dist/clients/attach/viewer.js +73 -73
- package/dist/core/canvas/executions.d.ts +4 -4
- package/dist/core/canvas/executions.js +1 -1
- package/dist/core/command-plugins/bundle.d.ts +6 -3
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/llm/run.js +2 -2
- package/dist/core/provider-error-recognition.d.ts +3 -0
- package/dist/core/provider-error-recognition.js +1 -1
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/roots.d.ts +4 -0
- package/dist/core/providers/roots.js +1 -1
- package/dist/core/runtime/broker/daemon-ops.js +1 -1
- package/dist/core/runtime/broker-request.js +1 -1
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/runner/blaxel/client.d.ts +2 -0
- package/dist/core/runtime/runner/blaxel/client.js +1 -1
- package/dist/core/runtime/runner/blaxel/runner.js +2 -2
- package/dist/core/runtime/view-socket.d.ts +1 -1
- package/dist/core/subscription-state.d.ts +4 -0
- package/dist/core/subscription-state.js +1 -1
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/crtrd.js +11 -11
- package/dist/daemon/reconcilers/broker-supervision.js +4 -4
- package/dist/daemon/reconcilers/live-obligation.d.ts +11 -0
- package/dist/daemon/reconcilers/live-obligation.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/docs/sdk/release-notes.md +4 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- 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
|
|
110
|
-
export declare function hasAbsorbedProfilePauseStop(
|
|
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
|
|
123
|
-
export declare function hasObservedExit(
|
|
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
|
|
144
|
-
* the config version and
|
|
145
|
-
*
|
|
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/llm/run.js
CHANGED
|
@@ -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
|
|
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.routeId!=="concrete"&&u.routeId!=="exact","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};
|
|
@@ -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=
|
|
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};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),
|
|
1
|
+
var fe=Object.defineProperty;var o=(e,n)=>fe(e,"name",{value:n,configurable:!0});import{existsSync as b,readdirSync as W,readFileSync as _,rmSync as p,statSync as C}from"node:fs";import{join as d}from"node:path";import{validateCommandManifest as le}from"../../api/command-manifest/manifest.js";import{crtrHome as z}from"../canvas/paths.js";import{isCloudRole as m}from"../canvas/person-state.js";import{withCanvasWrite as pe}from"../canvas/db.js";import{bundleVersion as me,isInstalledGeneration as ve,recordCheckedGeneration as he}from"../command-plugins/bundle.js";import{readState as ge,updateConfig as we,updateState as $e}from"../config.js";import{directoryIssuer as L}from"../directory/issuer.js";import{emitEvent as v}from"../events/emit.js";import{describeOutboundFailure as ye,outboundFetch as be,outboundRefusal as q,OutboundError as _e}from"../events/outbound.js";import{withExclusiveDirectoryLockAsync as Z}from"../exclusive-lock.js";import{atomicWriteText as T,ensureDir as D,pathExists as V}from"../fs-utils.js";import{reconcilePackages as Pe}from"../graph/package-reconcile.js";import{readPluginManifest as k}from"../manifest.js";import{bundleSwapLockPath as J}from"../plugin-swap-lock.js";import{pluginsDir as K,userScopeRoot as Q}from"../scope.js";import{installedProviderPlugins as Se}from"./roots.js";import{allGrants as Ee,localGrants as Me,providerIdsFromGrants as X}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const Te="[A-Za-z0-9_-]{1,64}",Y=new RegExp(`^${Te}$`),ke=3e4,ee=3e4;function h(e){return d(K("user"),e)}o(h,"pluginRoot");function P(e){return d(K("user"),`.${e}.etag`)}o(P,"etagFile");function Ie(e){if(!m())return ge("user").plugins[e]?.etag;try{return _(P(e),"utf8")}catch(n){if(n.code==="ENOENT")return;throw n}}o(Ie,"storedEtag");async function ne(e,n){if(!m())return he("user",e,n,{swapped:!1});n===void 0?p(P(e),{force:!0}):T(P(e),n)}o(ne,"recordEtag");async function re(e){if(!m())try{await pe(async n=>await Pe(n))}catch(n){v({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:n})}}o(re,"reconcileProviderDocs");function Ae(e,n,r){return m()?k(n)?.version===r&&V(d(n,"commands.json")):ve(e,"user",n,r)}o(Ae,"installedGeneration");function N(){return d(z(),"providers")}o(N,"providersRoot");function te(e,n){return d(N(),e,`v${n}`)}o(te,"majorDir");function R(e){if(!Y.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}o(R,"assertId");function I(){const e=L();if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e}o(I,"directoryBase");async function oe(e,n){const r=`${I()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await be("bundle_fetch",r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(ke)})}catch(i){return{status:"failed",message:`could not reach ${r}: ${i instanceof Error?i.message:String(i)}`,cause:i}}if(t.status===304){if(n.ifNoneMatch===void 0){const i=`HTTP 304 from ${r} without a conditional request`;return{status:"failed",message:i,cause:q("bundle_fetch",r,i)}}return{status:304}}if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300){await t.body?.cancel().catch(()=>{});{const i=`HTTP ${t.status} from ${r}`;return{status:"failed",message:i,cause:q("bundle_fetch",r,i)}}}let a;try{a=new Uint8Array(await t.arrayBuffer())}catch(i){return{status:"failed",message:`reading ${r} failed: ${i instanceof Error?i.message:String(i)}`,cause:new _e("bundle_fetch",r,i)}}const s=t.headers.get("etag");return{status:200,raw:a,...s!==null?{etag:s}:{}}}o(oe,"fetchProviderBundle");const A=new WeakSet;class x extends Error{static{o(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function ae(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(a=>r[a]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}o(ae,"wrongBundle");function B(e,n,r){v({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof x?{issues:r.issues}:{}},error:r})}o(B,"logInvalid");function ie(e,n,r){v({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r instanceof Error?r.message:String(r),...ye(r instanceof Error?r.cause:void 0)}})}o(ie,"logFailure");async function se(){return import("../../commands/pkg/plugin-manage.js")}o(se,"pluginManage");const S=new Map,E=o(e=>`${z()}\0${e}`,"installKey");function xe(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}o(xe,"publicInstallCause");function je(e){if(g(e)===void 0)return S.get(E(e))??{status:"installing"}}o(je,"providerInstallState");function Ce(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${je(e)?.error??"bundle installation pending"}`)}o(Ce,"unavailableProviderError");function g(e){const n=k(h(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&V(d(h(e),"commands.json"))){try{if(C(d(h(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:n.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:n.transport.endpoint}}}}o(g,"installedManifest");async function O(e,n,r=!1){const t=Q();g(e)===void 0&&S.set(E(e),{status:"installing"});try{return D(d(t,"tmp")),await Z(J(t,e),async()=>{const a=h(e),s=k(a);if(s!==null&&s.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const i=g(e)!==void 0;if(i&&r)return"unchanged";const f=i&&n?Ie(e):void 0,u=await oe(e,f!==void 0?{ifNoneMatch:f}:{});if(u.status===304)return"unchanged";if(u.status===404||u.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${u.message})`,{param:"provider"});if(u.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${u.message}`,{cause:u.cause});const l=me(u.raw);if(i&&Ae(e,a,l))return await ne(e,u.etag),"unchanged";const{installPluginArchive:$}=await se();m()&&p(P(e),{force:!0});try{await $(e,u.raw,{scope:"user",scopeRootPath:t,root:a,version:l,etag:u.etag,enable:!i,bundle:{endpoint:`${I()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:I()},accept:o(y=>ae(e,void 0,y),"accept"),record:!m()})}catch(y){B(e,void 0,y);const U=new c("not_found",`provider "${e}" served an invalid bundle: ${y instanceof Error?y.message:String(y)}`,{param:"provider"});throw A.add(U),U}return m()&&await ne(e,u.etag),await re(e),v({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:i}}),S.delete(E(e)),i?"updated":"installed"},{timeoutMs:ee,timeoutError:o(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(a){throw g(e)===void 0&&S.set(E(e),{status:"install_failed",error:xe(a)}),a instanceof c?a:(v({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:a}),Ce(e))}}o(O,"refreshInstalled");async function fn(e){R(e),g(e)===void 0&&await O(e,!1,!0)}o(fn,"ensureProviderBundle");async function ln(e,n){return R(e),n.version!==void 0&&g(e)?.provider.major!==n.version?H(e,n.version,n.conditional):O(e,n.conditional)}o(ln,"refreshProviderBundle");async function ue(e,n,r){const{validatePluginArchive:t}=await se(),a=await t(r);if(a.bundle===void 0)throw new x(`provider "${e}" major ${n} bundle is invalid: ${a.issues.map(i=>i.message).join("; ")}`,a.issues);const s=ae(e,n,a.bundle);if(s!==void 0)throw new x(`provider "${e}" major ${n} bundle is invalid: ${s}`,[]);if(a.bundle.transport===void 0)throw new x(`provider "${e}" major ${n} bundle declares no transport`,[]);return a.bundle}o(ue,"validateArchive");function De(e){D(e),T(d(e,"retired"),`${new Date().toISOString()}
|
|
2
|
+
`),p(d(e,"bundle.tar"),{force:!0}),p(d(e,"etag"),{force:!0})}o(De,"retire");function de(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}o(de,"retiredError");async function H(e,n,r){const t=te(e,n),a=d(t,"bundle.tar"),s=b(a),i=s&&r&&b(d(t,"etag"))?_(d(t,"etag"),"utf8"):void 0,f=await oe(e,{version:n,...i!==void 0?{ifNoneMatch:i}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw De(t),v({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),de(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`,{cause:f.cause});try{await ue(e,n,f.raw)}catch(l){B(e,n,l);const $=new c(s?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw A.add($),$}D(t),p(d(t,"retired"),{force:!0});const u=s&&Buffer.from(f.raw).equals(_(a));return u||T(a,f.raw),f.etag!==void 0?T(d(t,"etag"),f.etag):p(d(t,"etag"),{force:!0}),u?"unchanged":s?"updated":"installed"}o(H,"refreshPinned");function Ne(){const e=[],n=N();if(!b(n))return e;for(const r of W(n)){if(!Y.test(r))continue;let t;try{t=W(d(n,r))}catch{continue}for(const a of t){const s=/^v([1-9][0-9]*)$/.exec(a);s&&b(d(n,r,a,"bundle.tar"))&&e.push([r,Number(s[1])])}}return e}o(Ne,"cachedMajors");function Re(e){const n=le(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}o(Re,"readCommands");const M=new Map,F=new Map;function Be(e,n){const r=C(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,a=F.get(e);if(a?.stamp===t)return a.commands;const s=Re(JSON.parse(_(n,"utf8")));return F.set(e,{stamp:t,commands:s}),s}o(Be,"installedCommands");async function pn(e,n){R(e);const r=g(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const l=h(e),$=Be(e,d(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:$,transport:r.transport,source:"installed",root:l}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=te(e,n);if(b(d(t,"retired")))throw de(e,n);const a=d(t,"bundle.tar");b(a)||await H(e,n,!1);const s=`${e}/${n}`,i=C(a),f=`${i.mtimeMs}:${i.size}`;let u=M.get(s)?.stamp===f?M.get(s).bundle:void 0;if(u===void 0){try{u=await ue(e,n,new Uint8Array(_(a)))}catch(l){throw B(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}M.set(s,{stamp:f,bundle:u})}return{id:e,major:n,provider:u.provider,commands:u.commands,transport:u.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:u.transport.endpoint},source:"pinned"}}o(pn,"resolveProviderBundle");let w=null;const Oe=4*6e4;function j(e,n){e.stage=n,e.stageAt=Date.now()}o(j,"enter");function He(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${I()}/providers/${encodeURIComponent(n)}/plugin`}o(He,"isDirectoryBundle");async function G(){return m()?Ee():Me()}o(G,"bundleGrants");async function ce(e){return X(await G()).includes(e)}o(ce,"grantNames");async function Fe(e){await Z(J(Q(),e),async()=>{if(!(await ce(e)||!He(k(h(e)),e))){p(h(e),{recursive:!0,force:!0}),m()?p(P(e),{force:!0}):(await we("user",n=>{delete n.plugins[e]}),await $e("user",n=>{delete n.plugins[e]})),p(d(N(),e),{recursive:!0,force:!0}),F.delete(e),S.delete(E(e));for(const n of[...M.keys()])n.startsWith(`${e}/`)&&M.delete(n);await re(e),v({level:"info",event:"daemon.providers.bundle_uninstalled",fields:{provider:e}})}},{timeoutMs:ee,timeoutError:o(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}o(Fe,"uninstallProvider");async function Ge(){if(!(await G()).every(e=>e.status!=="active")){for(const e of Se())if(!(e.manifest.provider===void 0||await ce(e.name)))try{await Fe(e.name)}catch(n){v({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}o(Ge,"uninstallDisconnected");async function Ue(e){if(!L())return;j(e,"reading grants");const n=X(await G());for(const r of n){j(e,`refreshing installed bundle ${r}`);try{await O(r,!0)}catch(t){t instanceof Object&&A.has(t)||ie(r,void 0,t)}}j(e,"uninstalling disconnected bundles"),await Ge();for(const[r,t]of Ne()){j(e,`refreshing pinned bundle ${r} v${t}`);try{await H(r,t,!0)}catch(a){if(a instanceof c&&a.code==="provider_version_retired"||a instanceof Object&&A.has(a))continue;ie(r,t,a)}}}o(Ue,"syncOnce");function mn(){if(w!==null){w.rerun=!0;const n=Date.now()-w.stageAt;return n>=Oe&&v({level:"error",event:"daemon.providers.sync_stalled",fields:{stage:w.stage,stalled_ms:n}}),w.promise}const e={promise:Promise.resolve(),stage:"starting",stageAt:Date.now(),rerun:!1};return w=e,e.promise=(async()=>{try{do e.rerun=!1,await Ue(e);while(e.rerun)}finally{w=null}})(),e.promise}o(mn,"syncProviderBundles");export{fn as ensureProviderBundle,je as providerInstallState,ln as refreshProviderBundle,pn as resolveProviderBundle,mn as syncProviderBundles,Ce as unavailableProviderError};
|
|
@@ -4,6 +4,10 @@ import type { InstalledPlugin } from '../../types.js';
|
|
|
4
4
|
export declare function rootNameOfCommands(raw: unknown): string | undefined;
|
|
5
5
|
/** An installed provider plugin's root command name, cached per file replacement. */
|
|
6
6
|
export declare function installedRootName(plugin: Pick<InstalledPlugin, 'root'>): string | undefined;
|
|
7
|
+
/** The provider bundles this process serves. A cloud role holds one plugin directory for every account it serves, so a bundle there is
|
|
8
|
+
* installed for the deployment, not for a person: the directory's valid provider manifests, with no enabled flag. Anything else lists
|
|
9
|
+
* the user scope's plugins. */
|
|
10
|
+
export declare function installedProviderPlugins(): InstalledPlugin[];
|
|
7
11
|
export interface ProviderRootClaims {
|
|
8
12
|
/** root command name → provider id (the earliest-installed claimant). */
|
|
9
13
|
byRoot: Map<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var w=Object.defineProperty;var m=(o,n)=>w(o,"name",{value:n,configurable:!0});import{readdirSync as h,readFileSync as S,statSync as g}from"node:fs";import{join as u}from"node:path";import{emitEvent as $}from"../events/emit.js";import{isCloudRole as p}from"../canvas/person-state.js";import{readState as C}from"../config.js";import{readPluginManifest as M}from"../manifest.js";import{listInstalledPlugins as R}from"../resolver.js";import{pluginsDir as b}from"../scope.js";function x(o){const n=o?.mounts;if(!Array.isArray(n))return;const e=new Set;for(const r of n)Array.isArray(r?.parent)&&r.parent.length===0&&typeof r.node?.name=="string"&&e.add(r.node.name);return e.size===1?[...e][0]:void 0}m(x,"rootNameOfCommands");const l=new Map;function N(o){const n=u(o.root,"commands.json");try{const e=g(n),r=`${e.ino}:${e.mtimeMs}:${e.size}`,s=l.get(n);if(s?.stamp===r)return s.root;const a=x(JSON.parse(S(n,"utf8")));return l.set(n,{stamp:r,root:a}),a}catch{return}}m(N,"installedRootName");function v(){if(!p())return R("user");const o=b("user");let n;try{n=h(o,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort()}catch(e){if(e.code==="ENOENT")return[];throw e}return n.flatMap(e=>{const r=u(o,e),s=M(r);return s?.provider===void 0?[]:[{name:e,scope:"user",root:r,manifest:s,enabled:!0,version:s.version}]})}m(v,"installedProviderPlugins");const y=new Set;function P(o=v()){const n=o.filter(t=>t.enabled&&t.manifest.provider!==void 0&&t.manifest.provider.id===t.name),e=new Map(n.map(t=>[t.name,N(t)])),r=new Set,s=new Set;for(const t of e.values())t!==void 0&&(r.has(t)&&s.add(t),r.add(t));if(s.size>0){const t=p()?{}:C("user").plugins;n.sort((i,d)=>(t[i.name]?.last_updated??"").localeCompare(t[d.name]?.last_updated??"")||i.name.localeCompare(d.name))}const a=new Map,c=new Set;for(const t of n){const i=e.get(t.name);if(i===void 0)continue;const d=a.get(i);if(d===void 0){a.set(i,t.name);continue}c.add(t.name);const f=`${t.name}\0${i}\0${d}`;y.has(f)||(y.add(f),$({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:t.name,issues:[{code:"root_command_claimed",message:`root command "${i}" is already served by provider "${d}"; "${t.name}" is skipped`}]}}))}return{byRoot:a,skipped:c}}m(P,"providerRootClaims");function T(o){const n=v(),e=P(n);return e.skipped.has(o)?null:n.some(r=>r.name===o&&r.manifest.provider!==void 0)?o:e.byRoot.get(o)??o}m(T,"resolveProviderRef");export{v as installedProviderPlugins,N as installedRootName,P as providerRootClaims,T as resolveProviderRef,x as rootNameOfCommands};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var h=Object.defineProperty;var t=(e,r)=>h(e,"name",{value:r,configurable:!0});import{CrtrClient as R}from"../../../api/client.js";import{currentNodeToken as y}from"./node-token.js";import{localClient as b}from"../../../api/node-transport.js";import{ApiError as l}from"../../../api/errors.js";import{envExecutionId as w}from"../../../shared/env.js";import{brokerSignalClient as N}from"./signal-stream.js";import{cachedBrokerNodeFields as k}from"../../../daemon/broker-signals/bus.js";import{fetchRunAttachments as P}from"./attachments-fetch.js";import{reportRefNodeId as v}from"../../../shared/inbox-entry-body.js";function g(){const e=process.env.CRTR_API_URL;if(!(e===void 0||e==="")){if(!/^https?:\/\//.test(e))throw new Error(`CRTR_API_URL must be an http(s) URL: ${e}`);return e}}t(g,"brokerApiUrl");function o(){const e=g();if(e===void 0)return b({autostart:!1});const r=y();if(r===void 0)throw new Error("broker has no node token for CRTR_API_URL");return new R({baseUrl:e,headers:{authorization:`Bearer ${r}`}})}t(o,"daemonClient");function $(e,r){return o().appendNodeLog(e,{record:r,expected_execution_id:n()})}t($,"appendBrokerLog");function D(e,r){return o().putNodeTelemetry(e,{...r,expected_execution_id:n()})}t(D,"putBrokerTelemetry");function j(e,r){return o().putNodeRecap(e,{briefing:r,expected_execution_id:n()})}t(j,"putBrokerRecap");function z(e,r){return o().readNodePassiveMessage(e,r)}t(z,"readBrokerPassiveMessage");function G(e){return o().nodePushedFinal(e,{action:"clear",expected_execution_id:n()})}t(G,"clearBrokerPushedFinal");function O(e){return o().nodePushedFinal(e,{action:"consume",expected_execution_id:n()})}t(O,"consumeBrokerPushedFinal");function q(e,r){return o().request("POST",`/v1/nodes/${encodeURIComponent(e)}/jobs/start`,{...r,expected_execution_id:n()})}t(q,"startBrokerBashJob");function X(e,r,i){return o().recordBackgroundedJob(e,r,i)}t(X,"recordBackgroundedJob");function H(e,r,i){return o().reportBashJob(e,r,{...i,expected_execution_id:n()})}t(H,"reportBrokerJob");function K(e,r){return o().request("DELETE",`/v1/nodes/${encodeURIComponent(e)}/jobs/${encodeURIComponent(r)}?foreground=1`,{expected_execution_id:n()})}t(K,"stopBrokerBashJob");function n(){const e=w();if(e===void 0||e==="")throw new Error("broker launch is missing CRTR_EXECUTION_ID");return e}t(n,"brokerExecutionId");function Q(e,r){return o().bindBrokerSession(e,{...r,executionId:n()})}t(Q,"bindBrokerSession");function V(e,r){return o().settleBroker(e,{...r,expected_execution_id:n()})}t(V,"settleBroker");function W(e){return o().postBrokerIdle(e,{expected_execution_id:n()})}t(W,"postBrokerIdle");function Y(e,r){return o().completeBrokerPark(e,{...r,expected_execution_id:n()})}t(Y,"completeBrokerPark");function Z(e){return o().recordBrokerParkActivity(e,{expected_execution_id:n()})}t(Z,"recordBrokerParkActivity");function ee(e,r){return o().recordBrokerTelemetry(e,{...r,expected_execution_id:n()})}t(ee,"recordBrokerTelemetry");const m=new Set;async function re(e,r,i){const s=o(),f=n(),a=await s.claimNodeMail(e,{execution_id:f,channel:r});if(a.action==="hold")return a;const x=a.deliveries.filter(u=>!m.has(u.entry.entry_id));if(x.length>0){await P(e);for(const u of x)m.add(u.entry.entry_id)}const p=await Promise.all(a.deliveries.map(async u=>{const c=u.entry.ref;if(c===void 0||v(c)===void 0)return u;try{let d=i.get(c);d===void 0&&(d=s.readNodeInboxReport(e,c,f).then(({content:B})=>B),i.set(c,d));const _=await d;return{...u,entry:{...u.entry,data:{...u.entry.data,report_body:_}}}}catch(d){if(i.delete(c),d instanceof l&&d.status===404)return u;throw d}}));if(p.length>0){const u=new Set(p.map(({entry:c})=>c.ref).filter(c=>c!==void 0));for(const c of i.keys())u.has(c)||i.delete(c)}return{...a,deliveries:p}}t(re,"claimBrokerMail");async function te(e,r){return await(await import("../../conversation-store/index.js")).flushConversation(),o().acknowledgeNodeMail(e,{execution_id:n(),deliveries:r})}t(te,"acknowledgeBrokerMail");function oe(e,r){return o().recordBrokerTurn(e,{...r,expected_execution_id:n()})}t(oe,"recordBrokerTurn");function ne(e,r){return o().mutateBrokerProviderRetry(e,{...r,expected_execution_id:n()})}t(ne,"mutateBrokerProviderRetry");function ie(e,r){return o().mutateBrokerFault(e,{...r,expected_execution_id:n()})}t(ie,"mutateBrokerFault");function ce(e){return o().getBrokerRecovery(e,n())}t(ce,"brokerRecovery");function ue(e,r){return o().recordNodeFault(e,r)}t(ue,"recordNodeFault");function de(e,r={}){return o().clearNodeFault(e,{...r.link===void 0?{}:{link:r.link},...r.preserveEpisode===void 0?{}:{preserve_episode:r.preserveEpisode}}).then(i=>i.cleared)}t(de,"clearNodeFault");function ae(e,r){return o().commitBrokerModel(e,{...r,expected_execution_id:n()})}t(ae,"commitBrokerModel");function C(e){return o().brokerExtensionState(e)}t(C,"brokerExtensionState");async function pe(e){const r=N();return r.state==="connected"&&r.latestNode!==void 0?k(r.latestNode):k((await C(e)).node)}t(pe,"cachedBrokerNode");function se(e,r){return o().nodeDelivery(e,{...r,expected_execution_id:n()})}t(se,"brokerDelivery");function fe(e,r){return o().commitBrokerGeneratedName(e,{...r,expected_execution_id:n()})}t(fe,"commitBrokerGeneratedName");function xe(e,r){return o().commitBrokerPersonaAck(e,{...r,expected_execution_id:n()})}t(xe,"commitBrokerPersonaAck");async function le(e,r){try{await o().pushReport(e,r)}catch(i){if(r.tier==="final"&&i instanceof l&&i.code==="already_finalized")return;throw i}}t(le,"publishBrokerReport");function ke(e){return o().relaunchRoot(e)}t(ke,"relaunchBrokerRoot");export{te as acknowledgeBrokerMail,$ as appendBrokerLog,Q as bindBrokerSession,g as brokerApiUrl,se as brokerDelivery,n as brokerExecutionId,C as brokerExtensionState,ce as brokerRecovery,pe as cachedBrokerNode,re as claimBrokerMail,G as clearBrokerPushedFinal,de as clearNodeFault,fe as commitBrokerGeneratedName,ae as commitBrokerModel,xe as commitBrokerPersonaAck,Y as completeBrokerPark,O as consumeBrokerPushedFinal,o as daemonClient,ie as mutateBrokerFault,ne as mutateBrokerProviderRetry,W as postBrokerIdle,le as publishBrokerReport,j as putBrokerRecap,D as putBrokerTelemetry,z as readBrokerPassiveMessage,X as recordBackgroundedJob,Z as recordBrokerParkActivity,ee as recordBrokerTelemetry,oe as recordBrokerTurn,ue as recordNodeFault,ke as relaunchBrokerRoot,H as reportBrokerJob,V as settleBroker,q as startBrokerBashJob,K as stopBrokerBashJob};
|
|
1
|
+
var h=Object.defineProperty;var t=(e,r)=>h(e,"name",{value:r,configurable:!0});import{CrtrClient as R}from"../../../api/client.js";import{currentNodeToken as y}from"./node-token.js";import{localClient as b}from"../../../api/node-transport.js";import{ApiError as l}from"../../../api/errors.js";import{envExecutionId as w}from"../../../shared/env.js";import{brokerSignalClient as N}from"./signal-stream.js";import{cachedBrokerNodeFields as k}from"../../../daemon/broker-signals/bus.js";import{fetchRunAttachments as P}from"./attachments-fetch.js";import{reportRefNodeId as v}from"../../../shared/inbox-entry-body.js";function g(){const e=process.env.CRTR_API_URL;if(!(e===void 0||e==="")){if(!/^https?:\/\//.test(e))throw new Error(`CRTR_API_URL must be an http(s) URL: ${e}`);return e}}t(g,"brokerApiUrl");function o(){const e=g();if(e===void 0)return b({autostart:!1});const r=y();if(r===void 0)throw new Error("broker has no node token for CRTR_API_URL");return new R({baseUrl:e,headers:{authorization:`Bearer ${r}`}})}t(o,"daemonClient");function $(e,r){return o().appendNodeLog(e,{record:r,expected_execution_id:n()})}t($,"appendBrokerLog");function D(e,r){return o().putNodeTelemetry(e,{...r,expected_execution_id:n()})}t(D,"putBrokerTelemetry");function j(e,r){return o().putNodeRecap(e,{briefing:r,expected_execution_id:n()})}t(j,"putBrokerRecap");function z(e,r){return o().readNodePassiveMessage(e,r)}t(z,"readBrokerPassiveMessage");function G(e){return o().nodePushedFinal(e,{action:"clear",expected_execution_id:n()})}t(G,"clearBrokerPushedFinal");function O(e){return o().nodePushedFinal(e,{action:"consume",expected_execution_id:n()})}t(O,"consumeBrokerPushedFinal");function q(e,r){return o().request("POST",`/v1/nodes/${encodeURIComponent(e)}/jobs/start`,{...r,expected_execution_id:n()})}t(q,"startBrokerBashJob");function X(e,r,i){return o().recordBackgroundedJob(e,r,i)}t(X,"recordBackgroundedJob");function H(e,r,i){return o().reportBashJob(e,r,{...i,expected_execution_id:n()})}t(H,"reportBrokerJob");function K(e,r){return o().request("DELETE",`/v1/nodes/${encodeURIComponent(e)}/jobs/${encodeURIComponent(r)}?foreground=1`,{expected_execution_id:n()})}t(K,"stopBrokerBashJob");function n(){const e=w();if(e===void 0||e==="")throw new Error("broker launch is missing CRTR_EXECUTION_ID");return e}t(n,"brokerExecutionId");function Q(e,r){return o().bindBrokerSession(e,{...r,executionId:n()})}t(Q,"bindBrokerSession");function V(e,r){return o().settleBroker(e,{...r,expected_execution_id:n()})}t(V,"settleBroker");function W(e){return o().postBrokerIdle(e,{expected_execution_id:n()})}t(W,"postBrokerIdle");function Y(e,r){return o().completeBrokerPark(e,{...r,expected_execution_id:n()})}t(Y,"completeBrokerPark");function Z(e){return o().recordBrokerParkActivity(e,{expected_execution_id:n()})}t(Z,"recordBrokerParkActivity");function ee(e,r){return o().recordBrokerTelemetry(e,{...r,expected_execution_id:n()})}t(ee,"recordBrokerTelemetry");const m=new Set;async function re(e,r,i){const s=o(),f=n(),a=await s.claimNodeMail(e,{execution_id:f,channel:r});if(a.action==="hold")return a;const x=a.deliveries.filter(u=>!m.has(u.entry.entry_id));if(x.length>0){await P(e);for(const u of x)m.add(u.entry.entry_id)}const p=await Promise.all(a.deliveries.map(async u=>{const c=u.entry.ref;if(c===void 0||v(c)===void 0)return u;try{let d=i.get(c);d===void 0&&(d=s.readNodeInboxReport(e,c,f).then(({content:B})=>B),i.set(c,d));const _=await d;return{...u,entry:{...u.entry,data:{...u.entry.data,report_body:_}}}}catch(d){if(i.delete(c),d instanceof l&&d.status===404)return u;throw d}}));if(p.length>0){const u=new Set(p.map(({entry:c})=>c.ref).filter(c=>c!==void 0));for(const c of i.keys())u.has(c)||i.delete(c)}return{...a,deliveries:p}}t(re,"claimBrokerMail");async function te(e,r){return await(await import("../../conversation-store/index.js")).flushConversation(),o().acknowledgeNodeMail(e,{execution_id:n(),deliveries:r})}t(te,"acknowledgeBrokerMail");function oe(e,r){return o().recordBrokerTurn(e,{...r,expected_execution_id:n()})}t(oe,"recordBrokerTurn");function ne(e,r){return o().mutateBrokerProviderRetry(e,{...r,expected_execution_id:n()})}t(ne,"mutateBrokerProviderRetry");function ie(e,r){return o().mutateBrokerFault(e,{...r,expected_execution_id:n()})}t(ie,"mutateBrokerFault");function ce(e){return o().getBrokerRecovery(e,n())}t(ce,"brokerRecovery");async function ue(e,r){return o().recordNodeFault(e,r)}t(ue,"recordNodeFault");async function de(e,r={}){return o().clearNodeFault(e,{...r.link===void 0?{}:{link:r.link},...r.preserveEpisode===void 0?{}:{preserve_episode:r.preserveEpisode}}).then(i=>i.cleared)}t(de,"clearNodeFault");function ae(e,r){return o().commitBrokerModel(e,{...r,expected_execution_id:n()})}t(ae,"commitBrokerModel");function C(e){return o().brokerExtensionState(e)}t(C,"brokerExtensionState");async function pe(e){const r=N();return r.state==="connected"&&r.latestNode!==void 0?k(r.latestNode):k((await C(e)).node)}t(pe,"cachedBrokerNode");function se(e,r){return o().nodeDelivery(e,{...r,expected_execution_id:n()})}t(se,"brokerDelivery");function fe(e,r){return o().commitBrokerGeneratedName(e,{...r,expected_execution_id:n()})}t(fe,"commitBrokerGeneratedName");function xe(e,r){return o().commitBrokerPersonaAck(e,{...r,expected_execution_id:n()})}t(xe,"commitBrokerPersonaAck");async function le(e,r){try{await o().pushReport(e,r)}catch(i){if(r.tier==="final"&&i instanceof l&&i.code==="already_finalized")return;throw i}}t(le,"publishBrokerReport");function ke(e){return o().relaunchRoot(e)}t(ke,"relaunchBrokerRoot");export{te as acknowledgeBrokerMail,$ as appendBrokerLog,Q as bindBrokerSession,g as brokerApiUrl,se as brokerDelivery,n as brokerExecutionId,C as brokerExtensionState,ce as brokerRecovery,pe as cachedBrokerNode,re as claimBrokerMail,G as clearBrokerPushedFinal,de as clearNodeFault,fe as commitBrokerGeneratedName,ae as commitBrokerModel,xe as commitBrokerPersonaAck,Y as completeBrokerPark,O as consumeBrokerPushedFinal,o as daemonClient,ie as mutateBrokerFault,ne as mutateBrokerProviderRetry,W as postBrokerIdle,le as publishBrokerReport,j as putBrokerRecap,D as putBrokerTelemetry,z as readBrokerPassiveMessage,X as recordBackgroundedJob,Z as recordBrokerParkActivity,ee as recordBrokerTelemetry,oe as recordBrokerTurn,ue as recordNodeFault,ke as relaunchBrokerRoot,H as reportBrokerJob,V as settleBroker,q as startBrokerBashJob,K as stopBrokerBashJob};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var b=Object.defineProperty;var
|
|
1
|
+
var b=Object.defineProperty;var i=(e,t)=>b(e,"name",{value:t,configurable:!0});import{randomUUID as f}from"node:crypto";import{BrokerClient as h,SocketTransport as m}from"../broker-client/index.js";const u=15e3;class w extends Error{static{i(this,"BrokerUnreachableError")}constructor(t){super(t),this.name="BrokerUnreachableError"}}function I(e){return new Promise(async(t,c)=>{const o=new h(e.nodeId,{transport:new m(e.nodeId,{recordsViewerFaults:!1})}),k=f(),d=f();let a=!1,s=!1;const l=setTimeout(()=>n(()=>c(new Error(`timed out after ${u}ms waiting for the broker to ack ${e.what}`))),u);typeof l.unref=="function"&&l.unref();function n(r){if(!a){a=!0,clearTimeout(l);try{o.close()}catch{}r()}}i(n,"finish"),o.on("error",r=>n(()=>c(s?r:new w(`broker connection refused mid-revive: ${r.message}`)))),o.on("close",()=>n(()=>c(s?new Error(`broker connection closed before it acked ${e.what}`):new w("broker connection closed before it was established (mid-revive)")))),o.on("connect",()=>{s=!0,o.send({type:"hello",role:"controller",client_id:k}),o.send(e.frame(d))}),o.on("frame",r=>{r.type==="ack"&&r.for===e.ackFor&&(e.correlated!==!0||r.id===d)?r.ok?n(()=>t(e.result(r.detail))):n(()=>c(new Error(r.detail??`broker rejected ${e.what}`))):r.type==="error"?n(()=>c(new Error(r.message))):e.observe!==void 0&&e.observe(r)}),o.connect()})}i(I,"oneShotControllerRequest");export{w as BrokerUnreachableError,I as oneShotControllerRequest};
|