@north-light/cli 0.3.571 → 0.3.573
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/api/dto/broker-ops.d.ts +3 -0
- package/dist/api/dto/recovery.d.ts +7 -0
- package/dist/api/errors.d.ts +3 -0
- package/dist/api/errors.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +7 -0
- package/dist/commands/dashboard.js +3 -1
- package/dist/core/auth-file.d.ts +2 -0
- package/dist/core/auth-file.js +1 -1
- package/dist/core/canvas/node-tokens.js +3 -3
- package/dist/core/canvas/recovery.d.ts +3 -0
- package/dist/core/canvas/recovery.js +2 -2
- package/dist/core/command.d.ts +2 -3
- package/dist/core/command.js +6 -6
- package/dist/core/grants/mirror.d.ts +3 -1
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/grants/terminal.d.ts +10 -0
- package/dist/core/grants/terminal.js +1 -0
- package/dist/core/llm/run.js +2 -2
- package/dist/core/model-auth/readiness.js +1 -1
- package/dist/core/providers/audit.js +2 -2
- package/dist/core/providers/call.d.ts +4 -0
- package/dist/core/providers/call.js +1 -1
- package/dist/core/providers/files.js +1 -1
- package/dist/core/runs/operations.js +6 -6
- package/dist/core/runtime/blaxel-environment.d.ts +2 -0
- package/dist/core/runtime/blaxel-environment.js +1 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +4 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +1 -1
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/spawn.js +2 -2
- package/dist/core/runtime/start-check.js +1 -1
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/spaces/process.js +1 -1
- package/dist/core/spaces/space.d.ts +2 -1
- package/dist/core/spaces/space.js +2 -2
- package/dist/daemon/api/handlers/broker-ops.js +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/reconcilers/action-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/launch-failure.d.ts +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/launch-failure.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/node-person.d.ts +9 -0
- package/dist/daemon/reconcilers/node-lifecycle/node-person.js +1 -0
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +0 -2
- package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
- package/docs/sdk/release-notes.md +7 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
package/dist/api/contract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
|
|
2
|
-
export declare const CONTRACT_LEVEL = "1.
|
|
2
|
+
export declare const CONTRACT_LEVEL = "1.37";
|
|
3
3
|
/** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
|
|
4
4
|
export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
|
|
5
5
|
/** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
|
package/dist/api/contract.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.
|
|
1
|
+
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.37",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
|
|
@@ -27,6 +27,9 @@ export interface BrokerExecutionRequest {
|
|
|
27
27
|
export interface BrokerSettleRequest extends BrokerExecutionRequest {
|
|
28
28
|
stopReason: string;
|
|
29
29
|
pushedFinal: boolean;
|
|
30
|
+
/** Set only by the broker once it has recorded the settled turn's fatal provider fault and armed no retry or model fallback:
|
|
31
|
+
* the daemon may then idle a resident Blaxel broker. A settle without it never idles on an error. */
|
|
32
|
+
faultSettled?: boolean;
|
|
30
33
|
}
|
|
31
34
|
/** `POST /v1/nodes/{id}/broker/telemetry` body. Tokens are cumulative within
|
|
32
35
|
* the broker's current Pi session; null context/activity values preserve the
|
|
@@ -48,6 +48,13 @@ export interface ExecutionExitDiagnosticDTO {
|
|
|
48
48
|
routes: string[];
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
/** The diagnostic the daemon itself writes when a launch fails before the broker reports; it never crosses `/v1` as a request. */
|
|
52
|
+
export interface ExecutionLaunchFailureDiagnostic {
|
|
53
|
+
schema: 'crtr.execution-exit-diagnostic/v1';
|
|
54
|
+
launch_failure: {
|
|
55
|
+
message: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
51
58
|
export interface BrokerTurnResultDTO {
|
|
52
59
|
applied: boolean;
|
|
53
60
|
fault_link: FaultLink | null;
|
package/dist/api/errors.d.ts
CHANGED
|
@@ -43,5 +43,8 @@ export { APIError as ApiError };
|
|
|
43
43
|
/** A local API transport failure has stable status/code identity, independent
|
|
44
44
|
* of message prose. */
|
|
45
45
|
export declare function isDaemonTransportApiError(error: unknown): error is APIError;
|
|
46
|
+
/** The daemon refused the caller's node token. A teardown revokes the broker's token before its process stops, so a broker
|
|
47
|
+
* that sees this on a poll is being shut down, not hitting a corrupt state. */
|
|
48
|
+
export declare function isTokenRevokedApiError(error: unknown): error is APIError;
|
|
46
49
|
/** Type guard for an `ErrorBody`-shaped parsed payload. */
|
|
47
50
|
export declare function isErrorBody(value: unknown): value is ErrorBody;
|
package/dist/api/errors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var u=Object.defineProperty;var r=(t,e)=>u(t,"name",{value:e,configurable:!0});class n extends Error{static{r(this,"APIError")}status;code;details;headers;type;param;origin;retryable;retryAfterS;resetAt;requestId;runId;questionId;scope;userAction;constructor(e,i,a,d,
|
|
1
|
+
var u=Object.defineProperty;var r=(t,e)=>u(t,"name",{value:e,configurable:!0});class n extends Error{static{r(this,"APIError")}status;code;details;headers;type;param;origin;retryable;retryAfterS;resetAt;requestId;runId;questionId;scope;userAction;constructor(e,i,a,o,d,s){super(a),this.name="APIError",this.status=e,this.code=i,this.headers=new Headers(d),o!==void 0&&(this.details=o),this.type=s?.type,this.param=s?.param,this.origin=s?.origin,this.retryable=s?.retryable,this.retryAfterS=s?.retry_after_s,this.resetAt=s?.reset_at,this.requestId=s?.request_id??this.headers.get("Request-Id")??void 0,this.runId=s?.run_id,this.questionId=s?.question_id,this.scope=s?.scope,this.userAction=s?.user_action}}function h(t){return t instanceof n?t.status===503&&(t.code==="daemon_unavailable"||t.code==="transport_error"||t.code==="daemon_request_interrupted")||t.status===504&&(t.code==="request_timeout"||t.code==="deadline_exceeded")||t.status>=500&&t.code==="daemon_health_unavailable":!1}r(h,"isDaemonTransportApiError");function f(t){return t instanceof n&&t.status===401}r(f,"isTokenRevokedApiError");function p(t){if(typeof t!="object"||t===null)return!1;const e=t.error;if(typeof e!="object"||e===null)return!1;const{code:i,message:a}=e;return typeof i=="string"&&typeof a=="string"}r(p,"isErrorBody");export{n as APIError,n as ApiError,h as isDaemonTransportApiError,p as isErrorBody,f as isTokenRevokedApiError};
|
|
@@ -9,6 +9,13 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
|
|
|
9
9
|
|
|
10
10
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
11
11
|
|
|
12
|
+
## 0.3.572
|
|
13
|
+
|
|
14
|
+
`POST /v1/runs` works on a cloud role (a runtime started with `--role api` or `--role reconcile`). It answered `500 server_failure` because the role has no root launcher and the run start tried to prepare an app OS user and folder. Behavior on a person's Mac and on a hosted guest with a launcher is unchanged.
|
|
15
|
+
|
|
16
|
+
- A run whose profile points at a Blaxel environment is created dormant and started by the node-lifecycle lane through the Blaxel runner, with the prompt as its first turn, within about one lifecycle tick. `POST /v1/nodes` follows the same rule: it no longer fails `400 broker_launch_failed` for such a profile, and answers with the node frozen until the lane starts it.
|
|
17
|
+
- A provider call that returns a file result is refused `storage_unavailable` on a cloud role, which has no folder to put the file in.
|
|
18
|
+
|
|
12
19
|
## 0.3.562
|
|
13
20
|
|
|
14
21
|
A cloud role's runtime (one with `CRTR_DIRECTORY_URL` and `CRTR_PUBLIC_API_URL` set) accepts a directory person bearer on its TCP listener: the caller acts as that person and reads only the nodes that person may read; schedules, charges, files and provider calls are refused `403 permission_error` to it. Any other listener still refuses a person bearer `401`, now with the code `person_bearer_not_supported` (was `person_bearer_not_supported_on_cloud_role`). A cloud role also answers `GET /v1/runtime/vendor-status` and `POST /v1/runtime/vendor-account-remove` for the person the directory's token names, and refuses a token that names no person `401`.
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
var l=Object.defineProperty;var
|
|
1
|
+
var l=Object.defineProperty;var i=(r,e)=>l(r,"name",{value:e,configurable:!0});import{defineLeaf as h}from"../core/command.js";import{InputError as m}from"../core/io.js";import{cliCanvasSource as f}from"./api-client.js";import{renderTreeFromSource as p,renderForestFromSource as w,dashboardRowsFromSource as b,dashboardRowsAllFromSource as S,enrichRowsFromSource as g}from"../core/canvas/render-source.js";const C=h({name:"dashboard",description:"render the canvas as a subscription tree",whenToUse:"you want the whole graph at a glance as a rendered tree \u2014 the subscription forest drawn in ASCII so you can read its SHAPE: who reports to whom, how deep each branch runs, plus each node's status and context size. Scope to one root or show the full forest. Use `node inspect list` instead for a flat roster without the tree, `node inspect show` to drill into one node's neighbors, and `canvas attention` to find which nodes are blocked on the user",help:{name:"canvas dashboard",summary:"render the canvas as an ASCII subscription tree \u2014 scoped to a root or the full forest",params:[{kind:"flag",name:"root",type:"string",required:!1,constraint:"Node id to use as the tree root. Omit for the full forest (all canvas roots)."}],output:[{name:"tree",type:"string",required:!0,constraint:"Multi-line ASCII tree."},{name:"nodes",type:"integer",required:!0,constraint:"Total node count in this view."},{name:"rows",type:"object[]",required:!0,constraint:"One per node: {node_id,name,status,kind,mode,ctx_tokens,asks}. Emitted only under `--json`; the default prose output is the tree and node count."}],outputKind:"object",effects:["Read-only: queries canvas.db and node telemetry files."]},render:i(r=>`${String(r.tree)}
|
|
2
|
+
|
|
3
|
+
${String(r.nodes)} nodes`,"render"),run:i(async r=>{const e=r.root,t=f(),o=await t.listNodes();if(e!==void 0){if(!o.some(s=>s.node_id===e))throw new m({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list` to find a valid id."});let n={},c;try{n=await t.ticketCountsForView(e)}catch(s){c=s instanceof Error?s.message:String(s)}const u=await b(t,e,o,n);return{tree:await p(t,e,o,n,c),nodes:u.length,rows:u}}const d=await S(t,new Set,!1,o);let a={};try{a=await t.ticketCountsForNodes(o.map(n=>n.node_id))}catch{}return await g(t,d,a),{tree:await w(t,o,a),nodes:o.length,rows:d}},"run")});export{C as dashboardLeaf};
|
package/dist/core/auth-file.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ type AuthFileData = Record<string, Credential>;
|
|
|
10
10
|
* be sent to the provider verbatim.
|
|
11
11
|
*/
|
|
12
12
|
export declare function resolveCredentialValue(config: string, env?: Record<string, string>): string | undefined;
|
|
13
|
+
/** The api_key a model call can send for `providerId`: the stored row with its `$VAR` / `!cmd` reference resolved. Undefined when no api_key is stored or its reference does not resolve. */
|
|
14
|
+
export declare function storedApiKey(providerId: string): Promise<string | undefined>;
|
|
13
15
|
/** Read all stored credentials. */
|
|
14
16
|
export declare function readAuthCredentials(): Promise<AuthFileData>;
|
|
15
17
|
/** Read one provider's stored credential. */
|
package/dist/core/auth-file.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var A=Object.defineProperty;var a=(t,e)=>A(t,"name",{value:e,configurable:!0});import{execSync as O}from"node:child_process";import{openDb as _,withCanvasWrite as f}from"./canvas/db.js";import{personIdAsync as c}from"./canvas/person.js";import{loadSecretsKey as w,open as C,seal as v}from"./secrets/index.js";const k=a((t,e)=>`model_api_keys|${t}|${e}`,"aadOf");function y(t,e){return JSON.parse(C(e.credential_sealed,e.key_id,w(),k(t,e.provider_id)))}a(y,"openRow");async function m(t,e,n,i){const{sealed:r,keyId:d}=v(JSON.stringify(i),w(),k(e,n));await t.prepare("INSERT INTO model_api_keys(person,provider_id,credential_sealed,key_id,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(person,provider_id) DO UPDATE SET credential_sealed=EXCLUDED.credential_sealed,key_id=EXCLUDED.key_id,updated_at=EXCLUDED.updated_at").run(e,n,r,d,new Date().toISOString())}a(m,"put");const D=/^[A-Za-z_][A-Za-z0-9_]*/,x=/^[A-Za-z_][A-Za-z0-9_]*$/,E=new Map;function R(t){if(E.has(t))return E.get(t);let e;try{e=O(t,{encoding:"utf-8",timeout:1e4,stdio:["ignore","pipe","ignore"]}).trim()||void 0}catch{e=void 0}return E.set(t,e),e}a(R,"runValueCommand");function S(t,e){if(t.startsWith("!"))return R(t.slice(1));let n="",i=0;for(;i<t.length;){const r=t.indexOf("$",i);if(r<0){n+=t.slice(i);break}n+=t.slice(i,r);const d=t[r+1];if(d==="$"||d==="!"){n+=d,i=r+2;continue}let s,o=r+1;if(d==="{"){const p=t.indexOf("}",r+2),u=p<0?void 0:t.slice(r+2,p);u!==void 0&&x.test(u)&&(s=u,o=p+1)}else{const p=D.exec(t.slice(r+1));p!==null&&(s=p[0],o=r+1+s.length)}if(s===void 0){n+="$",i=r+1;continue}const l=e?.[s]||process.env[s];if(l===void 0||l==="")return;n+=l,i=o}return n}a(S,"resolveCredentialValue");async function $(t){const e=await new T().read(t);return e?.type==="api_key"&&e.key?e.key:void 0}a($,"storedApiKey");async function h(){const t=await c(),e=await(await _()).prepare("SELECT provider_id,credential_sealed,key_id FROM model_api_keys WHERE person=? ORDER BY provider_id").all(t);return Object.fromEntries(e.map(n=>[n.provider_id,y(t,n)]))}a(h,"readAuthCredentials");async function N(t){const e=await c(),n=await(await _()).prepare("SELECT provider_id,credential_sealed,key_id FROM model_api_keys WHERE person=? AND provider_id=?").get(e,t);return n?y(e,n):void 0}a(N,"readAuthCredential");async function I(t,e){const n=await c();await f(i=>m(i,n,t,e))}a(I,"setAuthCredential");async function L(t){const e=await c();return(await(await _()).prepare("DELETE FROM model_api_keys WHERE person=? AND provider_id=?").run(e,t)).changes>0}a(L,"deleteAuthCredential");class T{static{a(this,"CrouterCredentialStore")}async read(e){const n=await N(e);return n?.type!=="api_key"||n.key===void 0?n:{...n,key:S(n.key,n.env)}}async list(){return Object.entries(await h()).map(([e,n])=>({providerId:e,type:n.type}))}async modify(e,n){const i=await c();return f(async r=>{await r.exec("SET LOCAL lock_timeout = '15s'");const d=await r.prepare("SELECT provider_id,credential_sealed,key_id FROM model_api_keys WHERE person=? AND provider_id=? FOR UPDATE").get(i,e),s=d?y(i,d):void 0,o=await n(s);return o===void 0?s:(await m(r,i,e,o),o)})}async delete(e){await L(e)}}export{T as CrouterCredentialStore,L as deleteAuthCredential,N as readAuthCredential,h as readAuthCredentials,S as resolveCredentialValue,I as setAuthCredential,$ as storedApiKey};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var E=Object.defineProperty;var r=(n,e)=>E(n,"name",{value:e,configurable:!0});import{createHash as g,randomBytes as l}from"node:crypto";import{openDb as
|
|
1
|
+
var E=Object.defineProperty;var r=(n,e)=>E(n,"name",{value:e,configurable:!0});import{createHash as g,randomBytes as l}from"node:crypto";import{openDb as s}from"./db.js";import{terminalGrant as k}from"../grants/mirror.js";import{personIdAsync as w}from"./person.js";import{uidForApp as N}from"../spaces/users.js";const p=r(n=>g("sha256").update(n).digest("hex"),"hash");async function x(n,e,o,i){const t=await n.prepare("SELECT grantee, sub, grant_id, client_id, scopes_json, run_id FROM node_rows WHERE node_id = ?").get(o);if(!t)throw new Error(`cannot mint node token: missing node ${o}`);const a=t.run_id&&t.run_id!==o?await n.prepare("SELECT sub, grant_id, client_id FROM nodes WHERE node_id = ?").get(t.run_id):void 0,d=a?.sub??t.sub??await w(),_=t.grantee==="app:terminal"?await k(d):null;if(t.grantee==="app:terminal"&&!_)throw new Error("cannot mint node token: terminal grant is not active");const u=_?.id??a?.grant_id??t.grant_id??null,c=l(32).toString("base64url");if(!await n.prepare("SELECT 1 AS ok FROM executions WHERE execution_id = ? AND node_id = ? AND state = 'launching'").get(e,o))throw new Error(`cannot mint node token: execution ${e} is not launching`);return await n.prepare('INSERT INTO node_tokens (broker_process_id, node_id, token_hash, grantee, uid, sub, "grant", client_id, scopes_json, issued_at, revoked_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)').run(e,o,p(c),t.grantee,await N(t.grantee),d,u,a?.client_id??t.client_id,t.scopes_json,i),c}r(x,"mintNodeToken");async function A(n){if(!/^[A-Za-z0-9_-]{43}$/.test(n))return null;const e=await(await s()).prepare(`SELECT t.node_id, t.grantee, t.uid, t.sub, t."grant", t.client_id, t.scopes_json, n.run_id
|
|
2
2
|
FROM node_tokens t JOIN executions e ON e.execution_id = t.broker_process_id
|
|
3
3
|
JOIN nodes n ON n.node_id = t.node_id
|
|
4
|
-
WHERE t.token_hash = ? AND t.revoked_at IS NULL AND e.state != 'exited'`).get(
|
|
5
|
-
AND broker_process_id IN (SELECT execution_id FROM executions WHERE daemon_epoch IS DISTINCT FROM ?)`).run(new Date().toISOString(),n)}r(
|
|
4
|
+
WHERE t.token_hash = ? AND t.revoked_at IS NULL AND e.state != 'exited'`).get(p(n));return e?{app:e.grantee,node:e.node_id,run:e.run_id,uid:e.uid,sub:e.sub,grant:e.grant,client_id:e.client_id,scopes:e.scopes_json===null?null:JSON.parse(e.scopes_json)}:null}r(A,"resolveNodeToken");async function D(n){await m(await s(),"broker_process_id",n,new Date().toISOString())}r(D,"revokeExitedNodeToken");async function b(n){await(await s()).prepare(`UPDATE node_tokens SET revoked_at = ? WHERE revoked_at IS NULL
|
|
5
|
+
AND broker_process_id IN (SELECT execution_id FROM executions WHERE daemon_epoch IS DISTINCT FROM ?)`).run(new Date().toISOString(),n)}r(b,"revokePriorEpochNodeTokens");async function m(n,e,o,i){await n.prepareUncached(`UPDATE node_tokens SET revoked_at = ? WHERE ${e} = ? AND revoked_at IS NULL`).run(i,o)}r(m,"revokeNodeTokens");export{x as mintNodeToken,A as resolveNodeToken,D as revokeExitedNodeToken,m as revokeNodeTokens,b as revokePriorEpochNodeTokens};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import { type Fault, type FaultKind, type FaultLink, type FaultProviderError, type FaultRetry } from '../fault-classifier.js';
|
|
3
|
+
import type { ExecutionExitDiagnosticDTO, ExecutionLaunchFailureDiagnostic } from '../../api/dto/recovery.js';
|
|
3
4
|
export type { Fault, FaultKind, FaultLink, FaultProviderError, FaultRetry } from '../fault-classifier.js';
|
|
4
5
|
export declare const FAULT_QUIET_MS: number;
|
|
5
6
|
export interface FaultRecordInput {
|
|
@@ -44,6 +45,8 @@ export declare function closeTurn(executionId: string, opts: {
|
|
|
44
45
|
}): Promise<boolean>;
|
|
45
46
|
/** Stamps `read_at`: a viewer has just been shown this conversation. */
|
|
46
47
|
export declare function markNodeRead(nodeId: string): Promise<boolean>;
|
|
48
|
+
/** The one writer of an `executions.exit_diagnostic_json` envelope: the schema tag plus one payload the DTOs declare. */
|
|
49
|
+
export declare function exitDiagnosticJson(payload: Omit<ExecutionExitDiagnosticDTO, 'schema'> | Omit<ExecutionLaunchFailureDiagnostic, 'schema'>): string;
|
|
47
50
|
export declare function recordTransportPoisonVerdict(executionId: string, verdict: TransportPoisonVerdict): Promise<boolean>;
|
|
48
51
|
export declare function readTransportPoisonVerdict(nodeId: string): Promise<TransportPoisonVerdict | null>;
|
|
49
52
|
export declare function readLiveTurn(nodeId: string): Promise<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var se=Object.defineProperty;var o=(e,t)=>se(e,"name",{value:t,configurable:!0});import{prepared as _}from"../sqlite-statements.js";import{randomUUID as I}from"node:crypto";import{existsSync as f,readdirSync as de,readFileSync as ue,realpathSync as ce,statSync as _e}from"node:fs";import{join as N,resolve as pe}from"node:path";import{withCanvasWrite as l,openDb as p}from"./db.js";import{jobDir as le,nodesRoot as C}from"./paths.js";import{errorClassFromFault as B}from"../fault-classifier.js";import{parseProviderRateLimit as Ee}from"../provider-failure.js";import{emitEvent as J}from"../events/emit.js";import{operationIdContext as K,validateOperationId as Y}from"../events/operation-id.js";const Xe=5*6e4,fe=500,G=["pi\u2192provider","viewer\u2194broker","relay\u2194broker","viewer\u2194crtrd","daemon\u2192node","crtr\u2192pi"],ye=[...G,"broker\u2194crtrd"],Te=["rate-limit","overloaded","connection","auth","protocol","context-overflow","other","wedged","model-not-found"],Ne=["auto","manual","fatal"],ge=["sdk","daemon","client"],Le=["pending","admitted","invalidated"];function u(){return new Date().toISOString()}o(u,"now");function H(e,t){return new Set(e.prepare(`PRAGMA table_info(${t})`).all().map(({name:n})=>n))}o(H,"tableColumns");function me(e){const t=H(e,"executions"),n=[["turn_open_at","TEXT"],["turn_progress_at","TEXT"],["turn_closed_at","TEXT"],["turn_end_reason","TEXT CHECK (turn_end_reason IS NULL OR turn_end_reason IN ('settled', 'teardown-aborted'))"],["exit_diagnostic_json","TEXT"]];for(const[r,i]of n)t.has(r)||e.exec(`ALTER TABLE executions ADD COLUMN ${r} ${i}`)}o(me,"addExecutionColumns");function Ae(e){e.exec(`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS node_faults (
|
|
3
3
|
fault_id TEXT PRIMARY KEY,
|
|
4
4
|
node_id TEXT NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE,
|
|
@@ -23,4 +23,4 @@ CREATE TABLE IF NOT EXISTS node_faults (
|
|
|
23
23
|
);
|
|
24
24
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_faults_active ON node_faults(node_id) WHERE active = 1;
|
|
25
25
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_node_faults_episode ON node_faults(node_id) WHERE episode_state IS NOT NULL;
|
|
26
|
-
`)}o(me,"createFaultTable");function g(e,t){return typeof t=="string"&&e.includes(t)}o(g,"contains");function W(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(W,"isRecord");function q(e){if(!W(e)||typeof e.provider!="string"||e.provider===""||typeof e.model!="string"||e.model==="")return;const t=typeof e.status=="number"&&Number.isFinite(e.status)?e.status:void 0,n=typeof e.code=="string"||typeof e.code=="number"?e.code:void 0,r=typeof e.errno=="string"?e.errno:void 0,i=typeof e.body=="string"?e.body:void 0,a=le(e.ratelimit),d=Array.isArray(e.causes)?e.causes.flatMap(s=>{if(!W(s))return[];const E={...typeof s.message=="string"?{message:s.message}:{},...typeof s.status=="number"&&Number.isFinite(s.status)?{status:s.status}:{},...typeof s.code=="string"||typeof s.code=="number"?{code:s.code}:{},...typeof s.errno=="string"?{errno:s.errno}:{},...typeof s.body=="string"?{body:s.body}:{}};return Object.keys(E).length===0?[]:[E]}):void 0;return{provider:e.provider,model:e.model,...t===void 0?{}:{status:t},...n===void 0?{}:{code:n},...r===void 0?{}:{errno:r},...i===void 0?{}:{body:i},...d===void 0||d.length===0?{}:{causes:d},...a===void 0?{}:{ratelimit:a}}}o(q,"parseProviderError");function V(e){try{const t=JSON.parse(e);if(!g(fe,t.link)||typeof t.op!="string"||t.op===""||!g(ye,t.kind)||typeof t.message!="string"||typeof t.since!="string")return null;const n=Y(t.operation_id);if(!W(t.retry)||!g(Te,t.retry.disposition)||t.retry.by!==void 0&&!g(Ne,t.retry.by)||t.retry.attempt!==void 0&&typeof t.retry.attempt!="number"||t.retry.max!==void 0&&typeof t.retry.max!="number"||t.retry.nextAt!==void 0&&typeof t.retry.nextAt!="string"||t.anchorEntryId!==void 0&&(typeof t.anchorEntryId!="string"||t.anchorEntryId===""))return null;const r=t.providerError===void 0?void 0:q(t.providerError);return t.providerError!==void 0&&r===void 0?null:{link:t.link,op:t.op,kind:t.kind,retry:{disposition:t.retry.disposition,...t.retry.by===void 0?{}:{by:t.retry.by},...t.retry.attempt===void 0?{}:{attempt:t.retry.attempt},...t.retry.max===void 0?{}:{max:t.retry.max},...t.retry.nextAt===void 0?{}:{nextAt:t.retry.nextAt}},message:t.message,since:t.since,operation_id:n,...t.anchorEntryId===void 0?{}:{anchorEntryId:t.anchorEntryId},...r===void 0?{}:{providerError:r}}}catch{return null}}o(V,"parseLegacyFault");function Ae(e){const t=_e(e);try{return ue(t)}catch{return t}}o(Ae,"canonicalPath");function Se(e){try{const t=JSON.parse(e);if(!g(ge,t.state)||typeof t.sessionFile!="string"||t.sessionFile==="")return null;const n=JSON.stringify(t.fault),r=V(n);if(r===null||r.link!=="pi\u2192provider"||r.retry.disposition!=="auto"||r.retry.by!=="daemon")return null;const i=t.anchorEntryId;return i!==null&&typeof i!="string"||(r.anchorEntryId??null)!==i?null:{state:t.state,sessionFile:Ae(t.sessionFile),fault:r}}catch{return null}}o(Se,"parseLegacyEpisode");function Q(e){return e!==void 0?Y(e):K.current()??K.fresh(n=>n)}o(Q,"resolveOperationId");function Re(e){return e.slice(0,Ee)}o(Re,"clampMessage");function w(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="auto"&&e.retry.by==="daemon"?3:e.retry.disposition==="fatal"?2:1}o(w,"faultPriority");function z(e){return{link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operationId:e.operation_id,anchorEntryId:e.anchorEntryId??null,retryDisposition:e.retry.disposition,retryBy:e.retry.by??null,retryAttempt:e.retry.attempt??null,retryMax:e.retry.max??null,retryNextAt:e.retry.nextAt??null,providerError:e.providerError===void 0?null:JSON.stringify(e.providerError)}}o(z,"encodeFault");function b(e){const t=e.provider_error_json,n=typeof t=="string"?q(JSON.parse(t)):void 0;if(typeof t=="string"&&n===void 0)throw new Error(`invalid provider error on stored fault ${e.fault_id}`);const r={disposition:e.retry_disposition,...e.retry_by===null?{}:{by:e.retry_by},...e.retry_attempt===null?{}:{attempt:e.retry_attempt},...e.retry_max===null?{}:{max:e.retry_max},...e.retry_next_at===null?{}:{nextAt:e.retry_next_at}};return{faultId:e.fault_id,link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operation_id:e.operation_id,retry:r,...e.anchor_entry_id===null?{}:{anchorEntryId:e.anchor_entry_id},...n===void 0?{}:{providerError:n},...typeof e.notified_since=="string"?{notifiedSince:e.notified_since}:{}}}o(b,"hydrateFault");async function L(e,t){const n=await e.prepare("SELECT * FROM node_faults WHERE node_id = ? AND active = 1").get(t);return n===void 0?null:b(n)}o(L,"activeFault");function P(e){if(!g(G,e.link))throw new Error(`fault link is not durable: ${e.link}`);return{link:e.link,op:e.op,kind:e.kind,retry:e.retry,message:Re(e.message),since:e.since??u(),operation_id:Q(e.operation_id),...typeof e.anchorEntryId=="string"&&e.anchorEntryId!==""?{anchorEntryId:e.anchorEntryId}:{},...e.providerError===void 0?{}:{providerError:e.providerError}}}o(P,"storedFault");function M(e,t){J({level:t.retry.disposition==="fatal"?"error":"warn",event:"fault.recorded",operation_id:t.operation_id,node_id:e,error_class:B(t),fields:{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}})}o(M,"emitRecordedFault");const Z="INSERT INTO node_faults (fault_id, node_id, link, op, kind, message, since, operation_id, anchor_entry_id, retry_disposition, retry_by, retry_attempt, retry_max, retry_next_at, provider_error_json, active, episode_state, episode_binding_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";function ee(e,t,n,r,i){const a=z(n),d=u();return[e,t,a.link,a.op,a.kind,a.message,a.since,a.operationId,a.anchorEntryId,a.retryDisposition,a.retryBy,a.retryAttempt,a.retryMax,a.retryNextAt,a.providerError,r?1:0,i?.state??null,i?.bindingId??null,d,d]}o(ee,"faultInsertParams");async function te(e,t,n,r,i){const a=I();return await e.prepare(Z).run(...ee(a,t,n,r,i)),{...n,faultId:a}}o(te,"insertFault");async function D(e,t,n,r){const i=z(n);return await e.prepare("UPDATE node_faults SET link = ?, op = ?, kind = ?, message = ?, since = ?, operation_id = ?, anchor_entry_id = ?, retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, provider_error_json = ?, episode_state = COALESCE(?, episode_state), episode_binding_id = COALESCE(?, episode_binding_id), updated_at = ? WHERE fault_id = ?").run(i.link,i.op,i.kind,i.message,i.since,i.operationId,i.anchorEntryId,i.retryDisposition,i.retryBy,i.retryAttempt,i.retryMax,i.retryNextAt,i.providerError,r?.state??null,r?.bindingId??null,u(),t),{...n,faultId:t}}o(D,"updateFault");async function U(e,t){await e.prepare("DELETE FROM node_faults WHERE fault_id = ? AND active = 0 AND episode_state IS NULL").run(t)}o(U,"deleteIfUnretained");async function ne(e,t,n,r){const i=await L(e,t);if(i===null){const a=await e.prepare("SELECT fault_id, link, kind, since, operation_id FROM node_faults WHERE node_id = ? AND active = 0 AND episode_state IS NOT NULL").get(t);return a!==void 0&&a.link===n.link&&a.kind===n.kind&&a.since===n.since&&a.operation_id===n.operation_id?(await e.prepare("UPDATE node_faults SET active = 1, updated_at = ? WHERE fault_id = ?").run(u(),a.fault_id),await D(e,a.fault_id,n)):await te(e,t,n,!0,r)}if(i.link===n.link&&i.kind===n.kind){const a=n.retry.disposition==="auto"?{...i.retry,...n.retry}:n.retry,d=i.retry.disposition===n.retry.disposition&&(n.retry.disposition!=="fatal"||i.message===n.message),s=d?i.since:n.since,E=await D(e,i.faultId,{...n,since:s,anchorEntryId:n.anchorEntryId??i.anchorEntryId,retry:a},r);return d||await e.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(i.faultId),E}return i.link!==n.link&&w(i)>w(n)?null:(await e.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await U(e,i.faultId),await te(e,t,n,!0,r))}o(ne,"replaceActiveFault");async function m(e,t,n){return(await e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND session_id = ? AND unbound_at IS NULL").get(t,n))?.binding_id??null}o(m,"liveBinding");async function Xe(e,t){return await l(async n=>{const r=u();return(await n.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,e)).changes>0?!0:(await n.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ? AND state != 'exited'").run(r,r,e)).changes>0})}o(Xe,"openTurn");async function $e(e){return(await(await p()).prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(u(),e)).changes>0}o($e,"recordTurnProgress");async function je(e,t){return await l(async n=>{const r=u(),i=(await n.prepare("UPDATE executions SET turn_closed_at = ?, turn_end_reason = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,t.endReason,e)).changes>0;if(i&&t.endReason==="settled"){const a="(SELECT node_id FROM executions WHERE execution_id = ?)";t.viewerPresent===!0?await n.prepare(`UPDATE nodes SET reply_at = ?, read_at = ? WHERE node_id = ${a}`).run(r,r,e):await n.prepare(`UPDATE nodes SET reply_at = ? WHERE node_id = ${a}`).run(r,e)}return i})}o(je,"closeTurn");async function Be(e){return(await(await p()).prepare("UPDATE nodes SET read_at = ? WHERE node_id = ?").run(u(),e)).changes>0}o(Be,"markNodeRead");async function Je(e,t){if(!Number.isInteger(t.consecutivePasses)||t.consecutivePasses<1||t.routes.length===0||!t.routes.every(n=>n!==""))throw new Error("invalid transport-poison verdict");return(await(await p()).prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ? AND state != 'exited'").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:t.consecutivePasses,routes:t.routes}}),e)).changes>0}o(Je,"recordTransportPoisonVerdict");async function Ke(e){const t=await(await p()).prepare("SELECT exit_diagnostic_json FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);if(t===void 0||t.exit_diagnostic_json===null)return null;try{const n=JSON.parse(t.exit_diagnostic_json),r=n.transport_poison;return n.schema!=="crtr.execution-exit-diagnostic/v1"||r===void 0||!Number.isInteger(r.consecutive_passes)||r.consecutive_passes<1||!Array.isArray(r.routes)||r.routes.length===0||!r.routes.every(i=>typeof i=="string"&&i!=="")?null:{consecutivePasses:r.consecutive_passes,routes:r.routes}}catch{return null}}o(Ke,"readTransportPoisonVerdict");async function Ye(e){const t=await(await p()).prepare("SELECT turn_open_at, turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:{busy:t.turn_open_at!==null&&t.turn_closed_at===null,progressAt:t.turn_progress_at}}o(Ye,"readLiveTurn");async function Ge(e){const t=await p(),n=await t.prepare("SELECT turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL ORDER BY seq DESC LIMIT 1").get(e);if(n===void 0)return null;const r=await t.prepare("SELECT turn_end_reason FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);return{interrupted:n.turn_closed_at===null||r?.turn_end_reason==="teardown-aborted",progressAt:n.turn_progress_at}}o(Ge,"readInterruptedTurn");async function qe(e){return await(await p()).prepare("SELECT 1 FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL LIMIT 1").get(e)!==void 0}o(qe,"hasAdmittedTurn");async function Ve(e,t){const n=P(t);return M(e,n),await l(async r=>await ne(r,e,n))}o(Ve,"recordFault");async function Qe(e){return await l(async t=>(await t.prepare("UPDATE node_faults SET notified_since = since, updated_at = ? WHERE fault_id = ?").run(u(),e)).changes>0)}o(Qe,"markFaultNotified");function xe(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="fatal"&&e.notifiedSince!==e.since}o(xe,"isUnnotifiedFatalProviderFault");async function ze(){return(await(await p()).prepare("SELECT f.node_id FROM node_faults f JOIN nodes n ON n.node_id = f.node_id WHERE f.active = 1 AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'fatal' AND (f.notified_since IS NULL OR f.notified_since != f.since) AND n.status IN ('active', 'idle')").all()).map(e=>e.node_id)}o(ze,"listUnnotifiedFatalFaultNodes");let X=null;function Ze(e){X=e}o(Ze,"setFatalFaultAnnouncer");async function et(e,t){const n=await L(await p(),e);return n===null||t?.link!==void 0&&n.link!==t.link?!1:await l(async r=>{const i=await L(r,e);if(i===null||t?.link!==void 0&&i.link!==t.link)return!1;X!==null&&xe(i)&&await X(e);const a=await r.prepare("SELECT episode_state FROM node_faults WHERE fault_id = ?").get(i.faultId);await r.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),a.episode_state==="pending"&&t?.preserveEpisode!==!0&&await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await U(r,i.faultId);const d=Q();return J({level:"info",event:"fault.cleared",operation_id:d,node_id:e,error_class:B(i),fields:{cleared_fault_operation_id:i.operation_id,link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId}}}),!0})}o(et,"clearFault");async function re(e){return await L(await p(),e)}o(re,"readFault");async function tt(e,t){const n=P(t.input);if(n.link!=="pi\u2192provider"||n.retry.disposition!=="auto"||n.retry.by!=="daemon")throw new Error("provider retry episode requires an automatic daemon provider fault");return M(e,n),await l(async r=>{const i=await m(r,e,t.sessionId);if(i===null)return null;const a=await L(r,e);if(a!==null&&(a.link!==n.link||a.kind!==n.kind)&&w(a)>w(n))return null;const d=await r.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_state IS NOT NULL").all(e);await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_state IS NOT NULL").run(u(),e);for(const E of d)await U(r,E.fault_id);const s=await ne(r,e,n,{state:"pending",bindingId:i});return s===null?null:(await r.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run("pending",i,u(),s.faultId),s)})}o(tt,"recordPendingEpisode");async function nt(e,t){return await l(async n=>{if(await n.prepare("SELECT 1 FROM nodes WHERE node_id = ? AND intent = 'refresh'").get(e)!==void 0)return!1;const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'admitted', retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, updated_at = ? WHERE node_id = ? AND fault_id = ? AND episode_state = 'pending' AND episode_binding_id = ?").run(t.retry.disposition,t.retry.by??null,t.retry.attempt??null,t.retry.max??null,t.retry.nextAt??null,u(),e,t.faultId,r)).changes>0})}o(nt,"admitEpisode");async function rt(e,t){const n=P(t.fault);return await l(async r=>{const i=await m(r,e,t.sessionId);return i===null||(await L(r,e))?.faultId!==t.faultId||await r.prepare("SELECT 1 FROM node_faults WHERE node_id = ? AND fault_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").get(e,t.faultId,i)===void 0?!1:(M(e,n),t.reopen?await D(r,t.faultId,n,{state:"pending",bindingId:i}):(await D(r,t.faultId,n),await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),t.faultId),await r.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(t.faultId)),!0)})}o(rt,"settleRejectedEpisode");async function it(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'pending', updated_at = ? WHERE node_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(it,"reopenEpisode");async function ot(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'invalidated', updated_at = ? WHERE node_id = ? AND episode_state != 'invalidated' AND episode_state IS NOT NULL AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(ot,"invalidateEpisode");async function at(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);if(r===null)return!1;const i=await n.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").all(e,r);if(i.length===0)return!1;await n.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").run(u(),e,r);for(const a of i)await U(n,a.fault_id);return!0})}o(at,"clearEpisode");async function st(e){const t=await(await p()).prepare("SELECT f.*, s.session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.node_id = ? AND f.episode_state IS NOT NULL").get(e);return t===void 0?null:{state:t.episode_state,sessionId:t.session_id,fault:b(t)}}o(st,"readEpisode");async function dt(){return(await(await p()).prepare("SELECT f.*, s.session_id AS bound_session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.active = 1 AND f.episode_state = 'pending' AND s.unbound_at IS NULL AND s.session_id IS NOT NULL AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'auto' AND f.retry_by = 'daemon' AND f.kind IN ('connection', 'rate-limit', 'overloaded')").all()).map(t=>({nodeId:t.node_id,sessionId:t.bound_session_id,fault:b(t),updatedAt:t.updated_at,nudgedAt:t.nudged_at??null}))}o(dt,"listPendingProviderRetries");async function ut(e,t,n){return(await(await p()).prepare("UPDATE node_faults SET nudged_at = ? WHERE fault_id = ? AND active = 1 AND episode_state = 'pending' AND (nudged_at IS NULL OR nudged_at <= ?)").run(new Date(t).toISOString(),e,new Date(t-n).toISOString())).changes===1}o(ut,"claimFaultNudge");async function ct(e){const t=await re(e)===null;return{async explanation(){if(!t)return null;const n=await re(e);if(n===null||n.link!=="crtr\u2192pi"||n.retry.disposition!=="fatal")return null;const r=n.op.trim(),i=n.message.trim();return r===""||i===""?null:`${r}: ${i}`}}}o(ct,"beginBootFaultAttempt");function ie(e,t){try{return ce(e).mtime.toISOString()}catch{return t}}o(ie,"markerMtime");function $(e,t){if(!f(e))return null;try{return de(e,"utf8")}catch(n){if(!t)return null;throw new Error(`cannot import recovery input ${e}: ${n.message}`)}}o($,"legacyFile");function Ie(e,t){return _(e,"SELECT execution_id, started_at FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(t)??null}o(Ie,"latestExecution");function we(e,t){const r=H(e,"nodes").has("launched_at")?_(e,"SELECT COALESCE(launched_at, created) AS started_at FROM nodes WHERE node_id = ?").get(t):_(e,"SELECT created AS started_at FROM nodes WHERE node_id = ?").get(t);if(r===void 0)throw new Error(`cannot import recovery witness for unknown node ${t}`);const i=I();return _(e,"INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason) VALUES (?, ?, NULL, NULL, NULL, 'exited', ?, ?, 'pre-consolidation')").run(i,t,r.started_at,r.started_at),{execution_id:i,started_at:r.started_at}}o(we,"syntheticExecution");function oe(e,t,n,r,i){const a=I();return _(e,Z).run(...ee(a,t,n,r,i)),{...n,faultId:a}}o(oe,"insertMigratedFault");function De(e,t,n,r){const i=_(e,"SELECT binding_id, node_id FROM sessions WHERE transcript_path = ? AND unbound_at IS NULL").get(n);if(i!==void 0){if(i.node_id!==t)throw new Error(`cannot import provider retry ${r}: transcript is live-owned by ${i.node_id}`);return i.binding_id}const a=_(e,"SELECT binding_id FROM sessions WHERE node_id = ? AND transcript_path = ? ORDER BY rowid DESC LIMIT 1").get(t,n);if(a!==void 0)return a.binding_id;const d=_(e,"SELECT created FROM nodes WHERE node_id = ?").get(t);if(d===void 0)throw new Error(`cannot import provider retry ${r}: unknown node ${t}`);const s=I();return _(e,"INSERT INTO sessions (binding_id, session_id, node_id, transcript_path, source_session_id, fork_entry_id, created_at, bound_at, unbound_at) VALUES (?, NULL, ?, ?, NULL, NULL, ?, ?, ?)").run(s,t,n,d.created,d.created,u()),s}o(De,"migrationBinding");function _t(e){e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'node_faults'").get()!==void 0&&(H(e,"node_faults").has("notified_since")||e.exec("ALTER TABLE node_faults ADD COLUMN notified_since TEXT"))}o(_t,"addFaultNotifiedSince");function pt(e){if(Le(e),me(e),!f(C()))return;const t=se(C(),{withFileTypes:!0}).filter(r=>r.isDirectory()).sort((r,i)=>r.name.localeCompare(i.name)),n=_(e,"SELECT 1 FROM nodes WHERE node_id = ?");for(const r of t){const i=r.name,a=pe(i),d=N(a,"busy"),s=N(a,"clean-abort"),E=N(a,"transport-poison.json"),O=N(a,"fault"),A=N(a,"provider-retry"),h=f(d),v=f(s);if(!h&&!v&&!f(E)&&!f(O)&&!f(A)||!f(N(C(),i,"meta.json"))&&n.get(i)===void 0)continue;const k=$(E,!1),S=$(O,!0),R=$(A,!0);if(!h&&!v&&k===null&&S===null&&R===null)continue;let y=Ie(e,i);if(y===null&&(y=we(e,i)),h){const c=ie(d,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ?").run(c,c,y.execution_id)}else if(v){const c=ie(s,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = ?, turn_end_reason = 'teardown-aborted' WHERE execution_id = ?").run(c,c,c,y.execution_id)}if(k!==null)try{const c=JSON.parse(k);Number.isInteger(c.consecutivePasses)&&c.consecutivePasses>=1&&Array.isArray(c.routes)&&c.routes.length>0&&c.routes.every(j=>typeof j=="string"&&j!=="")&&_(e,"UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ?").run(JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",transport_poison:{consecutive_passes:c.consecutivePasses,routes:c.routes}}),y.execution_id)}catch{}const x=S===null?null:V(S);if(S!==null&&x===null)throw new Error(`cannot import recovery fault ${O}`);const T=R===null?null:Se(R);if(R!==null&&T===null)throw new Error(`cannot import provider retry ${A}`);let F=x===null||x.link==="broker\u2194crtrd"?null:oe(e,i,x,!0);if(T!==null){const c=De(e,i,T.sessionFile,A);F!==null&&F.since===T.fault.since?_(e,"UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run(T.state,c,u(),F.faultId):oe(e,i,T.fault,!1,{state:T.state,bindingId:c})}}}o(pt,"migrateRecoveryWitnesses");export{Me as FAULT_QUIET_MS,_t as addFaultNotifiedSince,nt as admitEpisode,ct as beginBootFaultAttempt,ut as claimFaultNudge,at as clearEpisode,et as clearFault,je as closeTurn,qe as hasAdmittedTurn,b as hydrateFault,ot as invalidateEpisode,dt as listPendingProviderRetries,ze as listUnnotifiedFatalFaultNodes,Qe as markFaultNotified,Be as markNodeRead,pt as migrateRecoveryWitnesses,Xe as openTurn,q as parseProviderError,st as readEpisode,re as readFault,Ge as readInterruptedTurn,Ye as readLiveTurn,Ke as readTransportPoisonVerdict,Ve as recordFault,tt as recordPendingEpisode,Je as recordTransportPoisonVerdict,$e as recordTurnProgress,it as reopenEpisode,Ze as setFatalFaultAnnouncer,rt as settleRejectedEpisode};
|
|
26
|
+
`)}o(Ae,"createFaultTable");function g(e,t){return typeof t=="string"&&e.includes(t)}o(g,"contains");function W(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(W,"isRecord");function q(e){if(!W(e)||typeof e.provider!="string"||e.provider===""||typeof e.model!="string"||e.model==="")return;const t=typeof e.status=="number"&&Number.isFinite(e.status)?e.status:void 0,n=typeof e.code=="string"||typeof e.code=="number"?e.code:void 0,r=typeof e.errno=="string"?e.errno:void 0,i=typeof e.body=="string"?e.body:void 0,a=Ee(e.ratelimit),d=Array.isArray(e.causes)?e.causes.flatMap(s=>{if(!W(s))return[];const E={...typeof s.message=="string"?{message:s.message}:{},...typeof s.status=="number"&&Number.isFinite(s.status)?{status:s.status}:{},...typeof s.code=="string"||typeof s.code=="number"?{code:s.code}:{},...typeof s.errno=="string"?{errno:s.errno}:{},...typeof s.body=="string"?{body:s.body}:{}};return Object.keys(E).length===0?[]:[E]}):void 0;return{provider:e.provider,model:e.model,...t===void 0?{}:{status:t},...n===void 0?{}:{code:n},...r===void 0?{}:{errno:r},...i===void 0?{}:{body:i},...d===void 0||d.length===0?{}:{causes:d},...a===void 0?{}:{ratelimit:a}}}o(q,"parseProviderError");function V(e){try{const t=JSON.parse(e);if(!g(ye,t.link)||typeof t.op!="string"||t.op===""||!g(Te,t.kind)||typeof t.message!="string"||typeof t.since!="string")return null;const n=Y(t.operation_id);if(!W(t.retry)||!g(Ne,t.retry.disposition)||t.retry.by!==void 0&&!g(ge,t.retry.by)||t.retry.attempt!==void 0&&typeof t.retry.attempt!="number"||t.retry.max!==void 0&&typeof t.retry.max!="number"||t.retry.nextAt!==void 0&&typeof t.retry.nextAt!="string"||t.anchorEntryId!==void 0&&(typeof t.anchorEntryId!="string"||t.anchorEntryId===""))return null;const r=t.providerError===void 0?void 0:q(t.providerError);return t.providerError!==void 0&&r===void 0?null:{link:t.link,op:t.op,kind:t.kind,retry:{disposition:t.retry.disposition,...t.retry.by===void 0?{}:{by:t.retry.by},...t.retry.attempt===void 0?{}:{attempt:t.retry.attempt},...t.retry.max===void 0?{}:{max:t.retry.max},...t.retry.nextAt===void 0?{}:{nextAt:t.retry.nextAt}},message:t.message,since:t.since,operation_id:n,...t.anchorEntryId===void 0?{}:{anchorEntryId:t.anchorEntryId},...r===void 0?{}:{providerError:r}}}catch{return null}}o(V,"parseLegacyFault");function Se(e){const t=pe(e);try{return ce(t)}catch{return t}}o(Se,"canonicalPath");function Re(e){try{const t=JSON.parse(e);if(!g(Le,t.state)||typeof t.sessionFile!="string"||t.sessionFile==="")return null;const n=JSON.stringify(t.fault),r=V(n);if(r===null||r.link!=="pi\u2192provider"||r.retry.disposition!=="auto"||r.retry.by!=="daemon")return null;const i=t.anchorEntryId;return i!==null&&typeof i!="string"||(r.anchorEntryId??null)!==i?null:{state:t.state,sessionFile:Se(t.sessionFile),fault:r}}catch{return null}}o(Re,"parseLegacyEpisode");function Q(e){return e!==void 0?Y(e):K.current()??K.fresh(n=>n)}o(Q,"resolveOperationId");function xe(e){return e.slice(0,fe)}o(xe,"clampMessage");function w(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="auto"&&e.retry.by==="daemon"?3:e.retry.disposition==="fatal"?2:1}o(w,"faultPriority");function z(e){return{link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operationId:e.operation_id,anchorEntryId:e.anchorEntryId??null,retryDisposition:e.retry.disposition,retryBy:e.retry.by??null,retryAttempt:e.retry.attempt??null,retryMax:e.retry.max??null,retryNextAt:e.retry.nextAt??null,providerError:e.providerError===void 0?null:JSON.stringify(e.providerError)}}o(z,"encodeFault");function b(e){const t=e.provider_error_json,n=typeof t=="string"?q(JSON.parse(t)):void 0;if(typeof t=="string"&&n===void 0)throw new Error(`invalid provider error on stored fault ${e.fault_id}`);const r={disposition:e.retry_disposition,...e.retry_by===null?{}:{by:e.retry_by},...e.retry_attempt===null?{}:{attempt:e.retry_attempt},...e.retry_max===null?{}:{max:e.retry_max},...e.retry_next_at===null?{}:{nextAt:e.retry_next_at}};return{faultId:e.fault_id,link:e.link,op:e.op,kind:e.kind,message:e.message,since:e.since,operation_id:e.operation_id,retry:r,...e.anchor_entry_id===null?{}:{anchorEntryId:e.anchor_entry_id},...n===void 0?{}:{providerError:n},...typeof e.notified_since=="string"?{notifiedSince:e.notified_since}:{}}}o(b,"hydrateFault");async function L(e,t){const n=await e.prepare("SELECT * FROM node_faults WHERE node_id = ? AND active = 1").get(t);return n===void 0?null:b(n)}o(L,"activeFault");function P(e){if(!g(G,e.link))throw new Error(`fault link is not durable: ${e.link}`);return{link:e.link,op:e.op,kind:e.kind,retry:e.retry,message:xe(e.message),since:e.since??u(),operation_id:Q(e.operation_id),...typeof e.anchorEntryId=="string"&&e.anchorEntryId!==""?{anchorEntryId:e.anchorEntryId}:{},...e.providerError===void 0?{}:{providerError:e.providerError}}}o(P,"storedFault");function M(e,t){J({level:t.retry.disposition==="fatal"?"error":"warn",event:"fault.recorded",operation_id:t.operation_id,node_id:e,error_class:B(t),fields:{link:t.link,op:t.op,kind:t.kind,retry:t.retry,message:t.message,...t.anchorEntryId===void 0?{}:{anchor_entry_id:t.anchorEntryId},...t.providerError===void 0?{}:{provider_error:t.providerError}}})}o(M,"emitRecordedFault");const Z="INSERT INTO node_faults (fault_id, node_id, link, op, kind, message, since, operation_id, anchor_entry_id, retry_disposition, retry_by, retry_attempt, retry_max, retry_next_at, provider_error_json, active, episode_state, episode_binding_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";function ee(e,t,n,r,i){const a=z(n),d=u();return[e,t,a.link,a.op,a.kind,a.message,a.since,a.operationId,a.anchorEntryId,a.retryDisposition,a.retryBy,a.retryAttempt,a.retryMax,a.retryNextAt,a.providerError,r?1:0,i?.state??null,i?.bindingId??null,d,d]}o(ee,"faultInsertParams");async function te(e,t,n,r,i){const a=I();return await e.prepare(Z).run(...ee(a,t,n,r,i)),{...n,faultId:a}}o(te,"insertFault");async function D(e,t,n,r){const i=z(n);return await e.prepare("UPDATE node_faults SET link = ?, op = ?, kind = ?, message = ?, since = ?, operation_id = ?, anchor_entry_id = ?, retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, provider_error_json = ?, episode_state = COALESCE(?, episode_state), episode_binding_id = COALESCE(?, episode_binding_id), updated_at = ? WHERE fault_id = ?").run(i.link,i.op,i.kind,i.message,i.since,i.operationId,i.anchorEntryId,i.retryDisposition,i.retryBy,i.retryAttempt,i.retryMax,i.retryNextAt,i.providerError,r?.state??null,r?.bindingId??null,u(),t),{...n,faultId:t}}o(D,"updateFault");async function U(e,t){await e.prepare("DELETE FROM node_faults WHERE fault_id = ? AND active = 0 AND episode_state IS NULL").run(t)}o(U,"deleteIfUnretained");async function ne(e,t,n,r){const i=await L(e,t);if(i===null){const a=await e.prepare("SELECT fault_id, link, kind, since, operation_id FROM node_faults WHERE node_id = ? AND active = 0 AND episode_state IS NOT NULL").get(t);return a!==void 0&&a.link===n.link&&a.kind===n.kind&&a.since===n.since&&a.operation_id===n.operation_id?(await e.prepare("UPDATE node_faults SET active = 1, updated_at = ? WHERE fault_id = ?").run(u(),a.fault_id),await D(e,a.fault_id,n)):await te(e,t,n,!0,r)}if(i.link===n.link&&i.kind===n.kind){const a=n.retry.disposition==="auto"?{...i.retry,...n.retry}:n.retry,d=i.retry.disposition===n.retry.disposition&&(n.retry.disposition!=="fatal"||i.message===n.message),s=d?i.since:n.since,E=await D(e,i.faultId,{...n,since:s,anchorEntryId:n.anchorEntryId??i.anchorEntryId,retry:a},r);return d||await e.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(i.faultId),E}return i.link!==n.link&&w(i)>w(n)?null:(await e.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await U(e,i.faultId),await te(e,t,n,!0,r))}o(ne,"replaceActiveFault");async function m(e,t,n){return(await e.prepare("SELECT binding_id FROM sessions WHERE node_id = ? AND session_id = ? AND unbound_at IS NULL").get(t,n))?.binding_id??null}o(m,"liveBinding");async function $e(e,t){return await l(async n=>{const r=u();return(await n.prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,e)).changes>0?!0:(await n.prepare("UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ? AND state != 'exited'").run(r,r,e)).changes>0})}o($e,"openTurn");async function je(e){return(await(await p()).prepare("UPDATE executions SET turn_progress_at = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(u(),e)).changes>0}o(je,"recordTurnProgress");async function Be(e,t){return await l(async n=>{const r=u(),i=(await n.prepare("UPDATE executions SET turn_closed_at = ?, turn_end_reason = ? WHERE execution_id = ? AND state != 'exited' AND turn_open_at IS NOT NULL AND turn_closed_at IS NULL").run(r,t.endReason,e)).changes>0;if(i&&t.endReason==="settled"){const a="(SELECT node_id FROM executions WHERE execution_id = ?)";t.viewerPresent===!0?await n.prepare(`UPDATE nodes SET reply_at = ?, read_at = ? WHERE node_id = ${a}`).run(r,r,e):await n.prepare(`UPDATE nodes SET reply_at = ? WHERE node_id = ${a}`).run(r,e)}return i})}o(Be,"closeTurn");async function Je(e){return(await(await p()).prepare("UPDATE nodes SET read_at = ? WHERE node_id = ?").run(u(),e)).changes>0}o(Je,"markNodeRead");function re(e){return JSON.stringify({schema:"crtr.execution-exit-diagnostic/v1",...e})}o(re,"exitDiagnosticJson");async function Ke(e,t){if(!Number.isInteger(t.consecutivePasses)||t.consecutivePasses<1||t.routes.length===0||!t.routes.every(n=>n!==""))throw new Error("invalid transport-poison verdict");return(await(await p()).prepare("UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ? AND state != 'exited'").run(re({transport_poison:{consecutive_passes:t.consecutivePasses,routes:t.routes}}),e)).changes>0}o(Ke,"recordTransportPoisonVerdict");async function Ye(e){const t=await(await p()).prepare("SELECT exit_diagnostic_json FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);if(t===void 0||t.exit_diagnostic_json===null)return null;try{const n=JSON.parse(t.exit_diagnostic_json),r=n.transport_poison;return n.schema!=="crtr.execution-exit-diagnostic/v1"||r===void 0||!Number.isInteger(r.consecutive_passes)||r.consecutive_passes<1||!Array.isArray(r.routes)||r.routes.length===0||!r.routes.every(i=>typeof i=="string"&&i!=="")?null:{consecutivePasses:r.consecutive_passes,routes:r.routes}}catch{return null}}o(Ye,"readTransportPoisonVerdict");async function Ge(e){const t=await(await p()).prepare("SELECT turn_open_at, turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND state != 'exited'").get(e);return t===void 0?null:{busy:t.turn_open_at!==null&&t.turn_closed_at===null,progressAt:t.turn_progress_at}}o(Ge,"readLiveTurn");async function qe(e){const t=await p(),n=await t.prepare("SELECT turn_closed_at, turn_progress_at FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL ORDER BY seq DESC LIMIT 1").get(e);if(n===void 0)return null;const r=await t.prepare("SELECT turn_end_reason FROM executions WHERE node_id = ? ORDER BY seq DESC LIMIT 1").get(e);return{interrupted:n.turn_closed_at===null||r?.turn_end_reason==="teardown-aborted",progressAt:n.turn_progress_at}}o(qe,"readInterruptedTurn");async function Ve(e){return await(await p()).prepare("SELECT 1 FROM executions WHERE node_id = ? AND turn_open_at IS NOT NULL LIMIT 1").get(e)!==void 0}o(Ve,"hasAdmittedTurn");async function Qe(e,t){const n=P(t);return M(e,n),await l(async r=>await ne(r,e,n))}o(Qe,"recordFault");async function ze(e){return await l(async t=>(await t.prepare("UPDATE node_faults SET notified_since = since, updated_at = ? WHERE fault_id = ?").run(u(),e)).changes>0)}o(ze,"markFaultNotified");function Ie(e){return e.link==="pi\u2192provider"&&e.retry.disposition==="fatal"&&e.notifiedSince!==e.since}o(Ie,"isUnnotifiedFatalProviderFault");async function Ze(){return(await(await p()).prepare("SELECT f.node_id FROM node_faults f JOIN nodes n ON n.node_id = f.node_id WHERE f.active = 1 AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'fatal' AND (f.notified_since IS NULL OR f.notified_since != f.since) AND n.status IN ('active', 'idle')").all()).map(e=>e.node_id)}o(Ze,"listUnnotifiedFatalFaultNodes");let X=null;function et(e){X=e}o(et,"setFatalFaultAnnouncer");async function tt(e,t){const n=await L(await p(),e);return n===null||t?.link!==void 0&&n.link!==t.link?!1:await l(async r=>{const i=await L(r,e);if(i===null||t?.link!==void 0&&i.link!==t.link)return!1;X!==null&&Ie(i)&&await X(e);const a=await r.prepare("SELECT episode_state FROM node_faults WHERE fault_id = ?").get(i.faultId);await r.prepare("UPDATE node_faults SET active = 0, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),a.episode_state==="pending"&&t?.preserveEpisode!==!0&&await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),i.faultId),await U(r,i.faultId);const d=Q();return J({level:"info",event:"fault.cleared",operation_id:d,node_id:e,error_class:B(i),fields:{cleared_fault_operation_id:i.operation_id,link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId}}}),!0})}o(tt,"clearFault");async function ie(e){return await L(await p(),e)}o(ie,"readFault");async function nt(e,t){const n=P(t.input);if(n.link!=="pi\u2192provider"||n.retry.disposition!=="auto"||n.retry.by!=="daemon")throw new Error("provider retry episode requires an automatic daemon provider fault");return M(e,n),await l(async r=>{const i=await m(r,e,t.sessionId);if(i===null)return null;const a=await L(r,e);if(a!==null&&(a.link!==n.link||a.kind!==n.kind)&&w(a)>w(n))return null;const d=await r.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_state IS NOT NULL").all(e);await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_state IS NOT NULL").run(u(),e);for(const E of d)await U(r,E.fault_id);const s=await ne(r,e,n,{state:"pending",bindingId:i});return s===null?null:(await r.prepare("UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run("pending",i,u(),s.faultId),s)})}o(nt,"recordPendingEpisode");async function rt(e,t){return await l(async n=>{if(await n.prepare("SELECT 1 FROM nodes WHERE node_id = ? AND intent = 'refresh'").get(e)!==void 0)return!1;const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'admitted', retry_disposition = ?, retry_by = ?, retry_attempt = ?, retry_max = ?, retry_next_at = ?, updated_at = ? WHERE node_id = ? AND fault_id = ? AND episode_state = 'pending' AND episode_binding_id = ?").run(t.retry.disposition,t.retry.by??null,t.retry.attempt??null,t.retry.max??null,t.retry.nextAt??null,u(),e,t.faultId,r)).changes>0})}o(rt,"admitEpisode");async function it(e,t){const n=P(t.fault);return await l(async r=>{const i=await m(r,e,t.sessionId);return i===null||(await L(r,e))?.faultId!==t.faultId||await r.prepare("SELECT 1 FROM node_faults WHERE node_id = ? AND fault_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").get(e,t.faultId,i)===void 0?!1:(M(e,n),t.reopen?await D(r,t.faultId,n,{state:"pending",bindingId:i}):(await D(r,t.faultId,n),await r.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE fault_id = ?").run(u(),t.faultId),await r.prepare("UPDATE node_faults SET notified_since = NULL WHERE fault_id = ?").run(t.faultId)),!0)})}o(it,"settleRejectedEpisode");async function ot(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'pending', updated_at = ? WHERE node_id = ? AND episode_state = 'admitted' AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(ot,"reopenEpisode");async function at(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);return r===null?!1:(await n.prepare("UPDATE node_faults SET episode_state = 'invalidated', updated_at = ? WHERE node_id = ? AND episode_state != 'invalidated' AND episode_state IS NOT NULL AND episode_binding_id = ?").run(u(),e,r)).changes>0})}o(at,"invalidateEpisode");async function st(e,t){return await l(async n=>{const r=await m(n,e,t.sessionId);if(r===null)return!1;const i=await n.prepare("SELECT fault_id FROM node_faults WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").all(e,r);if(i.length===0)return!1;await n.prepare("UPDATE node_faults SET episode_state = NULL, episode_binding_id = NULL, updated_at = ? WHERE node_id = ? AND episode_binding_id = ? AND episode_state IS NOT NULL").run(u(),e,r);for(const a of i)await U(n,a.fault_id);return!0})}o(st,"clearEpisode");async function dt(e){const t=await(await p()).prepare("SELECT f.*, s.session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.node_id = ? AND f.episode_state IS NOT NULL").get(e);return t===void 0?null:{state:t.episode_state,sessionId:t.session_id,fault:b(t)}}o(dt,"readEpisode");async function ut(){return(await(await p()).prepare("SELECT f.*, s.session_id AS bound_session_id FROM node_faults f JOIN sessions s ON s.binding_id = f.episode_binding_id WHERE f.active = 1 AND f.episode_state = 'pending' AND s.unbound_at IS NULL AND s.session_id IS NOT NULL AND f.link = 'pi\u2192provider' AND f.retry_disposition = 'auto' AND f.retry_by = 'daemon' AND f.kind IN ('connection', 'rate-limit', 'overloaded')").all()).map(t=>({nodeId:t.node_id,sessionId:t.bound_session_id,fault:b(t),updatedAt:t.updated_at,nudgedAt:t.nudged_at??null}))}o(ut,"listPendingProviderRetries");async function ct(e,t,n){return(await(await p()).prepare("UPDATE node_faults SET nudged_at = ? WHERE fault_id = ? AND active = 1 AND episode_state = 'pending' AND (nudged_at IS NULL OR nudged_at <= ?)").run(new Date(t).toISOString(),e,new Date(t-n).toISOString())).changes===1}o(ct,"claimFaultNudge");async function _t(e){const t=await ie(e)===null;return{async explanation(){if(!t)return null;const n=await ie(e);if(n===null||n.link!=="crtr\u2192pi"||n.retry.disposition!=="fatal")return null;const r=n.op.trim(),i=n.message.trim();return r===""||i===""?null:`${r}: ${i}`}}}o(_t,"beginBootFaultAttempt");function oe(e,t){try{return _e(e).mtime.toISOString()}catch{return t}}o(oe,"markerMtime");function $(e,t){if(!f(e))return null;try{return ue(e,"utf8")}catch(n){if(!t)return null;throw new Error(`cannot import recovery input ${e}: ${n.message}`)}}o($,"legacyFile");function we(e,t){return _(e,"SELECT execution_id, started_at FROM executions WHERE node_id = ? ORDER BY rowid DESC LIMIT 1").get(t)??null}o(we,"latestExecution");function De(e,t){const r=H(e,"nodes").has("launched_at")?_(e,"SELECT COALESCE(launched_at, created) AS started_at FROM nodes WHERE node_id = ?").get(t):_(e,"SELECT created AS started_at FROM nodes WHERE node_id = ?").get(t);if(r===void 0)throw new Error(`cannot import recovery witness for unknown node ${t}`);const i=I();return _(e,"INSERT INTO executions (execution_id, node_id, daemon_epoch, pid, process_identity, state, started_at, ended_at, exit_reason) VALUES (?, ?, NULL, NULL, NULL, 'exited', ?, ?, 'pre-consolidation')").run(i,t,r.started_at,r.started_at),{execution_id:i,started_at:r.started_at}}o(De,"syntheticExecution");function ae(e,t,n,r,i){const a=I();return _(e,Z).run(...ee(a,t,n,r,i)),{...n,faultId:a}}o(ae,"insertMigratedFault");function Ue(e,t,n,r){const i=_(e,"SELECT binding_id, node_id FROM sessions WHERE transcript_path = ? AND unbound_at IS NULL").get(n);if(i!==void 0){if(i.node_id!==t)throw new Error(`cannot import provider retry ${r}: transcript is live-owned by ${i.node_id}`);return i.binding_id}const a=_(e,"SELECT binding_id FROM sessions WHERE node_id = ? AND transcript_path = ? ORDER BY rowid DESC LIMIT 1").get(t,n);if(a!==void 0)return a.binding_id;const d=_(e,"SELECT created FROM nodes WHERE node_id = ?").get(t);if(d===void 0)throw new Error(`cannot import provider retry ${r}: unknown node ${t}`);const s=I();return _(e,"INSERT INTO sessions (binding_id, session_id, node_id, transcript_path, source_session_id, fork_entry_id, created_at, bound_at, unbound_at) VALUES (?, NULL, ?, ?, NULL, NULL, ?, ?, ?)").run(s,t,n,d.created,d.created,u()),s}o(Ue,"migrationBinding");function pt(e){e.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'node_faults'").get()!==void 0&&(H(e,"node_faults").has("notified_since")||e.exec("ALTER TABLE node_faults ADD COLUMN notified_since TEXT"))}o(pt,"addFaultNotifiedSince");function lt(e){if(me(e),Ae(e),!f(C()))return;const t=de(C(),{withFileTypes:!0}).filter(r=>r.isDirectory()).sort((r,i)=>r.name.localeCompare(i.name)),n=_(e,"SELECT 1 FROM nodes WHERE node_id = ?");for(const r of t){const i=r.name,a=le(i),d=N(a,"busy"),s=N(a,"clean-abort"),E=N(a,"transport-poison.json"),O=N(a,"fault"),A=N(a,"provider-retry"),h=f(d),k=f(s);if(!h&&!k&&!f(E)&&!f(O)&&!f(A)||!f(N(C(),i,"meta.json"))&&n.get(i)===void 0)continue;const v=$(E,!1),S=$(O,!0),R=$(A,!0);if(!h&&!k&&v===null&&S===null&&R===null)continue;let y=we(e,i);if(y===null&&(y=De(e,i)),h){const c=oe(d,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = NULL, turn_end_reason = NULL WHERE execution_id = ?").run(c,c,y.execution_id)}else if(k){const c=oe(s,y.started_at);_(e,"UPDATE executions SET turn_open_at = ?, turn_progress_at = ?, turn_closed_at = ?, turn_end_reason = 'teardown-aborted' WHERE execution_id = ?").run(c,c,c,y.execution_id)}if(v!==null)try{const c=JSON.parse(v);Number.isInteger(c.consecutivePasses)&&c.consecutivePasses>=1&&Array.isArray(c.routes)&&c.routes.length>0&&c.routes.every(j=>typeof j=="string"&&j!=="")&&_(e,"UPDATE executions SET exit_diagnostic_json = ? WHERE execution_id = ?").run(re({transport_poison:{consecutive_passes:c.consecutivePasses,routes:c.routes}}),y.execution_id)}catch{}const x=S===null?null:V(S);if(S!==null&&x===null)throw new Error(`cannot import recovery fault ${O}`);const T=R===null?null:Re(R);if(R!==null&&T===null)throw new Error(`cannot import provider retry ${A}`);let F=x===null||x.link==="broker\u2194crtrd"?null:ae(e,i,x,!0);if(T!==null){const c=Ue(e,i,T.sessionFile,A);F!==null&&F.since===T.fault.since?_(e,"UPDATE node_faults SET episode_state = ?, episode_binding_id = ?, updated_at = ? WHERE fault_id = ?").run(T.state,c,u(),F.faultId):ae(e,i,T.fault,!1,{state:T.state,bindingId:c})}}}o(lt,"migrateRecoveryWitnesses");export{Xe as FAULT_QUIET_MS,pt as addFaultNotifiedSince,rt as admitEpisode,_t as beginBootFaultAttempt,ct as claimFaultNudge,st as clearEpisode,tt as clearFault,Be as closeTurn,re as exitDiagnosticJson,Ve as hasAdmittedTurn,b as hydrateFault,at as invalidateEpisode,ut as listPendingProviderRetries,Ze as listUnnotifiedFatalFaultNodes,ze as markFaultNotified,Je as markNodeRead,lt as migrateRecoveryWitnesses,$e as openTurn,q as parseProviderError,dt as readEpisode,ie as readFault,qe as readInterruptedTurn,Ge as readLiveTurn,Ye as readTransportPoisonVerdict,Qe as recordFault,nt as recordPendingEpisode,Ke as recordTransportPoisonVerdict,je as recordTurnProgress,ot as reopenEpisode,et as setFatalFaultAnnouncer,it as settleRejectedEpisode};
|
package/dist/core/command.d.ts
CHANGED
|
@@ -8,9 +8,8 @@ export interface LeafRunContext {
|
|
|
8
8
|
* caller. For optional parameters, presence in this set means the value was
|
|
9
9
|
* supplied, even if it is falsy (empty string, 0, false, etc.). Parameters
|
|
10
10
|
* absent from this set were either defaulted or not provided. For stdin
|
|
11
|
-
* specifically, presence means
|
|
12
|
-
*
|
|
13
|
-
* pipe (truly absent). */
|
|
11
|
+
* specifically, presence means piped/redirected bytes or a positional argument
|
|
12
|
+
* supplied it; absence means a TTY, an empty stream, or nothing given. */
|
|
14
13
|
readonly providedParams: ReadonlySet<string>;
|
|
15
14
|
/** Absolute source paths for JSON values supplied through `--context-file`,
|
|
16
15
|
* keyed by their camel-cased parameter name. The parsed JSON remains the
|
package/dist/core/command.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
1
|
+
var z=Object.defineProperty;var f=(e,i)=>z(e,"name",{value:i,configurable:!0});import{renderRoot as F,renderBranch as V,renderLeafArgv as O,withAsyncHelpState as G,collapseWideChildren as L,rootPromotedChildren as J}from"./help.js";import{beginPreview as K,publishPreviewResult as D,readStdinRaw as B,peekStdinRaw as Y,emit as Q,handle as X,setJsonOutput as Z,isJsonOutput as ee}from"./io.js";import{renderResult as ne}from"./render.js";import{CrtrError as H}from"./errors.js";import{validateValue as te}from"../api/command-manifest/result.js";import{isManifestStaleError as ie}from"./manifest-stale.js";import{operationIdContext as re}from"./events/operation-id.js";import{ExitCode as C}from"../types.js";import{envNodeId as ae}from"../shared/env.js";import{readFileSync as oe}from"node:fs";import{spawnSync as se}from"node:child_process";import{constants as le}from"node:os";import{resolve as de}from"node:path";function W(e){if(e===null||typeof e!="object"||Object.isFrozen(e))return e;for(const i of Object.values(e))W(i);return Object.freeze(e)}f(W,"freezeCommandMetadata");function Fe(e){const i=W(e.help),{helpState:r}=e;if(r!==void 0&&e.effectiveHelp!==void 0)throw new Error(`leaf ${e.name} declares both helpState and effectiveHelp`);return Object.freeze({kind:"leaf",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:i,effectiveHelp:e.effectiveHelp??(r===void 0?void 0:n=>G(i,r,n)),slash:e.slash===void 0?void 0:W(e.slash),hookEligible:e.hookEligible,plugin:e.plugin,machineSelector:e.machineSelector,run:e.run,render:e.render,preview:e.preview,stdoutExact:e.stdoutExact})}f(Fe,"defineLeaf");function A(e){return e.kind==="branch"&&e.children.length>0&&I(e)===0?"hidden":e.tier??"normal"}f(A,"effectiveTier");function I(e){return e.kind!=="branch"?0:(e.help.listing??[]).filter(i=>i.tier!=="hidden").length}f(I,"visibleSubCount");function Ve(e){if(e.passthrough!==void 0&&e.children.length>0)throw new Error(`defineBranch("${e.name}"): a passthrough branch must declare no children (found ${e.children.length})`);return e.help.listing=e.children.map(i=>{const r=I(i);return{name:i.name,description:i.description??"",whenToUse:i.whenToUse??"",tier:A(i),kind:i.kind,...i.plugin!==void 0?{plugin:i.plugin}:{},...r>0?{subCount:r}:{}}}),{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:e.help,effectiveHelp:e.effectiveHelp,rootEntry:e.rootEntry,extensible:e.extensible,plugin:e.plugin,slash:e.slash,passthrough:e.passthrough,children:e.children}}f(Ve,"defineBranch");function Je(e){const i=[],r=f(n=>{if(n.slash!==void 0&&i.push(n.slash),n.kind==="branch")for(const a of n.children)r(a)},"visit");for(const n of e.subtrees)r(n);return i}f(Je,"collectSlashSpecs");function Be(e){const i=e.subtrees.filter(n=>n.rootEntry!==void 0&&A(n)!=="hidden").map(n=>{const a=(n.help.listing??[]).filter(s=>s.tier!=="hidden"),d=J(a).map(s=>({path:`${n.name} ${s.name}`,desc:s.tier==="important"?s.description:void 0}));return{name:n.name,concept:n.rootEntry.concept,desc:n.rootEntry.desc,useWhen:n.rootEntry.useWhen,dynamicState:n.rootEntry.dynamicState,subcommands:d.length>0?d:void 0,otherSubcommandCount:a.length-d.length}});return{kind:"root",help:{tagline:e.tagline,commands:i,globals:e.globals},subtrees:e.subtrees}}f(Be,"defineRoot");function fe(e){if(e.kind==="root")return e.subtrees.filter(i=>A(i)!=="hidden").map(i=>i.name);if(e.kind==="branch"){const{listed:i,unlisted:r}=L(e.children.filter(n=>A(n)!=="hidden"));return[...i.map(n=>n.name),...r>0?[`(and ${r} more)`]:[]]}return[]}f(fe,"childNames");function ce(e,i){let r=e;const n=[];let a=0;for(;a<i.length;){const d=i[a];if(d==="-h"||d==="--help")break;if(r.kind==="root"){const s=r.subtrees.find(o=>o.name===d);if(s===void 0)break;r=s,n.push(s.name),a++}else if(r.kind==="branch"){const s=r.children.find(o=>o.name===d);if(s===void 0)break;r=s,n.push(s.name),a++}else break}return{node:r,path:n,remaining:i.slice(a)}}f(ce,"walk");function ue(e){return e.kind==="root"?F(e.help):e.kind==="branch"?V(e.help):O(e.help)}f(ue,"renderNode");async function M(e,i,r){if(r!==void 0&&e.kind==="leaf")return O(e.effectiveHelp!==void 0?await e.effectiveHelp(r):e.help,r);const n=e.kind==="leaf"&&e.effectiveHelp!==void 0?O(await e.effectiveHelp()):e.kind==="branch"&&e.effectiveHelp!==void 0?V(await e.effectiveHelp()):ue(e);if(i.length===0)return n;const{hasEffectiveCommandPlugins:a}=await import("./command-plugins/presence.js");if(!a())return n;const[{collectHelpAddenda:d},{SUBTREE_NAMES:s,coreCommandPathsCovering:o,extensibleCoreBranches:k}]=await Promise.all([import("./command-plugins/help-addenda.js"),import("../build-root.js")]),v=await d(i.join(" "),{reservedCoreNames:new Set(s),coreCommandPaths:o,extensibleCoreBranches:k});return n+v.map(E=>`
|
|
2
2
|
|
|
3
3
|
<plugin-help plugin="${E.plugin}">
|
|
4
4
|
${E.text}
|
|
5
|
-
</plugin-help>`).join("")}f(
|
|
5
|
+
</plugin-help>`).join("")}f(M,"renderNodeWithAddenda");function he(e){return e.some(i=>i==="-h"||i==="--help")}f(he,"helpRequested");function me(e,i){if(e.kind!=="leaf"||i.length!==2)return;const[r,n]=i;if(n!=="-h"&&n!=="--help"||!r.startsWith("--")||r.includes("="))return;const a=r.slice(2);return(e.help.params??[]).find(s=>s.kind==="flag"&&s.name===a)?.focusedHelp!==void 0?a:void 0}f(me,"focusedHelpFlag");function pe(e,i){let r=0;for(let n=0;n<e.length;n++){if(r===i.length)return e.slice(n);e[n]!=="--json"&&e[n]===i[r]&&r++}return[]}f(pe,"rawArgsAfterPath");function ge(e,i){const r=se(e.bin,i,{stdio:"inherit"});if(r.error!==void 0){const n=r.error;throw new H("binary_missing",`${e.bin} could not be run (${n.code??n.message})`,C.USAGE,{received:e.bin,next:e.installHint})}if(r.status!==null&&process.exit(r.status),r.signal!==null){const n=le.signals[r.signal];n!==void 0&&process.exit(128+n)}process.exit(1)}f(ge,"runPassthrough");function we(e,i,r){const n=fe(e),a=n.length>0?n.join(", "):"(none)",d=i.length>0?`crtr ${i.join(" ")} -h`:"crtr -h",s=n.some(o=>o.startsWith("(and "));return new H("unknown_path",`unknown subcommand: ${r}`,C.USAGE,{received:r,next:s?`Valid children include: ${a}. Run \`${d}\` for how to find the rest.`:`Valid children: ${a}. Run \`${d}\` for the full list.`})}f(we,"unknownPathError");const ve="https://account.northlight.ai";function be(e){const i=ve,r=`open ${i}/account/machines, choose Create join token, then run \`crtr sys join --directory ${i} --token <token>\``;return new H("not_joined",`\`crtr ${e}\` isn't a command on this machine. If it comes from Northlight, this machine isn't joined to your account yet: ${r}.`,C.USAGE,{received:e,next:`${r[0].toUpperCase()}${r.slice(1)}.`})}f(be,"notJoinedError");async function xe(){try{const{readConfig:e}=await import("./config.js");return!e("user").runtime?.issuer}catch{return!1}}f(xe,"runtimeUnjoined");function p(e,i,r,n,a){return new H(e,i,C.USAGE,{received:r,field:n,next:a!==void 0?a:"Run the command with -h to see the parameter schema."})}f(p,"parseArgvError");function y(e){return e.replace(/-([a-z])/g,(i,r)=>r.toUpperCase())}f(y,"flagNameToKey");async function ke(e,i,r){const n={},a=new Set,d=new Map,s=e.filter(t=>t.kind==="positional"),o=e.find(t=>t.kind==="stdin"),k=e.find(t=>t.kind==="context-file"),v=e.filter(t=>t.kind==="flag"),E=new Map(v.map(t=>[t.name,t])),S=v.some(t=>t.sensitive===!0);for(const t of v)t.type==="bool"?n[y(t.name)]=t.default!==void 0?t.default:!1:t.default!==void 0&&(n[y(t.name)]=t.default);const w=[];let b=0,U=!1,u=0;for(;u<i.length;){const t=i[u];if(t==="--"&&!U){U=!0,u++;continue}if(!U&&t.startsWith("--")){const h=t.indexOf("=");let l,_;if(h!==-1?(l=t.slice(2,h),_=t.slice(h+1)):l=t.slice(2),l==="context-file"){if(k===void 0)throw p("unknown_flag","unknown flag: --context-file","--context-file",void 0,"This leaf does not accept --context-file.");const c=_!==void 0?_:i[++u];if(c===void 0)throw p("missing_parameter","--context-file requires a PATH argument","--context-file","context-file");let x;try{x=oe(c,"utf8")}catch{throw p("invalid_type",`--context-file: cannot read file: ${c}`,c,"context-file","Provide a readable file path.")}let q;try{q=JSON.parse(x)}catch{throw p("invalid_type",`--context-file: file is not valid JSON: ${c}`,c,"context-file","Ensure the file contains a valid JSON object.")}const T=y(k.name);n[T]=q,d.set(T,de(c)),a.add(T),u++;continue}const P=E.get(l);if(P===void 0)throw p("unknown_flag",`unknown flag: --${l}`,`--${l}`,void 0,"Use --<flag-name> for declared flags only. Run -h for the schema.");const R=y(l);if(a.add(R),P.type==="bool"){if(_!==void 0)throw p("invalid_type",`boolean flag --${l} takes no value`,`--${l}=${_}`,l,`Use --${l} (presence = true) with no value.`);n[R]=!0,u++;continue}const $=_!==void 0?_:i[++u];if($===void 0||_===void 0&&$.startsWith("--")){const c=P.focusedHelp!==void 0&&(r?.leafPath?.length??0)>0?`Run \`crtr ${r.leafPath.join(" ")} --${l} -h\` and read the focused value contract before retrying.`:void 0;throw p("missing_parameter",`--${l} requires a value`,`--${l}`,l,c)}let j;if(P.type==="int"){const c=Number($);if(!Number.isInteger(c))throw p("invalid_type",`--${l} must be an integer`,$,l,`Provide an integer value for --${l}.`);j=c}else if(P.type==="enum"){const c=P.choices;if(c!==void 0&&!c.includes($))throw p("invalid_type",`--${l} must be one of: ${c.join(", ")}`,$,l,`Retry with one of: ${c.join(", ")}.`);j=$}else if(P.type==="json"){let c;try{c=JSON.parse($)}catch{throw p("invalid_type",`--${l} is not valid JSON`,$,l,`Pass one JSON value for --${l}; run -h for its shape.`)}const x=P.shape===void 0?null:te(P.shape,c,`--${l}`);if(x!==null)throw p("invalid_type",`${x.path} must be ${x.expected}, got ${x.receivedType}`,$,x.path,`Fix ${x.path}; run -h for the shape of --${l}.`);j=c}else j=$;if(P.repeatable===!0){const c=n[R],x=Array.isArray(c)?c:[];x.push(j),n[R]=x}else n[R]=j;u++;continue}const m=s[b];if(m!==void 0){const h=y(m.name);if(m.repeatable===!0){const l=n[h]??[];l.push(t),n[h]=l}else n[h]=t,b++;w.push(t),a.add(h),u++;continue}if(w.length>0){if(o!==void 0&&s.length===0){w.push(t),u++;continue}throw o!==void 0?p("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${o.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument."):p("bad_invocation",S?"unexpected extra positional argument (value withheld: this leaf takes a secret)":`unexpected extra positional argument: ${t}`,S?void 0:i.join(" "),void 0,"Use declared flags or positional parameters. Run -h for the schema.")}if(s.length===0&&(o===void 0||o.allowPositional===!1))throw p("bad_invocation",S?"this leaf takes no positional arguments (value withheld: this leaf takes a secret)":`this leaf takes no positional arguments: ${t}`,S?void 0:t,void 0,"Use --flag for parameters. Run -h for the schema.");w.push(t),u++}let g=!1;if(o!==void 0){if(w.length>1&&s.length===0){const t=o.positionalStdinConflict?.(n);throw t!==void 0?p("bad_invocation",t.message,void 0,t.field,t.next):p("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${o.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument.")}if(w.length>0&&s.length===0&&o.allowPositional!==!1){const t=w[0];if(t==="-"){const m=await B();(m.trim()!==""||!o.required)&&(n[y(o.name)]=m),g=!0}else{if((await Y()).trim()!==""){const h=o.positionalStdinConflict?.(n);throw p("bad_invocation",h?.message??`both a positional argument ("${t}") and piped stdin were provided for ${o.name}`,t,h?.field??o.name,h?.next??"Pass exactly one: either the positional argument or piped stdin, not both.")}n[y(o.name)]=t,g=!0}}else{const t=await B();t!==""&&(t.trim()!==""||!o.required)&&(n[y(o.name)]=t,g=!0)}}g&&o!==void 0&&a.add(y(o.name));for(const t of e){if(t.kind!=="flag"&&t.kind!=="positional"||t.defaultFromEnv===void 0)continue;const m=y(t.name);if(a.has(m))continue;const h=process.env[t.defaultFromEnv];h===void 0||h===""||(n[m]=t.kind==="positional"&&t.repeatable===!0?[h]:h,a.add(m))}const N=[];for(const t of e){const m=y(t.name),h=t.kind==="flag"&&t.type==="bool"&&!a.has(m);if(t.required&&(h||n[m]===void 0||n[m]===null)){const l=t.kind==="positional"?`positional ${t.name.toUpperCase()}`:t.kind==="flag"?`--${t.name}`:t.kind==="context-file"?"--context-file":"stdin";N.push({display:l,name:t.name})}}if(N.length>0){const t=N.map(l=>l.display).join(", "),m=r?.leafPath,h=m!==void 0&&m.length>0?`\`crtr ${m.join(" ")} -h\``:"this command with -h";throw p("missing_parameter",N.length===1?`required parameter is missing: ${t}`:`required parameters are missing: ${t}`,void 0,N.map(l=>l.name).join(", "),`Run ${h} and read the full schema before re-issuing \u2014 do not guess parameters one at a time.`)}return r?.onProvidedParams!==void 0&&r.onProvidedParams(a),r?.onContextFilePaths!==void 0&&r.onContextFilePaths(d),n}f(ke,"parseArgv");function $e(e){const i=[];let r;for(let n=0;n<e.length;n++){const a=e[n];if(a==="--"){i.push(...e.slice(n));break}const d=a.startsWith("--machine=")?a.slice(10):void 0;if(a!=="--machine"&&d===void 0){i.push(a);continue}const s=d??e[n+1];if(s===void 0||s===""||d===void 0&&s.startsWith("-"))throw p("missing_parameter","--machine requires a machine id","--machine","machine","Pass the machine_id of one of the candidates a `machine_required` answer lists.");d===void 0&&n++,r=s}return r===void 0?{rest:i}:{rest:i,machine:r}}f($e,"takeMachineSelector");const ye=new Set(["--json","--no-autostart"]);async function Ie(e,i){const r=i.slice(2);r.includes("--json")&&Z(!0);const n=r.filter(o=>!ye.has(o));if(n.length===0||n.length===1&&(n[0]==="-h"||n[0]==="--help")){const o=ae()?{...e.help,commands:await Promise.all(e.help.commands.map(async k=>{const v=e.subtrees.find(b=>b.name===k.name);if(v?.effectiveHelp===void 0)return k;const S=((await v.effectiveHelp()).listing??[]).filter(b=>b.tier!=="hidden"),w=J(S).map(b=>({path:`${v.name} ${b.name}`,desc:b.tier==="important"?b.description:void 0}));return{...k,subcommands:w.length?w:void 0,otherSubcommandCount:S.length-w.length}}))}:e.help;process.stdout.write(F(o)+`
|
|
6
6
|
`),process.exitCode=C.SUCCESS;return}if(n.length===1&&(n[0]==="--version"||n[0]==="-v")){const{readPackageVersion:o}=await import("../commands/sys/shared.js");process.stdout.write(o()+`
|
|
7
|
-
`),process.exitCode=C.SUCCESS;return}let{node:a,path:d,remaining:s}=
|
|
8
|
-
`),process.exitCode=C.SUCCESS;return}if(a.kind==="root"||a.kind==="branch"){process.stdout.write(await
|
|
9
|
-
`),process.exitCode=C.SUCCESS;return}const o=a;
|
|
10
|
-
`))}}catch(o){if(
|
|
7
|
+
`),process.exitCode=C.SUCCESS;return}let{node:a,path:d,remaining:s}=ce(e,n);try{if(a.kind==="branch"&&a.passthrough!==void 0&&ge(a.passthrough,pe(r,d)),a.kind==="branch"&&a.unavailable!==void 0&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw new H("provider_unavailable",a.unavailable,C.NETWORK,{next:"The daemon retries bundle installation on grant sync; retry after the provider becomes available."});if((a.kind==="root"||a.kind==="branch")&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw a.kind==="root"&&!s[0].startsWith("-")&&await xe()?be(s[0]):we(a,d,s[0]);if(he(s)){process.stdout.write(await M(a,d,me(a,s))+`
|
|
8
|
+
`),process.exitCode=C.SUCCESS;return}if(a.kind==="root"||a.kind==="branch"){process.stdout.write(await M(a,d)+`
|
|
9
|
+
`),process.exitCode=C.SUCCESS;return}const o=a;K(d.join(" "));const k=o.help.params!==void 0?o.help.params:[];let v=new Set,E=new Map,S=s,w;if(o.machineSelector===!0&&!k.some(g=>g.kind==="flag"&&g.name==="machine")){const g=$e(s);S=g.rest,w=g.machine}const b=await ke(k,S,{leafPath:d,onProvidedParams:f(g=>{v=g},"onProvidedParams"),onContextFilePaths:f(g=>{E=g},"onContextFilePaths")}),U={providedParams:v,contextFilePaths:E,...w===void 0?{}:{machine:w}},u=await re.fresh(()=>o.run(b,U));if(D(u==null?u:o.preview?.(u)??u),u!=null)if(ee())Q(u);else{const g=o.render!==void 0?o.render(u):ne(u,o.help);process.stdout.write(g+(o.stdoutExact?.(u)?"":`
|
|
10
|
+
`))}}catch(o){if(ie(o))throw o;X(o)}}f(Ie,"runCli");export{ve as DEFAULT_DIRECTORY_ISSUER,ye as GLOBAL_TOKENS,Je as collectSlashSpecs,Ve as defineBranch,Fe as defineLeaf,Be as defineRoot,be as notJoinedError,ke as parseArgv,ue as renderNode,Ie as runCli,$e as takeMachineSelector,we as unknownPathError,ce as walk};
|
|
@@ -121,7 +121,9 @@ export declare function runNoticeTarget(grantId: string): Promise<{
|
|
|
121
121
|
sub: string;
|
|
122
122
|
grantee: string;
|
|
123
123
|
} | null>;
|
|
124
|
-
|
|
124
|
+
/** `person`'s (`user:<id>`) active `app:terminal` grant on this runtime, or null. A runtime serves many people on the fleet, so the person is always
|
|
125
|
+
* named: it is the request's person, or the `sub` of the node the call is about. Throws only when that one person holds two. */
|
|
126
|
+
export declare function terminalGrant(person: string): Promise<ActiveGrant | null>;
|
|
125
127
|
/** Where the grant mirror came from: `'local'` for an unjoined runtime's own grant, the directory issuer once joined, null before any install. */
|
|
126
128
|
export declare function grantsSource(): Promise<string | null>;
|
|
127
129
|
export declare function hasSyncedGrants(source: string): Promise<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var w=Object.defineProperty;var t=(e,r)=>w(e,"name",{value:r,configurable:!0});import{createHash as
|
|
1
|
+
var w=Object.defineProperty;var t=(e,r)=>w(e,"name",{value:r,configurable:!0});import{createHash as N}from"node:crypto";import{parseScope as O}from"@north-light/identity";import{openDb as d,withCanvasWrite as h}from"../canvas/db.js";import{isUnsandboxedMachine as j}from"../runtime/unsandboxed.js";import{replaceWrittenBy as A,userSubject as b}from"../permissions/facts.js";import{GROUP_TYPE as R}from"../permissions/builtin.js";const D=["not_started","pending","ready","failed","unavailable"],k=["ultra","strong","medium","light"];function I(){const e=process.env.CRTR_RUNTIME;return e?{sql:" AND runtime = ?",args:[e]}:{sql:"",args:[]}}t(I,"runtimeScope");async function u(e,r,n=[]){const s=I();return await(await d()).prepare(`SELECT ${e} FROM grants WHERE ${r}${s.sql}`).all(...n,...s.args)}t(u,"grantRows");async function c(e,r,n=[]){return(await u(e,r,n))[0]}t(c,"grantRow");function l(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(r=>{if(!r||typeof r!="object"||typeof r.scope!="string"||!["always","ask"].includes(r.mode))throw new TypeError("invalid grant scope entry");return{scope:r.scope,mode:r.mode}})}t(l,"entries");function C(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:r,message:n,next:s,step:a}=e;return typeof r!="string"||!D.includes(r)||n!==void 0&&typeof n!="string"||a!==void 0&&typeof a!="string"||typeof s!="string"||!s?null:{state:r,...n===void 0?{}:{message:n},next:s,...a===void 0?{}:{step:a}}}t(C,"dependencyOf");function m(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[n,s]of Object.entries(e)){const a=C(s);a&&(r[n]=a)}return r}t(m,"dependenciesOf");function F(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{base_url:r,rungs:n}=e;if(typeof r!="string"||!r||!n||typeof n!="object"||Array.isArray(n))return null;const s={};for(const a of k){const o=n[a];typeof o=="string"&&o&&(s[a]=o)}return Object.keys(s).length?{base_url:r.replace(/\/+$/,""),rungs:s}:null}t(F,"inferenceEndpointOf");function _(e){if(e.computed_rev!==e.rev||e.scopes_json===null||e.resolved_scopes_json===null)throw new TypeError("grant columns are not computed for this revision");return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:l(JSON.parse(e.scopes_json)),resolved_scopes:l(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:m(JSON.parse(e.dependencies_json??"{}")),run_notice_url:e.run_notice_url??null,inference_endpoint:e.inference_endpoint_json?F(JSON.parse(e.inference_endpoint_json)):null,synced_at:e.synced_at??""}}t(_,"record");function p(e){if(!e||e.status!=="active")return null;try{return H(_(e))}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(p,"active");function g(e){return e.grantee!=="app:terminal"&&j()}t(g,"mirrorOnly");async function Y(){return(await $()).filter(e=>!g(e))}t(Y,"localGrants");const E="[A-Za-z0-9_-]{1,64}",M=new RegExp(`^(${E}):(${E})$`);function L(e){const r=new Set;for(const n of e)if(n.status==="active")for(const{scope:s}of n.resolved_scopes){const a=M.exec(s);a&&a[1]!=="crtr"&&r.add(a[1])}return[...r].sort()}t(L,"providerIdsFromGrants");async function Z(e){const r=await u("*","sub = ? AND status = 'active'",[e]);return new Set(L(r.flatMap(n=>{try{return[_(n)]}catch(s){if(s instanceof TypeError||s instanceof SyntaxError)return[];throw s}})))}t(Z,"grantedProviderIds");function K(e,r){return e.source_marketplace===null&&e.source_path===null&&r.has(e.name)}t(K,"isProviderBundleRow");async function $(){return(await u("*","true")).flatMap(e=>{try{return[_(e)]}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return[];throw r}})}t($,"allGrants");function G(e){return e.expires_at!==null&&!(Date.parse(e.expires_at)>Date.now())}t(G,"grantExpired");function H(e){if(e.status!=="active"||G(e))return null;try{const r=e.resolved_scopes.map(({scope:n})=>O(n));return{...e,resolved:e.resolved_scopes.flatMap(({mode:n},s)=>n==="always"?[r[s]]:[])}}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(H,"activeRecord");function v(e){return Array.isArray(e)?`[${e.map(v).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(r=>e[r]!==void 0).map(r=>`${JSON.stringify(r)}:${v(e[r])}`).join(",")}}`:JSON.stringify(e)??"null"}t(v,"canonicalJson");function Q(e){const r={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{},...e.inference_endpoint?{inference_endpoint:e.inference_endpoint}:{}};return N("sha256").update(v(r)).digest("base64url").slice(0,22)}t(Q,"grantVersion");async function ee(e){return!!await c("1","id = ?",[e])}t(ee,"hasGrant");async function re(e){return await c("rev, status","id = ?",[e])??null}t(re,"grantSyncState");async function ne(e,r){return await c("rev, status","grantee = ? AND sub = ?",[e,r])??null}t(ne,"grantSyncStateFor");async function U(e){const r=await c("*","id = ?",[e]);if(!r)return null;try{return _(r)}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return null;throw n}}t(U,"grantRecord");async function te(e){const r=await c("*","id = ?",[e]);if(!r)return{standing:"inactive",sub:null,grantee:null,status:null,removal_done_at:null,record:null};const n={sub:r.sub,grantee:r.grantee,status:r.status,removal_done_at:r.removal_done_at??null};if(r.status!=="active")return{...n,standing:"inactive",record:null};if(r.computed_rev!==r.rev)return{...n,standing:"lagging",record:null};const s=p(r);return s===null?{...n,standing:"inactive",record:null}:{...n,standing:"active",record:s}}t(te,"grantStanding");async function se(e){return(await u("id",e)).map(r=>r.id)}t(se,"grantIdsWhere");async function ae(e){return(e?(await U(e))?.payer:null)??"user"}t(ae,"payerOfGrant");async function W(e){return p(await c("*","id = ?",[e]))}t(W,"activeGrant");async function oe(e,r){return p(await c("*","grantee = ? AND sub = ?",[e,r]))}t(oe,"grantFor");async function ie(e){if(g({grantee:e}))return null;const r=await u("*","grantee = ? AND status = 'active'",[e]);if(r.length>1)throw new Error(`multiple active grants for ${e}`);return p(r[0])}t(ie,"activeGrantForApp");async function ce(e){const r=await W(e);return r&&r.run_notice_url&&!g(r)?{url:r.run_notice_url,sub:r.sub,grantee:r.grantee}:null}t(ce,"runNoticeTarget");async function ue(e){const r=await u("*","grantee = 'app:terminal' AND sub = ? AND status = 'active'",[e]);if(r.length>1)throw new Error(`multiple active terminal grants for ${e}`);return p(r[0])}t(ue,"terminalGrant");async function pe(){return(await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value??null}t(pe,"grantsSource");async function de(e){return(await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value===e}t(de,"hasSyncedGrants");async function le(e){return(await e.prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value!=="local"?0:(await e.prepare("DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number((await e.prepare("DELETE FROM grants").run()).changes))}t(le,"dropLocalGrantMirror");function _e(e){if(e===void 0)return[];if(!Array.isArray(e))throw new TypeError("invalid grant sync response");return e.map(r=>{const{team:n,members:s}=r??{};if(typeof n!="string"||!/^team:\S+$/.test(n)||!Array.isArray(s)||s.some(a=>typeof a!="string"||!/^user:[^\s*]+$/.test(a)))throw new TypeError("invalid grant sync response");return{team:n,members:s}})}t(_e,"teamsOf");async function fe(e,r,n,s=[]){const a=new Set;for(const o of e){if(a.has(o.id)||!o.id||!o.runtime||!o.sub||!o.grantee||!Number.isInteger(o.rev)||!["active","revoked","removed"].includes(o.status))throw new TypeError("invalid grant sync response");a.add(o.id),l(o.scopes),l(o.resolved_scopes)}await h(async o=>{let f=0;const y=[...a];f+=Number((await(y.length?o.prepareUncached(`DELETE FROM grants WHERE id NOT IN (${y.map(()=>"?").join(",")})`).run(...y):o.prepareUncached("DELETE FROM grants").run())).changes);const x=o.prepare(`INSERT INTO grants
|
|
2
2
|
(id, runtime, sub, grantee, rev, scopes_json, resolved_scopes_json, spend_cap, expires_at,
|
|
3
3
|
status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, inference_endpoint_json, synced_at, computed_rev)
|
|
4
4
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -13,4 +13,4 @@ var w=Object.defineProperty;var t=(e,r)=>w(e,"name",{value:r,configurable:!0});i
|
|
|
13
13
|
grants.run_notice_url, grants.inference_endpoint_json, grants.computed_rev)
|
|
14
14
|
IS DISTINCT FROM (excluded.runtime, excluded.sub, excluded.grantee, excluded.rev, excluded.scopes_json, excluded.resolved_scopes_json,
|
|
15
15
|
excluded.spend_cap, excluded.expires_at, excluded.status, excluded.revoked_at, excluded.approved_at, excluded.payer,
|
|
16
|
-
excluded.dependencies_json, excluded.run_notice_url, excluded.inference_endpoint_json, excluded.computed_rev)`);for(const i of e)f+=Number((await x.run(i.id,i.runtime,i.sub,i.grantee,i.rev,JSON.stringify(i.scopes),JSON.stringify(i.resolved_scopes),i.spend_cap,i.expires_at,i.status,i.revoked_at,i.approved_at,i.payer,JSON.stringify(m(i.dependencies)),i.run_notice_url??null,i.inference_endpoint?JSON.stringify(i.inference_endpoint):null,i.synced_at,i.rev)).changes);await A(o,"directory",R,"member",s.flatMap(({team:i,members:S})=>S.map(T=>({resourceId:i,subject:b(T)})))),await o.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(n),f>0&&await o.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(runtime_state.value AS INTEGER) + 1 AS TEXT)").run()})}t(fe,"replaceGrants");async function ye(){const e=await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}t(ye,"grantsGeneration");export{W as activeGrant,ie as activeGrantForApp,H as activeRecord
|
|
16
|
+
excluded.dependencies_json, excluded.run_notice_url, excluded.inference_endpoint_json, excluded.computed_rev)`);for(const i of e)f+=Number((await x.run(i.id,i.runtime,i.sub,i.grantee,i.rev,JSON.stringify(i.scopes),JSON.stringify(i.resolved_scopes),i.spend_cap,i.expires_at,i.status,i.revoked_at,i.approved_at,i.payer,JSON.stringify(m(i.dependencies)),i.run_notice_url??null,i.inference_endpoint?JSON.stringify(i.inference_endpoint):null,i.synced_at,i.rev)).changes);await A(o,"directory",R,"member",s.flatMap(({team:i,members:S})=>S.map(T=>({resourceId:i,subject:b(T)})))),await o.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(n),f>0&&await o.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(runtime_state.value AS INTEGER) + 1 AS TEXT)").run()})}t(fe,"replaceGrants");async function ye(){const e=await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}t(ye,"grantsGeneration");export{W as activeGrant,ie as activeGrantForApp,H as activeRecord,$ as allGrants,m as dependenciesOf,le as dropLocalGrantMirror,G as grantExpired,oe as grantFor,se as grantIdsWhere,U as grantRecord,te as grantStanding,re as grantSyncState,ne as grantSyncStateFor,Q as grantVersion,Z as grantedProviderIds,ye as grantsGeneration,pe as grantsSource,ee as hasGrant,de as hasSyncedGrants,F as inferenceEndpointOf,K as isProviderBundleRow,Y as localGrants,g as mirrorOnly,ae as payerOfGrant,L as providerIdsFromGrants,fe as replaceGrants,ce as runNoticeTarget,_e as teamsOf,ue as terminalGrant};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ActiveGrant } from './mirror.js';
|
|
2
|
+
/** The request's person, or on a Mac the install's person. Null when there is none: a hosted first boot before its first grant snapshot, or a cloud
|
|
3
|
+
* role outside a request (a lane there works for the person on the row it claimed; it never borrows "the only grant"). */
|
|
4
|
+
export declare function callerPerson(): Promise<string | null>;
|
|
5
|
+
/** The terminal grant of the request person (or, on a Mac, the install's person); null when the call has no person. */
|
|
6
|
+
export declare function callerTerminalGrant(): Promise<ActiveGrant | null>;
|
|
7
|
+
/** The terminal grant of the person a node row belongs to: its own `sub`, else (a node born before its person was known) the caller's. */
|
|
8
|
+
export declare function nodeTerminalGrant(node: {
|
|
9
|
+
sub?: string | null;
|
|
10
|
+
}): Promise<ActiveGrant | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=Object.defineProperty;var n=(r,e)=>a(r,"name",{value:e,configurable:!0});import{PersonOutsideRequestError as o,PersonUnresolvedError as i,personIdAsync as s}from"../canvas/person.js";import{terminalGrant as t}from"./mirror.js";async function c(){try{return await s()}catch(r){if(r instanceof i||r instanceof o)return null;throw r}}n(c,"callerPerson");async function l(){const r=await c();return r===null?null:await t(r)}n(l,"callerTerminalGrant");async function p(r){return r.sub?await t(r.sub):await l()}n(p,"nodeTerminalGrant");export{c as callerPerson,l as callerTerminalGrant,p as nodeTerminalGrant};
|