@north-light/cli 0.3.567 → 0.3.569
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/grants/mirror.d.ts +14 -0
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/providers/bundles.d.ts +0 -4
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/scoped-state/materialized-plugins.d.ts +2 -0
- package/dist/core/scoped-state/materialized-plugins.js +1 -1
- package/dist/core/scoped-state/person-rows.js +5 -5
- package/dist/daemon/api/handlers/execution-start.js +2 -2
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
|
@@ -54,6 +54,20 @@ export declare function inferenceEndpointOf(value: unknown): InferenceEndpoint |
|
|
|
54
54
|
export declare function mirrorOnly(grant: Pick<GrantRecord, 'grantee'>): boolean;
|
|
55
55
|
/** Every mirrored grant this machine acts on: `allGrants()` without the mirror-only ones. */
|
|
56
56
|
export declare function localGrants(): Promise<GrantRecord[]>;
|
|
57
|
+
/** Every provider id a scope of an active grant's resolved set names: a
|
|
58
|
+
* `<id>:<group>` string whose id is not `crtr` (the runtime's own namespace). */
|
|
59
|
+
export declare function providerIdsFromGrants(grants: readonly GrantRecord[]): string[];
|
|
60
|
+
/** The provider ids an active grant of `person` names: the directory's provider bundles that person has, and so the only
|
|
61
|
+
* rows of their plugin list that a provider call installs on its own (`ensureProviderBundle`) rather than from a source. */
|
|
62
|
+
export declare function grantedProviderIds(person: string): Promise<Set<string>>;
|
|
63
|
+
/** Whether a `scope_plugins` row is a directory provider bundle rather than a plugin that has no source this process can read:
|
|
64
|
+
* it carries neither a marketplace nor a path (what `ensureProviderBundle` writes) AND its name is a provider an active grant of the
|
|
65
|
+
* person names. A source-less row for any other name (a git, local or HTTP-bundle install) is not one. */
|
|
66
|
+
export declare function isProviderBundleRow(row: {
|
|
67
|
+
name: string;
|
|
68
|
+
source_marketplace: string | null;
|
|
69
|
+
source_path: string | null;
|
|
70
|
+
}, granted: ReadonlySet<string>): boolean;
|
|
57
71
|
/** Every mirrored grant, whatever its status. */
|
|
58
72
|
export declare function allGrants(): Promise<GrantRecord[]>;
|
|
59
73
|
/** Whether a grant's `expires_at` has passed. An unparsable time reads as expired (fail closed). */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var w=Object.defineProperty;var t=(e,r)=>w(e,"name",{value:r,configurable:!0});import{createHash as O}from"node:crypto";import{parseScope as N}from"@north-light/identity";import{openDb as d,withCanvasWrite as h}from"../canvas/db.js";import{isUnsandboxedMachine as j}from"../runtime/unsandboxed.js";import{replaceWrittenBy as A,userSubject as b}from"../permissions/facts.js";import{GROUP_TYPE as R}from"../permissions/builtin.js";const D=["not_started","pending","ready","failed","unavailable"],k=["ultra","strong","medium","light"];function I(){const e=process.env.CRTR_RUNTIME;return e?{sql:" AND runtime = ?",args:[e]}:{sql:"",args:[]}}t(I,"runtimeScope");async function u(e,r,n=[]){const s=I();return await(await d()).prepare(`SELECT ${e} FROM grants WHERE ${r}${s.sql}`).all(...n,...s.args)}t(u,"grantRows");async function c(e,r,n=[]){return(await u(e,r,n))[0]}t(c,"grantRow");function l(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(r=>{if(!r||typeof r!="object"||typeof r.scope!="string"||!["always","ask"].includes(r.mode))throw new TypeError("invalid grant scope entry");return{scope:r.scope,mode:r.mode}})}t(l,"entries");function C(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:r,message:n,next:s,step:a}=e;return typeof r!="string"||!D.includes(r)||n!==void 0&&typeof n!="string"||a!==void 0&&typeof a!="string"||typeof s!="string"||!s?null:{state:r,...n===void 0?{}:{message:n},next:s,...a===void 0?{}:{step:a}}}t(C,"dependencyOf");function m(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[n,s]of Object.entries(e)){const a=C(s);a&&(r[n]=a)}return r}t(m,"dependenciesOf");function F(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{base_url:r,rungs:n}=e;if(typeof r!="string"||!r||!n||typeof n!="object"||Array.isArray(n))return null;const s={};for(const a of k){const o=n[a];typeof o=="string"&&o&&(s[a]=o)}return Object.keys(s).length?{base_url:r.replace(/\/+$/,""),rungs:s}:null}t(F,"inferenceEndpointOf");function _(e){if(e.computed_rev!==e.rev||e.scopes_json===null||e.resolved_scopes_json===null)throw new TypeError("grant columns are not computed for this revision");return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:l(JSON.parse(e.scopes_json)),resolved_scopes:l(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:m(JSON.parse(e.dependencies_json??"{}")),run_notice_url:e.run_notice_url??null,inference_endpoint:e.inference_endpoint_json?F(JSON.parse(e.inference_endpoint_json)):null,synced_at:e.synced_at??""}}t(_,"record");function p(e){if(!e||e.status!=="active")return null;try{return H(_(e))}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(p,"active");function g(e){return e.grantee!=="app:terminal"&&j()}t(g,"mirrorOnly");async function Y(){return(await G()).filter(e=>!g(e))}t(Y,"localGrants");const E="[A-Za-z0-9_-]{1,64}",M=new RegExp(`^(${E}):(${E})$`);function L(e){const r=new Set;for(const n of e)if(n.status==="active")for(const{scope:s}of n.resolved_scopes){const a=M.exec(s);a&&a[1]!=="crtr"&&r.add(a[1])}return[...r].sort()}t(L,"providerIdsFromGrants");async function Z(e){const r=await u("*","sub = ? AND status = 'active'",[e]);return new Set(L(r.flatMap(n=>{try{return[_(n)]}catch(s){if(s instanceof TypeError||s instanceof SyntaxError)return[];throw s}})))}t(Z,"grantedProviderIds");function K(e,r){return e.source_marketplace===null&&e.source_path===null&&r.has(e.name)}t(K,"isProviderBundleRow");async function G(){return(await u("*","true")).flatMap(e=>{try{return[_(e)]}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return[];throw r}})}t(G,"allGrants");function $(e){return e.expires_at!==null&&!(Date.parse(e.expires_at)>Date.now())}t($,"grantExpired");function H(e){if(e.status!=="active"||$(e))return null;try{const r=e.resolved_scopes.map(({scope:n})=>N(n));return{...e,resolved:e.resolved_scopes.flatMap(({mode:n},s)=>n==="always"?[r[s]]:[])}}catch(r){if(r instanceof TypeError||r instanceof SyntaxError)return null;throw r}}t(H,"activeRecord");function v(e){return Array.isArray(e)?`[${e.map(v).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(r=>e[r]!==void 0).map(r=>`${JSON.stringify(r)}:${v(e[r])}`).join(",")}}`:JSON.stringify(e)??"null"}t(v,"canonicalJson");function Q(e){const r={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{},...e.inference_endpoint?{inference_endpoint:e.inference_endpoint}:{}};return O("sha256").update(v(r)).digest("base64url").slice(0,22)}t(Q,"grantVersion");async function ee(e){return!!await c("1","id = ?",[e])}t(ee,"hasGrant");async function re(e){return await c("rev, status","id = ?",[e])??null}t(re,"grantSyncState");async function ne(e,r){return await c("rev, status","grantee = ? AND sub = ?",[e,r])??null}t(ne,"grantSyncStateFor");async function U(e){const r=await c("*","id = ?",[e]);if(!r)return null;try{return _(r)}catch(n){if(n instanceof TypeError||n instanceof SyntaxError)return null;throw n}}t(U,"grantRecord");async function te(e){const r=await c("*","id = ?",[e]);if(!r)return{standing:"inactive",sub:null,grantee:null,status:null,removal_done_at:null,record:null};const n={sub:r.sub,grantee:r.grantee,status:r.status,removal_done_at:r.removal_done_at??null};if(r.status!=="active")return{...n,standing:"inactive",record:null};if(r.computed_rev!==r.rev)return{...n,standing:"lagging",record:null};const s=p(r);return s===null?{...n,standing:"inactive",record:null}:{...n,standing:"active",record:s}}t(te,"grantStanding");async function se(e){return(await u("id",e)).map(r=>r.id)}t(se,"grantIdsWhere");async function ae(e){return(e?(await U(e))?.payer:null)??"user"}t(ae,"payerOfGrant");async function W(e){return p(await c("*","id = ?",[e]))}t(W,"activeGrant");async function oe(e,r){return p(await c("*","grantee = ? AND sub = ?",[e,r]))}t(oe,"grantFor");async function ie(e){if(g({grantee:e}))return null;const r=await u("*","grantee = ? AND status = 'active'",[e]);if(r.length>1)throw new Error(`multiple active grants for ${e}`);return p(r[0])}t(ie,"activeGrantForApp");async function ce(e){const r=await W(e);return r&&r.run_notice_url&&!g(r)?{url:r.run_notice_url,sub:r.sub,grantee:r.grantee}:null}t(ce,"runNoticeTarget");async function ue(){const e=await u("*","grantee = 'app:terminal' AND status = 'active'");if(e.length>1)throw new Error("multiple active terminal grants");return p(e[0])}t(ue,"terminalGrant");async function pe(){return(await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value??null}t(pe,"grantsSource");async function de(e){return(await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value===e}t(de,"hasSyncedGrants");async function le(e){return(await e.prepare("SELECT value FROM runtime_state WHERE key = 'grants_source'").get())?.value!=="local"?0:(await e.prepare("DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number((await e.prepare("DELETE FROM grants").run()).changes))}t(le,"dropLocalGrantMirror");function _e(e){if(e===void 0)return[];if(!Array.isArray(e))throw new TypeError("invalid grant sync response");return e.map(r=>{const{team:n,members:s}=r??{};if(typeof n!="string"||!/^team:\S+$/.test(n)||!Array.isArray(s)||s.some(a=>typeof a!="string"||!/^user:[^\s*]+$/.test(a)))throw new TypeError("invalid grant sync response");return{team:n,members:s}})}t(_e,"teamsOf");async function fe(e,r,n,s=[]){const a=new Set;for(const o of e){if(a.has(o.id)||!o.id||!o.runtime||!o.sub||!o.grantee||!Number.isInteger(o.rev)||!["active","revoked","removed"].includes(o.status))throw new TypeError("invalid grant sync response");a.add(o.id),l(o.scopes),l(o.resolved_scopes)}await h(async o=>{let f=0;const y=[...a];f+=Number((await(y.length?o.prepareUncached(`DELETE FROM grants WHERE id NOT IN (${y.map(()=>"?").join(",")})`).run(...y):o.prepareUncached("DELETE FROM grants").run())).changes);const x=o.prepare(`INSERT INTO grants
|
|
2
2
|
(id, runtime, sub, grantee, rev, scopes_json, resolved_scopes_json, spend_cap, expires_at,
|
|
3
3
|
status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, inference_endpoint_json, synced_at, computed_rev)
|
|
4
4
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -13,4 +13,4 @@ var T=Object.defineProperty;var r=(e,n)=>T(e,"name",{value:n,configurable:!0});i
|
|
|
13
13
|
grants.run_notice_url, grants.inference_endpoint_json, grants.computed_rev)
|
|
14
14
|
IS DISTINCT FROM (excluded.runtime, excluded.sub, excluded.grantee, excluded.rev, excluded.scopes_json, excluded.resolved_scopes_json,
|
|
15
15
|
excluded.spend_cap, excluded.expires_at, excluded.status, excluded.revoked_at, excluded.approved_at, excluded.payer,
|
|
16
|
-
excluded.dependencies_json, excluded.run_notice_url, excluded.inference_endpoint_json, excluded.computed_rev)`);for(const
|
|
16
|
+
excluded.dependencies_json, excluded.run_notice_url, excluded.inference_endpoint_json, excluded.computed_rev)`);for(const i of e)f+=Number((await x.run(i.id,i.runtime,i.sub,i.grantee,i.rev,JSON.stringify(i.scopes),JSON.stringify(i.resolved_scopes),i.spend_cap,i.expires_at,i.status,i.revoked_at,i.approved_at,i.payer,JSON.stringify(m(i.dependencies)),i.run_notice_url??null,i.inference_endpoint?JSON.stringify(i.inference_endpoint):null,i.synced_at,i.rev)).changes);await A(o,"directory",R,"member",s.flatMap(({team:i,members:S})=>S.map(T=>({resourceId:i,subject:b(T)})))),await o.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(n),f>0&&await o.prepare("INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(runtime_state.value AS INTEGER) + 1 AS TEXT)").run()})}t(fe,"replaceGrants");async function ye(){const e=await(await d()).prepare("SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}t(ye,"grantsGeneration");export{W as activeGrant,ie as activeGrantForApp,H as activeRecord,G as allGrants,m as dependenciesOf,le as dropLocalGrantMirror,$ as grantExpired,oe as grantFor,se as grantIdsWhere,U as grantRecord,te as grantStanding,re as grantSyncState,ne as grantSyncStateFor,Q as grantVersion,Z as grantedProviderIds,ye as grantsGeneration,pe as grantsSource,ee as hasGrant,de as hasSyncedGrants,F as inferenceEndpointOf,K as isProviderBundleRow,Y as localGrants,g as mirrorOnly,ae as payerOfGrant,L as providerIdsFromGrants,fe as replaceGrants,ce as runNoticeTarget,_e as teamsOf,ue as terminalGrant};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type ValidatedCommandManifest } from '../../api/command-manifest/manifest.js';
|
|
2
2
|
import { type ProviderBlock } from '../command-plugins/bundle.js';
|
|
3
|
-
import { type GrantRecord } from '../grants/mirror.js';
|
|
4
3
|
import { ProviderError } from './errors.js';
|
|
5
4
|
export type ProviderTransport = {
|
|
6
5
|
kind: 'http';
|
|
@@ -22,9 +21,6 @@ export interface ResolvedProviderBundle {
|
|
|
22
21
|
/** The installed plugin root; absent for a pinned major. */
|
|
23
22
|
root?: string;
|
|
24
23
|
}
|
|
25
|
-
/** Every provider id a scope of an active grant's resolved set names: a
|
|
26
|
-
* `<id>:<group>` string whose id is not `crtr` (the runtime's own namespace). */
|
|
27
|
-
export declare function providerIdsFromGrants(grants: readonly GrantRecord[]): string[];
|
|
28
24
|
/** Install attempts are owned by this daemon process. An installed bundle remains
|
|
29
25
|
* usable after a failed refresh; only absent bundles expose the last failure.
|
|
30
26
|
* A new daemon starts another grant-sync pass, so no duplicate on-disk ledger is
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`),$(d(e,"bundle.tar"),{force:!0}),$(d(e,"etag"),{force:!0})}
|
|
1
|
+
var se=Object.defineProperty;var o=(e,n)=>se(e,"name",{value:n,configurable:!0});import{existsSync as g,readdirSync as U,readFileSync as S,rmSync as $,statSync as E}from"node:fs";import{join as d}from"node:path";import{validateCommandManifest as ue}from"../../api/command-manifest/manifest.js";import{crtrHome as W}from"../canvas/paths.js";import{withCanvasWrite as z}from"../canvas/db.js";import{bundleVersion as de,isInstalledGeneration as ce,recordCheckedGeneration as fe}from"../command-plugins/bundle.js";import{readState as le,updateConfig as pe,updateState as me}from"../config.js";import{directoryIssuer as G}from"../directory/issuer.js";import{emitEvent as m}from"../events/emit.js";import{describeOutboundFailure as ve,outboundFetch as he,outboundRefusal as q,OutboundError as we}from"../events/outbound.js";import{withExclusiveDirectoryLockAsync as Z}from"../exclusive-lock.js";import{atomicWriteText as T,ensureDir as x,pathExists as ge}from"../fs-utils.js";import{reconcilePackages as L}from"../graph/package-reconcile.js";import{readPluginManifest as j}from"../manifest.js";import{bundleSwapLockPath as V}from"../plugin-swap-lock.js";import{pluginsDir as $e,userScopeRoot as J}from"../scope.js";import{listInstalledPlugins as ye}from"../resolver.js";import{localGrants as A,providerIdsFromGrants as K}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const be="[A-Za-z0-9_-]{1,64}",Q=new RegExp(`^${be}$`),_e=3e4,X=3e4;function v(e){return d($e("user"),e)}o(v,"pluginRoot");function C(){return d(W(),"providers")}o(C,"providersRoot");function Y(e,n){return d(C(),e,`v${n}`)}o(Y,"majorDir");function B(e){if(!Q.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}o(B,"assertId");function I(){const e=G();if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e}o(I,"directoryBase");async function ee(e,n){const r=`${I()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await he("bundle_fetch",r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(_e)})}catch(i){return{status:"failed",message:`could not reach ${r}: ${i instanceof Error?i.message:String(i)}`,cause:i}}if(t.status===304){if(n.ifNoneMatch===void 0){const i=`HTTP 304 from ${r} without a conditional request`;return{status:"failed",message:i,cause:q("bundle_fetch",r,i)}}return{status:304}}if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300){await t.body?.cancel().catch(()=>{});{const i=`HTTP ${t.status} from ${r}`;return{status:"failed",message:i,cause:q("bundle_fetch",r,i)}}}let a;try{a=new Uint8Array(await t.arrayBuffer())}catch(i){return{status:"failed",message:`reading ${r} failed: ${i instanceof Error?i.message:String(i)}`,cause:new we("bundle_fetch",r,i)}}const s=t.headers.get("etag");return{status:200,raw:a,...s!==null?{etag:s}:{}}}o(ee,"fetchProviderBundle");const M=new WeakSet;class k extends Error{static{o(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(a=>r[a]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}o(ne,"wrongBundle");function N(e,n,r){m({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof k?{issues:r.issues}:{}},error:r})}o(N,"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 instanceof Error?r.message:String(r),...ve(r instanceof Error?r.cause:void 0)}})}o(re,"logFailure");async function te(){return import("../../commands/pkg/plugin-manage.js")}o(te,"pluginManage");const y=new Map,b=o(e=>`${W()}\0${e}`,"installKey");function Pe(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}o(Pe,"publicInstallCause");function Se(e){if(h(e)===void 0)return y.get(b(e))??{status:"installing"}}o(Se,"providerInstallState");function Ie(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${Se(e)?.error??"bundle installation pending"}`)}o(Ie,"unavailableProviderError");function h(e){const n=j(v(e));if(!(n?.provider===void 0||n.transport?.kind!=="http"&&n.transport?.kind!=="machine")&&ge(d(v(e),"commands.json"))){try{if(E(d(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}}}}o(h,"installedManifest");async function R(e,n,r=!1){const t=J();h(e)===void 0&&y.set(b(e),{status:"installing"});try{return x(d(t,"tmp")),await Z(V(t,e),async()=>{const a=v(e),s=j(a);if(s!==null&&s.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const i=h(e)!==void 0;if(i&&r)return"unchanged";const f=i&&n?le("user").plugins[e]?.etag:void 0,u=await ee(e,f!==void 0?{ifNoneMatch:f}:{});if(u.status===304)return"unchanged";if(u.status===404||u.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${u.message})`,{param:"provider"});if(u.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${u.message}`,{cause:u.cause});const l=de(u.raw);if(i&&ce(e,"user",a,l))return await fe("user",e,u.etag,{swapped:!1}),"unchanged";const{installPluginArchive:w}=await te();try{await w(e,u.raw,{scope:"user",scopeRootPath:t,root:a,version:l,etag:u.etag,enable:!i,bundle:{endpoint:`${I()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:I()},accept:o(p=>ne(e,void 0,p),"accept")})}catch(p){N(e,void 0,p);const F=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw M.add(F),F}try{await z(async p=>await L(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:i}}),y.delete(b(e)),i?"updated":"installed"},{timeoutMs:X,timeoutError:o(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(a){throw h(e)===void 0&&y.set(b(e),{status:"install_failed",error:Pe(a)}),a instanceof c?a:(m({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:a}),Ie(e))}}o(R,"refreshInstalled");async function en(e){B(e),h(e)===void 0&&await R(e,!1,!0)}o(en,"ensureProviderBundle");async function nn(e,n){return B(e),n.version!==void 0&&h(e)?.provider.major!==n.version?D(e,n.version,n.conditional):R(e,n.conditional)}o(nn,"refreshProviderBundle");async function oe(e,n,r){const{validatePluginArchive:t}=await te(),a=await t(r);if(a.bundle===void 0)throw new k(`provider "${e}" major ${n} bundle is invalid: ${a.issues.map(i=>i.message).join("; ")}`,a.issues);const s=ne(e,n,a.bundle);if(s!==void 0)throw new k(`provider "${e}" major ${n} bundle is invalid: ${s}`,[]);if(a.bundle.transport===void 0)throw new k(`provider "${e}" major ${n} bundle declares no transport`,[]);return a.bundle}o(oe,"validateArchive");function Me(e){x(e),T(d(e,"retired"),`${new Date().toISOString()}
|
|
2
|
+
`),$(d(e,"bundle.tar"),{force:!0}),$(d(e,"etag"),{force:!0})}o(Me,"retire");function ae(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}o(ae,"retiredError");async function D(e,n,r){const t=Y(e,n),a=d(t,"bundle.tar"),s=g(a),i=s&&r&&g(d(t,"etag"))?S(d(t,"etag"),"utf8"):void 0,f=await ee(e,{version:n,...i!==void 0?{ifNoneMatch:i}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw Me(t),m({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),ae(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`,{cause:f.cause});try{await oe(e,n,f.raw)}catch(l){N(e,n,l);const w=new c(s?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw M.add(w),w}x(t),$(d(t,"retired"),{force:!0});const u=s&&Buffer.from(f.raw).equals(S(a));return u||T(a,f.raw),f.etag!==void 0?T(d(t,"etag"),f.etag):$(d(t,"etag"),{force:!0}),u?"unchanged":s?"updated":"installed"}o(D,"refreshPinned");function ke(){const e=[],n=C();if(!g(n))return e;for(const r of U(n)){if(!Q.test(r))continue;let t;try{t=U(d(n,r))}catch{continue}for(const a of t){const s=/^v([1-9][0-9]*)$/.exec(a);s&&g(d(n,r,a,"bundle.tar"))&&e.push([r,Number(s[1])])}}return e}o(ke,"cachedMajors");function Ee(e){const n=ue(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}o(Ee,"readCommands");const _=new Map,H=new Map;function Te(e,n){const r=E(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,a=H.get(e);if(a?.stamp===t)return a.commands;const s=Ee(JSON.parse(S(n,"utf8")));return H.set(e,{stamp:t,commands:s}),s}o(Te,"installedCommands");async function rn(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),w=Te(e,d(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:w,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(g(d(t,"retired")))throw ae(e,n);const a=d(t,"bundle.tar");g(a)||await D(e,n,!1);const s=`${e}/${n}`,i=E(a),f=`${i.mtimeMs}:${i.size}`;let u=_.get(s)?.stamp===f?_.get(s).bundle:void 0;if(u===void 0){try{u=await oe(e,n,new Uint8Array(S(a)))}catch(l){throw N(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}_.set(s,{stamp:f,bundle:u})}return{id:e,major:n,provider:u.provider,commands:u.commands,transport:u.transport.kind==="machine"?{kind:"machine"}:{kind:"http",endpoint:u.transport.endpoint},source:"pinned"}}o(rn,"resolveProviderBundle");let P=null,O=!1;function xe(e,n){return e?.provider?.id===n&&e.bundle?.endpoint===`${I()}/providers/${encodeURIComponent(n)}/plugin`}o(xe,"isDirectoryBundle");async function ie(e){return K(await A()).includes(e)}o(ie,"grantNames");async function je(e){await Z(V(J(),e),async()=>{if(!(await ie(e)||!xe(j(v(e)),e))){$(v(e),{recursive:!0,force:!0}),await pe("user",n=>{delete n.plugins[e]}),await me("user",n=>{delete n.plugins[e]}),$(d(C(),e),{recursive:!0,force:!0}),H.delete(e),y.delete(b(e));for(const n of[..._.keys()])n.startsWith(`${e}/`)&&_.delete(n);try{await z(async n=>await L(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:X,timeoutError:o(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}o(je,"uninstallProvider");async function Ae(){if(!(await A()).every(e=>e.status!=="active")){for(const e of ye("user"))if(!(e.manifest.provider===void 0||await ie(e.name)))try{await je(e.name)}catch(n){m({level:"error",event:"daemon.providers.bundle_uninstall_failed",fields:{provider:e.name},error:n})}}}o(Ae,"uninstallDisconnected");async function Ce(){if(!G())return;const e=K(await A());for(const n of e)try{await R(n,!0)}catch(r){r instanceof Object&&M.has(r)||re(n,void 0,r)}await Ae();for(const[n,r]of ke())try{await D(n,r,!0)}catch(t){if(t instanceof c&&t.code==="provider_version_retired"||t instanceof Object&&M.has(t))continue;re(n,r,t)}}o(Ce,"syncOnce");function tn(){return P!==null?(O=!0,P):(P=(async()=>{try{do O=!1,await Ce();while(O)}finally{P=null}})(),P)}o(tn,"syncProviderBundles");export{en as ensureProviderBundle,Se as providerInstallState,nn as refreshProviderBundle,rn as resolveProviderBundle,tn as syncProviderBundles,Ie as unavailableProviderError};
|
|
@@ -8,6 +8,8 @@ export interface PluginInstall {
|
|
|
8
8
|
url: string | null;
|
|
9
9
|
ref: string | null;
|
|
10
10
|
} | null;
|
|
11
|
+
/** A directory provider bundle (`isProviderBundleRow`): a provider call installs it into this process's own plugin directory, so there is nothing to fetch. */
|
|
12
|
+
providerBundle: boolean;
|
|
11
13
|
}
|
|
12
14
|
/** The directory holding this person's materialized plugins as `<dir>/<plugin name>`; undefined when none were materialized. */
|
|
13
15
|
export declare function materializedPluginsDir(person: string): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var z=Object.defineProperty;var u=(e,
|
|
1
|
+
var z=Object.defineProperty;var u=(e,r)=>z(e,"name",{value:r,configurable:!0});import{cpSync as S,existsSync as d,mkdirSync as b,renameSync as M,rmSync as w}from"node:fs";import{tmpdir as v}from"node:os";import{isAbsolute as $,join as l,relative as A,resolve as P}from"node:path";import{CrtrError as c}from"../errors.js";import{gitAsync as x}from"../git.js";import{readMarketplaceManifest as D}from"../manifest.js";const m=new Map,h=new Map;function Z(e){const r=m.get(e);if(r!==void 0&&"failure"in r)throw r.failure;return r?.dir}u(Z,"materializedPluginsDir");function y(e){return l(v(),"crtr-plugin-cache",e.replace(/[^A-Za-z0-9._-]/g,"_"))}u(y,"cacheRoot");async function _(e,r,o,n){const t=await x(["clone","--depth","1",...r?["--branch",r]:[],e,o]);if(t.status!==0)throw new c("plugin_install_failed",`${n}: git clone ${e} failed: ${t.stderr.trim()}`)}u(_,"cloneInto");async function R(e,r){const o=new Map;b(l(e,"plugins"),{recursive:!0});for(const n of r){if(n.sourcePath!==null)throw new c("plugin_source_unreachable",`${n.name}: its source is ${n.sourcePath} on the machine that registered it, which the control plane cannot read`);const t=n.marketplace;if(n.providerBundle)continue;if(t===null)throw new c("plugin_source_unreachable",`${n.name}: it names neither a marketplace nor a path`);if(t.url===null||t.url==="")throw new c("plugin_source_unreachable",`${n.name}: its marketplace ${t.name} has no url`);const s=`${t.url}\0${t.ref??""}`;let a=o.get(s);a===void 0&&(a=l(e,"marketplaces",String(o.size)),await _(t.url,t.ref,a,`${n.name}: marketplace ${t.name}`),o.set(s,a));const i=D(a)?.plugins.find(k=>k.name===n.name);if(i===void 0)throw new c("plugin_install_failed",`${n.name}: marketplace ${t.name} (${t.url}) does not list it`);const f=l(e,"plugins",n.name);if($(i.source)||/^[a-z][a-z0-9+.-]*:|^git@/i.test(i.source)||i.source.endsWith(".git")){await _(i.source,null,f,n.name);continue}const p=P(a,i.source),g=A(a,p);if(g.startsWith("..")||$(g))throw new c("plugin_install_failed",`${n.name}: its source ${i.source} leaves the marketplace`);if(!d(p))throw new c("plugin_install_failed",`${n.name}: its source ${i.source} is not in marketplace ${t.name}`);S(p,f,{recursive:!0})}w(l(e,"marketplaces"),{recursive:!0,force:!0})}u(R,"installAll");async function q(e,r,o){const n=m.get(e);if(n?.revision===r&&!("failure"in n))return;const t=l(y(e),String(r)),s=`${e}\0${r}`;let a=h.get(s);a===void 0&&(a=(async()=>{if(!d(l(t,"plugins"))){const i=l(y(e),`${r}.${process.pid}.${Date.now()}`);try{await R(i,o);try{M(i,t)}catch(f){if(!d(l(t,"plugins")))throw f}}finally{w(i,{recursive:!0,force:!0})}}return l(t,"plugins")})().finally(()=>h.delete(s)),h.set(s,a));try{m.set(e,{revision:r,dir:await a})}catch(i){if(!(i instanceof c))throw i;m.set(e,{revision:r,failure:i})}}u(q,"materializePlugins");export{q as materializePlugins,Z as materializedPluginsDir};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var O=Object.defineProperty;var _=(a,
|
|
1
|
+
var O=Object.defineProperty;var _=(a,t)=>O(a,"name",{value:t,configurable:!0});import{openDb as S,withCanvasWrite as N}from"../canvas/db.js";import{isCloudRole as R}from"../canvas/person-state.js";import{grantedProviderIds as T,isProviderBundleRow as y}from"../grants/mirror.js";import{materializePlugins as D}from"./materialized-plugins.js";import{revisionOf as v,setPersonSnapshot as L,setSettingsReloader as C,setSettingsTransport as h,SettingsRevisionConflictError as I}from"./person-store.js";const s=_(a=>typeof a=="string"?a:void 0,"s");async function w(a,t){const o=await a.prepare("SELECT * FROM profiles WHERE person = ? AND deleting_since IS NULL ORDER BY created_at, id").all(t),u=await a.prepare("SELECT p.profile_id, p.path FROM profile_projects p JOIN profiles f ON f.id = p.profile_id WHERE f.person = ? ORDER BY p.profile_id, p.position").all(t),n=new Map;for(const e of u)(n.get(e.profile_id)??n.set(e.profile_id,[]).get(e.profile_id)).push({path:e.path});const r=o.map(e=>{const l=JSON.parse(String(e.metadata_json)),p=JSON.parse(String(e.delivery_limits_json));return{profileId:String(e.id),manifest:{schema_version:1,name:String(e.name),projects:n.get(String(e.id))??[],paused_at:s(e.paused_at)??null,...s(e.default_kind)===void 0?{}:{default_kind:s(e.default_kind)},...s(e.grantee)===void 0?{}:{grantee:s(e.grantee)},...s(e.environment_id)===void 0?{}:{environment_id:s(e.environment_id)},...Object.keys(p).length===0?{}:{delivery_limits:p},...Object.keys(l).length===0?{}:{metadata:l},created_at:String(e.created_at),last_used_at:s(e.last_used_at)??null}}}),i=Object.fromEntries((await a.prepare("SELECT directory, profile_id FROM profile_defaults WHERE person = ?").all(t)).map(e=>[e.directory,e.profile_id])),f={};for(const e of await a.prepare("SELECT owner_key, raw_json FROM scope_settings WHERE person = ?").all(t))f[e.owner_key]=JSON.parse(e.raw_json);const m={},E=_(e=>m[e]??={marketplaces:{},plugins:{},activeCanvas:null},"stateOf");for(const e of await a.prepare("SELECT owner_key, last_self_check, active_canvas FROM scope_bookkeeping WHERE person = ?").all(t)){const l=E(e.owner_key);e.last_self_check!==null&&(l.last_self_check=e.last_self_check),l.activeCanvas=e.active_canvas}for(const e of await a.prepare("SELECT * FROM scope_marketplaces WHERE person = ? ORDER BY owner_key, name").all(t)){const l=String(e.owner_key),p=String(e.name);if(E(l).marketplaces[p]=s(e.last_updated)===void 0?{}:{last_updated:s(e.last_updated)},s(e.url)!==void 0){const d=f[l]??={},c=d.marketplaces??={};c[p]={url:s(e.url),ref:s(e.ref)??"",installed_at:s(e.installed_at)??""}}}for(const e of await a.prepare("SELECT * FROM scope_plugins WHERE person = ? ORDER BY owner_key, name").all(t)){const l=String(e.owner_key),p=String(e.name);E(l).plugins[p]={...s(e.last_updated)===void 0?{}:{last_updated:s(e.last_updated)},...s(e.etag)===void 0?{}:{etag:s(e.etag)},...s(e.checked_at)===void 0?{}:{checked_at:s(e.checked_at)}};const d=f[l]??={},c=d.plugins??={};c[p]={enabled:Number(e.enabled)===1,...s(e.source_marketplace)===void 0?{}:{source_marketplace:s(e.source_marketplace)},...s(e.version)===void 0?{}:{version:s(e.version)}}}return{profiles:r,defaults:i,settings:f,state:m}}_(w,"readSnapshotFromRows");async function g(a){const t=await S(),o=await w(t,a);return R()&&await j(t,a),L(a,o),o}_(g,"loadPersonSettings");async function j(a,t){const o=await a.prepare(`SELECT p.name, p.version, p.source_path, p.source_marketplace, m.url, m.ref FROM scope_plugins p
|
|
2
2
|
LEFT JOIN scope_marketplaces m ON m.person = p.person AND m.owner_key = p.owner_key AND m.name = p.source_marketplace
|
|
3
|
-
WHERE p.person = ? AND p.owner_key = 'user' AND p.enabled = 1 ORDER BY p.name`).all(
|
|
4
|
-
ON CONFLICT (person, owner_key, name) DO UPDATE SET enabled = EXCLUDED.enabled, source_marketplace = EXCLUDED.source_marketplace, version = EXCLUDED.version`).run(n
|
|
5
|
-
ON CONFLICT (person, owner_key, name) DO UPDATE SET url = EXCLUDED.url, ref = EXCLUDED.ref, installed_at = EXCLUDED.installed_at`).run(n
|
|
6
|
-
ON CONFLICT (person, owner_key) DO UPDATE SET last_self_check = EXCLUDED.last_self_check, active_canvas = EXCLUDED.active_canvas`).run(n
|
|
3
|
+
WHERE p.person = ? AND p.owner_key = 'user' AND p.enabled = 1 ORDER BY p.name`).all(t),u=await a.prepare("SELECT plugins_revision FROM person_settings WHERE person = ?").get(t),n=await T(t),r=o.map(i=>({name:String(i.name),version:s(i.version)??null,sourcePath:s(i.source_path)??null,marketplace:s(i.source_marketplace)===void 0?null:{name:s(i.source_marketplace),url:s(i.url)??null,ref:s(i.ref)??null},providerBundle:y({name:String(i.name),source_marketplace:s(i.source_marketplace)??null,source_path:s(i.source_path)??null},n)}));await D(t,u===void 0?0:Number(u.plugins_revision),r)}_(j,"materializePersonPlugins");async function A(a,t,o){return(await w(a,t)).settings[o]}_(A,"currentRaw");async function U(a,t,o){let u=!1;for(const n of o)if(n.op==="settings.replace"){const r=await A(a,t,n.owner);if(n.ifRevision!==v(r))throw new I;const{plugins:i,marketplaces:f,...m}=n.raw,E=JSON.stringify(m);await a.prepare("INSERT INTO scope_settings (person, owner_key, raw_json) VALUES (?, ?, ?) ON CONFLICT (person, owner_key) DO UPDATE SET raw_json = EXCLUDED.raw_json").run(t,n.owner,E);const e=i??{},l=f??{},p=JSON.stringify([r?.plugins,r?.marketplaces]);p!==JSON.stringify([i,f])&&!(p==="[null,null]"&&i===void 0&&f===void 0)&&(u=!0),await a.prepare("DELETE FROM scope_plugins WHERE person = ? AND owner_key = ? AND NOT (name IN (SELECT jsonb_array_elements_text(?::jsonb)))").run(t,n.owner,JSON.stringify(Object.keys(e)));for(const[d,c]of Object.entries(e))await a.prepare(`INSERT INTO scope_plugins (person, owner_key, name, enabled, source_marketplace, version) VALUES (?, ?, ?, ?, ?, ?)
|
|
4
|
+
ON CONFLICT (person, owner_key, name) DO UPDATE SET enabled = EXCLUDED.enabled, source_marketplace = EXCLUDED.source_marketplace, version = EXCLUDED.version`).run(t,n.owner,d,c.enabled===!1?0:1,c.source_marketplace??null,c.version??null);await a.prepare("DELETE FROM scope_marketplaces WHERE person = ? AND owner_key = ? AND NOT (name IN (SELECT jsonb_array_elements_text(?::jsonb)))").run(t,n.owner,JSON.stringify(Object.keys(l)));for(const[d,c]of Object.entries(l))await a.prepare(`INSERT INTO scope_marketplaces (person, owner_key, name, url, ref, installed_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
5
|
+
ON CONFLICT (person, owner_key, name) DO UPDATE SET url = EXCLUDED.url, ref = EXCLUDED.ref, installed_at = EXCLUDED.installed_at`).run(t,n.owner,d,c.url??null,c.ref??null,c.installed_at??null)}else if(n.op==="state.write"){await a.prepare(`INSERT INTO scope_bookkeeping (person, owner_key, last_self_check, active_canvas) VALUES (?, ?, ?, ?)
|
|
6
|
+
ON CONFLICT (person, owner_key) DO UPDATE SET last_self_check = EXCLUDED.last_self_check, active_canvas = EXCLUDED.active_canvas`).run(t,n.owner,n.state.last_self_check??null,n.state.activeCanvas??null);for(const[r,i]of Object.entries(n.state.marketplaces))await a.prepare("UPDATE scope_marketplaces SET last_updated = ? WHERE person = ? AND owner_key = ? AND name = ?").run(i.last_updated??null,t,n.owner,r);for(const[r,i]of Object.entries(n.state.plugins))await a.prepare("UPDATE scope_plugins SET last_updated = ?, etag = ?, checked_at = ? WHERE person = ? AND owner_key = ? AND name = ?").run(i.last_updated??null,i.etag??null,i.checked_at??null,t,n.owner,r)}else if(n.op==="profile.insert"){const r=n.profile.manifest;await a.prepare("INSERT INTO profiles (id, person, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json, environment_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(n.profile.profileId,t,r.name,r.created_at,r.last_used_at,r.paused_at,r.default_kind??null,r.grantee??null,JSON.stringify(r.metadata??{}),JSON.stringify(r.delivery_limits??{}),r.environment_id??null),await k(a,n.profile)}else if(n.op==="profile.update"){const r=n.profile.manifest;if((await a.prepare("UPDATE profiles SET name = ?, last_used_at = ?, paused_at = ?, default_kind = ?, grantee = ?, metadata_json = ?, delivery_limits_json = ?, environment_id = ? WHERE id = ? AND person = ?").run(r.name,r.last_used_at,r.paused_at,r.default_kind??null,r.grantee??null,JSON.stringify(r.metadata??{}),JSON.stringify(r.delivery_limits??{}),r.environment_id??null,n.profile.profileId,t)).changes!==1)throw new Error(`no stored profile ${n.profile.profileId}`);await k(a,n.profile)}else if(n.op==="profile.delete"){const r=`profile:${n.profileId}`;for(const i of["scope_settings","scope_bookkeeping","scope_marketplaces","scope_plugins"])await a.prepare(`DELETE FROM ${i} WHERE person = ? AND owner_key = ?`).run(t,r);await a.prepare("DELETE FROM profiles WHERE id = ? AND person = ?").run(n.profileId,t)}else{await a.prepare("DELETE FROM profile_defaults WHERE person = ?").run(t);for(const[r,i]of Object.entries(n.defaults))await a.prepare("INSERT INTO profile_defaults (person, directory, profile_id) VALUES (?, ?, ?)").run(t,r,i)}u&&await a.prepare("INSERT INTO person_settings (person, plugins_revision) VALUES (?, 1) ON CONFLICT (person) DO UPDATE SET plugins_revision = person_settings.plugins_revision + 1").run(t),await a.prepare("SELECT pg_notify('crtr_signal', ?)").get(`settings:${t}`)}_(U,"applyOpsToRows");async function k(a,t){await a.prepare("DELETE FROM profile_projects WHERE profile_id = ?").run(t.profileId);let o=0;for(const u of t.manifest.projects)await a.prepare("INSERT INTO profile_projects (profile_id, path, position) VALUES (?, ?, ?)").run(t.profileId,u.path,o++)}_(k,"replaceProjects");const F=_(async(a,t)=>(await N(o=>U(o,a,t)),await g(a)),"rowsTransport");h(F),C(g);export{U as applyOpsToRows,g as loadPersonSettings,w as readSnapshotFromRows,F as rowsTransport};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var w=Object.defineProperty;var
|
|
2
|
-
WHERE person = ? AND enabled = 1 AND owner_key IN (${u.map(()=>"?").join(", ")}) ORDER BY name`).all(r,...u),o=new Map;for(const e of t)(!o.has(e.name)||e.owner_key!=="user")&&o.set(e.name,e);const a=[];for(const e of o.values()){if(e.source_path!==null){a.push({name:e.name,source:{path:e.source_path},version:e.version});continue}if(e.source_marketplace===null)throw new s(500,"plugin_source_missing",`plugin ${e.name} names neither a marketplace nor a path`);const c=await i.prepare("SELECT url, ref FROM scope_marketplaces WHERE person = ? AND owner_key = ? AND name = ?").get(r,e.owner_key,e.source_marketplace);a.push({name:e.name,source:{marketplace:{url:c?.url??null,ref:c?.ref??null}},version:e.version})}return a}
|
|
1
|
+
var w=Object.defineProperty;var p=(r,n)=>w(r,"name",{value:n,configurable:!0});import{ApiError as s}from"../../../api/errors.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{openDb as d}from"../../../core/canvas/db.js";import{grantedProviderIds as h,isProviderBundleRow as g}from"../../../core/grants/mirror.js";import{environmentOfNode as E,recipeOf as k}from"../environment-of.js";import{mintInstallationToken as b}from"./git-credential.js";async function v(r,n){const i=await d(),u=n===null?["user"]:["user",`profile:${n}`],t=await i.prepare(`SELECT owner_key, name, version, source_marketplace, source_path FROM scope_plugins
|
|
2
|
+
WHERE person = ? AND enabled = 1 AND owner_key IN (${u.map(()=>"?").join(", ")}) ORDER BY name`).all(r,...u),o=new Map;for(const e of t)(!o.has(e.name)||e.owner_key!=="user")&&o.set(e.name,e);const a=[],l=await h(r);for(const e of o.values()){if(g(e,l))continue;if(e.source_path!==null){a.push({name:e.name,source:{path:e.source_path},version:e.version});continue}if(e.source_marketplace===null)throw new s(500,"plugin_source_missing",`plugin ${e.name} names neither a marketplace nor a path`);const c=await i.prepare("SELECT url, ref FROM scope_marketplaces WHERE person = ? AND owner_key = ? AND name = ?").get(r,e.owner_key,e.source_marketplace);a.push({name:e.name,source:{marketplace:{url:c?.url??null,ref:c?.ref??null}},version:e.version})}return a}p(v,"plugins");async function x(r){const{principal:n}=r;if(n.class!=="broker")throw new s(403,"broker_only","the start bundle is read with a node token");const i=r.params.execution_id??"",u=await d(),t=await u.prepare("SELECT node_id, state, launch_json FROM executions WHERE execution_id = ?").get(i);if(t===void 0||t.node_id!==n.node)throw new s(403,"not_your_execution",`execution ${i} is not an execution of this node`);if(t.state!=="launching"&&t.state!=="running")throw new s(403,"execution_ended",`execution ${i} is ${t.state}`);if(t.launch_json===null)throw new s(409,"launch_missing",`execution ${i} carries no launch recipe`);const o=await f(n.node);if(o===null)throw new s(404,"node_not_found",`no node ${n.node}`);const a=await u.prepare("SELECT plugins_revision FROM person_settings WHERE person = ?").get(n.sub),l=await E(o.profile_id??null),e=await k(l),c=e?.repositories.map(_=>_.github_full_name)??[],m=c.length===0?null:(await b(n.sub,c)).author;return{status:200,body:{launch:JSON.parse(t.launch_json),node:{id:o.node_id,app:n.app,cwd:o.cwd,profile:o.profile_id??null,isolated:o.isolated===!0,environment_id:l},recipe:e,git_identity:m,plugins_revision:a===void 0?0:Number(a.plugins_revision),plugins:await v(n.sub,o.profile_id??null)}}}p(x,"handleStart");const A=[{method:"GET",pattern:"/v1/executions/:execution_id/start",handler:x}];export{A as executionStartRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var I=Object.defineProperty;var s=(e,r)=>I(e,"name",{value:r,configurable:!0});import{randomUUID as R}from"node:crypto";import{cloudPersonScope as $}from"../../../core/runs/operations.js";import{directoryIssuer as O}from"../../../core/directory/issuer.js";import{activeGrantForApp as q,grantFor as G,localGrants as _}from"../../../core/grants/mirror.js";import{getNode as
|
|
1
|
+
var I=Object.defineProperty;var s=(e,r)=>I(e,"name",{value:r,configurable:!0});import{randomUUID as R}from"node:crypto";import{cloudPersonScope as $}from"../../../core/runs/operations.js";import{directoryIssuer as O}from"../../../core/directory/issuer.js";import{activeGrantForApp as q,grantFor as G,localGrants as _,providerIdsFromGrants as N}from"../../../core/grants/mirror.js";import{getNode as T}from"../../../core/canvas/canvas.js";import{scopeAllowed as F}from"../../../core/scopes.js";import{ProviderError as f,providerErrorBody as x,scopeMissing as j}from"../../../core/providers/errors.js";import{callProvider as k,providerTools as C}from"../../../core/providers/call.js";import{providerInstallState as B}from"../../../core/providers/bundles.js";import{providerRootClaims as D,resolveProviderRef as M}from"../../../core/providers/roots.js";import{listInstalledPlugins as U}from"../../../core/resolver.js";import{listAudit as z,writeProviderAudit as L}from"../../../core/providers/audit.js";import{emitEvent as g}from"../../../core/events/emit.js";import{resolvedScopes as y}from"../router.js";const _e=new Set(["provider.tools","provider.call","audit.list"]);async function V(e){if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null,origin:{node:null,run:null,machine:e.via}};const r=await G(e.actor.app,e.sub);if(!r)throw new f("grant_revoked",`app ${e.actor.app} has no active grant`);return{class:"app",sub:e.sub,grantee:e.actor.app,client_id:null,resolved:r.resolved,nodeScopes:e.actor.node_scopes,run_id:null,node_id:null,origin:{node:e.actor.node,run:e.actor.place?.run??null,machine:e.via}}}if(e.class==="machine")throw new f("permission_error","this operation is not available to the caller");if(e.class==="person"&&e.sub!==void 0)throw new f("permission_error","provider calls are made by the install's person, a node or an app");if(e.class==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null};if(e.class==="app"){const r=await q(e.app);if(!r)throw new f("grant_revoked",`app ${e.app} has no active grant`);return{class:"app",sub:r.sub,grantee:e.app,client_id:null,resolved:r.resolved,nodeScopes:e.scopes,run_id:null,node_id:null}}return e.class==="cron"?{class:"app",sub:e.sub,grantee:e.app,client_id:null,resolved:await y(e),nodeScopes:e.scopes,run_id:e.origin_run,node_id:null}:{class:"broker",sub:e.sub,grantee:e.app,client_id:e.client_id,resolved:await y(e),nodeScopes:e.scopes,run_id:e.run??(await T(e.node))?.run_id??null,node_id:e.node}}s(V,"callerFromRequest");function ge(e){return{class:"app",sub:e.sub,grantee:e.grantee,client_id:e.client_id,resolved:e.resolved,nodeScopes:null,run_id:null,node_id:null}}s(ge,"callerFromApp");const Z=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function W(e){const r=Z.exec(e);return r!==null&&r[1]!=="crtr"}s(W,"isProviderScope");const S=new WeakMap;function J(e){let r=S.get(e);return r===void 0&&(r=new Set(e),S.set(e,r)),r}s(J,"heldSet");function A(e,r){return e===null?!0:W(r)?J(e).has(r):F(e,r)}s(A,"holds");function P(e,r){return e.class==="person"?!0:A(e.resolved??[],r)&&A(e.nodeScopes,r)}s(P,"callerAllows");function ye(e,r,t){const o=`${r}:${t}`;if(!P(e,o))throw j(o)}s(ye,"requireGroup");function X(){return!!O()}s(X,"providerOperationsEnabled");const H=/^[A-Za-z0-9_-]{1,64}$/;function m(e,r){return new f("invalid_request",e,{param:r})}s(m,"invalid");function E(e){if(typeof e!="string"||!H.test(e)||e==="crtr")throw m("provider must be a provider id","provider");return e}s(E,"parseProvider");function K(e){if(!e||typeof e!="object"||Array.isArray(e))throw new f("invalid_request","a JSON object is required");const r=e,t=E(r.provider),o=r.tool;if(typeof o!="string"||!o.trim())throw m("tool must be a non-empty string","tool");const i=r.arguments??{};if(!i||typeof i!="object"||Array.isArray(i))throw m("arguments must be an object","arguments");const d=r.version;if(d!==void 0&&(typeof d!="number"||!Number.isInteger(d)||d<1))throw m("version must be an integer >= 1","version");const a=r.approval;if(a!==void 0&&(typeof a!="string"||!a))throw m("approval must be a question id","approval");const l=r.machine;if(l!==void 0&&(typeof l!="string"||!l.trim()))throw m("machine must be a machine id","machine");return{provider:t,tool:o.trim(),arguments:i,...d===void 0?{}:{version:d},...a===void 0?{}:{approval:a},...l===void 0?{}:{machine:l.trim()}}}s(K,"parseCallInput");async function Q(e,r=new URLSearchParams){const t=s((u,v,w)=>{const c=r.get(u);if(c===null)return v;if(!/^\d+$/.test(c)||Number(c)>w||Number(c)<1)throw m(`invalid ${u}`,u);return Number(c)},"numeric"),o=r.has("after")?t("after",0,Number.MAX_SAFE_INTEGER):void 0,i=t("limit",100,1e3),d=Object.fromEntries(["operation","outcome","run_id","since","until"].filter(u=>r.has(u)).map(u=>[u,r.get(u)])),a=$(e.sub),l=a===void 0?{}:{sub:a};return{status:200,body:await z(e.class==="person"?null:e.grantee,{after:o,limit:i,...l,...d})}}s(Q,"auditList");async function Y(e,r,t){let o=r;const i=performance.now(),d=U("user"),{skipped:a}=D(d),l=new Set(d.filter(n=>n.enabled&&n.manifest.provider?.id===n.name&&!a.has(n.name)).map(n=>n.name)),u=N(await _()).filter(n=>!a.has(n)),v=[...new Set([...l,...u])].sort();if(o!==void 0){const n=M(o);if(n===null)throw new f("not_found",`provider "${o}" is not available`);o=n}if(o!==void 0&&!v.includes(o))throw new f("not_found",`provider "${o}" is not available`);const w=o===void 0?v:[o],c=e.class==="person"?(await _()).flatMap(n=>n.status==="active"?n.resolved_scopes.map(({scope:p})=>p):[]):e.resolved??[],h=await Promise.all(w.map(async n=>{if(l.has(n))return C(e,n);if(!c.some(b=>b.startsWith(`${n}:`)&&P(e,b)))return null;const p=B(n);return{provider:n,display_name:n,status:p.status,...p.error?{error:p.error}:{},groups:[],commands:[]}})).then(n=>n.filter(p=>p!==null));if(g({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-i,fields:{caller:e.class,provider:o??null,providers:h.length}}),o!==void 0){if(h.length===0)throw new f("not_found",`provider "${o}" is not available`);return{status:200,body:h[0]}}return{status:200,body:{providers:h}}}s(Y,"providerTools");async function ee(e,r,t){return k(e,r,t)}s(ee,"providerCall");async function re(e,r,t,o){if(!X())throw new f("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return await Q(r,t.query);if(e==="provider.tools")return Y(r,t.params.provider===void 0?void 0:E(t.params.provider),o);if(e==="provider.call"){let i;try{i=K(t.body)}catch(d){const a=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body:{};throw await L({request_id:o.requestId,caller:r,provider:typeof a.provider=="string"?a.provider:"",tool:typeof a.tool=="string"?a.tool:"",outcome:`refused:${d instanceof f?d.code:"invalid_request"}`}),d}return ee(r,i,o)}throw new f("not_found","unknown operation")}s(re,"handleProviderOperation");function oe(e){const r=new AbortController,t=s(()=>{e.writableFinished||r.abort()},"abort");return e.on("close",t),{signal:r.signal,release:s(()=>e.off("close",t),"release")}}s(oe,"abortOnClose");function te(e,r,t,o){const i=x(e,r,t,o??void 0);return i.status>=500&&!(e instanceof f)&&g({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),i}s(te,"providerFailure");const Se=[{method:"GET",pattern:"/v1/audit",name:"audit.list"},{method:"GET",pattern:"/v1/providers/tools",name:"provider.tools"},{method:"GET",pattern:"/v1/providers/:provider/tools",name:"provider.tools"},{method:"POST",pattern:"/v1/provider/call",name:"provider.call"}].map(({method:e,pattern:r,name:t})=>({method:e,pattern:r,handler:s(async o=>{const i=R(),{signal:d,release:a}=oe(o.res);let l=null;try{return l=await V(o.principal),{...await re(t,l,{params:o.params,body:o.body,query:new URL(o.req.url??"/","http://runtime.local").searchParams},{requestId:i,signal:d,...o.causedBy===void 0?{}:{causedBy:o.causedBy}}),headers:{"Request-Id":i}}}catch(u){return te(u,i,!1,l?.run_id)}finally{a()}},"handler")}));export{_e as PROVIDER_OPERATIONS,oe as abortOnClose,P as callerAllows,ge as callerFromApp,V as callerFromRequest,re as handleProviderOperation,K as parseCallInput,E as parseProvider,te as providerFailure,X as providerOperationsEnabled,Se as providerRoutes,ye as requireGroup};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Ue=Object.defineProperty;var i=(e,r)=>Ue(e,"name",{value:r,configurable:!0});import{createServer as ee}from"node:http";import{createServer as Pe}from"node:https";import{chmodSync as ke,existsSync as $,readFileSync as re,unlinkSync as te}from"node:fs";import{MINIMUM_RUNTIME_VERSION_HEADER as ne,RUNTIME_VERSION_HEADER as j,minimumRuntimeRefusal as ze}from"../../api/runtime-version.js";import{CONTRACT_HEADER as M,CONTRACT_LEVEL as oe,REQUIRED_CONTRACT_HEADER as ie,contractRefusal as Ne}from"../../api/contract.js";import{crtrVersion as V}from"../../shared/crtr-version.js";import{apiSocketPath as He}from"../../core/canvas/paths.js";import{resolveNodeToken as Ie}from"../../core/canvas/node-tokens.js";import{isCloudRole as P,runAsPerson as B}from"../../core/canvas/person.js";import{personBearerVerifier as se}from"./auth/person-bearer.js";import{personHasSnapshot as W}from"../../core/scoped-state/person-store.js";import{loadPersonSettings as F}from"../../core/scoped-state/person-rows.js";import{AppPrincipalResolver as Le}from"./principal.js";import{createAppRequestHandler as Be}from"./app-listener.js";import{decodeJwt as De}from"jose";import{handleVendorSignIn as xe,isVendorSignInPath as Je}from"./vendor-signin.js";import{RunError as E}from"../../core/runs/operations.js";import{removeVendorAccount as $e,vendorStatus as je}from"./handlers/vendor-status.js";import{toErrorBody as Me}from"./map.js";import{CONTROL_BODY_MAX_BYTES as Ve}from"../../core/llm/body-limits.js";import{grantFor as We,hasSyncedGrants as Fe}from"../../core/grants/mirror.js";import{readConfig as ae}from"../../core/config.js";import{directoryIssuer as Ge}from"../../core/directory/issuer.js";import{peerUid as Ye}from"../../native/linux.js";import{findScopedApiToken as Ze,getApiToken as Ke,listScopedApiTokens as Xe}from"../../core/secrets.js";import{emitEvent as A}from"../../core/events/emit.js";import{envCrtrdTcp as Qe,envCrtrdToken as qe}from"../../shared/env.js";import{Router as le,actsAsPerson as ce}from"./router.js";import{handleAttachUpgrade as er,refuseUpgrade as y}from"./bridge.js";import{boundDaemonControl as rr}from"../control.js";import{attachRoutes as tr,mayAttachNode as ue}from"./handlers/attach.js";import{peerRoutes as nr}from"../peer/handlers.js";import{bashJobRoutes as or}from"./handlers/bash-jobs.js";import{bashRoutes as ir}from"./handlers/bash.js";import{hookExecRoutes as sr}from"./handlers/hook-exec.js";import{brokerOperationRoutes as de}from"./handlers/broker-ops.js";import{brokerSignalRoutes as ar,brokerSignalStreams as lr}from"./handlers/broker-signals.js";import{brokerRecoveryRoutes as pe,nodeFaultRoutes as cr}from"./handlers/broker-recovery.js";import{canvasRoutes as ur}from"./handlers/canvas.js";import{conversationRoutes as dr}from"./handlers/conversations.js";import{chatInventoryRoutes as pr}from"./handlers/chat-inventory.js";import{daemonRoutes as fr}from"./handlers/daemon.js";import{fileRoutes as mr}from"./handlers/files.js";import{focusRoutes as gr}from"./handlers/focus.js";import{analyticsRoutes as hr}from"./handlers/analytics.js";import{memoryReadsRoutes as Rr}from"./handlers/memory-reads.js";import{memoryRoutes as Ar}from"./handlers/memory.js";import{healthRoutes as yr}from"./handlers/health.js";import{humanRequestRoutes as vr}from"./handlers/human-requests.js";import{inboxRoutes as wr}from"./handlers/inbox.js";import{feedbackCommentRoutes as _r}from"./handlers/feedback-comments.js";import{objectRoutes as Sr}from"./handlers/objects.js";import{shareRoutes as Er}from"./handlers/shares.js";import{customObjectRoutes as br}from"./handlers/custom-objects.js";import{docRoutes as Tr}from"./handlers/docs.js";import{deliveryRoutes as fe}from"./handlers/delivery.js";import{deliveryRetryRoutes as Cr}from"./handlers/delivery-retry.js";import{packageDocRoutes as Or}from"./handlers/package-docs.js";import{documentAccessRoutes as Ur}from"./handlers/document-access.js";import{pluginAppRoutes as Pr}from"./handlers/plugin-app-token.js";import{brokerMailRoutes as me,messageRoutes as kr}from"./handlers/messages.js";import{modelAuthRoutes as zr}from"./handlers/modelauth.js";import{llmRoutes as Nr}from"./handlers/llm.js";import{providerRoutes as Hr}from"./handlers/providers.js";import{modelConfigRoutes as Ir}from"./handlers/model-config.js";import{settingsRoutes as Lr}from"./handlers/settings.js";import{nodeRoutes as Br}from"./handlers/nodes.js";import{nodeRecordRoutes as Dr}from"./handlers/node-records.js";import{nodeOutcomeRoutes as xr}from"./handlers/node-outcomes.js";import{nodeEventRoutes as Jr}from"./handlers/node-events.js";import{profileRoutes as $r}from"./handlers/profiles.js";import{prospectiveChatInventoryRoutes as jr}from"./handlers/prospective-chat-inventory.js";import{reportRoutes as Mr}from"./handlers/reports.js";import{runRoutes as Vr}from"./handlers/run-routes.js";import{reviewCommentRoutes as Wr}from"./handlers/review-comments.js";import{reviewRoutes as Fr}from"./handlers/reviews.js";import{subscriptionRoutes as Gr}from"./handlers/subscriptions.js";import{structuredRequestRoutes as Yr}from"./handlers/structured-requests.js";import{cronRoutes as Zr}from"./handlers/crons.js";import{setCronRequestHandler as ge}from"../cron/run-socket.js";import{worktreeRoutes as Kr}from"./handlers/worktree.js";import{limitsRoutes as Xr}from"./handlers/limits.js";import{chargeRoutes as Qr,spendLimitRoutes as qr}from"./handlers/charges.js";import{executionStartRoutes as et}from"./handlers/execution-start.js";import{gitCredentialRoutes as rt}from"./handlers/git-credential.js";function tt(e){return new le().registerAll(yr(e)).registerAll(fr).registerAll(Er).registerAll(Br).registerAll(Dr).registerAll(xr).registerAll(Cr).registerAll(Jr).registerAll(or).registerAll(ir).registerAll(sr).registerAll(de).registerAll(ar).registerAll(fe).registerAll(pe).registerAll(cr).registerAll(Kr).registerAll(Xr).registerAll(Qr).registerAll(et).registerAll(rt).registerAll(qr).registerAll(me).registerAll(kr).registerAll(Mr).registerAll(Vr).registerAll(Yr).registerAll(tr).registerAll(ur).registerAll(dr).registerAll(hr).registerAll(Rr).registerAll(Ar).registerAll(mr).registerAll(Sr).registerAll(br).registerAll(Tr).registerAll(Or).registerAll(Ur).registerAll(Pr).registerAll(gr).registerAll(Gr).registerAll(Zr).registerAll($r).registerAll(zr).registerAll(Nr).registerAll(Hr).registerAll(Ir).registerAll(Lr).registerAll(Fr).registerAll(Wr).registerAll(wr).registerAll(vr).registerAll(pr).registerAll(jr).registerAll(_r).registerAll(nr)}i(tt,"buildRouter");function nt(e,r){if(e.toUpperCase()!=="GET")return null;const s=r.split("/").filter(f=>f.length>0);if(s.length!==4||s[0]!=="v1"||s[1]!=="nodes"||s[3]!=="attach")return null;const o=s[2];return o===void 0||o===""?null:decodeURIComponent(o)}i(nt,"attachNodeId");function ot(e){const r=e.trim();if(r==="")return null;const s=r.lastIndexOf(":");if(s<0)return null;const o=r.slice(0,s),f=r.slice(s+1),p=Number(f);return!Number.isInteger(p)||p<0||p>65535?null:{host:o===""?"0.0.0.0":o,port:p}}i(ot,"parseTcp");function it(e,r){return`${e.includes(":")?`[${e}]`:e}:${r}`}i(it,"formatTcpAddress");const T={code:"unauthorized",message:"missing or invalid bearer token"},k={code:"owner_only",message:"attach is owner-only; a scoped token does not reach it"},H={code:"person_bearer_not_supported",message:"this listener accepts a person bearer only on a cloud role with CRTR_DIRECTORY_URL and CRTR_PUBLIC_API_URL set; call with a node, app or public token"},G={class:"anonymous",app:"",node:"",run:null,uid:-1,sub:"",grant:null,client_id:null,scopes:[]},z=new WeakMap;function I(e){return z.get(e)}i(I,"socketPrincipal");async function he(e,r){const s=process.platform==="linux"?Ye(e.socket):null,o=e.headers.authorization;if(o===void 0)return s===null||r.has(s)?{class:"person"}:"unauthorized";if(typeof o!="string"||!o.startsWith("Bearer "))return"unauthorized";if(o.slice(7).startsWith("crtr_pub_"))return G;if(o.slice(7).startsWith(Y)){const f=ve(o.slice(7));return f!==void 0&&(s===null||r.has(s))?f.principal:"unauthorized"}return Re(o.slice(7),s)}i(he,"resolveSocketPrincipal");async function Re(e,r){const s=await Ie(e);if(!s||r!==null&&r!==s.uid)return"unauthorized";const o=Ge();if(o&&!P()&&!await Fe(o))return"runtime_starting";const f=s.grant??(await We("app:terminal",s.sub))?.id??null;return{class:"broker",...s,grant:f}}i(Re,"brokerPrincipal");function Ae(e){if(e.class!=="anonymous")return e.sub!==void 0&&e.sub!==""?e.sub:void 0}i(Ae,"requestPerson");function ye(e,r){r.class==="broker"&&A({event:"api.request.principal",level:r.app==="app:terminal"?"debug":"info",node_id:r.node,fields:{principal:`${r.app} node:${r.node}`,method:e.method,path:e.url}})}i(ye,"noteBrokerRequest");const Y="crtr_app_";function ve(e){const r=e.startsWith(Y)?Ze(e):void 0;return r===void 0?void 0:{principal:{class:"app",app:r.grantee,scopes:r.scopes},ceiling:r.scopes}}i(ve,"appBearer");function st(e){const r=new Map;for(const s of Xe("user"))r.set(s.token,{principal:{class:"app",app:s.grantee,scopes:s.scopes},ceiling:s.scopes});return e!==void 0&&r.set(e,{principal:{class:"person"},ceiling:null}),r}i(st,"resolveBearers");function at(e){try{return De(e).act!==void 0}catch{return!1}}i(at,"carriesAct");const lt=/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/;async function we(e,r){const s=e.headers.authorization;if(s===void 0&&(e.method??"GET").toUpperCase()==="GET"&&(e.url??"").split("?")[0]==="/healthz")return{principal:G,ceiling:null};if(typeof s!="string"||!s.startsWith("Bearer "))return"unauthorized";const o=s.slice(7);if(o.startsWith("crtr_pub_"))return{principal:G,ceiling:null};if(o.startsWith(Y))return ve(o)??"unauthorized";const f=r.get(o);if(f!==void 0)return f;if(lt.test(o)){if(P()&&at(o)){const U=x();if(U===null)return"person_bearer";try{const v=await U.verifyCall(s);return v.chain[0]?.sub.startsWith("app:")?{call:v}:"unauthorized"}catch(v){return v instanceof E&&v.status===503?"runtime_starting":"unauthorized"}}const R=P()?se():null;if(R===null)return"person_bearer";const m=await R.verify(o);return m===null?"unauthorized":{principal:{class:"person",sub:m},ceiling:null}}const p=await Re(o,null);return typeof p=="string"?p:{principal:p,ceiling:null}}i(we,"authorizeTcp");let D;function x(){if(D!==void 0)return D;const e=process.env.CRTR_DIRECTORY_URL||void 0,r=process.env.CRTR_PUBLIC_API_URL||void 0;return D=e===void 0||r===void 0?null:new Le(e,r,async()=>{}),D}i(x,"cloudCallResolver");let _e;function ct(){const e=x();if(e===null)throw new E(503,"runtime_starting","app calls are not configured on this listener");return _e??=Be(e,async()=>!0,async()=>{}),_e}i(ct,"cloudAppCalls");async function ut(e,r){C(r),O(r);const s=x();try{if(s===null)throw new E(503,"runtime_starting","vendor sign-in is not configured on this listener");await xe(e,r,new URL(e.url??"/","http://runtime.local"),s)}catch(o){if(!(o instanceof E))throw o;if(r.headersSent){r.destroy();return}r.writeHead(o.status,{"content-type":"application/json; charset=utf-8","cache-control":"no-store"}),r.end(JSON.stringify({error:{code:o.code,message:o.message,...o.param?{param:o.param}:{},origin:"runtime"}}))}}i(ut,"serveVendorSignIn");function dt(){const e=process.env.CRTRD_TLS_CERT||void 0,r=process.env.CRTRD_TLS_KEY||void 0;if(e===void 0&&r===void 0)return null;if(e===void 0||r===void 0)throw new Error("CRTRD_TLS_CERT and CRTRD_TLS_KEY must be set together");return{cert:re(e),key:re(r)}}i(dt,"tcpTls");function C(e){e.setHeader(j,V()),e.setHeader(M,oe)}i(C,"advertiseRuntime");function O(e){e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Expose-Headers",`${j}, ${M}`)}i(O,"exposeRuntimeHeaders");function pt(e,r){return(e.method??"GET").toUpperCase()!=="OPTIONS"?!1:(C(r),r.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Expose-Headers":`${j}, ${M}`,"Access-Control-Allow-Headers":`authorization, content-type, ${ne.toLowerCase()}, ${ie.toLowerCase()}`,"Access-Control-Allow-Methods":"GET, POST, PATCH, PUT, DELETE, OPTIONS","Access-Control-Max-Age":"600"}),r.end(),!0)}i(pt,"answerPreflight");function J(e,r=!1){C(e),r&&O(e),e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{...T,origin:"runtime"}}))}i(J,"writeUnauthorized");function ft(e){C(e),O(e),e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{...H,origin:"runtime"}}))}i(ft,"writePersonBearerRefused");const mt={"/v1/runtime/vendor-status":{method:"GET",purpose:"vendor_status"},"/v1/runtime/vendor-account-remove":{method:"POST",purpose:"vendor_account_remove"}};async function gt(e,r){if(!P())return!1;const s=new URL(e.url??"/","http://runtime.local").pathname,o=mt[s];if(o===void 0||e.method!==o.method)return!1;const f=i((p,R)=>{C(r),O(r),r.writeHead(p,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(R))},"answer");try{const p=se();if(p===null)throw new E(401,H.code,H.message);const R=e.headers.authorization,m=R?.startsWith("Bearer ")?await p.verifyPurpose(R.slice(7),o.purpose):null;if(m===null)throw new E(401,"unauthorized","invalid directory token");const U=o.purpose==="vendor_status"?void 0:await ht(e);f(200,await B(m,async()=>o.purpose==="vendor_status"?je():await $e(U)))}catch(p){const{status:R,body:m}=Me(p);f(R,m)}return!0}i(gt,"serveCloudVendorRoute");async function ht(e){const r=[];let s=0;for await(const o of e){if(s+=o.length,s>Ve)throw new E(400,"invalid_request","request body too large");r.push(o)}if(s!==0)try{return JSON.parse(Buffer.concat(r).toString("utf8"))}catch{throw new E(400,"invalid_request","invalid JSON body")}}i(ht,"readVendorBody");function Se(e){C(e),e.writeHead(503,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{code:"runtime_starting",message:"grant sync is not ready",type:"server_error",origin:"runtime",retryable:!0}}))}i(Se,"writeRuntimeStarting");function Rt(e,r){e.writeHead(503,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{code:"startup_blocked",message:r,origin:"runtime"}}))}i(Rt,"writeStartupBlocked");function Ee(e,r,s){return new Promise(o=>{if(s===void 0){try{e.closeAllConnections()}catch{}e.close(()=>o());return}const f=[];e.close(()=>{for(const m of f)clearTimeout(m);o()});for(const m of r)m.destroy();const p=setInterval(()=>e.closeIdleConnections(),250),R=setTimeout(()=>e.closeAllConnections(),s);f.push(p,R),e.closeIdleConnections()})}i(Ee,"closeServer");function qn(e={}){let r=null;const s=tt(()=>r),o=new le().registerAll(de).registerAll(fe).registerAll(pe).registerAll(me),f=i((t,n)=>{const c=rr()?.startupBlock()??null;return c===null||t==="GET"&&(n==="/healthz"||n==="/v1/status"||n==="/v1/ready")||t==="POST"&&(n==="/v1/daemon/migrate"||n==="/v1/daemon/admit")||c.phase==="recovering"&&o.match(t,n)!==null?null:c},"startupBlockFor"),p=e.socketPath??He(),R=new Set([process.getuid?.()??0,...ae("user").runtime?.person_uids??[]]),m=i((t,n,c)=>{C(n);const u=i(a=>Array.isArray(a)?a[0]:a,"first"),b=u(t.headers[ne.toLowerCase()]),g=ze(b,V())??Ne(u(t.headers[ie.toLowerCase()]),oe,{legacyMinimum:b,runtimeVersion:V()});if(g!==null){n.writeHead(g.status,{"content-type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:g.code,message:g.message,type:"invalid_request_error",origin:"runtime",retryable:!1,..."param"in g?{param:g.param}:{details:g.details}}}));return}const w=i(a=>{const l=I(t);A({event:"api.request.failed",level:"error",...l?.class==="broker"?{node_id:l.node}:{},error:a instanceof Error?a:new Error(String(a))});try{n.headersSent||n.writeHead(500,{"content-type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:"internal",message:"internal error",origin:"runtime"}}))}catch{}},"internalFailure");let S;try{S=new URL(t.url??"/","http://127.0.0.1")}catch{try{n.writeHead(400,{"content-type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:"invalid_request",message:"request URL is invalid",origin:"runtime"}}))}catch{}return}try{const a=f((t.method??"GET").toUpperCase(),S.pathname);if(a!==null){Rt(n,a.message);return}const l=I(t)??{class:"person"},d=Ae(l),h=i(()=>s.handle(t,n,c,l),"handled");(d===void 0?h():B(d,i(async()=>{W()||await F(d),await h()},"handledAsPerson"))).catch(w)}catch(a){w(a)}},"onRequest"),U=i(async(t,n,c)=>{const u=I(t);return u===void 0||ce(u)||await ue(u,"either",c)?!0:(y(n,404,"Not Found","node_not_found",`no node ${c}`),!1)},"authorizeAttachUpgrade"),v=new Set,Z=i((t,n,c)=>{v.add(n),n.once("close",()=>v.delete(n)),n.on("error",()=>{});try{const u=new URL(t.url??"/","http://127.0.0.1"),b=f((t.method??"GET").toUpperCase(),u.pathname);if(b!==null){y(n,503,"Service Unavailable","startup_blocked",b.message);return}const g=nt(t.method??"GET",u.pathname);if(g===null){n.destroy();return}const w=(()=>{const a=I(t);return a===void 0?void 0:Ae(a)})(),S=i(()=>U(t,n,g).then(a=>a?er(t,n,c,g,async l=>{const d=I(t);return d===void 0||ce(d)||await ue(d,l,g)}):void 0),"attach");(w===void 0?S():B(w,async()=>{W()||await F(w),await S()})).catch(()=>{try{n.destroy()}catch{}})}catch{try{n.destroy()}catch{}}},"onUpgrade"),be=i(t=>{t.on("error",n=>{A({event:"api.server.failed",level:"error",error:n})})},"bindErrorHandler"),Te=i(async(t,n)=>{try{const c=await he(t,R);if(c==="unauthorized"){A({event:"api.principal.denied",level:"warn",fields:{method:t.method}}),J(n);return}if(c==="runtime_starting"){Se(n);return}z.set(t,c),ye(t,c),m(t,n,c.class==="app"?c.scopes:null)}catch(c){A({event:"api.principal.failed",level:"error",error:c instanceof Error?c:new Error(String(c))}),J(n)}},"onSocketRequest"),N=ee((t,n)=>{Te(t,n).catch(c=>A({event:"api.principal.failed",level:"error",error:c instanceof Error?c:new Error(String(c))}))}),Ce=i(async(t,n,c)=>{try{const u=await he(t,R);if(u==="unauthorized"||u==="runtime_starting"){u==="unauthorized"&&A({event:"api.principal.denied",level:"warn",fields:{method:t.method}}),y(n,u==="unauthorized"?401:503,"Unauthorized",u,"socket principal unavailable");return}if(u.class!=="person"&&u.class!=="broker"){y(n,403,"Forbidden",k.code,k.message);return}z.set(t,u),Z(t,n,c)}catch(u){A({event:"api.principal.failed",level:"error",error:u instanceof Error?u:new Error(String(u))}),y(n,401,"Unauthorized",T.code,T.message)}},"onSocketUpgrade");N.on("upgrade",(t,n,c)=>{Ce(t,n,c).catch(u=>A({event:"api.principal.failed",level:"error",error:u instanceof Error?u:new Error(String(u))}))}),ge((t,n,c)=>{z.set(t,c),m(t,n,null)});let K=!1,X,Q;const Oe=new Promise((t,n)=>{X=t,Q=n});N.once("listening",()=>{K=!0,X()}),N.on("error",t=>{A({event:"api.server.failed",level:"error",error:t}),K||Q(t)});try{$(p)&&te(p)}catch{}N.listen(p,()=>{try{ke(p,process.platform==="linux"&&$("/run/crtr/launcher.sock")?438:384)}catch(t){A({event:"api.socket.chmod_failed",level:"error",error:t instanceof Error?t:new Error(String(t))})}});let _;const L=e.tcp??Qe()??ae("user").api.tcp;if(L!==void 0&&L!==""){const t=ot(L);if(t===null)A({event:"api.tcp.invalid",level:"error",error:new Error(`invalid CRTRD_TCP/--tcp spec: ${L}`)});else{P()&&x();const n=e.token??qe()??Ke("user"),c=st(n),u=i(async(a,l)=>{if(pt(a,l))return;if(P()&&Je(new URL(a.url??"/","http://runtime.local").pathname)){await ut(a,l);return}if(await gt(a,l))return;const d=await we(a,c);if(d==="runtime_starting"){O(l),Se(l);return}if(d==="person_bearer"){ft(l);return}if(d==="unauthorized"){A({event:"api.principal.denied",level:"warn",fields:{method:a.method,listener:"tcp"}}),J(l,!0);return}if("call"in d){O(l);const h=d.call.sub;await B(h,async()=>{W()||await F(h),await ct()(a,l,d.call)});return}z.set(a,d.principal),ye(a,d.principal),O(l),m(a,l,d.ceiling)},"tcpOnRequest"),b=i(async(a,l,d)=>{const h=await we(a,c);if(h==="runtime_starting"){y(l,503,"Service Unavailable","runtime_starting","grant sync is not ready");return}if(h==="person_bearer"){y(l,401,"Unauthorized",H.code,H.message);return}if(h==="unauthorized"){y(l,401,"Unauthorized",T.code,T.message);return}if("call"in h){y(l,403,"Forbidden",k.code,k.message);return}if(h.ceiling!==null||h.principal.class==="anonymous"){y(l,403,"Forbidden",k.code,k.message);return}z.set(a,h.principal),Z(a,l,d)},"tcpOnUpgrade"),g=i(a=>{A({event:"api.principal.failed",level:"error",error:a instanceof Error?a:new Error(String(a))})},"failed"),w=i((a,l)=>{u(a,l).catch(d=>{g(d),l.headersSent?l.destroy():J(l,!0)})},"listener"),S=dt();_=S===null?ee(w):Pe(S,w),_.on("upgrade",(a,l,d)=>{l.on("error",()=>{}),b(a,l,d).catch(h=>{g(h),y(l,401,"Unauthorized",T.code,T.message)})}),be(_),_.once("listening",()=>{const a=_?.address();if(a!==null&&typeof a=="object"){const{address:l,port:d}=a;r=it(l,d)}}),_.listen(t.port,t.host)}}let q=!1;return{ready:Oe,tcpAddress:i(()=>r,"tcpAddress"),close:i(async(t={})=>{if(!q){q=!0,ge(null),lr.close(),await Promise.all([Ee(N,v,t.graceMs),_!==void 0?Ee(_,v,t.graceMs):Promise.resolve()]);try{$(p)&&te(p)}catch{}}},"close")}}i(qn,"createApiServer");export{tt as buildRouter,Ee as closeServer,qn as createApiServer,I as socketPrincipal};
|
|
1
|
+
var Pe=Object.defineProperty;var o=(e,r)=>Pe(e,"name",{value:r,configurable:!0});import{createServer as te}from"node:http";import{createServer as Ne}from"node:https";import{chmodSync as ze,existsSync as V,readFileSync as ne,unlinkSync as oe}from"node:fs";import{MINIMUM_RUNTIME_VERSION_HEADER as ie,RUNTIME_VERSION_HEADER as W,minimumRuntimeRefusal as He}from"../../api/runtime-version.js";import{CONTRACT_HEADER as F,CONTRACT_LEVEL as se,REQUIRED_CONTRACT_HEADER as ae,contractRefusal as Ie}from"../../api/contract.js";import{crtrVersion as G}from"../../shared/crtr-version.js";import{apiSocketPath as Le}from"../../core/canvas/paths.js";import{resolveNodeToken as Be}from"../../core/canvas/node-tokens.js";import{isCloudRole as P,runAsPerson as x}from"../../core/canvas/person.js";import{personBearerVerifier as le}from"./auth/person-bearer.js";import{personHasSnapshot as Y}from"../../core/scoped-state/person-store.js";import{loadPersonSettings as Z}from"../../core/scoped-state/person-rows.js";import{AppPrincipalResolver as De}from"./principal.js";import{createAppRequestHandler as xe}from"./app-listener.js";import{decodeJwt as $e}from"jose";import{handleVendorSignIn as Je,isVendorSignInPath as je}from"./vendor-signin.js";import{RunError as _}from"../../core/runs/operations.js";import{removeVendorAccount as Me,vendorStatus as Ve}from"./handlers/vendor-status.js";import{toErrorBody as We}from"./map.js";import{CONTROL_BODY_MAX_BYTES as Fe}from"../../core/llm/body-limits.js";import{grantFor as Ge,hasSyncedGrants as Ye}from"../../core/grants/mirror.js";import{readConfig as ce}from"../../core/config.js";import{directoryIssuer as Ze}from"../../core/directory/issuer.js";import{peerUid as Ke}from"../../native/linux.js";import{findScopedApiToken as Xe,getApiToken as Qe,listScopedApiTokens as qe}from"../../core/secrets.js";import{emitEvent as A}from"../../core/events/emit.js";import{envCrtrdTcp as er,envCrtrdToken as rr}from"../../shared/env.js";import{Router as ue,actsAsPerson as de}from"./router.js";import{handleAttachUpgrade as tr,refuseUpgrade as y}from"./bridge.js";import{boundDaemonControl as nr}from"../control.js";import{attachRoutes as or,mayAttachNode as pe}from"./handlers/attach.js";import{peerRoutes as ir}from"../peer/handlers.js";import{bashJobRoutes as sr}from"./handlers/bash-jobs.js";import{bashRoutes as ar}from"./handlers/bash.js";import{hookExecRoutes as lr}from"./handlers/hook-exec.js";import{brokerOperationRoutes as fe}from"./handlers/broker-ops.js";import{brokerSignalRoutes as cr,brokerSignalStreams as ur}from"./handlers/broker-signals.js";import{brokerRecoveryRoutes as me,nodeFaultRoutes as dr}from"./handlers/broker-recovery.js";import{canvasRoutes as pr}from"./handlers/canvas.js";import{conversationRoutes as fr}from"./handlers/conversations.js";import{chatInventoryRoutes as mr}from"./handlers/chat-inventory.js";import{daemonRoutes as gr}from"./handlers/daemon.js";import{fileRoutes as hr}from"./handlers/files.js";import{focusRoutes as Rr}from"./handlers/focus.js";import{analyticsRoutes as Ar}from"./handlers/analytics.js";import{memoryReadsRoutes as yr}from"./handlers/memory-reads.js";import{memoryRoutes as vr}from"./handlers/memory.js";import{healthRoutes as wr}from"./handlers/health.js";import{humanRequestRoutes as _r}from"./handlers/human-requests.js";import{inboxRoutes as Sr}from"./handlers/inbox.js";import{feedbackCommentRoutes as Er}from"./handlers/feedback-comments.js";import{objectRoutes as br}from"./handlers/objects.js";import{shareRoutes as Tr}from"./handlers/shares.js";import{customObjectRoutes as Cr}from"./handlers/custom-objects.js";import{docRoutes as kr}from"./handlers/docs.js";import{deliveryRoutes as ge}from"./handlers/delivery.js";import{deliveryRetryRoutes as Ur}from"./handlers/delivery-retry.js";import{packageDocRoutes as Or}from"./handlers/package-docs.js";import{documentAccessRoutes as Pr}from"./handlers/document-access.js";import{pluginAppRoutes as Nr}from"./handlers/plugin-app-token.js";import{brokerMailRoutes as he,messageRoutes as zr}from"./handlers/messages.js";import{modelAuthRoutes as Hr}from"./handlers/modelauth.js";import{llmRoutes as Ir}from"./handlers/llm.js";import{providerRoutes as Lr}from"./handlers/providers.js";import{modelConfigRoutes as Br}from"./handlers/model-config.js";import{settingsRoutes as Dr}from"./handlers/settings.js";import{nodeRoutes as xr}from"./handlers/nodes.js";import{nodeRecordRoutes as $r}from"./handlers/node-records.js";import{nodeOutcomeRoutes as Jr}from"./handlers/node-outcomes.js";import{nodeEventRoutes as jr}from"./handlers/node-events.js";import{profileRoutes as Mr}from"./handlers/profiles.js";import{prospectiveChatInventoryRoutes as Vr}from"./handlers/prospective-chat-inventory.js";import{reportRoutes as Wr}from"./handlers/reports.js";import{runRoutes as Fr}from"./handlers/run-routes.js";import{reviewCommentRoutes as Gr}from"./handlers/review-comments.js";import{reviewRoutes as Yr}from"./handlers/reviews.js";import{subscriptionRoutes as Zr}from"./handlers/subscriptions.js";import{structuredRequestRoutes as Kr}from"./handlers/structured-requests.js";import{cronRoutes as Xr}from"./handlers/crons.js";import{setCronRequestHandler as Re}from"../cron/run-socket.js";import{worktreeRoutes as Qr}from"./handlers/worktree.js";import{limitsRoutes as qr}from"./handlers/limits.js";import{chargeRoutes as et,spendLimitRoutes as rt}from"./handlers/charges.js";import{executionStartRoutes as tt}from"./handlers/execution-start.js";import{gitCredentialRoutes as nt}from"./handlers/git-credential.js";function ot(e){return new ue().registerAll(wr(e)).registerAll(gr).registerAll(Tr).registerAll(xr).registerAll($r).registerAll(Jr).registerAll(Ur).registerAll(jr).registerAll(sr).registerAll(ar).registerAll(lr).registerAll(fe).registerAll(cr).registerAll(ge).registerAll(me).registerAll(dr).registerAll(Qr).registerAll(qr).registerAll(et).registerAll(tt).registerAll(nt).registerAll(rt).registerAll(he).registerAll(zr).registerAll(Wr).registerAll(Fr).registerAll(Kr).registerAll(or).registerAll(pr).registerAll(fr).registerAll(Ar).registerAll(yr).registerAll(vr).registerAll(hr).registerAll(br).registerAll(Cr).registerAll(kr).registerAll(Or).registerAll(Pr).registerAll(Nr).registerAll(Rr).registerAll(Zr).registerAll(Xr).registerAll(Mr).registerAll(Hr).registerAll(Ir).registerAll(Lr).registerAll(Br).registerAll(Dr).registerAll(Yr).registerAll(Gr).registerAll(Sr).registerAll(_r).registerAll(mr).registerAll(Vr).registerAll(Er).registerAll(ir)}o(ot,"buildRouter");function it(e,r){if(e.toUpperCase()!=="GET")return null;const i=r.split("/").filter(d=>d.length>0);if(i.length!==4||i[0]!=="v1"||i[1]!=="nodes"||i[3]!=="attach")return null;const s=i[2];return s===void 0||s===""?null:decodeURIComponent(s)}o(it,"attachNodeId");function st(e){const r=e.trim();if(r==="")return null;const i=r.lastIndexOf(":");if(i<0)return null;const s=r.slice(0,i),d=r.slice(i+1),p=Number(d);return!Number.isInteger(p)||p<0||p>65535?null:{host:s===""?"0.0.0.0":s,port:p}}o(st,"parseTcp");function at(e,r){return`${e.includes(":")?`[${e}]`:e}:${r}`}o(at,"formatTcpAddress");const C={code:"unauthorized",message:"missing or invalid bearer token"},N={code:"owner_only",message:"attach is owner-only; a scoped token does not reach it"},L={code:"person_bearer_not_supported",message:"this listener accepts a person bearer only on a cloud role with CRTR_DIRECTORY_URL and CRTR_PUBLIC_API_URL set; call with a node, app or public token"},K={class:"anonymous",app:"",node:"",run:null,uid:-1,sub:"",grant:null,client_id:null,scopes:[]},z=new WeakMap;function B(e){return z.get(e)}o(B,"socketPrincipal");async function Ae(e,r){const i=process.platform==="linux"?Ke(e.socket):null,s=e.headers.authorization;if(s===void 0)return i===null||r.has(i)?{class:"person"}:"unauthorized";if(typeof s!="string"||!s.startsWith("Bearer "))return"unauthorized";if(s.slice(7).startsWith("crtr_pub_"))return K;if(s.slice(7).startsWith(X)){const d=_e(s.slice(7));return d!==void 0&&(i===null||r.has(i))?d.principal:"unauthorized"}return ye(s.slice(7),i)}o(Ae,"resolveSocketPrincipal");async function ye(e,r){const i=await Be(e);if(!i||r!==null&&r!==i.uid)return"unauthorized";const s=Ze();if(s&&!P()&&!await Ye(s))return"runtime_starting";const d=i.grant??(await Ge("app:terminal",i.sub))?.id??null;return{class:"broker",...i,grant:d}}o(ye,"brokerPrincipal");function ve(e){if(e.class!=="anonymous")return e.sub!==void 0&&e.sub!==""?e.sub:void 0}o(ve,"requestPerson");function we(e,r){r.class==="broker"&&A({event:"api.request.principal",level:r.app==="app:terminal"?"debug":"info",node_id:r.node,fields:{principal:`${r.app} node:${r.node}`,method:e.method,path:e.url}})}o(we,"noteBrokerRequest");const X="crtr_app_";function _e(e){const r=e.startsWith(X)?Xe(e):void 0;return r===void 0?void 0:{principal:{class:"app",app:r.grantee,scopes:r.scopes},ceiling:r.scopes}}o(_e,"appBearer");function lt(e){const r=new Map;for(const i of qe("user"))r.set(i.token,{principal:{class:"app",app:i.grantee,scopes:i.scopes},ceiling:i.scopes});return e!==void 0&&r.set(e,{principal:{class:"person"},ceiling:null}),r}o(lt,"resolveBearers");function ct(e){try{return $e(e).act!==void 0}catch{return!1}}o(ct,"carriesAct");const ut=/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/;function H(e,r){return e!==void 0&&(e.reason=r),"unauthorized"}o(H,"deny");async function Se(e,r,i){const s=e.headers.authorization;if(s===void 0&&(e.method??"GET").toUpperCase()==="GET"&&(e.url??"").split("?")[0]==="/healthz")return{principal:K,ceiling:null};if(typeof s!="string"||!s.startsWith("Bearer "))return H(i,s===void 0?"no_authorization_header":"not_a_bearer");const d=s.slice(7);if(d.startsWith("crtr_pub_"))return{principal:K,ceiling:null};if(d.startsWith(X))return _e(d)??H(i,"app_token_unknown");const p=r.get(d);if(p!==void 0)return p;if(ut.test(d)){if(P()&&ct(d)){const b=J();if(b===null)return"person_bearer";try{const v=await b.verifyCall(s);return v.chain[0]?.sub.startsWith("app:")?{call:v}:H(i,"call_token_presenter_not_an_app")}catch(v){return v instanceof _&&v.status===503?"runtime_starting":H(i,`call_token_${v instanceof _?v.code:"invalid"}`)}}const h=P()?le():null;if(h===null)return"person_bearer";const O=await h.verify(d);return O===null?H(i,"person_bearer_not_verified"):{principal:{class:"person",sub:O},ceiling:null}}const g=await ye(d,null);return g==="unauthorized"?H(i,"node_token_unknown"):typeof g=="string"?g:{principal:g,ceiling:null}}o(Se,"authorizeTcp");let $;function J(){if($!==void 0)return $;const e=process.env.CRTR_DIRECTORY_URL||void 0,r=process.env.CRTR_PUBLIC_API_URL||void 0;return $=e===void 0||r===void 0?null:new De(e,r,async()=>{}),$}o(J,"cloudCallResolver");let Ee;function dt(){const e=J();if(e===null)throw new _(503,"runtime_starting","app calls are not configured on this listener");return Ee??=xe(e,async()=>!0,async()=>{}),Ee}o(dt,"cloudAppCalls");async function pt(e,r){k(r),U(r);const i=J();try{if(i===null)throw new _(503,"runtime_starting","vendor sign-in is not configured on this listener");await Je(e,r,new URL(e.url??"/","http://runtime.local"),i)}catch(s){if(!(s instanceof _))throw s;if(r.headersSent){r.destroy();return}r.writeHead(s.status,{"content-type":"application/json; charset=utf-8","cache-control":"no-store"}),r.end(JSON.stringify({error:{code:s.code,message:s.message,...s.param?{param:s.param}:{},origin:"runtime"}}))}}o(pt,"serveVendorSignIn");function ft(){const e=process.env.CRTRD_TLS_CERT||void 0,r=process.env.CRTRD_TLS_KEY||void 0;if(e===void 0&&r===void 0)return null;if(e===void 0||r===void 0)throw new Error("CRTRD_TLS_CERT and CRTRD_TLS_KEY must be set together");return{cert:ne(e),key:ne(r)}}o(ft,"tcpTls");function k(e){e.setHeader(W,G()),e.setHeader(F,se)}o(k,"advertiseRuntime");function U(e){e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Expose-Headers",`${W}, ${F}`)}o(U,"exposeRuntimeHeaders");function mt(e,r){return(e.method??"GET").toUpperCase()!=="OPTIONS"?!1:(k(r),r.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Expose-Headers":`${W}, ${F}`,"Access-Control-Allow-Headers":`authorization, content-type, ${ie.toLowerCase()}, ${ae.toLowerCase()}`,"Access-Control-Allow-Methods":"GET, POST, PATCH, PUT, DELETE, OPTIONS","Access-Control-Max-Age":"600"}),r.end(),!0)}o(mt,"answerPreflight");function j(e,r=!1){k(e),r&&U(e),e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{...C,origin:"runtime"}}))}o(j,"writeUnauthorized");function gt(e){k(e),U(e),e.writeHead(401,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{...L,origin:"runtime"}}))}o(gt,"writePersonBearerRefused");const ht={"/v1/runtime/vendor-status":{method:"GET",purpose:"vendor_status"},"/v1/runtime/vendor-account-remove":{method:"POST",purpose:"vendor_account_remove"}};async function Rt(e,r){if(!P())return!1;const i=new URL(e.url??"/","http://runtime.local").pathname,s=ht[i];if(s===void 0||e.method!==s.method)return!1;const d=o((p,g)=>{k(r),U(r),r.writeHead(p,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify(g))},"answer");try{const p=le();if(p===null)throw new _(401,L.code,L.message);const g=e.headers.authorization,h=g?.startsWith("Bearer ")?await p.verifyPurpose(g.slice(7),s.purpose):null;if(h===null)throw new _(401,"unauthorized","invalid directory token");const O=s.purpose==="vendor_status"?void 0:await At(e);d(200,await x(h,async()=>s.purpose==="vendor_status"?Ve():await Me(O)))}catch(p){const{status:g,body:h}=We(p);d(g,h)}return!0}o(Rt,"serveCloudVendorRoute");async function At(e){const r=[];let i=0;for await(const s of e){if(i+=s.length,i>Fe)throw new _(400,"invalid_request","request body too large");r.push(s)}if(i!==0)try{return JSON.parse(Buffer.concat(r).toString("utf8"))}catch{throw new _(400,"invalid_request","invalid JSON body")}}o(At,"readVendorBody");function be(e){k(e),e.writeHead(503,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{code:"runtime_starting",message:"grant sync is not ready",type:"server_error",origin:"runtime",retryable:!0}}))}o(be,"writeRuntimeStarting");function yt(e,r){e.writeHead(503,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify({error:{code:"startup_blocked",message:r,origin:"runtime"}}))}o(yt,"writeStartupBlocked");function Te(e,r,i){return new Promise(s=>{if(i===void 0){try{e.closeAllConnections()}catch{}e.close(()=>s());return}const d=[];e.close(()=>{for(const h of d)clearTimeout(h);s()});for(const h of r)h.destroy();const p=setInterval(()=>e.closeIdleConnections(),250),g=setTimeout(()=>e.closeAllConnections(),i);d.push(p,g),e.closeIdleConnections()})}o(Te,"closeServer");function eo(e={}){let r=null;const i=ot(()=>r),s=new ue().registerAll(fe).registerAll(ge).registerAll(me).registerAll(he),d=o((t,n)=>{const c=nr()?.startupBlock()??null;return c===null||t==="GET"&&(n==="/healthz"||n==="/v1/status"||n==="/v1/ready")||t==="POST"&&(n==="/v1/daemon/migrate"||n==="/v1/daemon/admit")||c.phase==="recovering"&&s.match(t,n)!==null?null:c},"startupBlockFor"),p=e.socketPath??Le(),g=new Set([process.getuid?.()??0,...ce("user").runtime?.person_uids??[]]),h=o((t,n,c)=>{k(n);const u=o(a=>Array.isArray(a)?a[0]:a,"first"),T=u(t.headers[ie.toLowerCase()]),R=He(T,G())??Ie(u(t.headers[ae.toLowerCase()]),se,{legacyMinimum:T,runtimeVersion:G()});if(R!==null){n.writeHead(R.status,{"content-type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:R.code,message:R.message,type:"invalid_request_error",origin:"runtime",retryable:!1,..."param"in R?{param:R.param}:{details:R.details}}}));return}const w=o(a=>{const l=B(t);A({event:"api.request.failed",level:"error",...l?.class==="broker"?{node_id:l.node}:{},error:a instanceof Error?a:new Error(String(a))});try{n.headersSent||n.writeHead(500,{"content-type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:"internal",message:"internal error",origin:"runtime"}}))}catch{}},"internalFailure");let E;try{E=new URL(t.url??"/","http://127.0.0.1")}catch{try{n.writeHead(400,{"content-type":"application/json; charset=utf-8"}),n.end(JSON.stringify({error:{code:"invalid_request",message:"request URL is invalid",origin:"runtime"}}))}catch{}return}try{const a=d((t.method??"GET").toUpperCase(),E.pathname);if(a!==null){yt(n,a.message);return}const l=B(t)??{class:"person"},m=ve(l),f=o(()=>i.handle(t,n,c,l),"handled");(m===void 0?f():x(m,o(async()=>{Y()||await Z(m),await f()},"handledAsPerson"))).catch(w)}catch(a){w(a)}},"onRequest"),O=o(async(t,n,c)=>{const u=B(t);return u===void 0||de(u)||await pe(u,"either",c)?!0:(y(n,404,"Not Found","node_not_found",`no node ${c}`),!1)},"authorizeAttachUpgrade"),b=new Set,v=o((t,n,c)=>{b.add(n),n.once("close",()=>b.delete(n)),n.on("error",()=>{});try{const u=new URL(t.url??"/","http://127.0.0.1"),T=d((t.method??"GET").toUpperCase(),u.pathname);if(T!==null){y(n,503,"Service Unavailable","startup_blocked",T.message);return}const R=it(t.method??"GET",u.pathname);if(R===null){n.destroy();return}const w=(()=>{const a=B(t);return a===void 0?void 0:ve(a)})(),E=o(()=>O(t,n,R).then(a=>a?tr(t,n,c,R,async l=>{const m=B(t);return m===void 0||de(m)||await pe(m,l,R)}):void 0),"attach");(w===void 0?E():x(w,async()=>{Y()||await Z(w),await E()})).catch(()=>{try{n.destroy()}catch{}})}catch{try{n.destroy()}catch{}}},"onUpgrade"),Ce=o(t=>{t.on("error",n=>{A({event:"api.server.failed",level:"error",error:n})})},"bindErrorHandler"),ke=o(async(t,n)=>{try{const c=await Ae(t,g);if(c==="unauthorized"){A({event:"api.principal.denied",level:"warn",fields:{method:t.method}}),j(n);return}if(c==="runtime_starting"){be(n);return}z.set(t,c),we(t,c),h(t,n,c.class==="app"?c.scopes:null)}catch(c){A({event:"api.principal.failed",level:"error",error:c instanceof Error?c:new Error(String(c))}),j(n)}},"onSocketRequest"),I=te((t,n)=>{ke(t,n).catch(c=>A({event:"api.principal.failed",level:"error",error:c instanceof Error?c:new Error(String(c))}))}),Ue=o(async(t,n,c)=>{try{const u=await Ae(t,g);if(u==="unauthorized"||u==="runtime_starting"){u==="unauthorized"&&A({event:"api.principal.denied",level:"warn",fields:{method:t.method}}),y(n,u==="unauthorized"?401:503,"Unauthorized",u,"socket principal unavailable");return}if(u.class!=="person"&&u.class!=="broker"){y(n,403,"Forbidden",N.code,N.message);return}z.set(t,u),v(t,n,c)}catch(u){A({event:"api.principal.failed",level:"error",error:u instanceof Error?u:new Error(String(u))}),y(n,401,"Unauthorized",C.code,C.message)}},"onSocketUpgrade");I.on("upgrade",(t,n,c)=>{Ue(t,n,c).catch(u=>A({event:"api.principal.failed",level:"error",error:u instanceof Error?u:new Error(String(u))}))}),Re((t,n,c)=>{z.set(t,c),h(t,n,null)});let Q=!1,q,ee;const Oe=new Promise((t,n)=>{q=t,ee=n});I.once("listening",()=>{Q=!0,q()}),I.on("error",t=>{A({event:"api.server.failed",level:"error",error:t}),Q||ee(t)});try{V(p)&&oe(p)}catch{}I.listen(p,()=>{try{ze(p,process.platform==="linux"&&V("/run/crtr/launcher.sock")?438:384)}catch(t){A({event:"api.socket.chmod_failed",level:"error",error:t instanceof Error?t:new Error(String(t))})}});let S;const D=e.tcp??er()??ce("user").api.tcp;if(D!==void 0&&D!==""){const t=st(D);if(t===null)A({event:"api.tcp.invalid",level:"error",error:new Error(`invalid CRTRD_TCP/--tcp spec: ${D}`)});else{P()&&J();const n=e.token??rr()??Qe("user"),c=lt(n),u=o(async(a,l)=>{if(mt(a,l))return;if(P()&&je(new URL(a.url??"/","http://runtime.local").pathname)){await pt(a,l);return}if(await Rt(a,l))return;const m={},f=await Se(a,c,m);if(f==="runtime_starting"){U(l),be(l);return}if(f==="person_bearer"){gt(l);return}if(f==="unauthorized"){A({event:"api.principal.denied",level:"warn",fields:{method:a.method,listener:"tcp",path:new URL(a.url??"/","http://runtime.local").pathname,reason:m.reason??"unknown"}}),j(l,!0);return}if("call"in f){U(l);const M=f.call.sub;await x(M,async()=>{Y()||await Z(M),await dt()(a,l,f.call)});return}z.set(a,f.principal),we(a,f.principal),U(l),h(a,l,f.ceiling)},"tcpOnRequest"),T=o(async(a,l,m)=>{const f=await Se(a,c);if(f==="runtime_starting"){y(l,503,"Service Unavailable","runtime_starting","grant sync is not ready");return}if(f==="person_bearer"){y(l,401,"Unauthorized",L.code,L.message);return}if(f==="unauthorized"){y(l,401,"Unauthorized",C.code,C.message);return}if("call"in f){y(l,403,"Forbidden",N.code,N.message);return}if(f.ceiling!==null||f.principal.class==="anonymous"){y(l,403,"Forbidden",N.code,N.message);return}z.set(a,f.principal),v(a,l,m)},"tcpOnUpgrade"),R=o(a=>{A({event:"api.principal.failed",level:"error",error:a instanceof Error?a:new Error(String(a))})},"failed"),w=o((a,l)=>{u(a,l).catch(m=>{R(m),l.headersSent?l.destroy():j(l,!0)})},"listener"),E=ft();S=E===null?te(w):Ne(E,w),S.on("upgrade",(a,l,m)=>{l.on("error",()=>{}),T(a,l,m).catch(f=>{R(f),y(l,401,"Unauthorized",C.code,C.message)})}),Ce(S),S.once("listening",()=>{const a=S?.address();if(a!==null&&typeof a=="object"){const{address:l,port:m}=a;r=at(l,m)}}),S.listen(t.port,t.host)}}let re=!1;return{ready:Oe,tcpAddress:o(()=>r,"tcpAddress"),close:o(async(t={})=>{if(!re){re=!0,Re(null),ur.close(),await Promise.all([Te(I,b,t.graceMs),S!==void 0?Te(S,b,t.graceMs):Promise.resolve()]);try{V(p)&&oe(p)}catch{}}},"close")}}o(eo,"createApiServer");export{ot as buildRouter,Te as closeServer,eo as createApiServer,B as socketPrincipal};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.569",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
},
|
|
145
145
|
"license": "UNLICENSED",
|
|
146
146
|
"dependencies": {
|
|
147
|
-
"@north-light/identity": "0.3.
|
|
147
|
+
"@north-light/identity": "0.3.569",
|
|
148
148
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
149
149
|
"@earendil-works/pi-ai": "1.1.0",
|
|
150
150
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.569",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.569",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@milkdown/preset-gfm": "^7.22.3",
|
|
25
25
|
"@milkdown/prose": "^7.22.3",
|
|
26
26
|
"@milkdown/utils": "^7.22.3",
|
|
27
|
-
"@north-light/identity": "0.3.
|
|
27
|
+
"@north-light/identity": "0.3.569",
|
|
28
28
|
"cron-parser": "^5.6.0",
|
|
29
29
|
"esbuild": "^0.27.7",
|
|
30
30
|
"jose": "^6.2.12",
|
|
@@ -12455,17 +12455,17 @@
|
|
|
12455
12455
|
},
|
|
12456
12456
|
"packages/crouter-api": {
|
|
12457
12457
|
"name": "@north-light/api",
|
|
12458
|
-
"version": "0.3.
|
|
12458
|
+
"version": "0.3.569",
|
|
12459
12459
|
"license": "UNLICENSED"
|
|
12460
12460
|
},
|
|
12461
12461
|
"packages/crouter-env-docker": {
|
|
12462
12462
|
"name": "@north-light/env-docker",
|
|
12463
|
-
"version": "0.3.
|
|
12463
|
+
"version": "0.3.569",
|
|
12464
12464
|
"license": "UNLICENSED"
|
|
12465
12465
|
},
|
|
12466
12466
|
"packages/crouter-identity": {
|
|
12467
12467
|
"name": "@north-light/identity",
|
|
12468
|
-
"version": "0.3.
|
|
12468
|
+
"version": "0.3.569",
|
|
12469
12469
|
"license": "UNLICENSED",
|
|
12470
12470
|
"dependencies": {
|
|
12471
12471
|
"jose": "^6.2.1"
|
|
@@ -12473,21 +12473,21 @@
|
|
|
12473
12473
|
},
|
|
12474
12474
|
"packages/crouter-plugin": {
|
|
12475
12475
|
"name": "@north-light/plugin",
|
|
12476
|
-
"version": "0.3.
|
|
12476
|
+
"version": "0.3.569",
|
|
12477
12477
|
"license": "UNLICENSED",
|
|
12478
12478
|
"dependencies": {
|
|
12479
|
-
"@north-light/api": "0.3.
|
|
12480
|
-
"@north-light/identity": "0.3.
|
|
12479
|
+
"@north-light/api": "0.3.569",
|
|
12480
|
+
"@north-light/identity": "0.3.569"
|
|
12481
12481
|
}
|
|
12482
12482
|
},
|
|
12483
12483
|
"packages/crouter-sdk": {
|
|
12484
12484
|
"name": "@north-light/sdk",
|
|
12485
|
-
"version": "0.3.
|
|
12485
|
+
"version": "0.3.569",
|
|
12486
12486
|
"license": "UNLICENSED",
|
|
12487
12487
|
"dependencies": {
|
|
12488
|
-
"@north-light/api": "0.3.
|
|
12489
|
-
"@north-light/identity": "0.3.
|
|
12490
|
-
"@north-light/plugin": "0.3.
|
|
12488
|
+
"@north-light/api": "0.3.569",
|
|
12489
|
+
"@north-light/identity": "0.3.569",
|
|
12490
|
+
"@north-light/plugin": "0.3.569",
|
|
12491
12491
|
"jose": "^6.2.1"
|
|
12492
12492
|
},
|
|
12493
12493
|
"bin": {
|