@north-light/cli 0.3.483 → 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.
Files changed (92) hide show
  1. package/dist/api/client.d.ts +6 -0
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/custom-objects.d.ts +5 -1
  6. package/dist/api/dto/docs.d.ts +26 -0
  7. package/dist/api/dto/objects.d.ts +21 -1
  8. package/dist/api/dto/plugins.d.ts +22 -0
  9. package/dist/api/dto/plugins.js +0 -0
  10. package/dist/api/index.d.ts +1 -0
  11. package/dist/api/index.js +1 -1
  12. package/dist/api/routes.d.ts +2 -0
  13. package/dist/api/routes.js +1 -1
  14. package/dist/builtin-memory/crouter-plugin/deploying.md +107 -0
  15. package/dist/builtin-memory/crouter-sdk/release-notes.md +69 -0
  16. package/dist/clients/attach/viewer.js +579 -578
  17. package/dist/commands/pkg/plugin-manage.d.ts +4 -0
  18. package/dist/commands/pkg/plugin-manage.js +18 -16
  19. package/dist/core/canvas/migrations.js +27 -18
  20. package/dist/core/command-plugins/bundle.d.ts +4 -1
  21. package/dist/core/command-plugins/bundle.js +1 -1
  22. package/dist/core/command-plugins/caller-header.d.ts +10 -0
  23. package/dist/core/command-plugins/caller-header.js +1 -0
  24. package/dist/core/command-plugins/compose.js +1 -1
  25. package/dist/core/command-plugins/endpoint.d.ts +4 -0
  26. package/dist/core/command-plugins/endpoint.js +1 -1
  27. package/dist/core/command-plugins/transport/exec-invoke-async.d.ts +11 -0
  28. package/dist/core/command-plugins/transport/exec-invoke-async.js +1 -0
  29. package/dist/core/command-plugins/transport/exec-invoke.d.ts +14 -0
  30. package/dist/core/command-plugins/transport/exec-invoke.js +1 -1
  31. package/dist/core/command-plugins/transport/http-invoke.d.ts +9 -2
  32. package/dist/core/command-plugins/transport/http-invoke.js +3 -3
  33. package/dist/core/command-plugins/transport/http-plan.js +1 -1
  34. package/dist/core/command-plugins/transport/sign.d.ts +55 -0
  35. package/dist/core/command-plugins/transport/sign.js +3 -0
  36. package/dist/core/graph/access.d.ts +19 -0
  37. package/dist/core/graph/access.js +3 -3
  38. package/dist/core/graph/custom.d.ts +29 -9
  39. package/dist/core/graph/custom.js +9 -8
  40. package/dist/core/graph/events.js +8 -8
  41. package/dist/core/graph/package-kinds.d.ts +25 -0
  42. package/dist/core/graph/package-kinds.js +1 -0
  43. package/dist/core/graph/package-reconcile.d.ts +20 -0
  44. package/dist/core/graph/package-reconcile.js +1 -0
  45. package/dist/core/graph/reader.d.ts +5 -0
  46. package/dist/core/graph/reader.js +3 -3
  47. package/dist/core/graph/receivers.d.ts +2 -1
  48. package/dist/core/graph/types.d.ts +2 -0
  49. package/dist/core/graph/watches.d.ts +4 -0
  50. package/dist/core/graph/watches.js +9 -8
  51. package/dist/core/peer/directory.d.ts +2 -1
  52. package/dist/core/peer/directory.js +1 -1
  53. package/dist/core/peer/ingest.d.ts +6 -1
  54. package/dist/core/peer/ingest.js +1 -1
  55. package/dist/core/peer/names.d.ts +2 -0
  56. package/dist/core/peer/names.js +1 -1
  57. package/dist/core/peer/outbox-store.d.ts +2 -0
  58. package/dist/core/peer/receiver.js +1 -1
  59. package/dist/core/peer/stubs.d.ts +26 -1
  60. package/dist/core/peer/stubs.js +1 -1
  61. package/dist/core/plugin-app.d.ts +31 -0
  62. package/dist/core/plugin-app.js +1 -0
  63. package/dist/core/plugin-objects-manifest.d.ts +35 -0
  64. package/dist/core/plugin-objects-manifest.js +1 -0
  65. package/dist/core/providers/bundles.js +2 -2
  66. package/dist/core/providers/call.d.ts +3 -0
  67. package/dist/core/providers/call.js +1 -1
  68. package/dist/core/secrets.d.ts +11 -0
  69. package/dist/core/secrets.js +1 -1
  70. package/dist/daemon/api/app-listener.js +13 -13
  71. package/dist/daemon/api/handlers/custom-objects.js +3 -3
  72. package/dist/daemon/api/handlers/objects.js +3 -2
  73. package/dist/daemon/api/handlers/package-docs.js +1 -1
  74. package/dist/daemon/api/handlers/plugin-app-token.d.ts +6 -0
  75. package/dist/daemon/api/handlers/plugin-app-token.js +1 -0
  76. package/dist/daemon/api/handlers/providers.d.ts +7 -0
  77. package/dist/daemon/api/handlers/providers.js +1 -1
  78. package/dist/daemon/api/operations.js +1 -1
  79. package/dist/daemon/api/server.js +1 -1
  80. package/dist/daemon/crtrd.js +7 -5
  81. package/dist/daemon/peer/forward.d.ts +4 -1
  82. package/dist/daemon/peer/forward.js +1 -1
  83. package/dist/daemon/plugin-objects/read-leaf.d.ts +20 -0
  84. package/dist/daemon/plugin-objects/read-leaf.js +2 -0
  85. package/dist/types.d.ts +16 -0
  86. package/docs/plugin/deploying.md +107 -0
  87. package/docs/sdk/release-notes.md +69 -0
  88. package/package.json +2 -2
  89. package/packages/crouter-identity/dist/index.d.ts +1 -1
  90. package/packages/crouter-identity/dist/index.js +6 -2
  91. package/packages/crouter-identity/package.json +1 -1
  92. package/runtime.lock.json +12 -12
@@ -1 +1 @@
1
- var j=Object.defineProperty;var r=(n,e)=>j(n,"name",{value:e,configurable:!0});import{prepared as i}from"../sqlite-statements.js";import{createObject as b,ensureOwner as w,getById as m,getByRowid as f}from"../graph/objects.js";import{CrtrError as d}from"../errors.js";import{ExitCode as _}from"../../types.js";const R=r(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 h(n,e){const t=typeof e=="string"?m(n,e):e;if(t===null)return null;const o=i(n,"SELECT home_machine, envelope_json FROM peer_stubs WHERE object = ?").get(t.rowid);return o?{object:t,home_machine:o.home_machine,identity:o.envelope_json===null?null:JSON.parse(o.envelope_json)}:null}r(h,"stubOf");const g=r((n,e)=>i(n,"SELECT 1 FROM peer_stubs WHERE object = ?").get(e.rowid)!==void 0,"isStub");function E(n){return w(n,"person","local-user","user:local","user")}r(E,"localPerson");function S(n,e){return e==="user:local"?E(n):w(n,"app","app",e,e.replace(/^app:/,""))}r(S,"localApp");function T(n,e,t){if(e!==null)return e.app===null?E(n):S(n,e.app);const o=t?.owner;return o?.type==="app"?S(n,o.external_id??`app:${o.handle}`):E(n)}r(T,"ownerFor");function v(n,e,t,o,p,s){const l=p===void 0?null:R(p),u=m(n,e);if(u!==null){const a=h(n,u);if(a===null)throw new d("conflict",`${e} is a local ${u.type}, not a stub of ${o}`,_.GENERAL,{id:e});if(a.home_machine!==o)throw new d("conflict",`${e} is already a stub of ${a.home_machine}, not ${o}`,_.GENERAL,{id:e,home_machine:a.home_machine});return l!==null&&(i(n,"UPDATE peer_stubs SET envelope_json = ? WHERE object = ?").run(JSON.stringify(l),u.rowid),i(n,"UPDATE objects SET reception = 2 WHERE rowid = ?").run(u.rowid)),u}const y=T(n,l,s),c=b(n,{id:e,type:t,kind:s?.kind??null,owner:y.rowid,privacy:"public",name:s?.name??e,preview:s?.preview??""});return i(n,"UPDATE objects SET home_machine = ? WHERE rowid = ?").run(o,c.rowid),i(n,"INSERT INTO peer_stubs (object, home_machine, envelope_json, created_at) VALUES (?, ?, ?, ?)").run(c.rowid,o,l===null?null:JSON.stringify(l),new Date().toISOString()),l!==null&&i(n,"UPDATE objects SET reception = 2 WHERE rowid = ?").run(c.rowid),f(n,c.rowid)}r(v,"ensureStub");function D(n,e,t){const o=h(n,e);o===null||o.home_machine===t||(i(n,"UPDATE peer_stubs SET home_machine = ? WHERE object = ?").run(t,o.object.rowid),i(n,"UPDATE objects SET home_machine = ? WHERE rowid = ?").run(t,o.object.rowid))}r(D,"rehomeStub");function N(n,e){const t=e.owner===null?null:f(n,e.owner);let o=null;if(t!==null&&(t.type==="person"||t.type==="app"||t.type==="repo"||t.type==="profile")){const p=i(n,"SELECT scheme, external_id FROM identities WHERE object = ?").get(t.rowid);o={id:t.id,type:t.type,scheme:p?.scheme??null,external_id:p?.external_id??null,handle:t.name}}return{id:e.id,type:e.type,kind:e.kind,name:e.name,preview:e.preview,owner:o}}r(N,"describeObject");export{N as describeObject,v as ensureStub,R as identityOfEnvelope,g as isStub,D as rehomeStub,h as stubOf};
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{reconcilePackageDocuments as Z}from"../graph/package-docs.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 D(e){if(!K.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}i(D,"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 B(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(B,"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){B(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){D(e),h(e)===void 0&&await N(e,!1,!0)}i(Je,"ensureProviderBundle");async function Ke(e,n){return D(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){B(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){D(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 B(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};
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};
@@ -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
@@ -1 +1 @@
1
- var u=Object.defineProperty;var t=(e,o)=>u(e,"name",{value:o,configurable:!0});import{createHash as f}from"node:crypto";import{existsSync as y,readFileSync as d}from"node:fs";import{join as l}from"node:path";import{scopeRoot as A,requireScopeRoot as m,userScopeRoot as a}from"./scope.js";import{secretsDir as k}from"./layout.js";import{atomicWriteJson as x,ensureDir as g}from"./fs-utils.js";const S="secrets.json";function T(e){return l(e===a()?k():e,S)}t(T,"secretsPathFor");function s(e){const o=A(e);if(!o)return{relayTokens:{}};const r=T(o);if(!y(r))return{relayTokens:{}};try{const n=JSON.parse(d(r,"utf8")),c=Array.isArray(n.scopedApiTokens)?n.scopedApiTokens.filter(i=>typeof i?.token=="string"&&i.token!==""&&typeof i.grantee=="string"&&/^app:[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(i.grantee)&&Array.isArray(i.scopes)&&typeof i.created=="string"):[];return{relayTokens:n.relayTokens??{},...typeof n.apiToken=="string"&&n.apiToken!==""?{apiToken:n.apiToken}:{},...c.length>0?{scopedApiTokens:c}:{}}}catch{return{relayTokens:{}}}}t(s,"readSecrets");function p(e,o){const r=m(e),n=T(r);g(r===a()?k():r),x(n,o,{mode:384,durable:!0})}t(p,"writeSecrets");function J(e,o){return s(o).relayTokens[e]}t(J,"getRelayToken");function Z(e,o,r){const n=s(r);n.relayTokens[e]=o,p(r,n)}t(Z,"setRelayToken");function _(e,o){const r=s(o);delete r.relayTokens[e],p(o,r)}t(_,"deleteRelayToken");function b(e){return s(e).apiToken}t(b,"getApiToken");function q(e,o){const r=s(o);r.apiToken=e,p(o,r)}t(q,"setApiToken");function w(e){return s(e).scopedApiTokens??[]}t(w,"listScopedApiTokens");function C(e,o){const r=s(o);r.scopedApiTokens=[...r.scopedApiTokens??[],e],p(o,r)}t(C,"addScopedApiToken");function H(e){return f("sha256").update(e).digest("hex").slice(0,12)}t(H,"fingerprint");export{C as addScopedApiToken,_ as deleteRelayToken,H as fingerprint,b as getApiToken,J as getRelayToken,w as listScopedApiTokens,s as readSecrets,q as setApiToken,Z as setRelayToken,p as writeSecrets};
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 X=Object.defineProperty;var s=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as $}from"node:crypto";import{createServer as F}from"node:http";import{Router as q}from"./router.js";import{OPERATIONS as G}from"./operations.js";import{errorCodes as L}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as Y,RUNTIME_VERSION_HEADER as z,minimumRuntimeRefusal as Q}from"../../api/runtime-version.js";import{CONTRACT_HEADER as W,CONTRACT_LEVEL as U,REQUIRED_CONTRACT_HEADER as K,contractRefusal as Z}from"../../api/contract.js";import{crtrVersion as M}from"../../shared/crtr-version.js";import{decodeJwt as ee}from"jose";import{AppPrincipalResolver as te}from"./principal.js";import{runtimeOwner as ne,servePeer as re}from"../peer/ingress.js";import{applySyncNotice as ae}from"./sync-notice.js";import{ownMachineId as oe}from"../../core/peer/directory.js";import{handleRunOperation as se,integer as ie}from"./handlers/runs.js";import{parseLlmRequest as ue,runLlm as ce}from"../../core/llm/run.js";import{parseUsageMonth as de,usageFor as le}from"../../core/usage/ledger.js";import{capFields as fe}from"./handlers/llm.js";import{CONTROL_BODY_MAX_BYTES as me,MODEL_CONTEXT_BODY_MAX_BYTES as pe}from"../../core/llm/body-limits.js";import{RunError as d,runIsRunning as he}from"../../core/runs/operations.js";import{requireRun as H}from"./router.js";import{deferHead as _e}from"./deferred-head.js";import{readRunEvents as B,registerGrantStream as we,subscribeRunEvents as ye}from"../../core/runs/events.js";import{activeGrant as ve}from"../../core/grants/mirror.js";import{releaseGrantWaiters as ge}from"../../core/grants/watch.js";import{runNotices as Oe}from"../run-notices.js";import{handleVendorSignIn as Re,isVendorSignInPath as Ee}from"./vendor-signin.js";import{listAppProfiles as Se}from"./handlers/app-profiles.js";import{handleTransferDownload as Te,handleTransferOperation as Ae,TRANSFER_OPERATIONS as Ne}from"./handlers/transfer.js";import{handleQuestionOperation as be,QUESTION_OPERATIONS as Ce}from"./handlers/questions.js";import{vendorStatus as Ie}from"./handlers/vendor-status.js";import{handleMemoryOperation as Pe,MEMORY_OPERATIONS as Me}from"./handlers/memory.js";import{abortOnClose as He,callerFromApp as De,handleProviderOperation as ke,PROVIDER_OPERATIONS as xe,providerFailure as Je}from"./handlers/providers.js";function Le(e){return Object.prototype.hasOwnProperty.call(L,e)?L[e]:void 0}s(Le,"fixedRow");function Ue(e){const n=Le(e);return n?n.type:e==="stream_gap"||e==="provider_error"||e==="runtime_version_unsupported"?"invalid_request_error":"server_error"}s(Ue,"errorType");function Be(e){if(!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...a}=e;return a}s(Be,"appDetails");function I(e,n,a){const u=Be(e.details);return{error:{code:e.code,message:e.message,...u===void 0?{}:{details:u},type:Ue(e.code),...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:n,...a?{run_id:a}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...fe(e),...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(I,"envelope");function je(e,n,a,u){const i=n instanceof d?n:new d(500,"server_failure","runtime request failed");if(n instanceof d||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(I(i,a,u))}
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":a,"X-Content-Type-Options":"nosniff",...i.code==="all_routes_exhausted"?{"Retry-After":String(i.details.retry_after_s)}:{}}),e.end(JSON.stringify(I(i,a,u)))}s(je,"fail");function j(e){return Array.isArray(e)?e[0]:e}s(j,"headerValue");function Ve(e){if(!e?.startsWith("Bearer ")||e.length<=7)return!1;try{return typeof ee(e.slice(7)).machine_id=="string"}catch{return!1}}s(Ve,"isMachineBearer");function v(e,n,a){e.writeHead(n,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(a))}s(v,"json");async function C(e,n=me){const a=[];let u=0;for await(const i of e){if(u+=i.length,u>n)throw new d(400,"invalid_request","request body too large");a.push(i)}if(u)try{return JSON.parse(Buffer.concat(a).toString("utf8"))}catch{throw new d(400,"invalid_request","invalid JSON body")}}s(C,"bodyOf");function Xe(e,n){const a=e.headers["last-event-id"]??n.get("after");if(Array.isArray(a))throw new d(400,"invalid_request","invalid cursor","after");return ie(a??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(Xe,"cursor");function $e(e){return`id: ${e.sequence_number}
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($e,"frame");const Fe=6e4;function qe(e,n,a,u,i,w,g,A={}){H(u,a);let l=B(a,i);if(i>l.latest_sequence)throw new d(400,"invalid_request","cursor is beyond the run","after");if(i<l.latest_sequence&&(l.earliest_sequence===null||i<l.earliest_sequence-1))throw new d(409,"stream_gap","events have expired",void 0,{earliest_sequence:l.earliest_sequence,latest_sequence:l.latest_sequence});if(n.destroyed||e.destroyed)return n.destroy(),Promise.resolve();n.writeHead(200,{"Content-Type":g==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),g==="sse"?n.write(`: connected
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 E=i;const r=Oe().streamOpened(a);let t=!1,p=!1,_=!1,S;const y=new Promise(h=>{S=h}),f=s(()=>{t||(t=!0,r(E),clearInterval(T),clearInterval(x),N(),R(),n.off("close",f),n.off("drain",O),n.end(),S())},"end"),O=s(()=>{_=!1,c()},"onDrain"),o=s(h=>{t||n.write(g==="sse"?`event: error
11
- data: ${JSON.stringify(I(h,w,a))}
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",...I(h,w,a)})}
14
- `),f()},"error"),c=s(()=>{if(!(t||p)){p=!0;try{if(!m()){b();return}if(H(u,a),l=B(a,E),l.backlog_count>1e3||l.backlog_bytes>32*1024*1024){o(new d(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:E}));return}for(const h of l.events){if(_)break;_=!n.write(g==="sse"?$e(h):`${JSON.stringify(h)}
15
- `),E=h.sequence_number}}catch(h){o(h instanceof d?h:new d(500,"server_failure","stream failed"))}finally{p=!1}}},"flush"),b=s(()=>o(new d(401,"grant_revoked","grant is not active")),"revoked"),m=s(()=>"class"in u||ve(u.grant)!==null,"grantActive"),N=ye(a,c),R="class"in u?()=>{}:we(u.grant,b),T=setInterval(()=>{!t&&!_&&g==="sse"&&n.write(`: keepalive
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),P=A.idleMs??Fe,V=A.isRunning??he;let D=Date.now(),k=E;const x=setInterval(()=>{if(t||g!=="sse")return;const h=Date.now();let J=!0;try{J=V(a)}catch{}if(J||E!==k){D=h,k=E;return}h-D<P||_||(n.write(`event: idle
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
- `),f())},A.idleCheckMs??1e3);return x.unref(),n.on("drain",O),n.on("close",f),c(),y}s(qe,"streamRunEvents");function At(e,n,a,u={}){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 w=new te(e.issuer,e.audience,a,e.jwksUrl),g=new q,A=new Map;for(const r of G.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");g.register({method:r.method,pattern:r.path,handler:t}),A.set(t,r)}const l=F((r,t)=>{(async()=>{_e(r,t,u.deferHead);const p=$();t.setHeader("Request-Id",p),t.setHeader(z,M()),t.setHeader(W,U);let _;try{const S=j(r.headers[Y.toLowerCase()]),y=Q(S,M())??Z(j(r.headers[K.toLowerCase()]),U,{legacyMinimum:S,runtimeVersion:M()});if(y!==null)throw y.status===400?new d(400,y.code,y.message,y.param):new d(426,y.code,y.message,void 0,y.details);const f=new URL(r.url??"/","http://runtime.local");if(Ee(f.pathname)){await Re(r,t,f,w);return}if(Ve(r.headers.authorization)){await re(r,t,{issuer:e.issuer,jwks:w.jwks,audience:s(()=>e.audience,"audience"),owner:ne,self:oe,ready:s(()=>w.isReady()&&n(),"ready")});return}if((r.method!=="GET"||f.pathname!=="/v1/health")&&(!w.isReady()||!n()))throw new d(503,"runtime_starting","runtime_starting");const O=g.match(r.method??"GET",f.pathname);if(!O)throw new d(404,"not_found","unknown operation");const o=A.get(O.handler);if(!o)throw new d(404,"not_found","unknown operation");if(_=O.params.run_id,o.name==="health"){v(t,200,{status:"ok",api_version:"2026-09-23"});return}if(o.name==="runtime.sync-notice"){await ae(await w.verifySyncNotice(r.headers.authorization),a,p),t.writeHead(204),t.end();return}if(o.name==="runtime.vendor-status"){await w.verifyDirectoryRead(r.headers.authorization,"vendor_status"),v(t,200,Ie());return}const c=await w.resolve(r.headers.authorization,o.scope);if(t.setHeader("Request-Id",p),_&&H(c,_,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(o.name),o.name==="run.delete"),o.stream==="bytes"){await Te(r,t,c,f.searchParams);return}if(o.stream){qe(r,t,_,c,Xe(r,f.searchParams),p,"sse",u.stream);return}if(o.name==="llm"){const m=ue(await C(r,pe)),N=new AbortController,R=s(()=>N.abort(),"abort");t.on("close",R);try{const T=await ce(m,N.signal,m.stream?P=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(P)}
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});m.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
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
- `)):v(t,200,T.completion)}finally{t.off("close",R)}return}if(o.name==="usage.get"){v(t,200,le(c.grantee,c.spend_cap,de(f.searchParams.get("month"))));return}if(o.name==="profile.list"){v(t,200,Se(c));return}if(Ce.has(o.name)){const m=await be(o.name,c,O.params.question_id,o.method==="POST"?await C(r):void 0);v(t,m.status,m.body);return}if(xe.has(o.name)){const{signal:m,release:N}=He(t);try{const R=await ke(o.name,De(c),{params:O.params,body:o.method==="POST"?await C(r):void 0,query:f.searchParams},{requestId:p,signal:m});v(t,R.status,R.body)}catch(R){const T=Je(R,p,!0);t.writeHead(T.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...T.headers}),t.end(JSON.stringify(T.body))}finally{N()}return}if(Me.has(o.name)){const m=Pe(o.name,{class:"app",app:c.grantee,scopes:c.resolved},f.searchParams);v(t,m.status,m.body);return}if(Ne.has(o.name)){const m=await Ae(o,c,O.params,f.searchParams,await C(r));v(t,m.status,m.body);return}const b=await se(o,c,_,f.searchParams,await C(r),r.headers["idempotency-key"]);v(t,b.status,b.body)}catch(S){je(t,S,p,_)}})()});return{ready:new Promise((r,t)=>{l.once("error",t),l.listen(i.port,i.address,()=>{l.off("error",t),r()})}),close:s(async()=>{w.close(),ge(),l.closeAllConnections(),await new Promise((r,t)=>l.close(p=>p?t(p):r()))},"close")}}s(At,"createAppListener");export{Fe as RUN_STREAM_IDLE_MS,At as createAppListener,Xe as cursor,qe as streamRunEvents};
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 C=Object.defineProperty;var s=(r,e)=>C(r,"name",{value:e,configurable:!0});import{prepared as b}from"../../../core/sqlite-statements.js";import{ApiError as a}from"../../../api/errors.js";import{openDb as F,withCanvasWrite as p}from"../../../core/canvas/db.js";import{registerCustomObject as L,publishCustom as H,unregisterCustomObject as I,pendingFor as N,acknowledge as P,verifyPublishToken as j,mayActAs as _,onQueueSignal as W,customReachable as k}from"../../../core/graph/custom.js";import{getById as E,getByRowid as R}from"../../../core/graph/objects.js";import{readerFor as $}from"../../../core/graph/reader.js";import{resolveName as M}from"../../../core/graph/names.js";import{receiverForObject as z}from"../../../core/graph/receivers.js";import{readerFor as w,resolveRef as B}from"../reader.js";import{isRecord as l}from"./validate.js";import{receiverEndpoint as D,resolveReceiverAddress as Q}from"../../../core/graph/receiver-url.js";const d=s(r=>{throw new a(400,"invalid_request",`invalid ${r}`)},"bad"),f=s(()=>{throw new a(404,"not_found","custom object not found")},"missing");function m(r,e){const n=E(r,e)??(()=>{const o=$(r,{class:"person"},()=>[]),i=M(r,o,e);return"object"in i?i.object:null})();return!n||!n.type.includes(".")||n.deleted_at!==null?f():n}s(m,"customObjectRef");function G(r,e,t){if(z(r,t)==="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")}s(G,"fenceArgv");function v(r,e,t,n=!1){if(e.publishToken){if(j(r,t,e.publishToken))return t;if(n)return f();throw new a(401,"invalid_publish_token","invalid publish token")}if(e.principal.class==="anonymous")return f();const o=w(r,e),i=t.owner===null?null:R(r,t.owner);return!i||!_(r,o,i)||!k(r,o,t)?f():o.class==="person"?o.person:o.class==="app"?o.app:o.class==="node"?o.node:o.object}s(v,"authorizeCustom");async function J(r){const e=r.body;if((!l(e)||typeof e.kind!="string"||typeof e.name!="string"||typeof e.description!="string"||!l(e.events)||e.owner!==void 0&&typeof e.owner!="string"||e.rotate_token!==void 0&&typeof e.rotate_token!="boolean")&&d("register request"),l(e.receiver)&&"url"in e.receiver&&typeof e.receiver.url=="string")try{await Q(D(e.receiver.url))}catch(t){throw new a(400,"invalid_receiver",`invalid receiver URL: ${String(t)}`)}return p(t=>{const n=w(t,r),o=e.owner===void 0?n.class==="person"?n.person:n.app:(()=>{const u=B(t,n,e.owner);return"deleted"in u||"remote"in u?f():u})();["person","app","repo","profile"].includes(o.type)||d("owner");const i=L(t,n,{kind:e.kind,name:e.name,owner:o,description:e.description,events:e.events,...e.receiver===void 0?{}:{receiver:e.receiver},rotateToken:e.rotate_token});return{id:i.object.id,name:i.object.name,...i.token?{token:i.token}:{}}})}s(J,"register");function U(r){const e=r.body;return(!l(e)||typeof e.event!="string"||typeof e.summary!="string"||e.cause!==void 0&&typeof e.cause!="string")&&d("publish request"),p(t=>{const n=m(t,r.params.ref),o=v(t,r,n);return H(t,n,o,e)})}s(U,"publish");function K(r){p(e=>{const t=m(e,r.params.ref),n=v(e,r,t);G(e,r,t),I(e,t,n)})}s(K,"unregister");function V(r){const e=r.body;return(!l(e)||!Array.isArray(e.delivery_ids)||e.delivery_ids.some(t=>typeof t!="string"))&&d("delivery_ids"),p(t=>{const n=m(t,r.params.ref);return v(t,r,n,!0),T(t,n),P(t,n,e.delivery_ids)})}s(V,"ack");function T(r,e){if(b(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")}s(T,"requireQueue");function X(r){const e=r.query.get("after");e!==null&&(!/^\d+$/.test(e)||!Number.isSafeInteger(Number(e)))&&d("after");const t=F(),n=m(t,r.params.ref);v(t,r,n,!0),T(t,n);const o=r.res;o.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache","x-accel-buffering":"no"});let i=-1;const u=s(()=>{const c=E(t,n.id);if(!c||c.deleted_at!==null)return!1;if(r.publishToken)return j(t,c,r.publishToken)&&b(t,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(c.rowid)?.receiver_json==='"queue"';if(r.principal.class==="anonymous")return!1;const y=c.owner===null?null:R(t,c.owner),g=w(t,r);return y!==null&&_(t,g,y)&&k(t,g,c)&&b(t,"SELECT receiver_json FROM custom_objects WHERE object = ?").get(c.rowid)?.receiver_json==='"queue"'},"authorized"),h=s(()=>{if(!(o.destroyed||o.writableEnded)){if(!u()){o.end();return}for(const c of N(t,n,i))o.write(`${JSON.stringify(c)}
2
- `),i=c.seq}},"flush"),O=W(n.id,h),S=setInterval(()=>{h(),!o.destroyed&&!o.writableEnded&&o.write(`
3
- `)},15e3),A=s(()=>{clearInterval(S),O()},"close");return o.once("close",A),h(),{status:200}}s(X,"stream");const q=s(r=>e=>({status:200,body:r(e)}),"ok"),ue=[{method:"POST",pattern:"/v1/custom-objects",handler:s(async r=>({status:200,body:await J(r)}),"handler")},{method:"POST",pattern:"/v1/custom-objects/:ref/events",handler:q(U)},{method:"DELETE",pattern:"/v1/custom-objects/:ref",handler:s(r=>(K(r),{status:204}),"handler")},{method:"GET",pattern:"/v1/custom-objects/:ref/deliveries",handler:X},{method:"POST",pattern:"/v1/custom-objects/:ref/deliveries/ack",handler:q(V)}];export{v as authorizeCustom,m as customObjectRef,ue as customObjectRoutes,G as fenceArgv};
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};
@@ -1,2 +1,3 @@
1
- var we=Object.defineProperty;var u=(e,t)=>we(e,"name",{value:t,configurable:!0});import{prepared as ye}from"../../../core/sqlite-statements.js";import{ApiError as w}from"../../../api/errors.js";import{PRIVACY_LEVELS_DTO as be}from"../../../api/dto/objects.js";import{openDb as T,withCanvasWrite as y}from"../../../core/canvas/db.js";import{actorOf as V}from"../../../core/graph/documents.js";import{readerForObject as A}from"../../../core/graph/reader.js";import{canRead as ge}from"../../../core/graph/access.js";import{customObjectRef as H,authorizeCustom as B,fenceArgv as Z}from"./custom-objects.js";import{edgesOf as je}from"../../../core/graph/edges.js";import{publish as ke}from"../../../core/graph/events.js";import{objectDTO as b}from"../../../core/graph/kinds/dto.js";import{isKind as _e,kindNames as ve,kindOf as J}from"../../../core/graph/kinds/registry.js";import{listObjects as Ee,searchObjects as K}from"../../../core/graph/objects.js";import{checkoutsForCwd as Oe}from"../../../core/graph/repo-sync/repos.js";import{recordWatch as Q,unwatch as Y,watchersOf as qe,watchesOf as Re}from"../../../core/graph/watches.js";import{readerFor as g,resolveRef as L}from"../reader.js";import{invalidRequest as $,isRecord as k}from"./validate.js";import{callerNode as X,callerStub as N,forwardVerb as x,originates as ee,queryWithoutMachine as P,relayError as C,remoteStub as Te,unknownError as te,forwarding as S,withMachine as F}from"../../peer/forward.js";import{getById as ne}from"../../../core/graph/objects.js";import{isStub as re}from"../../../core/peer/stubs.js";import{isUnreachable as $e}from"../../../core/peer/client.js";import{normalizeMachineId as I}from"../../../core/peer/directory.js";const M=u(e=>t=>Oe(e,t),"reposFnFor");function oe(e,t){return{...b(e,t),...J(e,t.type).listItem?.(e,t)}}u(oe,"listItem");function Se(e){return`no object named ${e} on this runtime; it may be on another runtime, or the name may be misspelled`}u(Se,"remoteMessage");function ie(e,t,n){try{return R(e,t,n)}catch(r){if(r instanceof w&&r.code==="not_found"&&!r.details?.deleted)return null;throw r}}u(ie,"ownerFilter");function R(e,t,n){const r=L(e,t,n);if("remote"in r)throw new w(404,"not_found",Se(r.remote),{next:"Check the name with `crtr canvas search`."});if("deleted"in r)throw new w(404,"not_found",`${r.deleted.name} is deleted`,{id:r.deleted.id,deleted:!0});return r}u(R,"liveRef");const _=u(e=>decodeURIComponent(e.params.ref),"refParam"),se=u(e=>{const t=e.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),n=t.indexOf("/");return n>0?t.slice(0,n):t},"idOf");function ae(e){const t=ne(T(),se(e));return t!==null&&re(T(),t)?t.id:null}u(ae,"stubIdOf");async function W(e,t,n,r){const o=ae(t);return o!==null?x(e,o,n):r()?{kind:"local"}:ee(e.principal)?x(e,se(t),n):{kind:"local"}}u(W,"forwardRef");function D(e,t){return y(n=>{try{return!("remote"in L(n,g(n,t),e))}catch{return!1}})}u(D,"resolvesLocally");const G=u(e=>`/v1/objects/${encodeURIComponent(e)}`,"objectPath");function ue(e,t,n,r,o){const i=t?.object;!i?.id||!i.type||y(s=>{const a=X(s,e);if(a===null)return;const c=Te(s,i.id,i.type,n,{kind:i.kind??null,name:i.name??i.id,...i.preview===void 0?{}:{preview:i.preview},owner:i.owner??null});Q(s,a,c,r,o===void 0?{}:{patterns:o})})}u(ue,"watchRemote");async function Ie(e){const t=_(e),n=["1","true"].includes(e.query.get("fields")??""),r=await W(e,t,{method:"GET",path:G(t),query:P(e)},()=>D(t,e));if(r.kind==="unknown")return{state:"unknown",ref:t,unreachable:r.unreachable};if(r.kind==="remote"){if(r.status>=400)throw C(r.status,r.body);return ue(e,r.body,r.machine.id,"read"),F(r.body,r.machine)}return y(o=>{const i=N(o,e),s=i===null?g(o,e):A(o,i,M(o)),a=L(o,s,t);if("remote"in a)return{state:"remote",remote:a.remote};if("deleted"in a)return{state:"deleted",deleted:b(o,a.deleted)};const{data:c,text:f}=J(o,a.type).read(o,s,a,{fields:n,reposFn:M(o)});return{state:"object",type:a.type,object:b(o,a),text:f,data:c}})}u(Ie,"read");function z(e,t,n,r){if(e!=null){if(!n.includes(e)){r.push({field:t,problem:`must be one of ${n.join(", ")}`,received:e});return}return e}}u(z,"oneOf");function ce(e,t,n){if(t!=null){if(typeof t!="string"||!_e(e,t)){n.push({field:"type",problem:`must be a kind: one of ${ve(e).join(", ")}`,received:t});return}return t}}u(ce,"kindName");function le(e,t,n,r){if(e==null)return r;const o=Number(e);return!Number.isInteger(o)||o<1||o>100?(n.push({field:t,problem:"must be an integer from 1 to 100",received:e}),r):o}u(le,"limitOf");function fe(e){const t=e.query,n=[],r=ce(T(),t.get("type"),n),o=z(t.get("privacy"),"privacy",be,n),i=le(t.get("limit"),"limit",n,50),s=t.get("cursor");s!==null&&!/^\d+$/.test(s)&&n.push({field:"cursor",problem:"is not a cursor from a previous page",received:s});const a=t.get("deleted");return a!==null&&!["0","1","true","false"].includes(a)&&n.push({field:"deleted",problem:"must be true or false",received:a}),n.length>0&&$("object list request",n),y(c=>{const f=g(c,e),h={...r?{type:r}:{},...o?{privacy:o}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...a==="1"||a==="true"?{deleted:!0}:{}},p=t.get("owner");if(p!==null){const m=ie(c,f,p);if(m===null)return{items:[],next_cursor:null};h.owner=m}const v=t.get("name"),j=[];let d=s===null?0:Number(s),E=!0;for(;j.length<i&&E;){const m=Ee(c,f,h,{limit:100,after_rowid:d});E=m.length===100;for(const q of m)if(d=q.rowid,(v===null||q.name.startsWith(v))&&j.push(q),j.length===i)break}const l=j.at(-1);return{items:j.map(m=>oe(c,m)),next_cursor:j.length===i&&l?String(l.rowid):null}})}u(fe,"listLocal");const Ae={ms:3e4},Le="self",U=u((e,t)=>e.map(n=>({...n,machine:t})),"labelItems");async function de(e,t,n,r){const o=S.current;try{const i=await o.call(t.id,{method:"GET",path:"/v1/objects",query:n},o.envelope(e.principal),r);if($e(i))return{state:"unreachable",items:[],next:null};if(i.status===426)return{state:"too_old",items:[],next:null};const s=i.body;return i.status!==200||!Array.isArray(s?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:U(s.items,t),next:s.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}u(de,"remotePage");async function Ne(e){const t=new Promise(n=>e.addEventListener("abort",()=>n(null),{once:!0}));return Promise.race([S.current.machines().catch(()=>null),t])}u(Ne,"machinesWithin");async function Pe(e){const t=e.query.get("machine");if(!ee(e.principal)||!S.current.joined()||t===Le)return fe(me(e));const r=S.current.self(),o=t!==null&&I(t)!==r,i=o?null:fe(me(e)),s=new AbortController,a=setTimeout(()=>s.abort(),Ae.ms);a.unref?.();try{const c=await Ne(s.signal);if(c===null){if(o)throw new w(503,"runtime_unreachable",`The directory did not answer, so ${I(t)} cannot be listed.`,{machine:I(t)});return i}const f=u(l=>({id:l,name:c.find(m=>m.machine_id===l)?.name??l}),"labelOf"),h=f(r),p=c.filter(l=>l.machine_id!==r&&l.kind!=="device"),v=P(e);if(t!==null){if(!o)return{items:U(i.items,h),next_cursor:i.next_cursor,machines:[{machine_id:r,name:h.name,state:"ok",more:i.next_cursor!==null}]};const l=I(t);if(!p.some(q=>q.machine_id===l))throw new w(404,"not_found",`no machine ${l} on your account`,{machine:l});const m=await de(e,f(l),v,s.signal);return{items:m.items,next_cursor:m.next,machines:[{machine_id:l,name:f(l).name,state:m.state,more:m.next!==null}]}}const{cursor:j,...d}=v,E=await Promise.all(p.map(l=>de(e,f(l.machine_id),d,s.signal)));return{items:[...U(i.items,h),...E.flatMap(l=>l.items)],next_cursor:i.next_cursor,machines:[{machine_id:r,name:h.name,state:"ok",more:i.next_cursor!==null},...p.map((l,m)=>({machine_id:l.machine_id,name:l.name,state:E[m].state,more:E[m].next!==null}))]}}finally{clearTimeout(a)}}u(Pe,"list");function me(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}u(me,"withoutMachine");function Ce(e){const t=[],n=k(e.body)?e.body:{};k(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const r=n.text;(typeof r!="string"||r.trim()==="")&&t.push({field:"text",problem:"is required",received:r});const o=n.grep;if(o!==void 0)if(typeof o!="string")t.push({field:"grep",problem:"must be a regular expression string",received:o});else try{new RegExp(o)}catch(a){t.push({field:"grep",problem:`is not a valid regular expression: ${a.message}`,received:o})}n.body!==void 0&&typeof n.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:n.body});const i=ce(T(),n.type,t);n.kind!==void 0&&typeof n.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:n.kind}),n.owner!==void 0&&typeof n.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:n.owner});const s=le(n.limit,"limit",t,50);return t.length>0&&$("object search request",t),y(a=>{const c=g(a,e),f=typeof n.owner=="string"?ie(a,c,n.owner):null;if(typeof n.owner=="string"&&f===null)return{items:[]};const h=K(a,c,{text:r,body:n.body===!0||typeof o=="string"}),p=new Set(h.map(d=>d.object.rowid));return{items:(typeof o=="string"?K(a,c,{text:o,grep:!0}).filter(d=>p.has(d.object.rowid)):h).filter(d=>(!i||d.object.type===i)&&(n.kind===void 0||d.object.kind===n.kind)&&(!f||d.object.owner===f.rowid)).slice(0,s).map(d=>({object:oe(a,d.object),...d.line!==void 0?{matches:[{line:d.line,text:d.text??""}]}:{}}))}})}u(Ce,"search");const he=["interrupt","steer","after-turn","next-turn"],Fe=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function Me(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(n=>typeof n!="string"||!Fe.test(n)||n.includes(">")&&!n.endsWith(">"))){t.push({field:"patterns",problem:"must be a non-empty list of event patterns (dot-separated words, `*` for one word, a final `>` for the rest)",received:e});return}return e}}u(Me,"patternsOf");function We(e,t){if(e===void 0)return;if(!k(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const n=[];for(const[r,o]of Object.entries(e)){const i=Number(r);!Number.isInteger(i)||i<1||i>10?t.push({field:`config.${r}`,problem:"level must be an integer from 1 to 10"}):he.includes(o)?n.push({threshold:i,delivery:o}):t.push({field:`config.${r}`,problem:`delivery must be one of ${he.join(", ")}`,received:o})}return n.sort((r,o)=>o.threshold-r.threshold)}u(We,"configOf");function pe(e){const t=ae(e);if(t!==null)throw new w(400,"invalid_request",`${t} lives on another machine; an explicit watcher cannot watch it from here`,{id:t})}u(pe,"refuseStubWatch");async function De(e){const t=_(e);if(typeof(k(e.body)?e.body:{}).watcher=="string"&&pe(t),typeof(k(e.body)?e.body:{}).watcher!="string"){const n=await W(e,t,{method:"POST",path:`${G(t)}/watch`,body:e.body},()=>D(t,e));if(n.kind==="unknown")throw te(t,n.unreachable);if(n.kind==="remote"){if(n.status>=400)throw C(n.status,n.body);const r=n.body.patterns;return ue(e,n.body,n.machine.id,"ask",r),F(n.body,n.machine)}}return Ge(e)}u(De,"watch");function Ge(e){const t=[],n=e.body===void 0?{}:k(e.body)?e.body:(t.push({field:"body",problem:"must be an object"}),{}),r=Me(n.patterns,t),o=We(n.config,t);return n.filter!==void 0&&!k(n.filter)&&t.push({field:"filter",problem:"must be an object",received:n.filter}),n.watcher!==void 0&&typeof n.watcher!="string"&&t.push({field:"watcher",problem:"must be a custom-object ref",received:n.watcher}),t.length>0&&$("watch request",t),y(i=>{const s=typeof n.watcher=="string"?H(i,n.watcher):null;if(e.publishToken&&!s)throw new w(404,"not_found","watcher not found");if(e.principal.class==="anonymous"&&!s)throw new w(404,"not_found","watcher not found");const a=s?B(i,e,s):N(i,e)??V(g(i,e));s&&Z(i,e,s);const c=s?A(i,s):g(i,e),f=s?(()=>{const p=R(i,c,_(e));if(!ge(i,c,p))throw new w(404,"not_found","object not found");return p})():R(i,c,_(e)),h=r??[">"];return Q(i,s??a,f,s?"act":"ask",{patterns:h,...o?{config:o}:{},...n.filter!==void 0?{filter:n.filter}:{}}),ke(i,{event:"watched",object:f,actor:a,attrs:{}},M(i)),{object:b(i,f),patterns:h}})}u(Ge,"watchLocal");async function ze(e){const t=_(e);if(e.query.get("watcher")&&pe(t),!e.query.get("watcher")){const n=await W(e,t,{method:"DELETE",path:`${G(t)}/watch`,query:P(e)},()=>D(t,e));if(n.kind==="unknown")throw te(t,n.unreachable);if(n.kind==="remote"){if(n.status>=400)throw C(n.status,n.body);const r=n.body.object?.id;return y(o=>{const i=X(o,e),s=r===void 0?null:ne(o,r);i!==null&&s!==null&&re(o,s)&&Y(o,i,s,e.query.get("pattern")??void 0)}),F(n.body,n.machine)}}return Ue(e)}u(ze,"unwatchRoute");function Ue(e){const t=e.query.get("pattern"),n=e.query.get("watcher");return y(r=>{const o=n?H(r,n):null;if((e.publishToken||e.principal.class==="anonymous")&&!o)throw new w(404,"not_found","watcher not found");const i=o?B(r,e,o):N(r,e)??V(g(r,e));o&&Z(r,e,o);const s=o?A(r,o):g(r,e),a=R(r,s,_(e)),c=u(()=>ye(r,`SELECT count(*) AS n FROM watch_subscriptions s JOIN watches w ON w.rowid = s.watch
2
- WHERE w.watcher = ? AND w.object = ?`).get((o??i).rowid,a.rowid).n,"count"),f=c();return Y(r,o??i,a,t??void 0),{object:b(r,a),removed:f-c()}})}u(Ue,"unwatchLocal");function Ve(e){const t=[],n=z(e.query.get("type"),"type",["acted-on","links-to","watches"],t),r=z(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&$("edges request",t),y(o=>{const i=g(o,e),s=R(o,i,_(e)),a=[];if(n!=="watches")for(const c of je(o,i,s,{...n?{type:n}:{},direction:r??"both"}))a.push({type:c.type,src:b(o,c.src),dst:b(o,c.dst),...c.written_as?{written_as:c.written_as}:{}});if(n===void 0||n==="watches"){const c=[...r!=="in"?Re(o,i,s):[],...r!=="out"?qe(o,i,s):[]];for(const f of c)a.push({type:"watches",src:b(o,f.watcher),dst:b(o,f.object)})}return{items:a}})}u(Ve,"edges");const O=u(e=>async t=>({status:200,body:await e(t)}),"ok"),ht=[{method:"POST",pattern:"/v1/objects/search",handler:O(Ce)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:O(Ve)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:O(De)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:O(ze)},{method:"GET",pattern:"/v1/objects/:ref",handler:O(Ie)},{method:"GET",pattern:"/v1/objects",handler:O(Pe)}];export{Ae as listBudget,ht as objectRoutes};
1
+ var $e=Object.defineProperty;var c=(e,t)=>$e(e,"name",{value:t,configurable:!0});import{prepared as Se}from"../../../core/sqlite-statements.js";import{ApiError as y}from"../../../api/errors.js";import{PRIVACY_LEVELS_DTO as Ae}from"../../../api/dto/objects.js";import{openDb as T,withCanvasWrite as k}from"../../../core/canvas/db.js";import{actorOf as ee}from"../../../core/graph/documents.js";import{readerForObject as C,readerFor as Ie}from"../../../core/graph/reader.js";import{resolveName as Ne}from"../../../core/graph/names.js";import{canRead as te,customReachable as Fe,holdsObjectsScope as Le,mayActAs as ne}from"../../../core/graph/access.js";import{customObjectRef as re,authorizeCustom as oe,fenceArgv as D}from"./custom-objects.js";import{edgesOf as Pe}from"../../../core/graph/edges.js";import{publish as M}from"../../../core/graph/events.js";import{objectDTO as g}from"../../../core/graph/kinds/dto.js";import{isKind as We,kindNames as Ce,kindOf as G}from"../../../core/graph/kinds/registry.js";import{listObjects as De,searchObjects as ie}from"../../../core/graph/objects.js";import{checkoutsForCwd as Me}from"../../../core/graph/repo-sync/repos.js";import{recordWatch as P,unwatch as se,watchersOf as Ge,watchesOf as ze}from"../../../core/graph/watches.js";import{readerFor as v,resolveRef as z}from"../reader.js";import{readDelegationFor as ae,runReadLeaf as Ue}from"../../plugin-objects/read-leaf.js";import{invalidRequest as W,isRecord as R}from"./validate.js";import{callerNode as ue,callerStub as U,forwardVerb as ce,originates as le,queryWithoutMachine as V,relayError as B,remoteStub as Ve,unknownError as fe,forwarding as F,withMachine as H}from"../../peer/forward.js";import{getById as L,getByRowid as de}from"../../../core/graph/objects.js";import{ensureNodeStub as Be,isStub as me,stubOf as He,withKindDecl as $}from"../../../core/peer/stubs.js";import{isUnreachable as Ke}from"../../../core/peer/client.js";import{normalizeMachineId as S}from"../../../core/peer/directory.js";const A=c(e=>t=>Me(e,t),"reposFnFor");function he(e,t){return{...g(e,t),...G(e,t.type).listItem?.(e,t)}}c(he,"listItem");function Ze(e){return`no object named ${e} on this runtime; it may be on another runtime, or the name may be misspelled`}c(Ze,"remoteMessage");function pe(e,t,n){try{return q(e,t,n)}catch(r){if(r instanceof y&&r.code==="not_found"&&!r.details?.deleted)return null;throw r}}c(pe,"ownerFilter");function q(e,t,n){const r=z(e,t,n);if("remote"in r)throw new y(404,"not_found",Ze(r.remote),{next:"Check the name with `crtr canvas search`."});if("deleted"in r)throw new y(404,"not_found",`${r.deleted.name} is deleted`,{id:r.deleted.id,deleted:!0});return r}c(q,"liveRef");const _=c(e=>decodeURIComponent(e.params.ref),"refParam"),we=c(e=>{const t=e.trim().replace(/^\[\[(.*)\]\]$/,"$1").trim(),n=t.indexOf("/");return n>0?t.slice(0,n):t},"idOf");function ye(e){const t=L(T(),we(e));return t!==null&&me(T(),t)?t.id:null}c(ye,"stubIdOf");async function K(e,t,n,r){const o=ye(t);return o!==null?ce(e,o,n):r()?{kind:"local"}:le(e.principal)?ce(e,we(t),n):{kind:"local"}}c(K,"forwardRef");function Z(e,t){return k(n=>{try{return!("remote"in z(n,v(n,t),e))}catch{return!1}})}c(Z,"resolvesLocally");const J=c(e=>`/v1/objects/${encodeURIComponent(e)}`,"objectPath");function ge(e,t,n,r,o){const i=t?.object;!i?.id||!i.type||k(s=>{const a=ue(s,e);if(a===null)return;const l=Ve(s,i.id,i.type,n,{kind:i.kind??null,name:i.name??i.id,...i.preview===void 0?{}:{preview:i.preview},owner:i.owner??null,...i.kind_decl===void 0?{}:{kind_decl:i.kind_decl},...i.privacy===void 0?{}:{privacy:i.privacy}});P(s,a,l,r,o===void 0?{}:{patterns:o})})}c(ge,"watchRemote");async function Je(e){const t=_(e),n=["1","true"].includes(e.query.get("fields")??""),r=await K(e,t,{method:"GET",path:J(t),query:V(e)},()=>Z(t,e));if(r.kind==="unknown")return{state:"unknown",ref:t,unreachable:r.unreachable};if(r.kind==="remote"){if(r.status>=400)throw B(r.status,r.body);return ge(e,r.body,r.machine.id,"read"),H(r.body,r.machine)}const o=c(a=>{const l=U(a,e);return l===null?v(a,e):C(a,l,A(a))},"readerOf"),i=k(a=>{const l=o(a),u=z(a,l,t);if("remote"in u)return{state:"remote",remote:u.remote};if("deleted"in u)return{state:"deleted",deleted:g(a,u.deleted)};const f=ae(a,u);if(f!==null)return{delegate:f,hitId:u.id};const{data:m,text:b}=G(a,u.type).read(a,l,u,{fields:n,reposFn:A(a)});return{state:"object",type:u.type,object:$(a,u,g(a,u)),text:b,data:m}});if(!("delegate"in i))return i;const s=await Ue(i.delegate,e.principal);return k(a=>{const l=o(a),u=L(a,i.hitId);if(u===null)throw new y(404,"not_found","object not found");if(u.deleted_at!==null)return{state:"deleted",deleted:g(a,u)};if(!te(a,l,u))throw new y(404,"not_found","object not found");const{data:f,text:m}=G(a,u.type).read(a,l,u,{fields:n,reposFn:A(a)});return ae(a,u)===null?{state:"object",type:u.type,object:$(a,u,g(a,u)),text:m,data:f}:{state:"object",type:u.type,object:$(a,u,g(a,u)),text:"markdown"in s?s.markdown:`${m}
2
+ content unavailable: ${s.unavailable}`,data:{...f,content:"markdown"in s?"live":"unavailable"}}})}c(Je,"read");function Q(e,t,n,r){if(e!=null){if(!n.includes(e)){r.push({field:t,problem:`must be one of ${n.join(", ")}`,received:e});return}return e}}c(Q,"oneOf");function be(e,t,n){if(t!=null){if(typeof t!="string"||!We(e,t)){n.push({field:"type",problem:`must be a kind: one of ${Ce(e).join(", ")}`,received:t});return}return t}}c(be,"kindName");function je(e,t,n,r){if(e==null)return r;const o=Number(e);return!Number.isInteger(o)||o<1||o>100?(n.push({field:t,problem:"must be an integer from 1 to 100",received:e}),r):o}c(je,"limitOf");function ke(e){const t=e.query,n=[],r=be(T(),t.get("type"),n),o=Q(t.get("privacy"),"privacy",Ae,n),i=je(t.get("limit"),"limit",n,50),s=t.get("cursor");s!==null&&!/^\d+$/.test(s)&&n.push({field:"cursor",problem:"is not a cursor from a previous page",received:s});const a=t.get("deleted");return a!==null&&!["0","1","true","false"].includes(a)&&n.push({field:"deleted",problem:"must be true or false",received:a}),n.length>0&&W("object list request",n),k(l=>{const u=v(l,e),f={...r?{type:r}:{},...o?{privacy:o}:{},...t.get("kind")!==null?{kind:t.get("kind")}:{},...a==="1"||a==="true"?{deleted:!0}:{}},m=t.get("owner");if(m!==null){const h=pe(l,u,m);if(h===null)return{items:[],next_cursor:null};f.owner=h}const b=t.get("name"),j=[];let p=s===null?0:Number(s),w=!0;for(;j.length<i&&w;){const h=De(l,u,f,{limit:100,after_rowid:p});w=h.length===100;for(const E of h)if(p=E.rowid,(b===null||E.name.startsWith(b))&&j.push(E),j.length===i)break}const d=j.at(-1);return{items:j.map(h=>he(l,h)),next_cursor:j.length===i&&d?String(d.rowid):null}})}c(ke,"listLocal");const Qe={ms:3e4},Ye="self",Y=c((e,t)=>e.map(n=>({...n,machine:t})),"labelItems");async function _e(e,t,n,r){const o=F.current;try{const i=await o.call(t.id,{method:"GET",path:"/v1/objects",query:n},o.envelope(e.principal),r);if(Ke(i))return{state:"unreachable",items:[],next:null};if(i.status===426)return{state:"too_old",items:[],next:null};const s=i.body;return i.status!==200||!Array.isArray(s?.items)?{state:"unreachable",items:[],next:null}:{state:"ok",items:Y(s.items,t),next:s.next_cursor??null}}catch{return{state:"unreachable",items:[],next:null}}}c(_e,"remotePage");async function Xe(e){const t=new Promise(n=>e.addEventListener("abort",()=>n(null),{once:!0}));return Promise.race([F.current.machines().catch(()=>null),t])}c(Xe,"machinesWithin");async function xe(e){const t=e.query.get("machine");if(!le(e.principal)||!F.current.joined()||t===Ye)return ke(ve(e));const r=F.current.self(),o=t!==null&&S(t)!==r,i=o?null:ke(ve(e)),s=new AbortController,a=setTimeout(()=>s.abort(),Qe.ms);a.unref?.();try{const l=await Xe(s.signal);if(l===null){if(o)throw new y(503,"runtime_unreachable",`The directory did not answer, so ${S(t)} cannot be listed.`,{machine:S(t)});return i}const u=c(d=>({id:d,name:l.find(h=>h.machine_id===d)?.name??d}),"labelOf"),f=u(r),m=l.filter(d=>d.machine_id!==r&&d.kind!=="device"),b=V(e);if(t!==null){if(!o)return{items:Y(i.items,f),next_cursor:i.next_cursor,machines:[{machine_id:r,name:f.name,state:"ok",more:i.next_cursor!==null}]};const d=S(t);if(!m.some(E=>E.machine_id===d))throw new y(404,"not_found",`no machine ${d} on your account`,{machine:d});const h=await _e(e,u(d),b,s.signal);return{items:h.items,next_cursor:h.next,machines:[{machine_id:d,name:u(d).name,state:h.state,more:h.next!==null}]}}const{cursor:j,...p}=b,w=await Promise.all(m.map(d=>_e(e,u(d.machine_id),p,s.signal)));return{items:[...Y(i.items,f),...w.flatMap(d=>d.items)],next_cursor:i.next_cursor,machines:[{machine_id:r,name:f.name,state:"ok",more:i.next_cursor!==null},...m.map((d,h)=>({machine_id:d.machine_id,name:d.name,state:w[h].state,more:w[h].next!==null}))]}}finally{clearTimeout(a)}}c(xe,"list");function ve(e){const t=new URLSearchParams(e.query);return t.delete("machine"),{...e,query:t}}c(ve,"withoutMachine");function et(e){const t=[],n=R(e.body)?e.body:{};R(e.body)||t.push({field:"body",problem:"must be an object",received:e.body});const r=n.text;(typeof r!="string"||r.trim()==="")&&t.push({field:"text",problem:"is required",received:r});const o=n.grep;if(o!==void 0)if(typeof o!="string")t.push({field:"grep",problem:"must be a regular expression string",received:o});else try{new RegExp(o)}catch(a){t.push({field:"grep",problem:`is not a valid regular expression: ${a.message}`,received:o})}n.body!==void 0&&typeof n.body!="boolean"&&t.push({field:"body",problem:"must be a boolean",received:n.body});const i=be(T(),n.type,t);n.kind!==void 0&&typeof n.kind!="string"&&t.push({field:"kind",problem:"must be a string",received:n.kind}),n.owner!==void 0&&typeof n.owner!="string"&&t.push({field:"owner",problem:"must be a string",received:n.owner});const s=je(n.limit,"limit",t,50);return t.length>0&&W("object search request",t),k(a=>{const l=v(a,e),u=typeof n.owner=="string"?pe(a,l,n.owner):null;if(typeof n.owner=="string"&&u===null)return{items:[]};const f=ie(a,l,{text:r,body:n.body===!0||typeof o=="string"}),m=new Set(f.map(p=>p.object.rowid));return{items:(typeof o=="string"?ie(a,l,{text:o,grep:!0}).filter(p=>m.has(p.object.rowid)):f).filter(p=>(!i||p.object.type===i)&&(n.kind===void 0||p.object.kind===n.kind)&&(!u||p.object.owner===u.rowid)).slice(0,s).map(p=>({object:he(a,p.object),...p.line!==void 0?{matches:[{line:p.line,text:p.text??""}]}:{}}))}})}c(et,"search");const Oe=["interrupt","steer","after-turn","next-turn"],tt=/^(\*|>|[A-Za-z0-9_-]+)(\.(\*|>|[A-Za-z0-9_-]+))*$/;function nt(e,t){if(e!==void 0){if(!Array.isArray(e)||e.length===0||e.some(n=>typeof n!="string"||!tt.test(n)||n.includes(">")&&!n.endsWith(">"))){t.push({field:"patterns",problem:"must be a non-empty list of event patterns (dot-separated words, `*` for one word, a final `>` for the rest)",received:e});return}return e}}c(nt,"patternsOf");function rt(e,t){if(e===void 0)return;if(!R(e)||Object.keys(e).length===0){t.push({field:"config",problem:"must map levels 1-10 to a delivery",received:e});return}const n=[];for(const[r,o]of Object.entries(e)){const i=Number(r);!Number.isInteger(i)||i<1||i>10?t.push({field:`config.${r}`,problem:"level must be an integer from 1 to 10"}):Oe.includes(o)?n.push({threshold:i,delivery:o}):t.push({field:`config.${r}`,problem:`delivery must be one of ${Oe.join(", ")}`,received:o})}return n.sort((r,o)=>o.threshold-r.threshold)}c(rt,"configOf");function Ee(e){const t=ye(e);if(t!==null)throw new y(400,"invalid_request",`${t} lives on another machine; an explicit watcher cannot watch it from here`,{id:t})}c(Ee,"refuseStubWatch");function X(e,t){const n=L(e,t)??(()=>{const r=Ne(e,Ie(e,{class:"person"},()=>[]),t);return"object"in r?r.object:null})();return n===null||n.deleted_at!==null||!(n.type.includes(".")||n.type==="node")?null:n}c(X,"findWatcher");const O=c(()=>{throw new y(404,"not_found","watcher not found")},"watcherNotFound");async function qe(e,t,n){if(typeof t!="string"||n===void 0||n===null)return null;if(typeof n!="string")throw new y(400,"invalid_request","invalid watcher_machine");if(X(e,t)!==null)return null;const r=F.current;let o;try{o=await r.machines()}catch{throw new y(503,"runtime_unreachable","The directory did not answer, so the watcher machine cannot be checked.")}const i=S(n);return(i===r.self()||!o.some(s=>S(s.machine_id)===i))&&O(),i}c(qe,"remoteWatcherMachine");function Te(e,t,n,r){if(t.publishToken||t.principal.class==="anonymous")return O();const o=v(e,t);if(o.class==="person")return O();let i;try{i=q(e,o,r)}catch{return O()}const s=i.owner===null?null:de(e,i.owner);return s===null||!ne(e,o,s)||!Le(e,o,"link",n)?O():(D(e,t,n),{actor:o.app,object:i})}c(Te,"linkWatcher");function Re(e,t,n){if(t.publishToken)return!0;if(t.principal.class==="anonymous"||!n.type.includes("."))return!1;const r=v(e,t),o=n.owner===null?null:de(e,n.owner);return o!==null&&ne(e,r,o)&&Fe(e,r,n)}c(Re,"ownsWatcher");async function ot(e){const t=_(e),n=R(e.body)?e.body:{};if(typeof n.watcher=="string"&&Ee(t),typeof n.watcher!="string"){const o=await K(e,t,{method:"POST",path:`${J(t)}/watch`,body:e.body},()=>Z(t,e));if(o.kind==="unknown")throw fe(t,o.unreachable);if(o.kind==="remote"){if(o.status>=400)throw B(o.status,o.body);const i=o.body.patterns;return ge(e,o.body,o.machine.id,"ask",i),H(o.body,o.machine)}}const r=await qe(T(),n.watcher,n.watcher_machine);return it(e,r)}c(ot,"watch");function it(e,t=null){const n=[],r=e.body===void 0?{}:R(e.body)?e.body:(n.push({field:"body",problem:"must be an object"}),{}),o=nt(r.patterns,n),i=rt(r.config,n);return r.filter!==void 0&&!R(r.filter)&&n.push({field:"filter",problem:"must be an object",received:r.filter}),r.watcher!==void 0&&typeof r.watcher!="string"&&n.push({field:"watcher",problem:"must be a custom-object ref",received:r.watcher}),r.watcher_machine!==void 0&&typeof r.watcher_machine!="string"&&n.push({field:"watcher_machine",problem:"must be a machine id",received:r.watcher_machine}),n.length>0&&W("watch request",n),k(s=>{const a=typeof r.watcher=="string";if((e.publishToken||e.principal.class==="anonymous")&&!a)throw new y(404,"not_found","watcher not found");const l=o??[">"],u={patterns:l,...i?{config:i}:{},...r.filter!==void 0?{filter:r.filter}:{}};if(a){const j=r.watcher,p=t===null?null:st(s,e,j,t),w=p??(e.publishToken?re(s,j):X(s,j));if(w===null)return O();if(p===null&&Re(s,e,w)){const E=oe(s,e,w);D(s,e,w);const x=C(s,w),N=q(s,x,_(e));if(!te(s,x,N))throw new y(404,"not_found","object not found");return P(s,w,N,"act",u),M(s,{event:"watched",object:N,actor:E,attrs:{}},A(s)),{object:$(s,N,g(s,N)),patterns:l}}const{actor:d,object:h}=Te(s,e,w,_(e));return P(s,w,h,"act",{...u,linkApp:d}),M(s,{event:"watched",object:h,actor:d,attrs:{}},A(s)),{object:$(s,h,g(s,h)),patterns:l}}const f=v(s,e),m=U(s,e)??ee(f),b=q(s,f,_(e));return P(s,m,b,"ask",u),M(s,{event:"watched",object:b,actor:m,attrs:{}},A(s)),{object:$(s,b,g(s,b)),patterns:l}})}c(it,"watchLocal");function st(e,t,n,r){return t.publishToken||t.principal.class==="anonymous"?O():Be(e,n,r)}c(st,"ensureNodeStubFor");async function at(e){const t=_(e);if(e.query.get("watcher")&&Ee(t),!e.query.get("watcher")){const r=await K(e,t,{method:"DELETE",path:`${J(t)}/watch`,query:V(e)},()=>Z(t,e));if(r.kind==="unknown")throw fe(t,r.unreachable);if(r.kind==="remote"){if(r.status>=400)throw B(r.status,r.body);const o=r.body.object?.id;return k(i=>{const s=ue(i,e),a=o===void 0?null:L(i,o);s!==null&&a!==null&&me(i,a)&&se(i,s,a,e.query.get("pattern")??void 0)}),H(r.body,r.machine)}}const n=await qe(T(),e.query.get("watcher")??void 0,e.query.get("watcher_machine")??void 0);return ut(e,n)}c(at,"unwatchRoute");function ut(e,t=null){const n=e.query.get("pattern"),r=e.query.get("watcher");return k(o=>{if((e.publishToken||e.principal.class==="anonymous")&&!r)throw new y(404,"not_found","watcher not found");const i=c((u,f)=>Se(o,`SELECT count(*) AS n FROM watch_subscriptions s JOIN watches w ON w.rowid = s.watch
3
+ WHERE w.watcher = ? AND w.object = ?`).get(u.rowid,f.rowid).n,"count"),s=c((u,f)=>{const m=i(u,f);return se(o,u,f,n??void 0),{object:g(o,f),removed:m-i(u,f)}},"drop");if(r){const u=t===null?null:(()=>{const m=L(o,r);return m!==null&&He(o,m)!==null?m:null})();if(t!==null&&u===null)return O();const f=u??(e.publishToken?re(o,r):X(o,r));return f===null?O():u===null&&Re(o,e,f)?(oe(o,e,f),D(o,e,f),s(f,q(o,C(o,f),_(e)))):s(f,Te(o,e,f,_(e)).object)}const a=v(o,e),l=U(o,e)??ee(a);return s(l,q(o,a,_(e)))})}c(ut,"unwatchLocal");function ct(e){const t=[],n=Q(e.query.get("type"),"type",["acted-on","links-to","watches"],t),r=Q(e.query.get("direction"),"direction",["out","in"],t);return t.length>0&&W("edges request",t),k(o=>{const i=v(o,e),s=q(o,i,_(e)),a=[];if(n!=="watches")for(const l of Pe(o,i,s,{...n?{type:n}:{},direction:r??"both"}))a.push({type:l.type,src:g(o,l.src),dst:g(o,l.dst),...l.written_as?{written_as:l.written_as}:{}});if(n===void 0||n==="watches"){const l=[...r!=="in"?ze(o,i,s):[],...r!=="out"?Ge(o,i,s):[]];for(const u of l)a.push({type:"watches",src:g(o,u.watcher),dst:g(o,u.object)})}return{items:a}})}c(ct,"edges");const I=c(e=>async t=>({status:200,body:await e(t)}),"ok"),Lt=[{method:"POST",pattern:"/v1/objects/search",handler:I(et)},{method:"GET",pattern:"/v1/objects/:ref/edges",handler:I(ct)},{method:"POST",pattern:"/v1/objects/:ref/watch",handler:I(ot)},{method:"DELETE",pattern:"/v1/objects/:ref/watch",handler:I(at)},{method:"GET",pattern:"/v1/objects/:ref",handler:I(Je)},{method:"GET",pattern:"/v1/objects",handler:I(xe)}];export{Qe as listBudget,Lt as objectRoutes};
@@ -1 +1 @@
1
- var a=Object.defineProperty;var o=(e,t)=>a(e,"name",{value:t,configurable:!0});import{withCanvasWrite as c}from"../../../core/canvas/db.js";import{reconcilePackageDocuments as r}from"../../../core/graph/package-docs.js";const i=[{method:"POST",pattern:"/v1/documents/reconcile-packages",handler:o(()=>({status:200,body:c(e=>r(e))}),"handler")}];export{i as packageDocRoutes};
1
+ var a=Object.defineProperty;var o=(e,t)=>a(e,"name",{value:t,configurable:!0});import{withCanvasWrite as c}from"../../../core/canvas/db.js";import{reconcilePackages as r}from"../../../core/graph/package-reconcile.js";const m=[{method:"POST",pattern:"/v1/documents/reconcile-packages",handler:o(()=>({status:200,body:c(e=>r(e))}),"handler")}];export{m as packageDocRoutes};