@north-light/cli 0.3.482 → 0.3.484
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/client.d.ts +6 -0
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/custom-objects.d.ts +5 -1
- package/dist/api/dto/docs.d.ts +26 -0
- package/dist/api/dto/grants.d.ts +6 -4
- package/dist/api/dto/objects.d.ts +21 -1
- package/dist/api/dto/plugins.d.ts +22 -0
- package/dist/api/dto/plugins.js +0 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +2 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-plugin/deploying.md +107 -0
- package/dist/builtin-memory/crouter-sdk/release-notes.md +79 -0
- package/dist/builtin-memory/northlight-apps/concepts/providers.md +4 -3
- package/dist/builtin-memory/northlight-apps/guides/grant-changes.md +11 -9
- package/dist/clients/attach/viewer.js +579 -578
- package/dist/commands/pkg/plugin-manage.d.ts +4 -0
- package/dist/commands/pkg/plugin-manage.js +18 -16
- package/dist/core/canvas/migrations.js +27 -18
- package/dist/core/command-plugins/bundle.d.ts +4 -1
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/command-plugins/caller-header.d.ts +10 -0
- package/dist/core/command-plugins/caller-header.js +1 -0
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/endpoint.d.ts +4 -0
- package/dist/core/command-plugins/endpoint.js +1 -1
- package/dist/core/command-plugins/transport/exec-invoke-async.d.ts +11 -0
- package/dist/core/command-plugins/transport/exec-invoke-async.js +1 -0
- package/dist/core/command-plugins/transport/exec-invoke.d.ts +14 -0
- package/dist/core/command-plugins/transport/exec-invoke.js +1 -1
- package/dist/core/command-plugins/transport/http-invoke.d.ts +9 -2
- package/dist/core/command-plugins/transport/http-invoke.js +3 -3
- package/dist/core/command-plugins/transport/http-plan.js +1 -1
- package/dist/core/command-plugins/transport/sign.d.ts +55 -0
- package/dist/core/command-plugins/transport/sign.js +3 -0
- package/dist/core/grants/mirror.d.ts +1 -1
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/graph/access.d.ts +19 -0
- package/dist/core/graph/access.js +3 -3
- package/dist/core/graph/custom.d.ts +29 -9
- package/dist/core/graph/custom.js +9 -8
- package/dist/core/graph/events.js +8 -8
- package/dist/core/graph/package-kinds.d.ts +25 -0
- package/dist/core/graph/package-kinds.js +1 -0
- package/dist/core/graph/package-reconcile.d.ts +20 -0
- package/dist/core/graph/package-reconcile.js +1 -0
- package/dist/core/graph/reader.d.ts +5 -0
- package/dist/core/graph/reader.js +3 -3
- package/dist/core/graph/receivers.d.ts +2 -1
- package/dist/core/graph/types.d.ts +2 -0
- package/dist/core/graph/watches.d.ts +4 -0
- package/dist/core/graph/watches.js +9 -8
- package/dist/core/peer/directory.d.ts +2 -1
- package/dist/core/peer/directory.js +1 -1
- package/dist/core/peer/ingest.d.ts +6 -1
- package/dist/core/peer/ingest.js +1 -1
- package/dist/core/peer/names.d.ts +2 -0
- package/dist/core/peer/names.js +1 -1
- package/dist/core/peer/outbox-store.d.ts +2 -0
- package/dist/core/peer/receiver.js +1 -1
- package/dist/core/peer/stubs.d.ts +26 -1
- package/dist/core/peer/stubs.js +1 -1
- package/dist/core/plugin-app.d.ts +31 -0
- package/dist/core/plugin-app.js +1 -0
- package/dist/core/plugin-objects-manifest.d.ts +35 -0
- package/dist/core/plugin-objects-manifest.js +1 -0
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/providers/call.d.ts +3 -0
- package/dist/core/providers/call.js +1 -1
- package/dist/core/secrets.d.ts +11 -0
- package/dist/core/secrets.js +1 -1
- package/dist/daemon/api/app-listener.js +13 -13
- package/dist/daemon/api/handlers/custom-objects.js +3 -3
- package/dist/daemon/api/handlers/objects.js +3 -2
- package/dist/daemon/api/handlers/package-docs.js +1 -1
- package/dist/daemon/api/handlers/plugin-app-token.d.ts +6 -0
- package/dist/daemon/api/handlers/plugin-app-token.js +1 -0
- package/dist/daemon/api/handlers/providers.d.ts +7 -0
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/crtrd.js +7 -5
- package/dist/daemon/peer/forward.d.ts +4 -1
- package/dist/daemon/peer/forward.js +1 -1
- package/dist/daemon/plugin-objects/read-leaf.d.ts +20 -0
- package/dist/daemon/plugin-objects/read-leaf.js +2 -0
- package/dist/types.d.ts +16 -0
- package/docs/apps/concepts/providers.md +4 -3
- package/docs/apps/guides/grant-changes.md +11 -9
- package/docs/plugin/deploying.md +107 -0
- package/docs/sdk/release-notes.md +79 -0
- package/package.json +2 -2
- package/packages/crouter-identity/dist/index.d.ts +1 -1
- package/packages/crouter-identity/dist/index.js +6 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import type { ObjectRow } from '../graph/types.js';
|
|
2
|
+
import type { ObjectRow, Privacy } from '../graph/types.js';
|
|
3
|
+
import { type CustomEvents } from '../graph/custom.js';
|
|
4
|
+
import type { ObjectDTO } from '../../api/dto/objects.js';
|
|
3
5
|
import type { Envelope } from './envelope.js';
|
|
4
6
|
/** The part of an envelope a stub stores: who the remote watcher is, never what it may do. */
|
|
5
7
|
export interface StubIdentity {
|
|
@@ -21,6 +23,13 @@ export interface OwnerRef {
|
|
|
21
23
|
external_id: string | null;
|
|
22
24
|
handle: string;
|
|
23
25
|
}
|
|
26
|
+
/** A custom kind as the home machine declares it: carried so the receiver can hold a stub of an instance. It grants nothing (no `read` leaf). */
|
|
27
|
+
export interface KindDeclaration {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
events: CustomEvents;
|
|
31
|
+
owner: OwnerRef;
|
|
32
|
+
}
|
|
24
33
|
/** An object as it crosses the wire: identity only, never content. */
|
|
25
34
|
export interface ObjectRef {
|
|
26
35
|
id: string;
|
|
@@ -29,6 +38,10 @@ export interface ObjectRef {
|
|
|
29
38
|
name: string;
|
|
30
39
|
preview?: string;
|
|
31
40
|
owner: OwnerRef | null;
|
|
41
|
+
/** The declaration of a custom `type`; absent for a built-in. */
|
|
42
|
+
kind_decl?: KindDeclaration;
|
|
43
|
+
/** The home object's privacy; a stub is never more readable than it. Absent: `public`. */
|
|
44
|
+
privacy?: Privacy;
|
|
32
45
|
}
|
|
33
46
|
export interface StubRecord {
|
|
34
47
|
object: ObjectRow;
|
|
@@ -47,7 +60,19 @@ export declare const isStub: (db: DatabaseSync, object: ObjectRow) => boolean;
|
|
|
47
60
|
* understand. No `nodes` row is written.
|
|
48
61
|
*/
|
|
49
62
|
export declare function ensureStub(db: DatabaseSync, id: string, type: string, machine: string, envelope?: Envelope, ref?: ObjectRef): ObjectRow;
|
|
63
|
+
/**
|
|
64
|
+
* The stub of the node `nodeId` that lives on `machine`, as a watcher: created on first sight with no envelope
|
|
65
|
+
* identity, owned by the local person, and marked `reception = 2` so `publish` considers it and the node
|
|
66
|
+
* kind's receiver sends its events through the `peer` outbox. A later forwarded call by that node fills in the
|
|
67
|
+
* identity on the same row (`ensureStub` with an envelope). Refuses (`conflict`) an id that is a real local
|
|
68
|
+
* object or a stub of another machine. Writes no `nodes` row.
|
|
69
|
+
*/
|
|
70
|
+
export declare function ensureNodeStub(db: DatabaseSync, nodeId: string, machine: string): ObjectRow;
|
|
50
71
|
/** The object moved: the machine that answered for it is now its home. A no-op for a local object or a stub already homed there. */
|
|
51
72
|
export declare function rehomeStub(db: DatabaseSync, id: string, machine: string): void;
|
|
52
73
|
/** How `o` is described to another machine. */
|
|
53
74
|
export declare function describeObject(db: DatabaseSync, o: ObjectRow): ObjectRef;
|
|
75
|
+
/** The declaration of the custom kind `type`, or null for a built-in (no `object_kinds` row). Carries no `read` leaf. */
|
|
76
|
+
export declare function kindDeclarationOf(db: DatabaseSync, type: string): KindDeclaration | null;
|
|
77
|
+
/** `dto` with the declaration of its object's kind, when the kind is custom. */
|
|
78
|
+
export declare function withKindDecl(db: DatabaseSync, row: ObjectRow, dto: ObjectDTO): ObjectDTO;
|
package/dist/core/peer/stubs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var x=Object.defineProperty;var o=(n,e)=>x(n,"name",{value:e,configurable:!0});import{prepared as i}from"../sqlite-statements.js";import{createObject as A,ensureOwner as _,getById as h,getByRowid as p}from"../graph/objects.js";import{upsertKind as b}from"../graph/custom.js";import{CrtrError as E}from"../errors.js";import{ExitCode as f}from"../../types.js";import{normalizeMachineId as H}from"./names.js";const W=["public","friends","private"],D=o(n=>{const e=n.act.act;return e===void 0?{sub:n.sub,app:null,node:null,node_scopes:null,place:null}:{sub:n.sub,app:e.sub,node:e.node,node_scopes:e.node_scopes,place:e.place}},"identityOfEnvelope");function y(n,e){const r=typeof e=="string"?h(n,e):e;if(r===null)return null;const t=i(n,"SELECT home_machine, envelope_json FROM peer_stubs WHERE object = ?").get(r.rowid);return t?{object:r,home_machine:t.home_machine,identity:t.envelope_json===null?null:JSON.parse(t.envelope_json)}:null}o(y,"stubOf");const I=o((n,e)=>i(n,"SELECT 1 FROM peer_stubs WHERE object = ?").get(e.rowid)!==void 0,"isStub");function m(n){return _(n,"person","local-user","user:local","user")}o(m,"localPerson");function S(n,e){return e==="user:local"?m(n):_(n,"app","app",e,e.replace(/^app:/,""))}o(S,"localApp");function v(n,e,r){if(e!==null)return e.app===null?m(n):S(n,e.app);const t=r?.owner;return t?.type==="app"?S(n,t.external_id??`app:${t.handle}`):m(n)}o(v,"ownerFor");function N(n,e,r,t,w,u){const l=w===void 0?null:D(w),s=u?.privacy??"public";if(!W.includes(s))throw new E("invalid_event",`invalid privacy ${JSON.stringify(s)}`,f.GENERAL,{id:e});const c=h(n,e);if(c!==null){const d=y(n,c);if(d===null)throw new E("conflict",`${e} is a local ${c.type}, not a stub of ${t}`,f.GENERAL,{id:e});if(d.home_machine!==t)throw new E("conflict",`${e} is already a stub of ${d.home_machine}, not ${t}`,f.GENERAL,{id:e,home_machine:d.home_machine});return l!==null&&(i(n,"UPDATE peer_stubs SET envelope_json = ? WHERE object = ?").run(JSON.stringify(l),c.rowid),i(n,"UPDATE objects SET reception = 2 WHERE rowid = ?").run(c.rowid)),u!==void 0&&(R(n,r,u),u.privacy!==void 0&&c.privacy!==s)?(i(n,"UPDATE objects SET privacy = ? WHERE rowid = ?").run(s,c.rowid),p(n,c.rowid)):c}const O=v(n,l,u);R(n,r,u);const a=A(n,{id:e,type:r,kind:u?.kind??null,owner:O.rowid,privacy:s,name:u?.name??e,preview:u?.preview??""});return i(n,"UPDATE objects SET home_machine = ? WHERE rowid = ?").run(t,a.rowid),i(n,"INSERT INTO peer_stubs (object, home_machine, envelope_json, created_at) VALUES (?, ?, ?, ?)").run(a.rowid,t,l===null?null:JSON.stringify(l),new Date().toISOString()),l!==null&&i(n,"UPDATE objects SET reception = 2 WHERE rowid = ?").run(a.rowid),p(n,a.rowid)}o(N,"ensureStub");function R(n,e,r){const t=r?.kind_decl;t===void 0||t.name!==e||b(n,{name:t.name,owner:v(n,null,{...r,owner:t.owner}),description:t.description,events:t.events,read:null,source:"peer"})}o(R,"holdKind");function F(n,e,r){const t=N(n,e,"node",H(r));return i(n,"UPDATE objects SET reception = 2 WHERE rowid = ?").run(t.rowid),p(n,t.rowid)}o(F,"ensureNodeStub");function J(n,e,r){const t=y(n,e);t===null||t.home_machine===r||(i(n,"UPDATE peer_stubs SET home_machine = ? WHERE object = ?").run(r,t.object.rowid),i(n,"UPDATE objects SET home_machine = ? WHERE rowid = ?").run(r,t.object.rowid))}o(J,"rehomeStub");function M(n,e){const r=j(n,e.owner===null?null:p(n,e.owner)),t=T(n,e.type);return{id:e.id,type:e.type,kind:e.kind,name:e.name,preview:e.preview,owner:r,privacy:e.privacy,...t===null?{}:{kind_decl:t}}}o(M,"describeObject");function j(n,e){if(e===null||e.type!=="person"&&e.type!=="app"&&e.type!=="repo"&&e.type!=="profile")return null;const r=i(n,"SELECT scheme, external_id FROM identities WHERE object = ?").get(e.rowid);return{id:e.id,type:e.type,scheme:r?.scheme??null,external_id:r?.external_id??null,handle:e.name}}o(j,"ownerRefOf");function T(n,e){const r=i(n,"SELECT owner, description, events_json FROM object_kinds WHERE name = ?").get(e),t=r===void 0?null:j(n,p(n,r.owner));return r===void 0||t===null?null:{name:e,description:r.description,events:JSON.parse(r.events_json),owner:t}}o(T,"kindDeclarationOf");function G(n,e,r){const t=T(n,e.type);return t===null?r:{...r,kind_decl:t}}o(G,"withKindDecl");export{M as describeObject,F as ensureNodeStub,N as ensureStub,D as identityOfEnvelope,I as isStub,T as kindDeclarationOf,J as rehomeStub,y as stubOf,G as withKindDecl};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { InstalledPlugin } from '../types.js';
|
|
2
|
+
export interface InvalidAppPlugin {
|
|
3
|
+
plugin: InstalledPlugin;
|
|
4
|
+
error: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PluginAppCredential {
|
|
7
|
+
plugin: InstalledPlugin;
|
|
8
|
+
token: string;
|
|
9
|
+
socket: string;
|
|
10
|
+
/** The endpoint of the installed manifest's http transport, or null for an exec plugin. */
|
|
11
|
+
endpoint: string | null;
|
|
12
|
+
}
|
|
13
|
+
/** The space-joined path of every leaf below the plugin's root command, with the command-validation issues. */
|
|
14
|
+
export declare function pluginLeafPaths(plugin: InstalledPlugin): {
|
|
15
|
+
leafPaths: Set<string>;
|
|
16
|
+
streamingLeafPaths: Set<string>;
|
|
17
|
+
issues: string[];
|
|
18
|
+
};
|
|
19
|
+
/** Enabled user plugins that hold an app token: not a provider, declaring kinds or scopes or serving over HTTP, with a clean manifest. */
|
|
20
|
+
export declare function appPlugins(): InstalledPlugin[];
|
|
21
|
+
/** Enabled user plugins and provider bundles with a clean manifest that declare `object_kinds`, whether or not they hold a token. */
|
|
22
|
+
export declare function kindPlugins(): InstalledPlugin[];
|
|
23
|
+
/** The enabled plugins and provider bundles that would hold a token or declare object blocks but whose manifest or commands are invalid, with why. */
|
|
24
|
+
export declare function invalidAppPlugins(): InvalidAppPlugin[];
|
|
25
|
+
/** The environment an exec leaf of `plugin` gets, or null when it holds no token. */
|
|
26
|
+
export declare function pluginAppEnv(plugin: string): {
|
|
27
|
+
CRTR_APP_TOKEN: string;
|
|
28
|
+
CRTR_DAEMON_SOCKET: string;
|
|
29
|
+
} | null;
|
|
30
|
+
/** The credential of an app plugin, or null when it is not in `appPlugins()` or holds no token. */
|
|
31
|
+
export declare function pluginAppCredential(name: string): PluginAppCredential | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var o=(n,t)=>c(n,"name",{value:t,configurable:!0});import{apiSocketPath as l}from"./canvas/paths.js";import{validatePluginCommands as d}from"./command-plugins/discovery.js";import{declaresObjectBlocks as m,leafPathsBelowRoot as g,needsAppToken as f,objectBlocksReasons as h,streamingLeafPathsBelowRoot as P}from"./plugin-objects-manifest.js";import{listInstalledPlugins as k}from"./resolver.js";import{pluginAppToken as u}from"./secrets.js";function v(n){const t=d(n),e=t.manifest?.roots??[];return{leafPaths:g(n.name,e),streamingLeafPaths:P(n.name,e),issues:t.issues.map(i=>i.message)}}o(v,"pluginLeafPaths");function a(){const n=[],t=[];for(const e of k("user")){if(!e.enabled||!(f(e.manifest)||m(e.manifest)))continue;const{leafPaths:i,streamingLeafPaths:s,issues:p}=v(e),r=[...p,...h(e.manifest,{name:e.name,leafPaths:i,streamingLeafPaths:s})];r.length===0?n.push(e):t.push({plugin:e,error:r.join("; ")})}return{valid:n,invalid:t}}o(a,"classify");function A(){return a().valid.filter(n=>f(n.manifest))}o(A,"appPlugins");function O(){return a().valid.filter(n=>Object.keys(n.manifest.object_kinds??{}).length>0)}o(O,"kindPlugins");function _(){return a().invalid}o(_,"invalidAppPlugins");function B(n){const t=u(n);return t===void 0?null:{CRTR_APP_TOKEN:t.token,CRTR_DAEMON_SOCKET:l()}}o(B,"pluginAppEnv");function E(n){const t=A().find(s=>s.name===n);if(t===void 0)return null;const e=u(n);if(e===void 0)return null;const i=t.manifest.transport;return{plugin:t,token:e.token,socket:l(),endpoint:i?.kind==="http"?i.endpoint:null}}o(E,"pluginAppCredential");export{A as appPlugins,_ as invalidAppPlugins,O as kindPlugins,E as pluginAppCredential,B as pluginAppEnv,v as pluginLeafPaths};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DeclLeaf, DeclNode } from '../api/command-manifest/schema.js';
|
|
2
|
+
import type { PluginManifest } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `object_kinds` and `scopes` blocks of a plugin manifest (`plugin.json`, `bundle.json`) and the one
|
|
5
|
+
* validator every install, update, enable, provider-bundle and reconcile path shares. Canvas-free by
|
|
6
|
+
* design: the CLI and the bundle parser import it, so it must not reach `node:sqlite` or the canvas.
|
|
7
|
+
*/
|
|
8
|
+
export declare const KIND: RegExp;
|
|
9
|
+
export declare const LOCAL: RegExp;
|
|
10
|
+
/** A kind's first segment may not be one of the built-in object types. */
|
|
11
|
+
export declare const RESERVED_KIND_ROOTS: readonly string[];
|
|
12
|
+
export declare const MAX_KIND_EVENTS = 64;
|
|
13
|
+
export declare const MIN_EVENT_LEVEL = 1;
|
|
14
|
+
export declare const MAX_EVENT_LEVEL = 10;
|
|
15
|
+
/** Reasons a catalog (`events` map) is invalid; shared with `registerCustomObject`. */
|
|
16
|
+
export declare function eventCatalogReasons(events: unknown, where: string): string[];
|
|
17
|
+
/** The JSON shape of both blocks of a raw manifest record, independent of the plugin's name and commands. */
|
|
18
|
+
export declare function objectBlocksShapeReasons(record: Record<string, unknown>): string[];
|
|
19
|
+
/**
|
|
20
|
+
* The full check. `leafPaths` holds the space-joined path of every leaf below the plugin's root command
|
|
21
|
+
* (`page read` for plugin `search`'s leaf `search page read`); `streamingLeafPaths` holds those declared `rest.streaming: true`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function objectBlocksReasons(manifest: Pick<PluginManifest, 'object_kinds' | 'scopes' | 'provider'>, context: {
|
|
24
|
+
name: string;
|
|
25
|
+
leafPaths: ReadonlySet<string>;
|
|
26
|
+
streamingLeafPaths: ReadonlySet<string>;
|
|
27
|
+
}): string[];
|
|
28
|
+
/** The manifest carries an `object_kinds` or `scopes` block, whether or not it is valid or the plugin holds a token. */
|
|
29
|
+
export declare function declaresObjectBlocks(manifest: Pick<PluginManifest, 'object_kinds' | 'scopes'>): boolean;
|
|
30
|
+
/** A user plugin holds a `crtr_app_` token when it is not a provider and declares kinds or scopes, or serves over HTTP. */
|
|
31
|
+
export declare function needsAppToken(manifest: Pick<PluginManifest, 'object_kinds' | 'scopes' | 'provider' | 'transport'>): boolean;
|
|
32
|
+
/** The space-joined path of every leaf below the plugin's root command (`page read` for plugin `search`'s leaf `search page read`). */
|
|
33
|
+
export declare function leafPathsBelowRoot(name: string, roots: ReadonlyArray<DeclNode<DeclLeaf>>): Set<string>;
|
|
34
|
+
/** The leaves of `leafPathsBelowRoot` that declare `rest.streaming: true`. */
|
|
35
|
+
export declare function streamingLeafPathsBelowRoot(name: string, roots: ReadonlyArray<DeclNode<DeclLeaf>>): Set<string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var b=Object.defineProperty;var d=(e,n)=>b(e,"name",{value:n,configurable:!0});import{parseScope as y}from"@north-light/identity";const k=/^[a-z][a-z0-9-]*\.[a-z][a-z0-9-]*$/,$=/^[a-z][a-z0-9-]*$/,g=["report","notice","node","job","document","person","app","repo","profile","action"],u=64,l=1,h=10,j=new Set(["description","events","read"]),O=new Set(["level","supersedes"]),S=/^crtr:objects:(?:read|watch|link|write):/;function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}d(f,"isRecord");function N(e,n){if(!f(e))return[`${n} events must be an object`];const s=[],i=Object.entries(e);i.length>u&&s.push(`${n} declares more than ${u} events`);for(const[c,t]of i){const r=`${n} event ${JSON.stringify(c)}`;if($.test(c)||s.push(`${r} is not a valid event name`),!f(t)){s.push(`${r} must be an object { level, supersedes? }`);continue}for(const a of Object.keys(t))O.has(a)||s.push(`${r} has unknown key ${JSON.stringify(a)}`);const o=t.level;(typeof o!="number"||!Number.isInteger(o)||o<l||o>h)&&s.push(`${r} level must be an integer ${l}-${h}`),t.supersedes!==void 0&&typeof t.supersedes!="boolean"&&s.push(`${r} supersedes must be a boolean`)}return s}d(N,"eventCatalogReasons");function _(e){const n=[],s=e.object_kinds;if(s!==void 0)if(!f(s))n.push("object_kinds must be an object keyed by kind name");else for(const[c,t]of Object.entries(s)){const r=`object_kinds ${JSON.stringify(c)}`;if(k.test(c)?g.includes(c.split(".")[0])&&n.push(`${r} uses a reserved first segment`):n.push(`${r} is not a valid kind name (<plugin>.<name>, lowercase)`),!f(t)){n.push(`${r} must be an object { description, events, read? }`);continue}for(const a of Object.keys(t))j.has(a)||n.push(`${r} has unknown key ${JSON.stringify(a)}`);(typeof t.description!="string"||t.description.trim()==="")&&n.push(`${r} description must be non-empty text`),n.push(...N(t.events,r));const o=t.read;if(o!==void 0){const a=f(o)?o.command:void 0;!f(o)||Object.keys(o).some(p=>p!=="command")?n.push(`${r} read must be { command: [leaf path] }`):(!Array.isArray(a)||a.length===0||a.some(p=>typeof p!="string"||p===""))&&n.push(`${r} read.command must be a non-empty array of command names`)}}const i=e.scopes;return i!==void 0&&(!Array.isArray(i)||i.some(c=>typeof c!="string"))&&n.push("scopes must be an array of scope strings"),n}d(_,"objectBlocksShapeReasons");function v(e,n){const s=_(e);if(s.length>0)return s;const{name:i,leafPaths:c,streamingLeafPaths:t}=n;for(const[o,a]of Object.entries(e.object_kinds??{})){o.split(".")[0]!==i&&s.push(`object_kinds ${JSON.stringify(o)} must start with the plugin name "${i}."`);const p=a.read?.command;p!==void 0&&!c.has(p.join(" "))?s.push(`object_kinds ${JSON.stringify(o)} read.command "${p.join(" ")}" is not a leaf of plugin "${i}"`):p!==void 0&&t.has(p.join(" "))&&s.push(`object_kinds ${JSON.stringify(o)} read.command "${p.join(" ")}" streams; a read is one {markdown} answer`)}const r=e.scopes??[];e.scopes!==void 0&&e.provider!==void 0&&s.push("scopes is for local plugins; a provider gets its scopes from directory consent");for(const o of r){try{y(o)}catch{s.push(`scope ${JSON.stringify(o)} is not a recognized scope`);continue}S.test(o)?o.endsWith(`:app:${i}`)&&s.push(`scope ${JSON.stringify(o)} names the plugin's own space; an app needs no scope for its own objects`):s.push(`scope ${JSON.stringify(o)} must be crtr:objects:<read|watch|link|write>:<target>`)}return s}d(v,"objectBlocksReasons");function x(e){return e.object_kinds!==void 0||e.scopes!==void 0}d(x,"declaresObjectBlocks");function A(e){return e.provider!==void 0?!1:Object.keys(e.object_kinds??{}).length>0||(e.scopes?.length??0)>0||e.transport?.kind==="http"}d(A,"needsAppToken");function J(e,n){return m(e,n,()=>!0)}d(J,"leafPathsBelowRoot");function R(e,n){return m(e,n,s=>"rest"in s&&s.rest.streaming===!0)}d(R,"streamingLeafPathsBelowRoot");function m(e,n,s){const i=new Set,c=d((t,r)=>{if(t.kind==="leaf")s(t)&&i.add([...r,t.name].join(" "));else for(const o of t.children)c(o,[...r,t.name])},"walk");for(const t of n)if(t.name===e&&t.kind==="branch")for(const r of t.children)c(r,[]);return i}d(m,"collectLeafPaths");export{k as KIND,$ as LOCAL,h as MAX_EVENT_LEVEL,u as MAX_KIND_EVENTS,l as MIN_EVENT_LEVEL,g as RESERVED_KIND_ROOTS,x as declaresObjectBlocks,N as eventCatalogReasons,J as leafPathsBelowRoot,A as needsAppToken,v as objectBlocksReasons,_ as objectBlocksShapeReasons,R as streamingLeafPathsBelowRoot};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var se=Object.defineProperty;var i=(e,n)=>se(e,"name",{value:n,configurable:!0});import{existsSync as w,readdirSync as z,readFileSync as S,rmSync as $,statSync as k}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as de}from"../../api/command-manifest/manifest.js";import{crtrHome as F}from"../canvas/paths.js";import{withCanvasWrite as G}from"../canvas/db.js";import{bundleVersion as ue,isInstalledGeneration as ce,recordCheckedGeneration as fe}from"../command-plugins/bundle.js";import{readConfig as q,readState as le,updateConfig as pe,updateState as me}from"../config.js";import{emitEvent as m}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as V}from"../exclusive-lock.js";import{atomicWriteText as x,ensureDir as T,pathExists as ve}from"../fs-utils.js";import{
|
|
2
|
-
`),$(u(e,"bundle.tar"),{force:!0}),$(u(e,"etag"),{force:!0})}i(Pe,"retire");function ie(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}i(ie,"retiredError");async function H(e,n,r){const t=Y(e,n),o=u(t,"bundle.tar"),a=w(o),s=a&&r&&w(u(t,"etag"))?S(u(t,"etag"),"utf8"):void 0,f=await ee(e,{version:n,...s!==void 0?{ifNoneMatch:s}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Pe(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ie(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`);try{await oe(e,n,f.raw)}catch(l){
|
|
1
|
+
var se=Object.defineProperty;var i=(e,n)=>se(e,"name",{value:n,configurable:!0});import{existsSync as w,readdirSync as z,readFileSync as S,rmSync as $,statSync as k}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as de}from"../../api/command-manifest/manifest.js";import{crtrHome as F}from"../canvas/paths.js";import{withCanvasWrite as G}from"../canvas/db.js";import{bundleVersion as ue,isInstalledGeneration as ce,recordCheckedGeneration as fe}from"../command-plugins/bundle.js";import{readConfig as q,readState as le,updateConfig as pe,updateState as me}from"../config.js";import{emitEvent as m}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as V}from"../exclusive-lock.js";import{atomicWriteText as x,ensureDir as T,pathExists as ve}from"../fs-utils.js";import{reconcilePackages as Z}from"../graph/package-reconcile.js";import{readPluginManifest as C}from"../manifest.js";import{bundleSwapLockPath as L}from"../plugin-swap-lock.js";import{pluginsDir as he,userScopeRoot as J}from"../scope.js";import{listInstalledPlugins as ge}from"../resolver.js";import{localGrants as j}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const A="[A-Za-z0-9_-]{1,64}",we=new RegExp(`^(${A}):(${A})$`),K=new RegExp(`^${A}$`),$e=3e4,Q=3e4;function X(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=we.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}i(X,"providerIdsFromGrants");function v(e){return u(he("user"),e)}i(v,"pluginRoot");function R(){return u(F(),"providers")}i(R,"providersRoot");function Y(e,n){return u(R(),e,`v${n}`)}i(Y,"majorDir");function B(e){if(!K.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}i(B,"assertId");function E(){const e=q("user").runtime?.issuer;if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e.replace(/\/+$/,"")}i(E,"directoryBase");async function ee(e,n){const r=`${E()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await fetch(r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout($e)})}catch(s){return{status:"failed",message:`could not reach ${r}: ${s instanceof Error?s.message:String(s)}`}}if(t.status===304)return n.ifNoneMatch===void 0?{status:"failed",message:`HTTP 304 from ${r} without a conditional request`}:{status:304};if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300)return await t.body?.cancel().catch(()=>{}),{status:"failed",message:`HTTP ${t.status} from ${r}`};let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(s){return{status:"failed",message:`reading ${r} failed: ${s instanceof Error?s.message:String(s)}`}}const a=t.headers.get("etag");return{status:200,raw:o,...a!==null?{etag:a}:{}}}i(ee,"fetchProviderBundle");const I=new WeakSet;class M extends Error{static{i(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function ne(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}i(ne,"wrongBundle");function D(e,n,r){m({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof M?{issues:r.issues}:{}},error:r})}i(D,"logInvalid");function re(e,n,r){m({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r}})}i(re,"logFailure");async function te(){return import("../../commands/pkg/plugin-manage.js")}i(te,"pluginManage");const y=new Map,b=i(e=>`${F()}\0${e}`,"installKey");function ye(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}i(ye,"publicInstallCause");function be(e){if(h(e)===void 0)return y.get(b(e))??{status:"installing"}}i(be,"providerInstallState");function _e(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${be(e)?.error??"bundle installation pending"}`)}i(_e,"unavailableProviderError");function h(e){const n=C(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&ve(u(v(e),"commands.json"))){try{if(k(u(v(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:n.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:n.transport.endpoint}}}}i(h,"installedManifest");async function N(e,n,r=!1){const t=J();h(e)===void 0&&y.set(b(e),{status:"installing"});try{return T(u(t,"tmp")),await V(L(t,e),async()=>{const o=v(e),a=C(o);if(a!==null&&a.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const s=h(e)!==void 0;if(s&&r)return"unchanged";const f=s&&n?le("user").plugins[e]?.etag:void 0,d=await ee(e,f!==void 0?{ifNoneMatch:f}:{});if(d.status===304)return"unchanged";if(d.status===404||d.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${d.message})`,{param:"provider"});if(d.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${d.message}`);const l=ue(d.raw);if(s&&ce(e,"user",o,l))return fe("user",e,d.etag,{swapped:!1}),"unchanged";const{installPluginArchive:g}=await te();try{await g(e,d.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:d.etag,enable:!s,bundle:{endpoint:`${E()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:E()},accept:i(p=>ne(e,void 0,p),"accept")})}catch(p){D(e,void 0,p);const W=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw I.add(W),W}try{G(p=>Z(p))}catch(p){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return m({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:s}}),y.delete(b(e)),s?"updated":"installed"},{timeoutMs:Q,timeoutError:i(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw h(e)===void 0&&y.set(b(e),{status:"install_failed",error:ye(o)}),o instanceof c?o:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),_e(e))}}i(N,"refreshInstalled");async function Je(e){B(e),h(e)===void 0&&await N(e,!1,!0)}i(Je,"ensureProviderBundle");async function Ke(e,n){return B(e),n.version!==void 0&&h(e)?.provider.major!==n.version?H(e,n.version,n.conditional):N(e,n.conditional)}i(Ke,"refreshProviderBundle");async function oe(e,n,r){const{validatePluginArchive:t}=await te(),o=await t(r);if(o.bundle===void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(s=>s.message).join("; ")}`,o.issues);const a=ne(e,n,o.bundle);if(a!==void 0)throw new M(`provider "${e}" major ${n} bundle is invalid: ${a}`,[]);if(o.bundle.transport===void 0)throw new M(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}i(oe,"validateArchive");function Pe(e){T(e),x(u(e,"retired"),`${new Date().toISOString()}
|
|
2
|
+
`),$(u(e,"bundle.tar"),{force:!0}),$(u(e,"etag"),{force:!0})}i(Pe,"retire");function ie(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}i(ie,"retiredError");async function H(e,n,r){const t=Y(e,n),o=u(t,"bundle.tar"),a=w(o),s=a&&r&&w(u(t,"etag"))?S(u(t,"etag"),"utf8"):void 0,f=await ee(e,{version:n,...s!==void 0?{ifNoneMatch:s}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Pe(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ie(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`);try{await oe(e,n,f.raw)}catch(l){D(e,n,l);const g=new c(a?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw I.add(g),g}T(t),$(u(t,"retired"),{force:!0});const d=a&&Buffer.from(f.raw).equals(S(o));return d||x(o,f.raw),f.etag!==void 0?x(u(t,"etag"),f.etag):$(u(t,"etag"),{force:!0}),d?"unchanged":a?"updated":"installed"}i(H,"refreshPinned");function Se(){const e=[],n=R();if(!w(n))return e;for(const r of z(n)){if(!K.test(r))continue;let t;try{t=z(u(n,r))}catch{continue}for(const o of t){const a=/^v([1-9][0-9]*)$/.exec(o);a&&w(u(n,r,o,"bundle.tar"))&&e.push([r,Number(a[1])])}}return e}i(Se,"cachedMajors");function Ee(e){const n=de(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}i(Ee,"readCommands");const _=new Map,O=new Map;function Ie(e,n){const r=k(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=O.get(e);if(o?.stamp===t)return o.commands;const a=Ee(JSON.parse(S(n,"utf8")));return O.set(e,{stamp:t,commands:a}),a}i(Ie,"installedCommands");async function Qe(e,n){B(e);const r=h(e);if(n===void 0||r?.provider.major===n){if(r===void 0)throw new c("not_found",`provider "${e}" has no installed bundle`,{param:"provider"});const l=v(e),g=Ie(e,u(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:g,transport:r.transport,source:"installed",root:l}}if(!Number.isInteger(n)||n<1)throw new c("invalid_request","version must be an integer of at least 1",{param:"version"});const t=Y(e,n);if(w(u(t,"retired")))throw ie(e,n);const o=u(t,"bundle.tar");w(o)||await H(e,n,!1);const a=`${e}/${n}`,s=k(o),f=`${s.mtimeMs}:${s.size}`;let d=_.get(a)?.stamp===f?_.get(a).bundle:void 0;if(d===void 0){try{d=await oe(e,n,new Uint8Array(S(o)))}catch(l){throw D(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}_.set(a,{stamp:f,bundle:d})}return{id:e,major:n,provider:d.provider,commands:d.commands,transport:d.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:d.transport.endpoint},source:"pinned"}}i(Qe,"resolveProviderBundle");let P=null,U=!1;function Me(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${E()}/providers/${encodeURIComponent(n)}/plugin`}i(Me,"isDirectoryBundle");function ae(e){return X(j()).includes(e)}i(ae,"grantNames");async function ke(e){await V(L(J(),e),async()=>{if(!(ae(e)||!Me(C(v(e)),e))){$(v(e),{recursive:!0,force:!0}),pe("user",n=>{delete n.plugins[e]}),me("user",n=>{delete n.plugins[e]}),$(u(R(),e),{recursive:!0,force:!0}),O.delete(e),y.delete(b(e));for(const n of[..._.keys()])n.startsWith(`${e}/`)&&_.delete(n);try{G(n=>Z(n))}catch(n){m({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:n})}m({level:"info",event:"daemon.providers.bundle_uninstalled",fields:{provider:e}})}},{timeoutMs:Q,timeoutError:i(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}i(ke,"uninstallProvider");async function xe(){if(!j().every(e=>e.status!=="active")){for(const e of ge("user"))if(!(e.manifest.provider===void 0||ae(e.name)))try{await ke(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}i(xe,"uninstallDisconnected");async function Te(){if(!q("user").runtime?.issuer)return;const e=X(j());for(const n of e)try{await N(n,!0)}catch(r){r instanceof Object&&I.has(r)||re(n,void 0,r instanceof Error?r.message:String(r))}await xe();for(const[n,r]of Se())try{await H(n,r,!0)}catch(t){if(t instanceof c&&t.code==="provider_version_retired"||t instanceof Object&&I.has(t))continue;re(n,r,t instanceof Error?t.message:String(t))}}i(Te,"syncOnce");function Xe(){return P!==null?(U=!0,P):(P=(async()=>{try{do U=!1,await Te();while(U)}finally{P=null}})(),P)}i(Xe,"syncProviderBundles");export{Je as ensureProviderBundle,X as providerIdsFromGrants,be as providerInstallState,Ke as refreshProviderBundle,Qe as resolveProviderBundle,Xe as syncProviderBundles,_e as unavailableProviderError};
|
|
@@ -65,4 +65,7 @@ export interface ProviderResponseContext {
|
|
|
65
65
|
}
|
|
66
66
|
export declare function onProviderResponse(context: ProviderResponseContext): string | undefined;
|
|
67
67
|
export declare function mapProviderResponse(status: number, body: unknown, headers: Record<string, string | string[] | undefined>, approved?: boolean): ProviderError;
|
|
68
|
+
/** `Crtr-Caller` for a provider HTTP call: the calling node and run, that node's profile, and this machine once the runtime is joined (`ownMachineId`
|
|
69
|
+
* throws on an unjoined runtime). Empty fields are omitted; no field means no header. Never carries a token or scopes. */
|
|
70
|
+
export declare function crtrCallerHeader(caller: ProviderCaller): Record<string, string>;
|
|
68
71
|
export declare function callProvider(caller: ProviderCaller, rawInput: ProviderCallInput, request: ProviderRequest): Promise<ProviderCallResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var re=Object.defineProperty;var v=(e,t)=>re(e,"name",{value:t,configurable:!0});import{validateDeclaredResult as I}from"../../api/command-manifest/result.js";import{callerAllows as x,requireGroup as te}from"../../daemon/api/handlers/providers.js";import{terminalGrant as oe}from"../grants/mirror.js";import{UNCAPPED_GRANTEE as ie}from"../usage/ledger.js";import{listInstalledPlugins as ne}from"../resolver.js";import{ProviderError as l}from"./errors.js";import{resolveProviderRef as se}from"./roots.js";import{ensureProviderBundle as ae,refreshProviderBundle as de,resolveProviderBundle as B,unavailableProviderError as ue}from"./bundles.js";import{prepareFileCall as le,resignForRetry as z,resolveFileResult as Q}from"./files.js";import{consumeApproval as pe,requireProviderApproval as ce}from"./approval.js";import{outboundToken as fe}from"./mint.js";import{discardFile as me}from"./token-cache.js";import{registerProviderCall as ve}from"./inflight.js";import{buildRequestPlan as ge}from"../command-plugins/transport/http-plan.js";import{sendProviderRequest as he,ProviderTransportError as we}from"./request.js";import{acceptProviderReceipt as be}from"./receipts.js";import{writeProviderAudit as ye}from"./audit.js";import{selectDevice as _e,sendDeviceCall as je,deviceFailure as Ae,deviceUnreachable as V,DEFAULT_DEVICE_REQUEST_MS as ke}from"./device.js";import{isLibsSearch as K,wantsBundling as qe,callLibsSearch as Pe,bundleExec as $e,mapLibOutcome as Ee,reportLibCalls as Re}from"./device-libs.js";import{envelopeForProviderCaller as Se}from"../peer/envelope.js";function W(e){const t=[],a=e.roots.length===1?e.roots[0].name:void 0,c=v((r,i)=>{const d=[...i,r.name];if(r.kind==="branch"){for(const _ of r.children)c(_,d);return}const p=d[0]===a?d.slice(1).join(" "):d.join(" ");t.push({tool:p,leaf:r})},"walk");for(const r of e.roots)c(r,[]);return t}v(W,"providerLeaves");function X(e,t,a){if(e.kind!=="branch")return null;const c=[];for(const n of e.children)if(n.kind==="branch"){const h=X(n,t,a);h&&c.push(h)}else if("group"in n&&n.group&&x(a,`${t}:${n.group}`)){const{kind:h,name:m,description:y,whenToUse:P,tier:o,summary:A,params:s,output:w,effects:k,group:R}=n;c.push({kind:h,name:m,description:y,whenToUse:P,...o===void 0?{}:{tier:o},summary:A,params:s,output:w,effects:k,group:R})}if(c.length===0)return null;const{kind:r,name:i,description:d,whenToUse:p,tier:_,summary:g,model:j,rootEntry:q}=e;return{kind:r,name:i,description:d,whenToUse:p,..._===void 0?{}:{tier:_},summary:g,...j===void 0?{}:{model:j},...q===void 0?{}:{rootEntry:q},children:c}}v(X,"providerHelpBranch");async function ir(e,t){const a=await B(t),c=W(a.commands),r=ne("user").find(i=>i.name===t)?.manifest.description??t;return{provider:t,status:"available",major:a.major,display_name:r,transport:a.transport.kind,groups:a.provider.groups.filter(i=>x(e,`${t}:${i.group}`)).map(i=>({group:i.group,leaves:c.filter(({leaf:d})=>d.group===i.group).map(({tool:d,leaf:p})=>({tool:d,description:p.description,summary:p.summary,params:p.params,output:p.output}))})),commands:a.commands.roots.flatMap(i=>{const d=X(i,t,e);return d?[d]:[]})}}v(ir,"providerTools");function De(e){return be(e.headers["crtr-receipt"],e)}v(De,"onProviderResponse");function Fe(e){if(e.class==="person"){const t=oe()?.sub;if(!t)throw new l("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:t,grantee:t}}if(!e.sub||!e.grantee)throw new l("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}v(Fe,"identity");function Ne(e,t){return[`${e.id}:${t.group}`]}v(Ne,"callScope");function Oe(e,t){const a=W(e.commands).find(c=>c.tool===t);if(!a)throw new l("not_found",`tool "${t}" is not in provider "${e.id}"`,{param:"tool"});if(!a.leaf.group)throw new l("not_found",`tool "${t}" has no group`,{param:"tool"});return a.leaf}v(Oe,"leafFor");function L(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t=e.error;return!t||typeof t!="object"||Array.isArray(t)?{}:t}v(L,"errorBody");function Te(e){const t=Array.isArray(e)?e[0]:e;if(!t)return;const a=Number(t);if(Number.isFinite(a)&&a>=0)return a;const c=Date.parse(t);return Number.isFinite(c)?Math.max(0,Math.ceil((c-Date.now())/1e3)):void 0}v(Te,"retryAfter");function T(e,t,a,c=!1){const r=L(t),i=typeof r.code=="string"?r.code:void 0,d=typeof r.message=="string"?r.message:`provider answered HTTP ${e}`,p=typeof r.field=="string"?r.field:void 0,g={origin:"provider",details:{code:i??null,message:d,...p?{field:p}:{},...typeof r.next=="string"?{next:r.next}:{},...r.received!==void 0?{received:r.received}:{},status:e},...p?{param:p}:{}};return e===429?new l("provider_rate_limited",d,{...g,retry_after_s:Te(a["retry-after"])}):e===401||e>=500?new l("provider_unavailable",d,g):e===403&&i==="provider_refused"?new l("provider_refused",d,g):e===403&&i==="approval_required"?new l("provider_error",c?"provider asked for approval again":"provider approval question is invalid",{...g,status:e}):new l("provider_error",d,{...g,status:e})}v(T,"mapProviderResponse");function Y(e,t){return JSON.stringify({major:e.major,endpoint:e.transport.kind==="http"?e.transport.endpoint:null,baseUrl:e.commands.baseUrl??null,rest:t.rest,params:t.params,output:t.output})}v(Y,"approvalShape");async function nr(e,t,a){const c=se(t.provider);if(c===null)throw new l("not_found",`provider "${t.provider}" is not available`,{param:"provider"});const r={...t,provider:c},i=new AbortController,d=v(()=>i.abort(a.signal.reason),"abort");a.signal.aborted?d():a.signal.addEventListener("abort",d,{once:!0});let p=a.requestId;const _=ve({request_id:p,run_id:e.run_id,node_id:e.node_id,controller:i});let g,j,q,n,h,m,y,P="allowed";try{try{await ae(r.provider)}catch{throw ue(r.provider)}let o=await B(r.provider,r.version),A=!1,s,w,k,R=!1,J=!1,C,H=0;for(;;){const b=Oe(o,r.tool);if(q=`${o.id}:${b.group}`,te(e,o.id,b.group),o.transport.kind!=="machine"&&r.machine!==void 0)throw new l("invalid_argument",`provider "${o.id}" is not served by a device, so it takes no machine`,{param:"machine"});o.transport.kind==="machine"&&C===void 0&&(C=await _e(r.machine,void 0,{newest:K(o.major,r.tool)}),H=Date.now()+(o.commands.timeouts?.requestMs??ke));let M=null;if(r.approval&&!R){const u=pe(e,r,Y(o,b));k=u.option,M=u.prepared,y=r.approval,p=u.requestId,R=!0}const S={params:b.params.filter(u=>"type"in u&&u.type==="file").map(u=>u.name),output:b.output.find(u=>u.type==="file")?.name??null},G=JSON.stringify(S);if(w!==void 0&&w!==G)throw new l("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});w=G,M&&(s=await z(e,S,r.arguments,M,{signal:i.signal})),s??=await le(e,S,r.arguments,{signal:i.signal});const{sub:U,grantee:D}=Fe(e);if(o.transport.kind==="machine"){const u=await Ce(e,o,r,b,s,{sub:U,requestId:p,deadlineAt:H,signal:i.signal,machine:C});return m=u.responder,g=u.cleanup,{status:200,body:u.result}}const $=await fe({audience:o.transport.endpoint,sub:U,grantee:D,initiating_app:D,scope:Ne(o,b)},{signal:i.signal});n=$.jti;const Z={registration:{name:o.id,endpoint:o.transport.endpoint},...o.commands.baseUrl?{baseUrl:o.commands.baseUrl}:{},...o.commands.timeouts?{timeouts:o.commands.timeouts}:{},params:b.params,rest:b.rest,output:b.output,commandPath:[o.id,...r.tool.split(" ")]},ee=ge(Z,s.arguments,void 0,{authorization:`Bearer ${$.access_token}`,requestId:p,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...k?{"Crtr-Approval":k}:{},...s.headers}});j??=Date.now()+(o.commands.timeouts?.requestMs??3e4);let f;try{if(f=await he(ee,{connectMs:o.commands.timeouts?.connectMs,requestMs:o.commands.timeouts?.requestMs,deadlineAt:j,signal:i.signal}),i.signal.aborted)throw i.signal.reason??new Error("provider call aborted")}catch(u){throw i.signal.aborted?i.signal.reason??u:u instanceof we?new l("provider_unavailable",u.message,{origin:"provider",details:{status:null,code:u.kind}}):u}if(h??=De({provider:o.id,tool:r.tool,request_id:p,run_id:e.run_id,sub:U,grantee:D,payer:$.payer,ledger_grantee:e.class==="person"?ie:D,jti:$.jti,status:f.status,headers:f.headers}),m=f.headers["crtr-responder"],f.status===401&&me($.key),f.status>=200&&f.status<300){const u=f.body;if(!u||typeof u!="object"||Array.isArray(u))throw new l("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:f.status,message:"provider result is not a JSON object"}});let N;try{N=await Q(e,s,u,{signal:i.signal})}catch(E){throw E instanceof l&&E.code==="provider_error"?new l("provider_bad_response",E.message,{origin:"provider",details:{status:f.status,message:E.message}}):E}g=N.cleanup;const O=I(b.output,N.result);if(O)throw new l("provider_bad_response",`provider result has an invalid output field "${O.field}"`,{origin:"provider",param:O.field,details:{status:f.status,...O}});if(i.signal.aborted)throw i.signal.reason??new Error("provider call aborted");return{status:200,body:N.result}}const F=L(f.body);if(f.status===403&&F.code==="approval_required"&&!k&&!J){J=!0;const u=await ce(e,r,p,F.question,i.signal,Y(o,b),s);if(y=u.questionId,k=u.option,s=await z(e,S,r.arguments,s,{signal:i.signal}),j=void 0,i.signal.aborted)throw new l("user_denied","the approval wait was canceled");continue}if(f.status===403&&F.code==="approval_required"&&k)throw T(f.status,f.body,f.headers,!0);if(f.status!==401&&F.manifest_stale===!0&&!A){A=!0,await de(o.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),o=await B(o.id,r.version);continue}throw T(f.status,f.body,f.headers,!!k)}}catch(o){throw o instanceof l&&o.question_id&&(y=o.question_id),m===void 0&&o instanceof l&&typeof o.responder=="string"&&(m=o.responder),P=`refused:${o instanceof l?o.code:"server_error"}`,g?.(),o}finally{try{ye({request_id:p,caller:e,provider:r.provider,tool:r.tool,scope_checked:q,outcome:P,jti:n,receipt_id:h,responder:m,question_id:y})}finally{_()}a.signal.removeEventListener("abort",d)}}v(nr,"callProvider");async function Ce(e,t,a,c,r,i){const{machine:d}=i,p=Se(e,i.sub),_={machine:d,envelope:p,signal:i.signal,deadlineAt:i.deadlineAt,mapFailure:T};if(K(t.major,a.tool)){const s=await Pe(_,r.arguments,t.major),w=I(c.output,s);if(w)throw new l("provider_bad_response",`the libraries result has an invalid output field "${w.field}"`,{origin:"provider",param:w.field,details:{status:200,...w}});return{result:s,responder:void 0,cleanup:void 0}}let g=r.arguments,j=null,q="";if(qe(t.major,a.tool,r.arguments)){const s=await $e(_,r.arguments,t.major);if(i.deadlineAt-Date.now()<=0)throw V(d,"timeout","");s!==null&&(g=s.arguments,j=s.services,q=s.release)}const n=await je(d,{tool:a.tool,arguments:g,version:t.major,requestId:i.requestId,runId:e.run_id,deadlineAt:i.deadlineAt,upload:r.upload===null?null:{id:r.upload.upload_id,link:r.upload.upload_link}},p,i.signal);if(i.signal.aborted)throw i.signal.reason??new Error("provider call aborted");if("unreachable"in n)throw V(d,n.unreachable.reason,n.unreachable.message);const h=n.headers["crtr-responder"],m=L(n.body);if(n.status<200||n.status>=300){const s=Ae(d,n.status,typeof m.code=="string"?m.code:void 0,typeof m.message=="string"?m.message:void 0,n.body),w=s===null&&n.status>=500&&typeof m.code=="string"?new l("provider_error",typeof m.message=="string"?m.message:`the device answered HTTP ${n.status}`,{origin:"runtime",status:n.status,details:{code:m.code,status:n.status}}):null;throw Object.assign(s??w??T(n.status,n.body,n.headers),{responder:h})}const y=n.body&&typeof n.body=="object"&&!Array.isArray(n.body)?n.body.result:void 0;if(!y||typeof y!="object"||Array.isArray(y))throw Object.assign(new l("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:n.status,message:"the device result is not a JSON object"}}),{responder:h});let P=y;if(j!==null){const s=Ee(P,j);if(Re(d,p,q,s.calls),"error"in s)throw Object.assign(s.error,{responder:h});P=s.result}let o;try{o=await Q(e,r,P,{signal:i.signal})}catch(s){throw s instanceof l&&s.code==="provider_error"?Object.assign(new l("provider_bad_response",s.message,{origin:"provider",details:{status:n.status,message:s.message}}),{responder:h}):s}const A=I(c.output,o.result);if(A)throw o.cleanup?.(),Object.assign(new l("provider_bad_response",`the device result has an invalid output field "${A.field}"`,{origin:"provider",param:A.field,details:{status:n.status,...A}}),{responder:h});return{result:o.result,responder:h,cleanup:o.cleanup}}v(Ce,"callDevice");export{nr as callProvider,T as mapProviderResponse,De as onProviderResponse,W as providerLeaves,ir as providerTools};
|
|
1
|
+
var te=Object.defineProperty;var m=(e,t)=>te(e,"name",{value:t,configurable:!0});import{validateDeclaredResult as U}from"../../api/command-manifest/result.js";import{callerAllows as G,requireGroup as oe}from"../../daemon/api/handlers/providers.js";import{terminalGrant as ne}from"../grants/mirror.js";import{UNCAPPED_GRANTEE as ie}from"../usage/ledger.js";import{listInstalledPlugins as se}from"../resolver.js";import{ProviderError as l}from"./errors.js";import{resolveProviderRef as ae}from"./roots.js";import{ensureProviderBundle as de,refreshProviderBundle as ue,resolveProviderBundle as B,unavailableProviderError as le}from"./bundles.js";import{prepareFileCall as pe,resignForRetry as z,resolveFileResult as Q}from"./files.js";import{consumeApproval as ce,requireProviderApproval as fe}from"./approval.js";import{outboundToken as me}from"./mint.js";import{discardFile as ve}from"./token-cache.js";import{registerProviderCall as ge}from"./inflight.js";import{buildRequestPlan as he}from"../command-plugins/transport/http-plan.js";import{sendProviderRequest as we,ProviderTransportError as be}from"./request.js";import{acceptProviderReceipt as ye}from"./receipts.js";import{writeProviderAudit as _e}from"./audit.js";import{selectDevice as je,sendDeviceCall as Ae,deviceFailure as ke,deviceUnreachable as V,DEFAULT_DEVICE_REQUEST_MS as qe}from"./device.js";import{isLibsSearch as K,wantsBundling as Pe,callLibsSearch as $e,bundleExec as Ee,mapLibOutcome as Ce,reportLibCalls as Re}from"./device-libs.js";import{directoryIssuer as Se,ownMachineId as Ne}from"../peer/directory.js";import{formatCrtrCaller as W}from"../command-plugins/caller-header.js";import{getNode as De}from"../canvas/canvas.js";import{envelopeForProviderCaller as Fe}from"../peer/envelope.js";function X(e){const t=[],a=e.roots.length===1?e.roots[0].name:void 0,c=m((r,n)=>{const d=[...n,r.name];if(r.kind==="branch"){for(const _ of r.children)c(_,d);return}const p=d[0]===a?d.slice(1).join(" "):d.join(" ");t.push({tool:p,leaf:r})},"walk");for(const r of e.roots)c(r,[]);return t}m(X,"providerLeaves");function Y(e,t,a){if(e.kind!=="branch")return null;const c=[];for(const i of e.children)if(i.kind==="branch"){const h=Y(i,t,a);h&&c.push(h)}else if("group"in i&&i.group&&G(a,`${t}:${i.group}`)){const{kind:h,name:v,description:y,whenToUse:P,tier:o,summary:A,params:s,output:w,effects:k,group:C}=i;c.push({kind:h,name:v,description:y,whenToUse:P,...o===void 0?{}:{tier:o},summary:A,params:s,output:w,effects:k,group:C})}if(c.length===0)return null;const{kind:r,name:n,description:d,whenToUse:p,tier:_,summary:g,model:j,rootEntry:q}=e;return{kind:r,name:n,description:d,whenToUse:p,..._===void 0?{}:{tier:_},summary:g,...j===void 0?{}:{model:j},...q===void 0?{}:{rootEntry:q},children:c}}m(Y,"providerHelpBranch");async function cr(e,t){const a=await B(t),c=X(a.commands),r=se("user").find(n=>n.name===t)?.manifest.description??t;return{provider:t,status:"available",major:a.major,display_name:r,transport:a.transport.kind,groups:a.provider.groups.filter(n=>G(e,`${t}:${n.group}`)).map(n=>({group:n.group,leaves:c.filter(({leaf:d})=>d.group===n.group).map(({tool:d,leaf:p})=>({tool:d,description:p.description,summary:p.summary,params:p.params,output:p.output}))})),commands:a.commands.roots.flatMap(n=>{const d=Y(n,t,e);return d?[d]:[]})}}m(cr,"providerTools");function Me(e){return ye(e.headers["crtr-receipt"],e)}m(Me,"onProviderResponse");function Oe(e){if(e.class==="person"){const t=ne()?.sub;if(!t)throw new l("grant_revoked","This machine has no active Northlight grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:t,grantee:t}}if(!e.sub||!e.grantee)throw new l("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}m(Oe,"identity");function Te(e,t){return[`${e.id}:${t.group}`]}m(Te,"callScope");function Ie(e,t){const a=X(e.commands).find(c=>c.tool===t);if(!a)throw new l("not_found",`tool "${t}" is not in provider "${e.id}"`,{param:"tool"});if(!a.leaf.group)throw new l("not_found",`tool "${t}" has no group`,{param:"tool"});return a.leaf}m(Ie,"leafFor");function L(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t=e.error;return!t||typeof t!="object"||Array.isArray(t)?{}:t}m(L,"errorBody");function Ue(e){const t=Array.isArray(e)?e[0]:e;if(!t)return;const a=Number(t);if(Number.isFinite(a)&&a>=0)return a;const c=Date.parse(t);return Number.isFinite(c)?Math.max(0,Math.ceil((c-Date.now())/1e3)):void 0}m(Ue,"retryAfter");function M(e,t,a,c=!1){const r=L(t),n=typeof r.code=="string"?r.code:void 0,d=typeof r.message=="string"?r.message:`provider answered HTTP ${e}`,p=typeof r.field=="string"?r.field:void 0,g={origin:"provider",details:{code:n??null,message:d,...p?{field:p}:{},...typeof r.next=="string"?{next:r.next}:{},...r.received!==void 0?{received:r.received}:{},status:e},...p?{param:p}:{}};return e===429?new l("provider_rate_limited",d,{...g,retry_after_s:Ue(a["retry-after"])}):e===401||e>=500?new l("provider_unavailable",d,g):e===403&&n==="provider_refused"?new l("provider_refused",d,g):e===403&&n==="approval_required"?new l("provider_error",c?"provider asked for approval again":"provider approval question is invalid",{...g,status:e}):new l("provider_error",d,{...g,status:e})}m(M,"mapProviderResponse");function Z(e,t){return JSON.stringify({major:e.major,endpoint:e.transport.kind==="http"?e.transport.endpoint:null,baseUrl:e.commands.baseUrl??null,rest:t.rest,params:t.params,output:t.output})}m(Z,"approvalShape");function Be(e){const t=e.origin!==void 0?W({node:e.origin.node??void 0,run:e.origin.run??void 0,machine:e.origin.machine}):W({node:e.node_id??void 0,run:e.run_id??void 0,profile:e.node_id?De(e.node_id)?.profile_id??void 0:void 0,machine:Se()!==null?Ne():void 0});return t===""?{}:{"Crtr-Caller":t}}m(Be,"crtrCallerHeader");async function fr(e,t,a){const c=ae(t.provider);if(c===null)throw new l("not_found",`provider "${t.provider}" is not available`,{param:"provider"});const r={...t,provider:c},n=new AbortController,d=m(()=>n.abort(a.signal.reason),"abort");a.signal.aborted?d():a.signal.addEventListener("abort",d,{once:!0});let p=a.requestId;const _=ge({request_id:p,run_id:e.run_id,node_id:e.node_id,controller:n});let g,j,q,i,h,v,y,P="allowed";try{try{await de(r.provider)}catch{throw le(r.provider)}let o=await B(r.provider,r.version),A=!1,s,w,k,C=!1,J=!1,O,H=0;for(;;){const b=Ie(o,r.tool);if(q=`${o.id}:${b.group}`,oe(e,o.id,b.group),o.transport.kind!=="machine"&&r.machine!==void 0)throw new l("invalid_argument",`provider "${o.id}" is not served by a device, so it takes no machine`,{param:"machine"});o.transport.kind==="machine"&&O===void 0&&(O=await je(r.machine,void 0,{newest:K(o.major,r.tool)}),H=Date.now()+(o.commands.timeouts?.requestMs??qe));let T=null;if(r.approval&&!C){const u=ce(e,r,Z(o,b));k=u.option,T=u.prepared,y=r.approval,p=u.requestId,C=!0}const R={params:b.params.filter(u=>"type"in u&&u.type==="file").map(u=>u.name),output:b.output.find(u=>u.type==="file")?.name??null},x=JSON.stringify(R);if(w!==void 0&&w!==x)throw new l("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});w=x,T&&(s=await z(e,R,r.arguments,T,{signal:n.signal})),s??=await pe(e,R,r.arguments,{signal:n.signal});const{sub:I,grantee:S}=Oe(e);if(o.transport.kind==="machine"){const u=await Le(e,o,r,b,s,{sub:I,requestId:p,deadlineAt:H,signal:n.signal,machine:O});return v=u.responder,g=u.cleanup,{status:200,body:u.result}}const $=await me({audience:o.transport.endpoint,sub:I,grantee:S,initiating_app:S,scope:Te(o,b)},{signal:n.signal});i=$.jti;const ee={registration:{name:o.id,endpoint:o.transport.endpoint},...o.commands.baseUrl?{baseUrl:o.commands.baseUrl}:{},...o.commands.timeouts?{timeouts:o.commands.timeouts}:{},params:b.params,rest:b.rest,output:b.output,commandPath:[o.id,...r.tool.split(" ")]},re=he(ee,s.arguments,void 0,{authorization:`Bearer ${$.access_token}`,requestId:p,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...Be(e),...k?{"Crtr-Approval":k}:{},...s.headers}});j??=Date.now()+(o.commands.timeouts?.requestMs??3e4);let f;try{if(f=await we(re,{connectMs:o.commands.timeouts?.connectMs,requestMs:o.commands.timeouts?.requestMs,deadlineAt:j,signal:n.signal}),n.signal.aborted)throw n.signal.reason??new Error("provider call aborted")}catch(u){throw n.signal.aborted?n.signal.reason??u:u instanceof be?new l("provider_unavailable",u.message,{origin:"provider",details:{status:null,code:u.kind}}):u}if(h??=Me({provider:o.id,tool:r.tool,request_id:p,run_id:e.run_id,sub:I,grantee:S,payer:$.payer,ledger_grantee:e.class==="person"?ie:S,jti:$.jti,status:f.status,headers:f.headers}),v=f.headers["crtr-responder"],f.status===401&&ve($.key),f.status>=200&&f.status<300){const u=f.body;if(!u||typeof u!="object"||Array.isArray(u))throw new l("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:f.status,message:"provider result is not a JSON object"}});let D;try{D=await Q(e,s,u,{signal:n.signal})}catch(E){throw E instanceof l&&E.code==="provider_error"?new l("provider_bad_response",E.message,{origin:"provider",details:{status:f.status,message:E.message}}):E}g=D.cleanup;const F=U(b.output,D.result);if(F)throw new l("provider_bad_response",`provider result has an invalid output field "${F.field}"`,{origin:"provider",param:F.field,details:{status:f.status,...F}});if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");return{status:200,body:D.result}}const N=L(f.body);if(f.status===403&&N.code==="approval_required"&&!k&&!J){J=!0;const u=await fe(e,r,p,N.question,n.signal,Z(o,b),s);if(y=u.questionId,k=u.option,s=await z(e,R,r.arguments,s,{signal:n.signal}),j=void 0,n.signal.aborted)throw new l("user_denied","the approval wait was canceled");continue}if(f.status===403&&N.code==="approval_required"&&k)throw M(f.status,f.body,f.headers,!0);if(f.status!==401&&N.manifest_stale===!0&&!A){A=!0,await ue(o.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),o=await B(o.id,r.version);continue}throw M(f.status,f.body,f.headers,!!k)}}catch(o){throw o instanceof l&&o.question_id&&(y=o.question_id),v===void 0&&o instanceof l&&typeof o.responder=="string"&&(v=o.responder),P=`refused:${o instanceof l?o.code:"server_error"}`,g?.(),o}finally{try{_e({request_id:p,caller:e,provider:r.provider,tool:r.tool,scope_checked:q,outcome:P,jti:i,receipt_id:h,responder:v,question_id:y})}finally{_()}a.signal.removeEventListener("abort",d)}}m(fr,"callProvider");async function Le(e,t,a,c,r,n){const{machine:d}=n,p=Fe(e,n.sub),_={machine:d,envelope:p,signal:n.signal,deadlineAt:n.deadlineAt,mapFailure:M};if(K(t.major,a.tool)){const s=await $e(_,r.arguments,t.major),w=U(c.output,s);if(w)throw new l("provider_bad_response",`the libraries result has an invalid output field "${w.field}"`,{origin:"provider",param:w.field,details:{status:200,...w}});return{result:s,responder:void 0,cleanup:void 0}}let g=r.arguments,j=null,q="";if(Pe(t.major,a.tool,r.arguments)){const s=await Ee(_,r.arguments,t.major);if(n.deadlineAt-Date.now()<=0)throw V(d,"timeout","");s!==null&&(g=s.arguments,j=s.services,q=s.release)}const i=await Ae(d,{tool:a.tool,arguments:g,version:t.major,requestId:n.requestId,runId:e.run_id,deadlineAt:n.deadlineAt,upload:r.upload===null?null:{id:r.upload.upload_id,link:r.upload.upload_link}},p,n.signal);if(n.signal.aborted)throw n.signal.reason??new Error("provider call aborted");if("unreachable"in i)throw V(d,i.unreachable.reason,i.unreachable.message);const h=i.headers["crtr-responder"],v=L(i.body);if(i.status<200||i.status>=300){const s=ke(d,i.status,typeof v.code=="string"?v.code:void 0,typeof v.message=="string"?v.message:void 0,i.body),w=s===null&&i.status>=500&&typeof v.code=="string"?new l("provider_error",typeof v.message=="string"?v.message:`the device answered HTTP ${i.status}`,{origin:"runtime",status:i.status,details:{code:v.code,status:i.status}}):null;throw Object.assign(s??w??M(i.status,i.body,i.headers),{responder:h})}const y=i.body&&typeof i.body=="object"&&!Array.isArray(i.body)?i.body.result:void 0;if(!y||typeof y!="object"||Array.isArray(y))throw Object.assign(new l("provider_bad_response","the device result is not a JSON object",{origin:"provider",details:{status:i.status,message:"the device result is not a JSON object"}}),{responder:h});let P=y;if(j!==null){const s=Ce(P,j);if(Re(d,p,q,s.calls),"error"in s)throw Object.assign(s.error,{responder:h});P=s.result}let o;try{o=await Q(e,r,P,{signal:n.signal})}catch(s){throw s instanceof l&&s.code==="provider_error"?Object.assign(new l("provider_bad_response",s.message,{origin:"provider",details:{status:i.status,message:s.message}}),{responder:h}):s}const A=U(c.output,o.result);if(A)throw o.cleanup?.(),Object.assign(new l("provider_bad_response",`the device result has an invalid output field "${A.field}"`,{origin:"provider",param:A.field,details:{status:i.status,...A}}),{responder:h});return{result:o.result,responder:h,cleanup:o.cleanup}}m(Le,"callDevice");export{fr as callProvider,Be as crtrCallerHeader,M as mapProviderResponse,Me as onProviderResponse,X as providerLeaves,cr as providerTools};
|
package/dist/core/secrets.d.ts
CHANGED
|
@@ -32,6 +32,17 @@ export declare function getApiToken(scope: Scope): string | undefined;
|
|
|
32
32
|
export declare function setApiToken(token: string, scope: Scope): void;
|
|
33
33
|
export declare function listScopedApiTokens(scope: Scope): ScopedApiToken[];
|
|
34
34
|
export declare function addScopedApiToken(entry: ScopedApiToken, scope: Scope): void;
|
|
35
|
+
/** The `crtr_app_` token minted for an installed plugin, if it holds one. */
|
|
36
|
+
export declare function pluginAppToken(plugin: string): ScopedApiToken | undefined;
|
|
37
|
+
/** Mint the plugin's `crtr_app_` token when absent; rewrite its `scopes` in place (same token) when they
|
|
38
|
+
* differ; otherwise write nothing. */
|
|
39
|
+
export declare function ensurePluginAppToken(plugin: string, scopes: readonly string[]): ScopedApiToken;
|
|
40
|
+
/** Remove the plugin's `crtr_app_` tokens. Entries minted by `crtr sys connect` are never touched. */
|
|
41
|
+
export declare function revokePluginAppTokens(plugin: string): boolean;
|
|
42
|
+
/** Look a raw scoped bearer up in the user store. Cached; the cache is dropped by every in-process
|
|
43
|
+
* `writeSecrets` and re-read when the file's (inode, size, mtime) changes, so a revocation by any
|
|
44
|
+
* process, including an atomic replace that keeps the mtime, is seen on the next lookup. */
|
|
45
|
+
export declare function findScopedApiToken(token: string): ScopedApiToken | undefined;
|
|
35
46
|
/** A short, non-reversible display fingerprint of a raw token — safe to print
|
|
36
47
|
* in `canvas config list`/`show` output. NEVER derive this from anything
|
|
37
48
|
* that could round-trip back to the token, and never log/return the raw
|
package/dist/core/secrets.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var h=Object.defineProperty;var o=(e,n)=>h(e,"name",{value:n,configurable:!0});import{createHash as E,randomBytes as R}from"node:crypto";import{existsSync as $,readFileSync as _,statSync as b}from"node:fs";import{join as w}from"node:path";import{scopeRoot as y,requireScopeRoot as P,userScopeRoot as g}from"./scope.js";import{secretsDir as A}from"./layout.js";import{atomicWriteJson as z,ensureDir as F}from"./fs-utils.js";const N="secrets.json";function T(e){return w(e===g()?A():e,N)}o(T,"secretsPathFor");function p(e){const n=y(e);if(!n)return{relayTokens:{}};const t=T(n);if(!$(t))return{relayTokens:{}};try{const r=JSON.parse(_(t,"utf8")),i=Array.isArray(r.scopedApiTokens)?r.scopedApiTokens.filter(s=>typeof s?.token=="string"&&s.token!==""&&typeof s.grantee=="string"&&/^app:[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(s.grantee)&&Array.isArray(s.scopes)&&typeof s.created=="string"):[];return{relayTokens:r.relayTokens??{},...typeof r.apiToken=="string"&&r.apiToken!==""?{apiToken:r.apiToken}:{},...i.length>0?{scopedApiTokens:i}:{}}}catch{return{relayTokens:{}}}}o(p,"readSecrets");function a(e,n){const t=P(e),r=T(t);F(t===g()?A():t),z(r,n,{mode:384,durable:!0}),u=void 0}o(a,"writeSecrets");function L(e,n){return p(n).relayTokens[e]}o(L,"getRelayToken");function W(e,n,t){const r=p(t);r.relayTokens[e]=n,a(t,r)}o(W,"setRelayToken");function q(e,n){const t=p(n);delete t.relayTokens[e],a(n,t)}o(q,"deleteRelayToken");function B(e){return p(e).apiToken}o(B,"getApiToken");function C(e,n){const t=p(n);t.apiToken=e,a(n,t)}o(C,"setApiToken");function m(e){return p(e).scopedApiTokens??[]}o(m,"listScopedApiTokens");function H(e,n){const t=p(n);t.scopedApiTokens=[...t.scopedApiTokens??[],e],a(n,t)}o(H,"addScopedApiToken");const O=/^app:[a-zA-Z0-9][a-zA-Z0-9._-]*$/,x="crtr_app_";function l(e){const n=`app:${e}`;if(!O.test(n))throw new Error(`invalid plugin name for an app token: ${JSON.stringify(e)}`);return n}o(l,"appGrantee");const d=o(e=>e.token.startsWith(x),"isAppEntry");function M(e){const n=l(e);return m("user").find(t=>t.grantee===n&&d(t))}o(M,"pluginAppToken");function X(e,n){const t=l(e),r=p("user"),i=r.scopedApiTokens??[],s=i.find(c=>c.grantee===t&&d(c)),k=[...n];if(s&&s.scopes.length===k.length&&s.scopes.every((c,S)=>c===k[S]))return s;const f=s?{...s,scopes:k}:{token:`${x}${R(32).toString("base64url")}`,grantee:t,scopes:k,created:new Date().toISOString()};return r.scopedApiTokens=s?i.map(c=>c===s?f:c):[...i,f],a("user",r),f}o(X,"ensurePluginAppToken");function Q(e){const n=l(e),t=p("user"),r=t.scopedApiTokens??[],i=r.filter(s=>!(s.grantee===n&&d(s)));return i.length===r.length?!1:(i.length>0?t.scopedApiTokens=i:delete t.scopedApiTokens,a("user",t),!0)}o(Q,"revokePluginAppTokens");let u;function Z(){const e=y("user");if(!e)return"none";try{const n=b(T(e),{bigint:!0});return`${n.ino}:${n.size}:${n.mtimeNs}`}catch{return"absent"}}o(Z,"secretsFileKey");function U(e){const n=Z();return(!u||u.key!==n)&&(u={key:n,byToken:new Map(m("user").map(t=>[t.token,t]))}),u.byToken.get(e)}o(U,"findScopedApiToken");function V(e){return E("sha256").update(e).digest("hex").slice(0,12)}o(V,"fingerprint");export{H as addScopedApiToken,q as deleteRelayToken,X as ensurePluginAppToken,U as findScopedApiToken,V as fingerprint,B as getApiToken,L as getRelayToken,m as listScopedApiTokens,M as pluginAppToken,p as readSecrets,Q as revokePluginAppTokens,C as setApiToken,W as setRelayToken,a as writeSecrets};
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
var
|
|
2
|
-
data: ${JSON.stringify(
|
|
1
|
+
var $=Object.defineProperty;var s=(e,n)=>$(e,"name",{value:n,configurable:!0});import{randomUUID as F}from"node:crypto";import{createServer as G}from"node:http";import{Router as J}from"./router.js";import{toErrorBody as Y}from"./map.js";import{objectRoutes as q}from"./handlers/objects.js";import{customObjectRoutes as z}from"./handlers/custom-objects.js";import{docRoutes as Q}from"./handlers/docs.js";import{OPERATIONS as W}from"./operations.js";import{errorCodes as L}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as K,RUNTIME_VERSION_HEADER as Z,minimumRuntimeRefusal as ee}from"../../api/runtime-version.js";import{CONTRACT_HEADER as te,CONTRACT_LEVEL as U,REQUIRED_CONTRACT_HEADER as re,contractRefusal as ne}from"../../api/contract.js";import{crtrVersion as H}from"../../shared/crtr-version.js";import{decodeJwt as ae}from"jose";import{AppPrincipalResolver as oe}from"./principal.js";import{runtimeOwner as se,servePeer as ie}from"../peer/ingress.js";import{applySyncNotice as ue}from"./sync-notice.js";import{ownMachineId as ce}from"../../core/peer/directory.js";import{handleRunOperation as de,integer as le}from"./handlers/runs.js";import{parseLlmRequest as fe,runLlm as me}from"../../core/llm/run.js";import{parseUsageMonth as pe,usageFor as he}from"../../core/usage/ledger.js";import{capFields as we}from"./handlers/llm.js";import{CONTROL_BODY_MAX_BYTES as ye,MODEL_CONTEXT_BODY_MAX_BYTES as _e}from"../../core/llm/body-limits.js";import{RunError as f,runIsRunning as ge}from"../../core/runs/operations.js";import{requireRun as k}from"./router.js";import{deferHead as ve}from"./deferred-head.js";import{readRunEvents as V,registerGrantStream as Re,subscribeRunEvents as Oe}from"../../core/runs/events.js";import{activeGrant as Ee}from"../../core/grants/mirror.js";import{releaseGrantWaiters as be}from"../../core/grants/watch.js";import{runNotices as Se}from"../run-notices.js";import{handleVendorSignIn as Ae,isVendorSignInPath as Te}from"./vendor-signin.js";import{listAppProfiles as Ne}from"./handlers/app-profiles.js";import{handleTransferDownload as Ce,handleTransferOperation as Pe,TRANSFER_OPERATIONS as Ie}from"./handlers/transfer.js";import{handleQuestionOperation as Me,QUESTION_OPERATIONS as He}from"./handlers/questions.js";import{vendorStatus as ke}from"./handlers/vendor-status.js";import{handleMemoryOperation as De,MEMORY_OPERATIONS as xe}from"./handlers/memory.js";import{abortOnClose as Be,callerFromApp as je,handleProviderOperation as Je,PROVIDER_OPERATIONS as Le,providerFailure as Ue}from"./handlers/providers.js";function Ve(e){return Object.prototype.hasOwnProperty.call(L,e)?L[e]:void 0}s(Ve,"fixedRow");function Xe(e){const n=Ve(e);return n?n.type:e==="stream_gap"||e==="provider_error"||e==="runtime_version_unsupported"?"invalid_request_error":"server_error"}s(Xe,"errorType");function $e(e){if(!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...o}=e;return o}s($e,"appDetails");function M(e,n,o){const l=$e(e.details);return{error:{code:e.code,message:e.message,...l===void 0?{}:{details:l},type:Xe(e.code),...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:n,...o?{run_id:o}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...we(e),...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(M,"envelope");function Fe(e,n,o,l){const i=n instanceof f?n:new f(500,"server_failure","runtime request failed");if(n instanceof f||console.error("[app-listener]",n),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
|
|
2
|
+
data: ${JSON.stringify(M(i,o,l))}
|
|
3
3
|
|
|
4
|
-
`);return}e.writeHead(i.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":
|
|
4
|
+
`);return}e.writeHead(i.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":o,"X-Content-Type-Options":"nosniff",...i.code==="all_routes_exhausted"?{"Retry-After":String(i.details.retry_after_s)}:{}}),e.end(JSON.stringify(M(i,o,l)))}s(Fe,"fail");function X(e){return Array.isArray(e)?e[0]:e}s(X,"headerValue");function Ge(e){if(!e?.startsWith("Bearer ")||e.length<=7)return!1;try{return typeof ae(e.slice(7)).machine_id=="string"}catch{return!1}}s(Ge,"isMachineBearer");function O(e,n,o){e.writeHead(n,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(o))}s(O,"json");async function P(e,n=ye){const o=[];let l=0;for await(const i of e){if(l+=i.length,l>n)throw new f(400,"invalid_request","request body too large");o.push(i)}if(l)try{return JSON.parse(Buffer.concat(o).toString("utf8"))}catch{throw new f(400,"invalid_request","invalid JSON body")}}s(P,"bodyOf");function Ye(e,n){const o=e.headers["last-event-id"]??n.get("after");if(Array.isArray(o))throw new f(400,"invalid_request","invalid cursor","after");return le(o??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(Ye,"cursor");function qe(e){return`id: ${e.sequence_number}
|
|
5
5
|
event: ${e.type}
|
|
6
6
|
data: ${JSON.stringify(e)}
|
|
7
7
|
|
|
8
|
-
`}s(
|
|
8
|
+
`}s(qe,"frame");const ze=6e4;function Qe(e,n,o,l,i,g,E,A={}){k(l,o);let _=V(o,i);if(i>_.latest_sequence)throw new f(400,"invalid_request","cursor is beyond the run","after");if(i<_.latest_sequence&&(_.earliest_sequence===null||i<_.earliest_sequence-1))throw new f(409,"stream_gap","events have expired",void 0,{earliest_sequence:_.earliest_sequence,latest_sequence:_.latest_sequence});if(n.destroyed||e.destroyed)return n.destroy(),Promise.resolve();n.writeHead(200,{"Content-Type":E==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),E==="sse"?n.write(`: connected
|
|
9
9
|
|
|
10
|
-
`):n.flushHeaders();let
|
|
11
|
-
data: ${JSON.stringify(
|
|
10
|
+
`):n.flushHeaders();let w=i;const D=Se().streamOpened(o);let r=!1,t=!1,p=!1,b;const T=new Promise(h=>{b=h}),y=s(()=>{r||(r=!0,D(w),clearInterval(d),clearInterval(B),u(),S(),n.off("close",y),n.off("drain",m),n.end(),b())},"end"),m=s(()=>{p=!1,a()},"onDrain"),v=s(h=>{r||n.write(E==="sse"?`event: error
|
|
11
|
+
data: ${JSON.stringify(M(h,g,o))}
|
|
12
12
|
|
|
13
|
-
`:`${JSON.stringify({type:"error",...
|
|
14
|
-
`),
|
|
15
|
-
`),
|
|
13
|
+
`:`${JSON.stringify({type:"error",...M(h,g,o)})}
|
|
14
|
+
`),y()},"error"),a=s(()=>{if(!(r||t)){t=!0;try{if(!I()){c();return}if(k(l,o),_=V(o,w),_.backlog_count>1e3||_.backlog_bytes>32*1024*1024){v(new f(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:w}));return}for(const h of _.events){if(p)break;p=!n.write(E==="sse"?qe(h):`${JSON.stringify(h)}
|
|
15
|
+
`),w=h.sequence_number}}catch(h){v(h instanceof f?h:new f(500,"server_failure","stream failed"))}finally{t=!1}}},"flush"),c=s(()=>v(new f(401,"grant_revoked","grant is not active")),"revoked"),I=s(()=>"class"in l||Ee(l.grant)!==null,"grantActive"),u=Oe(o,a),S="class"in l?()=>{}:Re(l.grant,c),d=setInterval(()=>{!r&&!p&&E==="sse"&&n.write(`: keepalive
|
|
16
16
|
|
|
17
|
-
`)},15e3),
|
|
17
|
+
`)},15e3),R=A.idleMs??ze,N=A.isRunning??ge;let C=Date.now(),x=w;const B=setInterval(()=>{if(r||E!=="sse")return;const h=Date.now();let j=!0;try{j=N(o)}catch{}if(j||w!==x){C=h,x=w;return}h-C<R||p||(n.write(`event: idle
|
|
18
18
|
data: {}
|
|
19
19
|
|
|
20
|
-
`),
|
|
20
|
+
`),y())},A.idleCheckMs??1e3);return B.unref(),n.on("drain",m),n.on("close",y),a(),T}s(Qe,"streamRunEvents");function kt(e,n,o,l={}){const i=e.listener;if(!e.issuer||!e.audience||!i?.address||!Number.isInteger(i.port)||i.port<1||i.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(i.address))throw new Error("runtime listener must bind a specific address");const g=new oe(e.issuer,e.audience,o,e.jwksUrl),E=new J,A=new Map;for(const r of W.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");E.register({method:r.method,pattern:r.path,handler:t}),A.set(t,r)}const _=new J().registerAll(q).registerAll(z).registerAll(Q),w=G((r,t)=>{(async()=>{ve(r,t,l.deferHead);const p=F();t.setHeader("Request-Id",p),t.setHeader(Z,H()),t.setHeader(te,U);let b;try{const T=X(r.headers[K.toLowerCase()]),y=ee(T,H())??ne(X(r.headers[re.toLowerCase()]),U,{legacyMinimum:T,runtimeVersion:H()});if(y!==null)throw y.status===400?new f(400,y.code,y.message,y.param):new f(426,y.code,y.message,void 0,y.details);const m=new URL(r.url??"/","http://runtime.local");if(Te(m.pathname)){await Ae(r,t,m,g);return}if(Ge(r.headers.authorization)){await ie(r,t,{issuer:e.issuer,jwks:g.jwks,audience:s(()=>e.audience,"audience"),owner:se,self:ce,ready:s(()=>g.isReady()&&n(),"ready")});return}if((r.method!=="GET"||m.pathname!=="/v1/health")&&(!g.isReady()||!n()))throw new f(503,"runtime_starting","runtime_starting");const v=E.match(r.method??"GET",m.pathname);if(!v)throw new f(404,"not_found","unknown operation");const a=A.get(v.handler);if(!a)throw new f(404,"not_found","unknown operation");if(b=v.params.run_id,a.name==="health"){O(t,200,{status:"ok",api_version:"2026-09-23"});return}if(a.name==="runtime.sync-notice"){await ue(await g.verifySyncNotice(r.headers.authorization),o,p),t.writeHead(204),t.end();return}if(a.name==="runtime.vendor-status"){await g.verifyDirectoryRead(r.headers.authorization,"vendor_status"),O(t,200,ke());return}const c=await g.resolve(r.headers.authorization,a.scope);if(t.setHeader("Request-Id",p),b&&k(c,b,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(a.name),a.name==="run.delete"),a.stage==="objects"){const u=_.match(a.method,m.pathname);if(!u)throw new f(404,"not_found","unknown operation");const S={method:a.method,path:m.pathname,params:u.params,query:m.searchParams,body:await P(r,u.maxBodyBytes),ceiling:c.resolved,principal:{class:"app",app:c.grantee,scopes:c.resolved},req:r,res:t};let d;try{d=await u.handler(S)}catch(R){const{status:N,body:C}=Y(R);throw N>=500&&console.error("[app-listener]",R),new f(N,C.error.code,C.error.message,void 0,C.error.details)}if(t.headersSent)return;if(d.rawBody!==void 0||d.status===204||d.body===void 0){t.writeHead(d.status,d.headers??{}),t.end(d.rawBody);return}t.writeHead(d.status,{"Content-Type":"application/json; charset=utf-8",...d.headers??{}}),t.end(JSON.stringify(d.body));return}if(a.stream==="bytes"){await Ce(r,t,c,m.searchParams);return}if(a.stream){Qe(r,t,b,c,Ye(r,m.searchParams),p,"sse",l.stream);return}if(a.name==="llm"){const u=fe(await P(r,_e)),S=new AbortController,d=s(()=>S.abort(),"abort");t.on("close",d);try{const R=await me(u,S.signal,u.stream?N=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(N)}
|
|
21
21
|
|
|
22
|
-
`)}:void 0,void 0,{grantee:c.grantee,spend_cap:c.spend_cap});
|
|
22
|
+
`)}:void 0,void 0,{grantee:c.grantee,spend_cap:c.spend_cap});u.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
|
|
23
23
|
|
|
24
|
-
`)):
|
|
24
|
+
`)):O(t,200,R.completion)}finally{t.off("close",d)}return}if(a.name==="usage.get"){O(t,200,he(c.grantee,c.spend_cap,pe(m.searchParams.get("month"))));return}if(a.name==="profile.list"){O(t,200,Ne(c));return}if(He.has(a.name)){const u=await Me(a.name,c,v.params.question_id,a.method==="POST"?await P(r):void 0);O(t,u.status,u.body);return}if(Le.has(a.name)){const{signal:u,release:S}=Be(t);try{const d=await Je(a.name,je(c),{params:v.params,body:a.method==="POST"?await P(r):void 0,query:m.searchParams},{requestId:p,signal:u});O(t,d.status,d.body)}catch(d){const R=Ue(d,p,!0);t.writeHead(R.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...R.headers}),t.end(JSON.stringify(R.body))}finally{S()}return}if(xe.has(a.name)){const u=De(a.name,{class:"app",app:c.grantee,scopes:c.resolved},m.searchParams);O(t,u.status,u.body);return}if(Ie.has(a.name)){const u=await Pe(a,c,v.params,m.searchParams,await P(r));O(t,u.status,u.body);return}const I=await de(a,c,b,m.searchParams,await P(r),r.headers["idempotency-key"]);O(t,I.status,I.body)}catch(T){Fe(t,T,p,b)}})()});return{ready:new Promise((r,t)=>{w.once("error",t),w.listen(i.port,i.address,()=>{w.off("error",t),r()})}),close:s(async()=>{g.close(),be(),w.closeAllConnections(),await new Promise((r,t)=>w.close(p=>p?t(p):r()))},"close")}}s(kt,"createAppListener");export{ze as RUN_STREAM_IDLE_MS,kt as createAppListener,Ye as cursor,Qe as streamRunEvents};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),
|
|
3
|
-
`)},15e3),
|
|
1
|
+
var A=Object.defineProperty;var i=(r,e)=>A(r,"name",{value:e,configurable:!0});import{prepared as p}from"../../../core/sqlite-statements.js";import{ApiError as a}from"../../../api/errors.js";import{openDb as F,withCanvasWrite as m}from"../../../core/canvas/db.js";import{registerCustomObject as $,publishCustom as L,unregisterCustomObject as P,pendingFor as H,acknowledge as I,verifyPublishToken as k,mayActAs as b,onQueueSignal as W,customReachable as E}from"../../../core/graph/custom.js";import{getById as R,getByRowid as g}from"../../../core/graph/objects.js";import{readerFor as M}from"../../../core/graph/reader.js";import{resolveName as N}from"../../../core/graph/names.js";import{stubOf as D}from"../../../core/peer/stubs.js";import{PRIVACY_LEVELS_DTO as z}from"../../../api/dto/objects.js";import{receiverForObject as B}from"../../../core/graph/receivers.js";import{readerFor as v,resolveRef as Q}from"../reader.js";import{isRecord as d}from"./validate.js";import{receiverEndpoint as V,resolveReceiverAddress as G}from"../../../core/graph/receiver-url.js";const l=i(r=>{throw new a(400,"invalid_request",`invalid ${r}`)},"bad"),f=i(()=>{throw new a(404,"not_found","custom object not found")},"missing");function h(r,e){const t=R(r,e)??(()=>{const o=M(r,{class:"person"},()=>[]),s=N(r,o,e);return"object"in s?s.object:null})();return!t||!t.type.includes(".")||t.deleted_at!==null?f():t}i(h,"customObjectRef");function J(r,e,n){if(B(r,n)==="argv"&&(e.publishToken||e.principal.class!=="person"&&e.principal.class!=="broker"))throw new a(403,"argv_forbidden","argv object requires a person or node principal")}i(J,"fenceArgv");function w(r,e,n,t=!1){if(e.publishToken){if(k(r,n,e.publishToken))return n;if(t)return f();throw new a(401,"invalid_publish_token","invalid publish token")}if(e.principal.class==="anonymous")return f();const o=v(r,e),s=n.owner===null?null:g(r,n.owner);return!s||!b(r,o,s)||!E(r,o,n)?f():o.class==="person"?o.person:o.class==="app"?o.app:o.class==="node"?o.node:o.object}i(w,"authorizeCustom");async function U(r){const e=r.body;if((!d(e)||typeof e.kind!="string"||typeof e.name!="string"||typeof e.description!="string"||e.events!==void 0&&!d(e.events)||e.owner!==void 0&&typeof e.owner!="string"||e.rotate_token!==void 0&&typeof e.rotate_token!="boolean"||e.privacy!==void 0&&!z.includes(e.privacy))&&l("register request"),d(e.receiver)&&"url"in e.receiver&&typeof e.receiver.url=="string")try{await G(V(e.receiver.url))}catch(n){throw new a(400,"invalid_receiver",`invalid receiver URL: ${String(n)}`)}return m(n=>{const t=v(n,r),o=e.owner===void 0?t.class==="person"?t.person:t.app:(()=>{const u=Q(n,t,e.owner);return"deleted"in u||"remote"in u?f():u})();["person","app","repo","profile"].includes(o.type)||l("owner");const s=$(n,t,{kind:e.kind,name:e.name,owner:o,description:e.description,...e.events===void 0?{}:{events:e.events},...e.receiver===void 0?{}:{receiver:e.receiver},rotateToken:e.rotate_token,...e.privacy===void 0?{}:{privacy:e.privacy}});return{id:s.object.id,name:s.object.name,...s.token?{token:s.token}:{}}})}i(U,"register");function Y(r,e,n){const t=p(r,"SELECT owner, source, declared FROM object_kinds WHERE name = ?").get(n.type);if(!t||t.source==="register")return;if(t.source==="manifest"&&t.declared===0)throw new a(409,"kind_inert",`${n.type} is no longer declared by an installed plugin`);if(e.publishToken)return;const o=g(r,t.owner);if(!o||!b(r,v(r,e),o))throw new a(403,"forbidden",`only ${o?.name??"the kind owner"} may publish ${n.type}`)}i(Y,"requirePublisher");function K(r){const e=r.body;return(!d(e)||typeof e.event!="string"||typeof e.summary!="string"||e.cause!==void 0&&typeof e.cause!="string")&&l("publish request"),m(n=>{const t=h(n,r.params.ref),o=D(n,t);if(o!==null)throw new a(404,"not_here",`${t.id} is not homed on this machine (it is a stub of ${o.home_machine})`,{id:t.id,home_machine:o.home_machine});const s=w(n,r,t);return Y(n,r,t),L(n,t,s,e)})}i(K,"publish");function X(r){m(e=>{const n=h(e,r.params.ref),t=w(e,r,n);J(e,r,n),P(e,n,t)})}i(X,"unregister");function Z(r){const e=r.body;return(!d(e)||!Array.isArray(e.delivery_ids)||e.delivery_ids.some(n=>typeof n!="string"))&&l("delivery_ids"),m(n=>{const t=h(n,r.params.ref);return w(n,r,t,!0),T(n,t),I(n,t,e.delivery_ids)})}i(Z,"ack");function T(r,e){if(p(r,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(e.rowid)?.receiver_json!=='"queue"')throw new a(409,"not_a_queue","object does not have a queue receiver")}i(T,"requireQueue");function x(r){const e=r.query.get("after");e!==null&&(!/^\d+$/.test(e)||!Number.isSafeInteger(Number(e)))&&l("after");const n=F(),t=h(n,r.params.ref);w(n,r,t,!0),T(n,t);const o=r.res;o.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache","x-accel-buffering":"no"});let s=-1;const u=i(()=>{const c=R(n,t.id);if(!c||c.deleted_at!==null)return!1;if(r.publishToken)return k(n,c,r.publishToken)&&p(n,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(c.rowid)?.receiver_json==='"queue"';if(r.principal.class==="anonymous")return!1;const _=c.owner===null?null:g(n,c.owner),j=v(n,r);return _!==null&&b(n,j,_)&&E(n,j,c)&&p(n,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(c.rowid)?.receiver_json==='"queue"'},"authorized"),y=i(()=>{if(!(o.destroyed||o.writableEnded)){if(!u()){o.end();return}for(const c of H(n,t,s))o.write(`${JSON.stringify(c)}
|
|
2
|
+
`),s=c.seq}},"flush"),q=W(t.id,y),S=setInterval(()=>{y(),!o.destroyed&&!o.writableEnded&&o.write(`
|
|
3
|
+
`)},15e3),C=i(()=>{clearInterval(S),q()},"close");return o.once("close",C),y(),{status:200}}i(x,"stream");const O=i(r=>e=>({status:200,body:r(e)}),"ok"),me=[{method:"POST",pattern:"/v1/custom-objects",handler:i(async r=>({status:200,body:await U(r)}),"handler")},{method:"POST",pattern:"/v1/custom-objects/:ref/events",handler:O(K)},{method:"DELETE",pattern:"/v1/custom-objects/:ref",handler:i(r=>(X(r),{status:204}),"handler")},{method:"GET",pattern:"/v1/custom-objects/:ref/deliveries",handler:x},{method:"POST",pattern:"/v1/custom-objects/:ref/deliveries/ack",handler:O(Z)}];export{w as authorizeCustom,h as customObjectRef,me as customObjectRoutes,J as fenceArgv};
|