@north-light/cli 0.3.442 → 0.3.445

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/builtin-memory/crouter-sdk/release-notes.md +6 -0
  4. package/dist/clients/attach/viewer.js +570 -570
  5. package/dist/core/audit.d.ts +23 -0
  6. package/dist/core/audit.js +3 -0
  7. package/dist/core/canvas/migrations.js +12 -12
  8. package/dist/core/directory/join.d.ts +2 -0
  9. package/dist/core/directory/join.js +1 -1
  10. package/dist/core/grants/mirror.d.ts +11 -0
  11. package/dist/core/grants/mirror.js +2 -2
  12. package/dist/core/grants/on-sync.js +1 -1
  13. package/dist/core/grants/remove.js +1 -1
  14. package/dist/core/grants/sync.js +1 -1
  15. package/dist/core/graph/reader.js +3 -3
  16. package/dist/core/peer/client.d.ts +57 -0
  17. package/dist/core/peer/client.js +1 -0
  18. package/dist/core/peer/directory.d.ts +65 -0
  19. package/dist/core/peer/directory.js +1 -0
  20. package/dist/core/peer/envelope.d.ts +40 -0
  21. package/dist/core/peer/envelope.js +1 -0
  22. package/dist/core/peer/events.d.ts +9 -0
  23. package/dist/core/peer/events.js +1 -0
  24. package/dist/core/peer/token.d.ts +15 -0
  25. package/dist/core/peer/token.js +2 -0
  26. package/dist/core/peer/version.d.ts +16 -0
  27. package/dist/core/peer/version.js +1 -0
  28. package/dist/core/providers/bundles.js +1 -1
  29. package/dist/daemon/api/app-listener.js +12 -12
  30. package/dist/daemon/api/handlers/bash.d.ts +1 -1
  31. package/dist/daemon/api/handlers/bash.js +1 -1
  32. package/dist/daemon/api/handlers/crons.js +1 -1
  33. package/dist/daemon/api/handlers/files.js +1 -1
  34. package/dist/daemon/api/handlers/human-requests.d.ts +1 -1
  35. package/dist/daemon/api/handlers/human-requests.js +1 -1
  36. package/dist/daemon/api/handlers/messages.js +1 -1
  37. package/dist/daemon/api/handlers/profiles.js +1 -1
  38. package/dist/daemon/api/handlers/providers.js +1 -1
  39. package/dist/daemon/api/operations.d.ts +1 -1
  40. package/dist/daemon/api/operations.js +1 -1
  41. package/dist/daemon/api/principal.d.ts +3 -1
  42. package/dist/daemon/api/principal.js +1 -1
  43. package/dist/daemon/api/router.d.ts +29 -1
  44. package/dist/daemon/api/router.js +1 -1
  45. package/dist/daemon/api/server.js +1 -1
  46. package/dist/daemon/crtrd.js +7 -7
  47. package/dist/daemon/peer/handlers.d.ts +2 -0
  48. package/dist/daemon/peer/handlers.js +1 -0
  49. package/dist/daemon/peer/ingress.d.ts +34 -0
  50. package/dist/daemon/peer/ingress.js +1 -0
  51. package/docs/sdk/release-notes.md +6 -0
  52. package/package.json +2 -2
  53. package/packages/crouter-identity/package.json +1 -1
  54. package/runtime.lock.json +12 -12
@@ -0,0 +1,40 @@
1
+ import type { RequestPrincipal } from '../../daemon/api/router.js';
2
+ import { type RunPlace } from '../runs/confinement.js';
3
+ export declare const ENVELOPE_HEADER = "Crtr-Envelope";
4
+ /** A header larger than this is refused unread. */
5
+ export declare const MAX_ENVELOPE_BYTES: number;
6
+ /** The app (and its node) a call is made for. `node` is null for a cron with no anchor node. */
7
+ export interface EnvelopeApp {
8
+ sub: string;
9
+ node: string | null;
10
+ node_scopes: string[] | null;
11
+ place: RunPlace | null;
12
+ }
13
+ export interface Envelope {
14
+ sub: string;
15
+ act: {
16
+ sub: string;
17
+ act?: EnvelopeApp;
18
+ };
19
+ }
20
+ /** The caller kinds that originate a peer call. A forwarded caller never re-forwards, so it is not here. */
21
+ export type EnvelopeCaller = Extract<RequestPrincipal, {
22
+ class: 'person' | 'broker' | 'cron';
23
+ }>;
24
+ export declare class EnvelopeError extends Error {
25
+ constructor(message: string);
26
+ }
27
+ export interface EnvelopeContext {
28
+ /** The account owner (`user:<id>`) the call is made for. */
29
+ owner?: string;
30
+ /** This machine's id (`runtime:<id>`). */
31
+ via?: string;
32
+ /** A node's run place. */
33
+ placeOf?: (nodeId: string) => RunPlace | null;
34
+ }
35
+ /** The envelope for a call made by `principal` on this machine. Broker → its app, node, stored scopes and run place; cron → its app, anchor node
36
+ * (null when unanchored), stored scopes and origin run; person → no inner `act`. */
37
+ export declare function envelopeFor(principal: EnvelopeCaller, context?: EnvelopeContext): Envelope;
38
+ export declare function encodeEnvelope(envelope: Envelope): string;
39
+ /** Parse and validate a `Crtr-Envelope` header value. Throws `EnvelopeError` for anything that is not the documented shape. */
40
+ export declare function decodeEnvelope(header: string): Envelope;
@@ -0,0 +1 @@
1
+ var i=Object.defineProperty;var l=(e,o)=>i(e,"name",{value:o,configurable:!0});import{joinedOwner as d}from"../directory/join.js";import{runPlace as h}from"../runs/confinement.js";import{ownMachineId as p}from"./directory.js";const g="Crtr-Envelope",b=16*1024;class n extends Error{static{l(this,"EnvelopeError")}constructor(o){super(o),this.name="EnvelopeError"}}function y(e,o={}){const s=o.via??p(),t=e.class==="person"?"":e.sub,r=o.owner??(t.startsWith("user:")?t:d());if(r==null)throw new n("this machine has no account owner to make a peer call for");if(e.class==="person")return{sub:r,act:{sub:s}};if(e.class==="broker"){const a=(o.placeOf??(u=>h(u)))(e.node);return{sub:r,act:{sub:s,act:{sub:e.app,node:e.node,node_scopes:e.scopes===null?null:[...e.scopes],place:a}}}}return{sub:r,act:{sub:s,act:{sub:e.app,node:e.anchor,node_scopes:e.scopes===null?null:[...e.scopes],place:{run:e.origin_run??"",isolated:e.isolated}}}}}l(y,"envelopeFor");function _(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}l(_,"encodeEnvelope");const c=l(e=>typeof e=="string"&&e!=="","isString"),f=l(e=>typeof e=="object"&&e!==null&&!Array.isArray(e),"isRecord");function A(e){if(e.length>b)throw new n("the envelope is too large");if(!/^[A-Za-z0-9_-]+$/.test(e))throw new n("the envelope is not base64url");let o;try{o=JSON.parse(Buffer.from(e,"base64url").toString("utf8"))}catch{throw new n("the envelope is not JSON")}if(!f(o)||!c(o.sub)||!o.sub.startsWith("user:"))throw new n("the envelope names no user");const s=o.act;if(!f(s)||!c(s.sub)||!s.sub.startsWith("runtime:"))throw new n("the envelope names no calling machine");const t=s.act;if(t===void 0)return{sub:o.sub,act:{sub:s.sub}};if(!f(t)||!c(t.sub)||!t.sub.startsWith("app:"))throw new n("the envelope names no calling app");const{node:r,node_scopes:a,place:u}=t;if(r!==null&&!c(r))throw new n("the envelope node is not a node id");if(a!==null&&!(Array.isArray(a)&&a.every(c)))throw new n("the envelope node_scopes is not a list of scopes");if(u!==null&&!(f(u)&&typeof u.run=="string"&&typeof u.isolated=="boolean"))throw new n("the envelope place is malformed");return{sub:o.sub,act:{sub:s.sub,act:{sub:t.sub,node:r,node_scopes:a,place:u===null?null:{run:u.run,isolated:u.isolated}}}}}l(A,"decodeEnvelope");export{g as ENVELOPE_HEADER,n as EnvelopeError,b as MAX_ENVELOPE_BYTES,A as decodeEnvelope,_ as encodeEnvelope,y as envelopeFor};
@@ -0,0 +1,9 @@
1
+ export interface PeerEvent {
2
+ /** Subscribe; returns the function that unsubscribes. */
3
+ on(listener: () => void): () => void;
4
+ /** Call every listener once. A throwing listener never stops the others. */
5
+ emit(): void;
6
+ listenerCount(): number;
7
+ }
8
+ /** Emitted on every successful connect of this daemon's WebSocket to the account relay. */
9
+ export declare const relayConnected: PeerEvent;
@@ -0,0 +1 @@
1
+ var r=Object.defineProperty;var n=(e,t)=>r(e,"name",{value:t,configurable:!0});function o(){const e=new Set;return{on(t){return e.add(t),()=>{e.delete(t)}},emit(){for(const t of[...e])try{t()}catch{}},listenerCount:n(()=>e.size,"listenerCount")}}n(o,"createPeerEvent");const s=o();export{s as relayConnected};
@@ -0,0 +1,15 @@
1
+ import { type DirectoryToken } from '../grants/sync.js';
2
+ export declare const REFRESH_BEFORE_EXPIRY_MS = 30000;
3
+ export type Mint = (issuer: string, audience?: string) => Promise<DirectoryToken>;
4
+ export interface TokenCache {
5
+ /** A token for `audience` (a peer machine URL or the relay origin), or for the directory's own `/v1` when omitted. */
6
+ get(issuer: string, audience?: string): Promise<string>;
7
+ /** Forget a token the far side refused (revoked key, clock skew); the next `get` mints a new one. */
8
+ drop(issuer: string, audience?: string): void;
9
+ clear(): void;
10
+ }
11
+ export declare function createTokenCache(mint?: Mint, now?: () => number): TokenCache;
12
+ /** The daemon's process-wide cache. */
13
+ export declare const peerTokens: TokenCache;
14
+ /** A machine token whose `aud` is `audience` (a peer machine's URL, or `https://relay.northlight.ai`). */
15
+ export declare function machineToken(issuer: string, audience: string): Promise<string>;
@@ -0,0 +1,2 @@
1
+ var k=Object.defineProperty;var i=(n,r)=>k(n,"name",{value:r,configurable:!0});import{mintDirectoryToken as _}from"../grants/sync.js";const g=3e4;function h(n=_,r=Date.now){const c=new Map,a=new Map,p=i((e,t)=>`${e.replace(/\/$/,"")}
2
+ ${t??""}`,"keyOf");return{async get(e,t){const o=p(e,t),l=c.get(o);if(l&&l.expires_at-g>r())return l.access_token;let s=a.get(o);return s||(s=n(e,t).then(f=>(c.set(o,f),f)).finally(()=>{a.delete(o)}),a.set(o,s)),(await s).access_token},drop(e,t){c.delete(p(e,t))},clear(){c.clear(),a.clear()}}}i(h,"createTokenCache");const u=h();function E(n,r){return u.get(n,r)}i(E,"machineToken");export{g as REFRESH_BEFORE_EXPIRY_MS,h as createTokenCache,E as machineToken,u as peerTokens};
@@ -0,0 +1,16 @@
1
+ /** Request header naming the sending machine's crtr release. */
2
+ export declare const PEER_VERSION_HEADER = "Crtr-Version";
3
+ /** The oldest crtr release that speaks the peer protocol. L1 sets this to the release that ships C1–C6. */
4
+ export declare const PEER_VERSION_FLOOR = "0.3.442";
5
+ export interface PeerVersionRefusal {
6
+ status: 426;
7
+ code: 'peer_version_too_old';
8
+ message: string;
9
+ details: {
10
+ peer_version: string | null;
11
+ floor: string;
12
+ receiver_version: string;
13
+ };
14
+ }
15
+ /** The refusal for a peer announcing `peerVersion` (the `Crtr-Version` header, or a reach answer's `crtr_version`), or null when it is at or above the floor. */
16
+ export declare function peerVersionRefusal(peerVersion: string | null | undefined, floor?: string, own?: string): PeerVersionRefusal | null;
@@ -0,0 +1 @@
1
+ var n=Object.defineProperty;var i=(r,e)=>n(r,"name",{value:e,configurable:!0});import{compareReleaseVersions as a}from"../../api/runtime-version.js";import{crtrVersion as o}from"../../shared/crtr-version.js";const l="Crtr-Version",c="0.3.442";function d(r,e=c,s=o()){const t=r?.trim()?r.trim():null;return t!==null&&a(t,e)>=0?null:{status:426,code:"peer_version_too_old",message:t===null?`The peer did not name its crtr version; machines below ${e} cannot take part (this machine is ${s}). Update it with \`crtr sys update\`.`:`The peer runs crtr ${t}; machines below ${e} cannot take part (this machine is ${s}). Update it with \`crtr sys update\`.`,details:{peer_version:t,floor:e,receiver_version:s}}}i(d,"peerVersionRefusal");export{c as PEER_VERSION_FLOOR,l as PEER_VERSION_HEADER,d as peerVersionRefusal};
@@ -1,2 +1,2 @@
1
- var X=Object.defineProperty;var i=(e,n)=>X(e,"name",{value:n,configurable:!0});import{existsSync as h,readdirSync as H,readFileSync as y,rmSync as b,statSync as x}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as Y}from"../../api/command-manifest/manifest.js";import{crtrHome as O}from"../canvas/paths.js";import{withCanvasWrite as ee}from"../canvas/db.js";import{bundleVersion as ne,isInstalledGeneration as re,recordCheckedGeneration as te}from"../command-plugins/bundle.js";import{readConfig as U,readState as oe}from"../config.js";import{emitEvent as g}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as ie}from"../exclusive-lock.js";import{atomicWriteText as M,ensureDir as I,pathExists as ae}from"../fs-utils.js";import{reconcilePackageDocuments as se}from"../graph/package-docs.js";import{readPluginManifest as W}from"../manifest.js";import{bundleSwapLockPath as de}from"../plugin-swap-lock.js";import{pluginsDir as ue,userScopeRoot as ce}from"../scope.js";import{allGrants as fe}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const T="[A-Za-z0-9_-]{1,64}",le=new RegExp(`^(${T}):(${T})$`),z=new RegExp(`^${T}$`),pe=3e4,me=3e4;function ve(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=le.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}i(ve,"providerIdsFromGrants");function w(e){return u(ue("user"),e)}i(w,"pluginRoot");function F(){return u(O(),"providers")}i(F,"providersRoot");function G(e,n){return u(F(),e,`v${n}`)}i(G,"majorDir");function j(e){if(!z.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}i(j,"assertId");function A(){const e=U("user").runtime?.issuer;if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e.replace(/\/+$/,"")}i(A,"directoryBase");async function q(e,n){const r=`${A()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await fetch(r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(pe)})}catch(s){return{status:"failed",message:`could not reach ${r}: ${s instanceof Error?s.message:String(s)}`}}if(t.status===304)return n.ifNoneMatch===void 0?{status:"failed",message:`HTTP 304 from ${r} without a conditional request`}:{status:304};if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300)return await t.body?.cancel().catch(()=>{}),{status:"failed",message:`HTTP ${t.status} from ${r}`};let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(s){return{status:"failed",message:`reading ${r} failed: ${s instanceof Error?s.message:String(s)}`}}const a=t.headers.get("etag");return{status:200,raw:o,...a!==null?{etag:a}:{}}}i(q,"fetchProviderBundle");const _=new WeakSet;class S extends Error{static{i(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function V(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}i(V,"wrongBundle");function C(e,n,r){g({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof S?{issues:r.issues}:{}},error:r})}i(C,"logInvalid");function Z(e,n,r){g({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r}})}i(Z,"logFailure");async function L(){return import("../../commands/pkg/plugin-manage.js")}i(L,"pluginManage");const P=new Map,E=i(e=>`${O()}\0${e}`,"installKey");function he(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}i(he,"publicInstallCause");function ge(e){if(m(e)===void 0)return P.get(E(e))??{status:"installing"}}i(ge,"providerInstallState");function we(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${ge(e)?.error??"bundle installation pending"}`)}i(we,"unavailableProviderError");function m(e){const n=W(w(e));if(!(n?.provider===void 0||n.transport?.kind!=="http")&&ae(u(w(e),"commands.json"))){try{if(x(u(w(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:{kind:"http",endpoint:n.transport.endpoint}}}}i(m,"installedManifest");async function k(e,n,r=!1){const t=ce();m(e)===void 0&&P.set(E(e),{status:"installing"});try{return I(u(t,"tmp")),await ie(de(t,e),async()=>{const o=w(e),a=W(o);if(a!==null&&a.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const s=m(e)!==void 0;if(s&&r)return"unchanged";const f=s&&n?oe("user").plugins[e]?.etag:void 0,d=await q(e,f!==void 0?{ifNoneMatch:f}:{});if(d.status===304)return"unchanged";if(d.status===404||d.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${d.message})`,{param:"provider"});if(d.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${d.message}`);const l=ne(d.raw);if(s&&re(e,"user",o,l))return te("user",e,d.etag,{swapped:!1}),"unchanged";const{installPluginArchive:v}=await L();try{await v(e,d.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:d.etag,enable:!s,bundle:{endpoint:`${A()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:A()},accept:i(p=>V(e,void 0,p),"accept")})}catch(p){C(e,void 0,p);const N=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw _.add(N),N}try{ee(p=>se(p))}catch(p){g({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return g({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:s}}),P.delete(E(e)),s?"updated":"installed"},{timeoutMs:me,timeoutError:i(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw m(e)===void 0&&P.set(E(e),{status:"install_failed",error:he(o)}),o instanceof c?o:(g({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),we(e))}}i(k,"refreshInstalled");async function We(e){j(e),m(e)===void 0&&await k(e,!1,!0)}i(We,"ensureProviderBundle");async function ze(e,n){return j(e),n.version!==void 0&&m(e)?.provider.major!==n.version?R(e,n.version,n.conditional):k(e,n.conditional)}i(ze,"refreshProviderBundle");async function J(e,n,r){const{validatePluginArchive:t}=await L(),o=await t(r);if(o.bundle===void 0)throw new S(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(s=>s.message).join("; ")}`,o.issues);const a=V(e,n,o.bundle);if(a!==void 0)throw new S(`provider "${e}" major ${n} bundle is invalid: ${a}`,[]);if(o.bundle.transport===void 0)throw new S(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}i(J,"validateArchive");function $e(e){I(e),M(u(e,"retired"),`${new Date().toISOString()}
1
+ var X=Object.defineProperty;var i=(e,n)=>X(e,"name",{value:n,configurable:!0});import{existsSync as h,readdirSync as H,readFileSync as y,rmSync as b,statSync as x}from"node:fs";import{join as u}from"node:path";import{validateCommandManifest as Y}from"../../api/command-manifest/manifest.js";import{crtrHome as O}from"../canvas/paths.js";import{withCanvasWrite as ee}from"../canvas/db.js";import{bundleVersion as ne,isInstalledGeneration as re,recordCheckedGeneration as te}from"../command-plugins/bundle.js";import{readConfig as U,readState as oe}from"../config.js";import{emitEvent as g}from"../events/emit.js";import{withExclusiveDirectoryLockAsync as ie}from"../exclusive-lock.js";import{atomicWriteText as M,ensureDir as I,pathExists as ae}from"../fs-utils.js";import{reconcilePackageDocuments as se}from"../graph/package-docs.js";import{readPluginManifest as W}from"../manifest.js";import{bundleSwapLockPath as de}from"../plugin-swap-lock.js";import{pluginsDir as ue,userScopeRoot as ce}from"../scope.js";import{localGrants as fe}from"../grants/mirror.js";import{ProviderError as c}from"./errors.js";const T="[A-Za-z0-9_-]{1,64}",le=new RegExp(`^(${T}):(${T})$`),z=new RegExp(`^${T}$`),pe=3e4,me=3e4;function ve(e){const n=new Set;for(const r of e)if(r.status==="active")for(const{scope:t}of r.resolved_scopes){const o=le.exec(t);o&&o[1]!=="crtr"&&n.add(o[1])}return[...n].sort()}i(ve,"providerIdsFromGrants");function w(e){return u(ue("user"),e)}i(w,"pluginRoot");function F(){return u(O(),"providers")}i(F,"providersRoot");function G(e,n){return u(F(),e,`v${n}`)}i(G,"majorDir");function j(e){if(!z.test(e))throw new c("not_found",`"${e}" is not a provider id`,{param:"provider"})}i(j,"assertId");function A(){const e=U("user").runtime?.issuer;if(!e)throw new c("not_found","provider bundles need a directory-connected runtime");return e.replace(/\/+$/,"")}i(A,"directoryBase");async function q(e,n){const r=`${A()}/providers/${encodeURIComponent(e)}/plugin${n.version!==void 0?`?version=${n.version}`:""}`;let t;try{t=await fetch(r,{headers:{Accept:"application/x-tar",...n.ifNoneMatch!==void 0?{"If-None-Match":n.ifNoneMatch}:{}},signal:AbortSignal.timeout(pe)})}catch(s){return{status:"failed",message:`could not reach ${r}: ${s instanceof Error?s.message:String(s)}`}}if(t.status===304)return n.ifNoneMatch===void 0?{status:"failed",message:`HTTP 304 from ${r} without a conditional request`}:{status:304};if(t.status===404||t.status===410)return await t.body?.cancel().catch(()=>{}),{status:t.status===404?404:410,message:`HTTP ${t.status} from ${r}`};if(t.status<200||t.status>=300)return await t.body?.cancel().catch(()=>{}),{status:"failed",message:`HTTP ${t.status} from ${r}`};let o;try{o=new Uint8Array(await t.arrayBuffer())}catch(s){return{status:"failed",message:`reading ${r} failed: ${s instanceof Error?s.message:String(s)}`}}const a=t.headers.get("etag");return{status:200,raw:o,...a!==null?{etag:a}:{}}}i(q,"fetchProviderBundle");const _=new WeakSet;class S extends Error{static{i(this,"BundleInvalid")}issues;constructor(n,r){super(n),this.issues=r}}function V(e,n,r){if(r.provider===void 0)return"the archive carries no provider block";if(r.provider.id!==e)return`the archive is provider "${r.provider.id}", not "${e}"`;if(n!==void 0&&r.provider.major!==n)return`the archive is major ${r.provider.major}, not ${n}`;const t=["hooks","kinds","page_components","memory_extensions"].filter(o=>r[o]!==void 0);if(t.length>0)return`a provider bundle may not carry ${t.join(", ")}`}i(V,"wrongBundle");function C(e,n,r){g({level:"error",event:"daemon.providers.bundle_invalid",fields:{provider:e,...n!==void 0?{major:n}:{},...r instanceof S?{issues:r.issues}:{}},error:r})}i(C,"logInvalid");function Z(e,n,r){g({level:"warn",event:"daemon.providers.bundle_fetch_failed",fields:{provider:e,...n!==void 0?{major:n}:{},message:r}})}i(Z,"logFailure");async function L(){return import("../../commands/pkg/plugin-manage.js")}i(L,"pluginManage");const P=new Map,E=i(e=>`${O()}\0${e}`,"installKey");function he(e){const n=e,r=n?.code??n?.cause?.code;return typeof r=="string"&&/^[A-Z][A-Z0-9_]{1,30}$/.test(r)?`${r} on provider install directory`:e instanceof c&&e.message.includes("HTTP 404")?"directory returned HTTP 404":e instanceof c&&e.message.includes("HTTP 410")?"directory returned HTTP 410":"provider bundle could not be installed"}i(he,"publicInstallCause");function ge(e){if(m(e)===void 0)return P.get(E(e))??{status:"installing"}}i(ge,"providerInstallState");function we(e){return new c("provider_unavailable",`provider "${e}" unavailable: ${ge(e)?.error??"bundle installation pending"}`)}i(we,"unavailableProviderError");function m(e){const n=W(w(e));if(!(n?.provider===void 0||n.transport?.kind!=="http")&&ae(u(w(e),"commands.json"))){try{if(x(u(w(e),"commands.json")).size===0)return}catch{return}return{provider:n.provider,transport:{kind:"http",endpoint:n.transport.endpoint}}}}i(m,"installedManifest");async function k(e,n,r=!1){const t=ce();m(e)===void 0&&P.set(E(e),{status:"installing"});try{return I(u(t,"tmp")),await ie(de(t,e),async()=>{const o=w(e),a=W(o);if(a!==null&&a.provider===void 0)throw new c("provider_unavailable",`plugin "${e}" is installed and is not the directory's provider bundle`);const s=m(e)!==void 0;if(s&&r)return"unchanged";const f=s&&n?oe("user").plugins[e]?.etag:void 0,d=await q(e,f!==void 0?{ifNoneMatch:f}:{});if(d.status===304)return"unchanged";if(d.status===404||d.status===410)throw new c("not_found",`the directory serves no provider "${e}" (${d.message})`,{param:"provider"});if(d.status==="failed")throw new c("provider_unavailable",`provider "${e}" bundle fetch failed: ${d.message}`);const l=ne(d.raw);if(s&&re(e,"user",o,l))return te("user",e,d.etag,{swapped:!1}),"unchanged";const{installPluginArchive:v}=await L();try{await v(e,d.raw,{scope:"user",scopeRootPath:t,root:o,version:l,etag:d.etag,enable:!s,bundle:{endpoint:`${A()}/providers/${encodeURIComponent(e)}/plugin`},transport:{kind:"http",endpoint:A()},accept:i(p=>V(e,void 0,p),"accept")})}catch(p){C(e,void 0,p);const N=new c("not_found",`provider "${e}" served an invalid bundle: ${p instanceof Error?p.message:String(p)}`,{param:"provider"});throw _.add(N),N}try{ee(p=>se(p))}catch(p){g({level:"error",event:"daemon.providers.docs_reconcile_failed",fields:{provider:e},error:p})}return g({level:"info",event:"daemon.providers.bundle_installed",fields:{provider:e,version:l,replaced:s}}),P.delete(E(e)),s?"updated":"installed"},{timeoutMs:me,timeoutError:i(()=>new c("provider_unavailable",`timed out waiting for another process replacing plugin "${e}"`),"timeoutError")})}catch(o){throw m(e)===void 0&&P.set(E(e),{status:"install_failed",error:he(o)}),o instanceof c?o:(g({level:"error",event:"daemon.providers.bundle_install_failed",fields:{provider:e},error:o}),we(e))}}i(k,"refreshInstalled");async function We(e){j(e),m(e)===void 0&&await k(e,!1,!0)}i(We,"ensureProviderBundle");async function ze(e,n){return j(e),n.version!==void 0&&m(e)?.provider.major!==n.version?R(e,n.version,n.conditional):k(e,n.conditional)}i(ze,"refreshProviderBundle");async function J(e,n,r){const{validatePluginArchive:t}=await L(),o=await t(r);if(o.bundle===void 0)throw new S(`provider "${e}" major ${n} bundle is invalid: ${o.issues.map(s=>s.message).join("; ")}`,o.issues);const a=V(e,n,o.bundle);if(a!==void 0)throw new S(`provider "${e}" major ${n} bundle is invalid: ${a}`,[]);if(o.bundle.transport===void 0)throw new S(`provider "${e}" major ${n} bundle declares no transport`,[]);return o.bundle}i(J,"validateArchive");function $e(e){I(e),M(u(e,"retired"),`${new Date().toISOString()}
2
2
  `),b(u(e,"bundle.tar"),{force:!0}),b(u(e,"etag"),{force:!0})}i($e,"retire");function K(e,n){return new c("provider_version_retired",`provider "${e}" major ${n} is retired`,{param:"version"})}i(K,"retiredError");async function R(e,n,r){const t=G(e,n),o=u(t,"bundle.tar"),a=h(o),s=a&&r&&h(u(t,"etag"))?y(u(t,"etag"),"utf8"):void 0,f=await q(e,{version:n,...s!==void 0?{ifNoneMatch:s}:{}});if(f.status===304)return"unchanged";if(f.status===410)throw $e(t),g({level:"info",event:"daemon.providers.major_retired",fields:{provider:e,major:n}}),K(e,n);if(f.status===404)throw new c("not_found",`provider "${e}" never served major ${n}`,{param:"version"});if(f.status==="failed")throw new c("provider_unavailable",`provider "${e}" major ${n} bundle fetch failed: ${f.message}`);try{await J(e,n,f.raw)}catch(l){C(e,n,l);const v=new c(a?"provider_unavailable":"not_found",l instanceof Error?l.message:String(l),{param:"version"});throw _.add(v),v}I(t),b(u(t,"retired"),{force:!0});const d=a&&Buffer.from(f.raw).equals(y(o));return d||M(o,f.raw),f.etag!==void 0?M(u(t,"etag"),f.etag):b(u(t,"etag"),{force:!0}),d?"unchanged":a?"updated":"installed"}i(R,"refreshPinned");function ye(){const e=[],n=F();if(!h(n))return e;for(const r of H(n)){if(!z.test(r))continue;let t;try{t=H(u(n,r))}catch{continue}for(const o of t){const a=/^v([1-9][0-9]*)$/.exec(o);a&&h(u(n,r,o,"bundle.tar"))&&e.push([r,Number(a[1])])}}return e}i(ye,"cachedMajors");function be(e){const n=Y(e,{transport:"http",reservedCoreNames:new Set});if(n.manifest===void 0)throw new c("not_found","the provider bundle's commands.json no longer validates");return n.manifest}i(be,"readCommands");const B=new Map,Q=new Map;function _e(e,n){const r=x(n),t=`${r.ino}:${r.mtimeMs}:${r.size}`,o=Q.get(e);if(o?.stamp===t)return o.commands;const a=be(JSON.parse(y(n,"utf8")));return Q.set(e,{stamp:t,commands:a}),a}i(_e,"installedCommands");async function Fe(e,n){j(e);const r=m(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=w(e),v=_e(e,u(l,"commands.json"));return{id:e,major:r.provider.major,provider:r.provider,commands:v,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=G(e,n);if(h(u(t,"retired")))throw K(e,n);const o=u(t,"bundle.tar");h(o)||await R(e,n,!1);const a=`${e}/${n}`,s=x(o),f=`${s.mtimeMs}:${s.size}`;let d=B.get(a)?.stamp===f?B.get(a).bundle:void 0;if(d===void 0){try{d=await J(e,n,new Uint8Array(y(o)))}catch(l){throw C(e,n,l),new c("not_found",`provider "${e}" has no valid bundle for major ${n}`,{param:"version"})}B.set(a,{stamp:f,bundle:d})}return{id:e,major:n,provider:d.provider,commands:d.commands,transport:{kind:"http",endpoint:d.transport.endpoint},source:"pinned"}}i(Fe,"resolveProviderBundle");let $=null,D=!1;async function Se(){if(U("user").runtime?.issuer){for(const e of ve(fe()))try{await k(e,!0)}catch(n){n instanceof Object&&_.has(n)||Z(e,void 0,n instanceof Error?n.message:String(n))}for(const[e,n]of ye())try{await R(e,n,!0)}catch(r){if(r instanceof c&&r.code==="provider_version_retired"||r instanceof Object&&_.has(r))continue;Z(e,n,r instanceof Error?r.message:String(r))}}}i(Se,"syncOnce");function Ge(){return $!==null?(D=!0,$):($=(async()=>{try{do D=!1,await Se();while(D)}finally{$=null}})(),$)}i(Ge,"syncProviderBundles");export{We as ensureProviderBundle,ve as providerIdsFromGrants,ge as providerInstallState,ze as refreshProviderBundle,Fe as resolveProviderBundle,Ge as syncProviderBundles,we as unavailableProviderError};
@@ -1,24 +1,24 @@
1
- var B=Object.defineProperty;var i=(e,n)=>B(e,"name",{value:n,configurable:!0});import{randomUUID as $}from"node:crypto";import{createServer as j}from"node:http";import{Router as F}from"./router.js";import{OPERATIONS as G}from"./operations.js";import{errorCodes as L}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as Y,RUNTIME_VERSION_HEADER as z,minimumRuntimeRefusal as Q}from"../../api/runtime-version.js";import{CONTRACT_HEADER as W,CONTRACT_LEVEL as U,REQUIRED_CONTRACT_HEADER as K,contractRefusal as Z}from"../../api/contract.js";import{crtrVersion as H}from"../../shared/crtr-version.js";import{AppPrincipalResolver as ee}from"./principal.js";import{handleRunOperation as te,integer as ne}from"./handlers/runs.js";import{parseLlmRequest as re,runLlm as ae}from"../../core/llm/run.js";import{parseUsageMonth as oe,usageFor as se}from"../../core/usage/ledger.js";import{capFields as ie}from"./handlers/llm.js";import{CONTROL_BODY_MAX_BYTES as ue,MODEL_CONTEXT_BODY_MAX_BYTES as ce}from"../../core/llm/body-limits.js";import{RunError as d,runIsRunning as de}from"../../core/runs/operations.js";import{requireRun as M}from"./router.js";import{deferHead as le}from"./deferred-head.js";import{readRunEvents as V,registerGrantStream as fe,subscribeRunEvents as me}from"../../core/runs/events.js";import{activeGrant as pe}from"../../core/grants/mirror.js";import{releaseGrantWaiters as he}from"../../core/grants/watch.js";import{emitEvent as _e}from"../../core/events/emit.js";import{runNotices as ye}from"../run-notices.js";import{handleVendorSignIn as we,isVendorSignInPath as ve}from"./vendor-signin.js";import{listAppProfiles as ge}from"./handlers/app-profiles.js";import{handleTransferDownload as Oe,handleTransferOperation as Re,TRANSFER_OPERATIONS as Ee}from"./handlers/transfer.js";import{handleQuestionOperation as Se,QUESTION_OPERATIONS as Te}from"./handlers/questions.js";import{vendorStatus as Ae}from"./handlers/vendor-status.js";import{handleMemoryOperation as be,MEMORY_OPERATIONS as Ne}from"./handlers/memory.js";import{abortOnClose as Ce,callerFromApp as Ie,handleProviderOperation as Pe,PROVIDER_OPERATIONS as He,providerFailure as Me}from"./handlers/providers.js";function De(e){return Object.prototype.hasOwnProperty.call(L,e)?L[e]:void 0}i(De,"fixedRow");function ke(e){const n=De(e);return n?n.type:e==="stream_gap"||e==="provider_error"||e==="runtime_version_unsupported"?"invalid_request_error":"server_error"}i(ke,"errorType");function xe(e){if(!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...r}=e;return r}i(xe,"appDetails");function I(e,n,r){const u=xe(e.details);return{error:{code:e.code,message:e.message,...u===void 0?{}:{details:u},type:ke(e.code),...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:n,...r?{run_id:r}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...ie(e),...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}i(I,"envelope");function Je(e,n,r,u){const s=n instanceof d?n:new d(500,"server_failure","runtime request failed");if(n instanceof d||console.error("[app-listener]",n),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
2
- data: ${JSON.stringify(I(s,r,u))}
1
+ var X=Object.defineProperty;var s=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as $}from"node:crypto";import{createServer as q}from"node:http";import{Router as F}from"./router.js";import{OPERATIONS as G}from"./operations.js";import{errorCodes as L}from"../../api/error-codes.js";import{MINIMUM_RUNTIME_VERSION_HEADER as Y,RUNTIME_VERSION_HEADER as z,minimumRuntimeRefusal as Q}from"../../api/runtime-version.js";import{CONTRACT_HEADER as W,CONTRACT_LEVEL as U,REQUIRED_CONTRACT_HEADER as K,contractRefusal as Z}from"../../api/contract.js";import{crtrVersion as M}from"../../shared/crtr-version.js";import{decodeJwt as ee}from"jose";import{AppPrincipalResolver as te}from"./principal.js";import{runtimeOwner as ne,servePeer as re}from"../peer/ingress.js";import{ownMachineId as ae}from"../../core/peer/directory.js";import{handleRunOperation as oe,integer as se}from"./handlers/runs.js";import{parseLlmRequest as ie,runLlm as ue}from"../../core/llm/run.js";import{parseUsageMonth as ce,usageFor as de}from"../../core/usage/ledger.js";import{capFields as le}from"./handlers/llm.js";import{CONTROL_BODY_MAX_BYTES as fe,MODEL_CONTEXT_BODY_MAX_BYTES as me}from"../../core/llm/body-limits.js";import{RunError as d,runIsRunning as pe}from"../../core/runs/operations.js";import{requireRun as H}from"./router.js";import{deferHead as he}from"./deferred-head.js";import{readRunEvents as B,registerGrantStream as _e,subscribeRunEvents as we}from"../../core/runs/events.js";import{activeGrant as ye}from"../../core/grants/mirror.js";import{releaseGrantWaiters as ve}from"../../core/grants/watch.js";import{emitEvent as ge}from"../../core/events/emit.js";import{runNotices as Oe}from"../run-notices.js";import{handleVendorSignIn as Re,isVendorSignInPath as Ee}from"./vendor-signin.js";import{listAppProfiles as Se}from"./handlers/app-profiles.js";import{handleTransferDownload as Te,handleTransferOperation as Ae,TRANSFER_OPERATIONS as be}from"./handlers/transfer.js";import{handleQuestionOperation as Ne,QUESTION_OPERATIONS as Ce}from"./handlers/questions.js";import{vendorStatus as Ie}from"./handlers/vendor-status.js";import{handleMemoryOperation as Pe,MEMORY_OPERATIONS as Me}from"./handlers/memory.js";import{abortOnClose as He,callerFromApp as De,handleProviderOperation as ke,PROVIDER_OPERATIONS as xe,providerFailure as Je}from"./handlers/providers.js";function Le(e){return Object.prototype.hasOwnProperty.call(L,e)?L[e]:void 0}s(Le,"fixedRow");function Ue(e){const n=Le(e);return n?n.type:e==="stream_gap"||e==="provider_error"||e==="runtime_version_unsupported"?"invalid_request_error":"server_error"}s(Ue,"errorType");function Be(e){if(!e||typeof e!="object"||Array.isArray(e)||!("rate_limit"in e))return e;const{rate_limit:n,...a}=e;return a}s(Be,"appDetails");function I(e,n,a){const u=Be(e.details);return{error:{code:e.code,message:e.message,...u===void 0?{}:{details:u},type:Ue(e.code),...e.param?{param:e.param}:{},origin:"runtime",retryable:e.retryable||e.code==="stream_dropped",request_id:n,...a?{run_id:a}:{},...e.scope?{scope:e.scope}:{},...e.code==="unauthorized"?{user_action:"reconnect"}:{},...le(e),...e.code==="all_routes_exhausted"?{retry_after_s:e.details.retry_after_s}:{}}}}s(I,"envelope");function je(e,n,a,u){const i=n instanceof d?n:new d(500,"server_failure","runtime request failed");if(n instanceof d||console.error("[app-listener]",n),e.headersSent&&!String(e.getHeader("Content-Type")??"").startsWith("text/event-stream")){e.destroy();return}if(e.headersSent){e.end(`event: error
2
+ data: ${JSON.stringify(I(i,a,u))}
3
3
 
4
- `);return}e.writeHead(s.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":r,"X-Content-Type-Options":"nosniff",...s.code==="all_routes_exhausted"?{"Retry-After":String(s.details.retry_after_s)}:{}}),e.end(JSON.stringify(I(s,r,u)))}i(Je,"fail");function X(e){return Array.isArray(e)?e[0]:e}i(X,"headerValue");function w(e,n,r){e.writeHead(n,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(r))}i(w,"json");async function C(e,n=ue){const r=[];let u=0;for await(const s of e){if(u+=s.length,u>n)throw new d(400,"invalid_request","request body too large");r.push(s)}if(u)try{return JSON.parse(Buffer.concat(r).toString("utf8"))}catch{throw new d(400,"invalid_request","invalid JSON body")}}i(C,"bodyOf");function Le(e,n){const r=e.headers["last-event-id"]??n.get("after");if(Array.isArray(r))throw new d(400,"invalid_request","invalid cursor","after");return ne(r??null,"after",0,Number.MAX_SAFE_INTEGER,0)}i(Le,"cursor");function Ue(e){return`id: ${e.sequence_number}
4
+ `);return}e.writeHead(i.status,{"Content-Type":"application/json; charset=utf-8","Request-Id":a,"X-Content-Type-Options":"nosniff",...i.code==="all_routes_exhausted"?{"Retry-After":String(i.details.retry_after_s)}:{}}),e.end(JSON.stringify(I(i,a,u)))}s(je,"fail");function j(e){return Array.isArray(e)?e[0]:e}s(j,"headerValue");function Ve(e){if(!e?.startsWith("Bearer ")||e.length<=7)return!1;try{return typeof ee(e.slice(7)).machine_id=="string"}catch{return!1}}s(Ve,"isMachineBearer");function v(e,n,a){e.writeHead(n,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(a))}s(v,"json");async function C(e,n=fe){const a=[];let u=0;for await(const i of e){if(u+=i.length,u>n)throw new d(400,"invalid_request","request body too large");a.push(i)}if(u)try{return JSON.parse(Buffer.concat(a).toString("utf8"))}catch{throw new d(400,"invalid_request","invalid JSON body")}}s(C,"bodyOf");function Xe(e,n){const a=e.headers["last-event-id"]??n.get("after");if(Array.isArray(a))throw new d(400,"invalid_request","invalid cursor","after");return se(a??null,"after",0,Number.MAX_SAFE_INTEGER,0)}s(Xe,"cursor");function $e(e){return`id: ${e.sequence_number}
5
5
  event: ${e.type}
6
6
  data: ${JSON.stringify(e)}
7
7
 
8
- `}i(Ue,"frame");const Ve=6e4;function Xe(e,n,r,u,s,v,g,A={}){M(u,r);let l=V(r,s);if(s>l.latest_sequence)throw new d(400,"invalid_request","cursor is beyond the run","after");if(s<l.latest_sequence&&(l.earliest_sequence===null||s<l.earliest_sequence-1))throw new d(409,"stream_gap","events have expired",void 0,{earliest_sequence:l.earliest_sequence,latest_sequence:l.latest_sequence});if(n.destroyed||e.destroyed)return n.destroy(),Promise.resolve();n.writeHead(200,{"Content-Type":g==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),g==="sse"?n.write(`: connected
8
+ `}s($e,"frame");const qe=6e4;function Fe(e,n,a,u,i,w,g,A={}){H(u,a);let l=B(a,i);if(i>l.latest_sequence)throw new d(400,"invalid_request","cursor is beyond the run","after");if(i<l.latest_sequence&&(l.earliest_sequence===null||i<l.earliest_sequence-1))throw new d(409,"stream_gap","events have expired",void 0,{earliest_sequence:l.earliest_sequence,latest_sequence:l.latest_sequence});if(n.destroyed||e.destroyed)return n.destroy(),Promise.resolve();n.writeHead(200,{"Content-Type":g==="sse"?"text/event-stream":"application/x-ndjson","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),g==="sse"?n.write(`: connected
9
9
 
10
- `):n.flushHeaders();let E=s;const a=ye().streamOpened(r);let t=!1,p=!1,_=!1,S;const y=new Promise(h=>{S=h}),f=i(()=>{t||(t=!0,a(E),clearInterval(T),clearInterval(x),b(),R(),n.off("close",f),n.off("drain",O),n.end(),S())},"end"),O=i(()=>{_=!1,c()},"onDrain"),o=i(h=>{t||n.write(g==="sse"?`event: error
11
- data: ${JSON.stringify(I(h,v,r))}
10
+ `):n.flushHeaders();let E=i;const r=Oe().streamOpened(a);let t=!1,p=!1,_=!1,S;const y=new Promise(h=>{S=h}),f=s(()=>{t||(t=!0,r(E),clearInterval(T),clearInterval(x),b(),R(),n.off("close",f),n.off("drain",O),n.end(),S())},"end"),O=s(()=>{_=!1,c()},"onDrain"),o=s(h=>{t||n.write(g==="sse"?`event: error
11
+ data: ${JSON.stringify(I(h,w,a))}
12
12
 
13
- `:`${JSON.stringify({type:"error",...I(h,v,r)})}
14
- `),f()},"error"),c=i(()=>{if(!(t||p)){p=!0;try{if(!m()){N();return}if(M(u,r),l=V(r,E),l.backlog_count>1e3||l.backlog_bytes>32*1024*1024){o(new d(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:E}));return}for(const h of l.events){if(_)break;_=!n.write(g==="sse"?Ue(h):`${JSON.stringify(h)}
15
- `),E=h.sequence_number}}catch(h){o(h instanceof d?h:new d(500,"server_failure","stream failed"))}finally{p=!1}}},"flush"),N=i(()=>o(new d(401,"grant_revoked","grant is not active")),"revoked"),m=i(()=>"class"in u||pe(u.grant)!==null,"grantActive"),b=me(r,c),R="class"in u?()=>{}:fe(u.grant,N),T=setInterval(()=>{!t&&!_&&g==="sse"&&n.write(`: keepalive
13
+ `:`${JSON.stringify({type:"error",...I(h,w,a)})}
14
+ `),f()},"error"),c=s(()=>{if(!(t||p)){p=!0;try{if(!m()){N();return}if(H(u,a),l=B(a,E),l.backlog_count>1e3||l.backlog_bytes>32*1024*1024){o(new d(503,"stream_dropped","stream reader fell behind",void 0,{last_sequence:E}));return}for(const h of l.events){if(_)break;_=!n.write(g==="sse"?$e(h):`${JSON.stringify(h)}
15
+ `),E=h.sequence_number}}catch(h){o(h instanceof d?h:new d(500,"server_failure","stream failed"))}finally{p=!1}}},"flush"),N=s(()=>o(new d(401,"grant_revoked","grant is not active")),"revoked"),m=s(()=>"class"in u||ye(u.grant)!==null,"grantActive"),b=we(a,c),R="class"in u?()=>{}:_e(u.grant,N),T=setInterval(()=>{!t&&!_&&g==="sse"&&n.write(`: keepalive
16
16
 
17
- `)},15e3),P=A.idleMs??Ve,q=A.isRunning??de;let D=Date.now(),k=E;const x=setInterval(()=>{if(t||g!=="sse")return;const h=Date.now();let J=!0;try{J=q(r)}catch{}if(J||E!==k){D=h,k=E;return}h-D<P||_||(n.write(`event: idle
17
+ `)},15e3),P=A.idleMs??qe,V=A.isRunning??pe;let D=Date.now(),k=E;const x=setInterval(()=>{if(t||g!=="sse")return;const h=Date.now();let J=!0;try{J=V(a)}catch{}if(J||E!==k){D=h,k=E;return}h-D<P||_||(n.write(`event: idle
18
18
  data: {}
19
19
 
20
- `),f())},A.idleCheckMs??1e3);return x.unref(),n.on("drain",O),n.on("close",f),c(),y}i(Xe,"streamRunEvents");function wt(e,n,r,u={}){const s=e.listener;if(!e.issuer||!e.audience||!s?.address||!Number.isInteger(s.port)||s.port<1||s.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(s.address))throw new Error("runtime listener must bind a specific address");const v=new ee(e.issuer,e.audience,r,e.jwksUrl),g=new F,A=new Map;for(const a of G.filter(t=>t.stage!==void 0)){const t=i(()=>({status:500}),"handler");g.register({method:a.method,pattern:a.path,handler:t}),A.set(t,a)}const l=j((a,t)=>{(async()=>{le(a,t,u.deferHead);const p=$();t.setHeader("Request-Id",p),t.setHeader(z,H()),t.setHeader(W,U);let _;try{const S=X(a.headers[Y.toLowerCase()]),y=Q(S,H())??Z(X(a.headers[K.toLowerCase()]),U,{legacyMinimum:S,runtimeVersion:H()});if(y!==null)throw y.status===400?new d(400,y.code,y.message,y.param):new d(426,y.code,y.message,void 0,y.details);const f=new URL(a.url??"/","http://runtime.local");if(ve(f.pathname)){await we(a,t,f,v);return}if((a.method!=="GET"||f.pathname!=="/v1/health")&&(!v.isReady()||!n()))throw new d(503,"runtime_starting","runtime_starting");const O=g.match(a.method??"GET",f.pathname);if(!O)throw new d(404,"not_found","unknown operation");const o=A.get(O.handler);if(!o)throw new d(404,"not_found","unknown operation");if(_=O.params.run_id,o.name==="health"){w(t,200,{status:"ok",api_version:"2026-09-23"});return}if(o.name==="runtime.sync-notice"){if(!await v.verifySyncNotice(a.headers.authorization)){t.writeHead(204),t.end();return}_e({level:"info",event:"daemon.grants.sync_notice",fields:{request_id:p}});try{await r({fresh:!0})}catch{throw new d(503,"service_unavailable","grant sync failed")}t.writeHead(204),t.end();return}if(o.name==="runtime.vendor-status"){await v.verifyDirectoryRead(a.headers.authorization,"vendor_status"),w(t,200,Ae());return}const c=await v.resolve(a.headers.authorization,o.scope);if(t.setHeader("Request-Id",p),_&&M(c,_,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(o.name),o.name==="run.delete"),o.stream==="bytes"){await Oe(a,t,c,f.searchParams);return}if(o.stream){Xe(a,t,_,c,Le(a,f.searchParams),p,"sse",u.stream);return}if(o.name==="llm"){const m=re(await C(a,ce)),b=new AbortController,R=i(()=>b.abort(),"abort");t.on("close",R);try{const T=await ae(m,b.signal,m.stream?P=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(P)}
20
+ `),f())},A.idleCheckMs??1e3);return x.unref(),n.on("drain",O),n.on("close",f),c(),y}s(Fe,"streamRunEvents");function At(e,n,a,u={}){const i=e.listener;if(!e.issuer||!e.audience||!i?.address||!Number.isInteger(i.port)||i.port<1||i.port>65535)throw new Error("runtime issuer, audience and listener address/port are required");if(["0.0.0.0","::","[::]"].includes(i.address))throw new Error("runtime listener must bind a specific address");const w=new te(e.issuer,e.audience,a,e.jwksUrl),g=new F,A=new Map;for(const r of G.filter(t=>t.stage!==void 0)){const t=s(()=>({status:500}),"handler");g.register({method:r.method,pattern:r.path,handler:t}),A.set(t,r)}const l=q((r,t)=>{(async()=>{he(r,t,u.deferHead);const p=$();t.setHeader("Request-Id",p),t.setHeader(z,M()),t.setHeader(W,U);let _;try{const S=j(r.headers[Y.toLowerCase()]),y=Q(S,M())??Z(j(r.headers[K.toLowerCase()]),U,{legacyMinimum:S,runtimeVersion:M()});if(y!==null)throw y.status===400?new d(400,y.code,y.message,y.param):new d(426,y.code,y.message,void 0,y.details);const f=new URL(r.url??"/","http://runtime.local");if(Ee(f.pathname)){await Re(r,t,f,w);return}if(Ve(r.headers.authorization)){await re(r,t,{issuer:e.issuer,jwks:w.jwks,audience:s(()=>e.audience,"audience"),owner:ne,self:ae,ready:s(()=>w.isReady()&&n(),"ready")});return}if((r.method!=="GET"||f.pathname!=="/v1/health")&&(!w.isReady()||!n()))throw new d(503,"runtime_starting","runtime_starting");const O=g.match(r.method??"GET",f.pathname);if(!O)throw new d(404,"not_found","unknown operation");const o=A.get(O.handler);if(!o)throw new d(404,"not_found","unknown operation");if(_=O.params.run_id,o.name==="health"){v(t,200,{status:"ok",api_version:"2026-09-23"});return}if(o.name==="runtime.sync-notice"){if(!await w.verifySyncNotice(r.headers.authorization)){t.writeHead(204),t.end();return}ge({level:"info",event:"daemon.grants.sync_notice",fields:{request_id:p}});try{await a({fresh:!0})}catch{throw new d(503,"service_unavailable","grant sync failed")}t.writeHead(204),t.end();return}if(o.name==="runtime.vendor-status"){await w.verifyDirectoryRead(r.headers.authorization,"vendor_status"),v(t,200,Ie());return}const c=await w.resolve(r.headers.authorization,o.scope);if(t.setHeader("Request-Id",p),_&&H(c,_,["run.message","run.interrupt","run.cancel","run.rename","run.delete"].includes(o.name),o.name==="run.delete"),o.stream==="bytes"){await Te(r,t,c,f.searchParams);return}if(o.stream){Fe(r,t,_,c,Xe(r,f.searchParams),p,"sse",u.stream);return}if(o.name==="llm"){const m=ie(await C(r,me)),b=new AbortController,R=s(()=>b.abort(),"abort");t.on("close",R);try{const T=await ue(m,b.signal,m.stream?P=>{t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.write(`data: ${JSON.stringify(P)}
21
21
 
22
22
  `)}:void 0,void 0,{grantee:c.grantee,spend_cap:c.spend_cap});m.stream?(t.headersSent||t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache","X-Accel-Buffering":"no"}),t.end(`data: [DONE]
23
23
 
24
- `)):w(t,200,T.completion)}finally{t.off("close",R)}return}if(o.name==="usage.get"){w(t,200,se(c.grantee,c.spend_cap,oe(f.searchParams.get("month"))));return}if(o.name==="profile.list"){w(t,200,ge(c));return}if(Te.has(o.name)){const m=await Se(o.name,c,O.params.question_id,o.method==="POST"?await C(a):void 0);w(t,m.status,m.body);return}if(He.has(o.name)){const{signal:m,release:b}=Ce(t);try{const R=await Pe(o.name,Ie(c),{params:O.params,body:o.method==="POST"?await C(a):void 0,query:f.searchParams},{requestId:p,signal:m});w(t,R.status,R.body)}catch(R){const T=Me(R,p,!0);t.writeHead(T.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...T.headers}),t.end(JSON.stringify(T.body))}finally{b()}return}if(Ne.has(o.name)){const m=be(o.name,{class:"app",app:c.grantee,scopes:c.resolved},f.searchParams);w(t,m.status,m.body);return}if(Ee.has(o.name)){const m=await Re(o,c,O.params,f.searchParams,await C(a));w(t,m.status,m.body);return}const N=await te(o,c,_,f.searchParams,await C(a),a.headers["idempotency-key"]);w(t,N.status,N.body)}catch(S){Je(t,S,p,_)}})()});return{ready:new Promise((a,t)=>{l.once("error",t),l.listen(s.port,s.address,()=>{l.off("error",t),a()})}),close:i(async()=>{v.close(),he(),l.closeAllConnections(),await new Promise((a,t)=>l.close(p=>p?t(p):a()))},"close")}}i(wt,"createAppListener");export{Ve as RUN_STREAM_IDLE_MS,wt as createAppListener,Le as cursor,Xe as streamRunEvents};
24
+ `)):v(t,200,T.completion)}finally{t.off("close",R)}return}if(o.name==="usage.get"){v(t,200,de(c.grantee,c.spend_cap,ce(f.searchParams.get("month"))));return}if(o.name==="profile.list"){v(t,200,Se(c));return}if(Ce.has(o.name)){const m=await Ne(o.name,c,O.params.question_id,o.method==="POST"?await C(r):void 0);v(t,m.status,m.body);return}if(xe.has(o.name)){const{signal:m,release:b}=He(t);try{const R=await ke(o.name,De(c),{params:O.params,body:o.method==="POST"?await C(r):void 0,query:f.searchParams},{requestId:p,signal:m});v(t,R.status,R.body)}catch(R){const T=Je(R,p,!0);t.writeHead(T.status,{"Content-Type":"application/json; charset=utf-8","X-Content-Type-Options":"nosniff",...T.headers}),t.end(JSON.stringify(T.body))}finally{b()}return}if(Me.has(o.name)){const m=Pe(o.name,{class:"app",app:c.grantee,scopes:c.resolved},f.searchParams);v(t,m.status,m.body);return}if(be.has(o.name)){const m=await Ae(o,c,O.params,f.searchParams,await C(r));v(t,m.status,m.body);return}const N=await oe(o,c,_,f.searchParams,await C(r),r.headers["idempotency-key"]);v(t,N.status,N.body)}catch(S){je(t,S,p,_)}})()});return{ready:new Promise((r,t)=>{l.once("error",t),l.listen(i.port,i.address,()=>{l.off("error",t),r()})}),close:s(async()=>{w.close(),ve(),l.closeAllConnections(),await new Promise((r,t)=>l.close(p=>p?t(p):r()))},"close")}}s(At,"createAppListener");export{qe as RUN_STREAM_IDLE_MS,At as createAppListener,Xe as cursor,Fe as streamRunEvents};
@@ -1,2 +1,2 @@
1
- import type { RouteTable } from '../router.js';
1
+ import { type RouteTable } from '../router.js';
2
2
  export declare const bashRoutes: RouteTable;
@@ -1 +1 @@
1
- var m=Object.defineProperty;var p=(o,t)=>m(o,"name",{value:t,configurable:!0});import{statSync as u}from"node:fs";import{isAbsolute as l}from"node:path";import{ApiError as d}from"../../../api/index.js";import{buildOperationalEnvBase as f}from"../../../core/runtime/spawn-env.js";import{runAppCommand as w}from"../../../core/runtime/run-command.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{readProfileEnvVars as b}from"../../../core/profiles/env-store.js";import{readAppEnvVars as y}from"../../../core/profiles/app-env-store.js";import{usage as i}from"../../../core/errors.js";const h=60,c=600,T=256*1024;function E(o){if(typeof o!="object"||o===null||Array.isArray(o))throw i("bash requires an object body");const t=o;if(typeof t.command!="string"||t.command==="")throw i("bash requires a non-empty `command`");if(typeof t.cwd!="string"||!l(t.cwd))throw new d(404,"cwd_not_found","cwd must name an absolute directory");try{if(!u(t.cwd).isDirectory())throw new Error("not a directory")}catch{throw new d(404,"cwd_not_found",`cwd not found: ${t.cwd}`)}const r=t.timeout_s??h;if(typeof r!="number"||!Number.isFinite(r)||r<=0||r>c)throw new d(422,"invalid_timeout",`timeout_s must be greater than 0 and at most ${c}`);if(t.profile!==void 0&&typeof t.profile!="string")throw i("profile must be a string");let n;if(t.env!==void 0){if(typeof t.env!="object"||t.env===null||Array.isArray(t.env))throw i("env must be an object of string values");n={};for(const[s,a]of Object.entries(t.env)){if(typeof a!="string")throw i("env must be an object of string values");n[s]=a}}return{command:t.command,cwd:t.cwd,timeout_s:r,env:n,profile:t.profile}}p(E,"parseRequest");async function A(o){const t=E(o.body),r=o.principal.class==="person"?"app:terminal":o.principal.app;if(r!=="app:terminal"&&t.profile!==void 0)throw new d(403,"permission_error","app bash cannot select an owner profile");const n=r==="app:terminal"?t.profile??null:null,s=f({targetCwd:t.cwd,targetProfileId:n,skipSpawnAllow:r!=="app:terminal"}),a={...s,...y(r),...r==="app:terminal"?b(n):{},...t.env,...n===null?{}:{CRTR_PROFILE_ID:n},CRTR_ADMITTED_HOST_PATH:s.CRTR_ADMITTED_HOST_PATH},e=await(await w({app:r,...o.principal.class==="broker"?{nodeId:o.principal.node,isolated:_(o.principal.node)?.isolated===!0}:{},command:t.command,cwd:t.cwd,env:a,timeoutMs:t.timeout_s*1e3,headBytes:T})).completion;if(e.processError!==null)throw e.processError;return{status:200,body:{exit_code:e.timedOut?null:e.exitCode,signal:e.signal,stdout:e.stdout,stdout_truncated:e.stdoutTruncated,stderr:e.stderr,stderr_truncated:e.stderrTruncated,timed_out:e.timedOut,duration_ms:e.durationMs}}}p(A,"handleBash");const H=[{method:"POST",pattern:"/v1/bash",scope:"crtr:act",handler:A}];export{H as bashRoutes};
1
+ var c=Object.defineProperty;var p=(o,t)=>c(o,"name",{value:t,configurable:!0});import{statSync as u}from"node:fs";import{isAbsolute as f}from"node:path";import{ApiError as d}from"../../../api/index.js";import{buildOperationalEnvBase as l}from"../../../core/runtime/spawn-env.js";import{runAppCommand as w}from"../../../core/runtime/run-command.js";import{getNode as _}from"../../../core/canvas/canvas.js";import{readProfileEnvVars as b}from"../../../core/profiles/env-store.js";import{readAppEnvVars as y}from"../../../core/profiles/app-env-store.js";import{usage as i}from"../../../core/errors.js";import{appOf as h}from"../router.js";const T=60,m=600,E=256*1024;function A(o){if(typeof o!="object"||o===null||Array.isArray(o))throw i("bash requires an object body");const t=o;if(typeof t.command!="string"||t.command==="")throw i("bash requires a non-empty `command`");if(typeof t.cwd!="string"||!f(t.cwd))throw new d(404,"cwd_not_found","cwd must name an absolute directory");try{if(!u(t.cwd).isDirectory())throw new Error("not a directory")}catch{throw new d(404,"cwd_not_found",`cwd not found: ${t.cwd}`)}const r=t.timeout_s??T;if(typeof r!="number"||!Number.isFinite(r)||r<=0||r>m)throw new d(422,"invalid_timeout",`timeout_s must be greater than 0 and at most ${m}`);if(t.profile!==void 0&&typeof t.profile!="string")throw i("profile must be a string");let n;if(t.env!==void 0){if(typeof t.env!="object"||t.env===null||Array.isArray(t.env))throw i("env must be an object of string values");n={};for(const[s,a]of Object.entries(t.env)){if(typeof a!="string")throw i("env must be an object of string values");n[s]=a}}return{command:t.command,cwd:t.cwd,timeout_s:r,env:n,profile:t.profile}}p(A,"parseRequest");async function g(o){const t=A(o.body),r=h(o.principal)??"app:terminal";if(r!=="app:terminal"&&t.profile!==void 0)throw new d(403,"permission_error","app bash cannot select an owner profile");const n=r==="app:terminal"?t.profile??null:null,s=l({targetCwd:t.cwd,targetProfileId:n,skipSpawnAllow:r!=="app:terminal"}),a={...s,...y(r),...r==="app:terminal"?b(n):{},...t.env,...n===null?{}:{CRTR_PROFILE_ID:n},CRTR_ADMITTED_HOST_PATH:s.CRTR_ADMITTED_HOST_PATH},e=await(await w({app:r,...o.principal.class==="broker"?{nodeId:o.principal.node,isolated:_(o.principal.node)?.isolated===!0}:{},command:t.command,cwd:t.cwd,env:a,timeoutMs:t.timeout_s*1e3,headBytes:E})).completion;if(e.processError!==null)throw e.processError;return{status:200,body:{exit_code:e.timedOut?null:e.exitCode,signal:e.signal,stdout:e.stdout,stdout_truncated:e.stdoutTruncated,stderr:e.stderr,stderr_truncated:e.stderrTruncated,timed_out:e.timedOut,duration_ms:e.durationMs}}}p(g,"handleBash");const q=[{method:"POST",pattern:"/v1/bash",scope:"crtr:act",handler:g}];export{q as bashRoutes};
@@ -1 +1 @@
1
- var V=Object.defineProperty;var t=(e,n)=>V(e,"name",{value:n,configurable:!0});import{randomUUID as X}from"node:crypto";import{relative as nn,resolve as z,sep as B}from"node:path";import{ApiError as a}from"../../../api/errors.js";import{armCron as en,cancelCron as rn,getCron as y,listCrons as T,listCronRuns as G,pokeHeldCrons as on,setCronState as F}from"../../../core/canvas/crons.js";import{getNode as h}from"../../../core/canvas/canvas.js";import{appSpace as tn,cronFolder as sn}from"../../../core/layout.js";import{appProcessesSandboxed as an,cwdIsMounted as cn}from"../../../core/spaces/process.js";import{activeGrant as ln,activeGrantForApp as dn,grantFor as un,terminalGrant as pn}from"../../../core/grants/mirror.js";import{readConfig as fn}from"../../../core/config.js";import{assertScope as L}from"../../../core/scopes.js";import{InputError as i}from"../../../core/io.js";import{nextSlotAfter as _n}from"../../../core/wake.js";import{executeCron as hn,markSelfCanceledCronRun as mn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as Z}from"../map.js";import{assertCeiling as gn,canReadNode as wn}from"../router.js";import{checkCronBirth as bn,checkCronFire as vn}from"../../cron/authority.js";import{requireUsableProfile as yn}from"./profiles.js";function A(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new i({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(A,"requireString");function C(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new i({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(C,"requireEnum");function kn(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new i({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(h(n)===null)throw new i({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new i({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new i({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(kn,"parseSinkSpec");const Sn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function H(e,n){if(!Sn.test(e)||Number.isNaN(Date.parse(e)))throw new i({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(H,"requireUtcIso");function $n(e){try{_n(e,new Date)}catch(n){throw new i({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t($n,"requireRecur");function Cn(e){let n;try{n=JSON.parse(e)}catch{throw new i({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new i({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new i({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(Cn,"requireEnvJson");function E(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(E,"callerProfile");function M(e,n){return n===null||e.scope==="global"||e.profile===n}t(M,"inScope");function Y(e){return e.principal.class==="person"?null:e.principal.app}t(Y,"callerApp");function U(e,n){return wn(e.principal,n.grantee,n.created_by??void 0)}t(U,"visibleTo");function On(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(On,"anchorNodeOf");function In(e,n,r,o,c,u){const l=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!an()){if(l===null)throw new i({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return l}const p=o!==null&&h(o)!==null?o:void 0,d=t(N=>cn(n,N,p,c,void 0,u),"reaches");if(l!==null&&d(l))return l;const s=tn(n).files;if(l!==null&&e.cwd_is_default!==!0)throw new a(400,"invalid_request",`cwd ${l} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:l,default:s},void 0,{param:"cwd"});const m=r===null?null:h(r)?.cwd??null;return m!==null&&d(m)?m:s}t(In,"resolveArmedCwd");function Nn(e,n,r){const o=e.principal;if(o.class==="person"){const s=pn();return{sub:s?.sub??"user:local",grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=dn(n);if(s===null)throw new a(401,"grant_revoked",`no active grant for ${n}`);const m=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:[...m??s.resolved],origin_run_id:null}}if(o.class==="cron"){const s=vn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new a(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?[...s.effectiveScopes]:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new a(401,"unauthorized","this caller cannot arm a cron");const c=r??h(o.node),u=c?.grant_id??o.grant,l=c?.sub??o.sub,p=u!=null?ln(u):un(n,l);if(p===null){if(n==="app:terminal"&&!fn("user").runtime?.issuer)return{sub:l,grant_id:u??null,scopes:c?.scopes??o.scopes,origin_run_id:c?.run_id??o.run};throw new a(401,"grant_revoked","the node grant is not active")}const d=c?.scopes??o.scopes;return{sub:p.sub,grant_id:p.id,scopes:d===null?[...p.resolved]:[...d],origin_run_id:c?.run_id??o.run}}t(Nn,"armPrincipal");function qn(e){const n=e.body??{},r=A(n,"name"),o=A(n,"command"),c=H(A(n,"fire_at"),"fire_at"),u=typeof n.recur=="string"&&n.recur!==""?$n(n.recur):null,l=typeof n.expires_at=="string"&&n.expires_at!==""?H(n.expires_at,"expires_at"):null,p=typeof n.env_json=="string"&&n.env_json!==""?Cn(n.env_json):null,d=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=C(n.on_output,"on_output",["silent","on-failure","always","on-change"],d?.startsWith("node:")?"on-change":"on-failure"),m=C(n.overlap,"overlap",["skip","queue","replace"],"skip"),N=C(n.tier,"tier",["normal","urgent","critical"],"normal"),R=s==="always"||s==="on-change";if(R&&d===null)throw new i({error:"sink_required",message:`on_output "${s}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!R&&d!==null)throw new i({error:"sink_unused",message:`on_output "${s}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const K=d!==null?kn(d):"{}";if(d?.startsWith("spawn:")&&(gn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const _=bn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!_.allowed)throw new a(_.reason==="scope_missing"?403:401,_.reason==="scope_missing"?"scope_missing":"grant_revoked",_.message)}const b=e.principal.class==="person"?"app:terminal":e.principal.app,v=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==v)throw new a(403,"permission_error","a broker may only arm a cron as its own node");const g=v===null?null:h(v);if(g!==null){if(g.grantee!==b)throw new i({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});L(g,"crtr:schedule"),d?.startsWith("spawn:")&&L(g,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new a(400,"invalid_request","isolated must be a boolean");if(g?.isolated&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const D=g?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(D&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const q=D||n.isolated===!0,w=Nn(e,b,g),j=X(),S=q?sn(b,j):null;if(S!==null&&n.cwd_is_default!==!0){const _=nn(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(_===".."||_.startsWith(`..${B}`)||_.startsWith(B))throw new a(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const Q=S??In(n,b,v,On(n),q,{sub:w.sub,grantId:w.grant_id,scopes:w.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&h(f)!==null&&h(f).grantee!==b)throw new i({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(f!==null&&h(f)===null)throw new i({error:"anchor_not_found",message:`anchor node ${f} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const W=n.cancel_on_wake===!0;if(W&&f===null)throw new i({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new i({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const P=typeof n.profile=="string"&&n.profile!==""?yn(e,n.profile).profileId:null,x=C(n.scope,"scope",["profile","global"],P!==null?"profile":"global");if(x==="profile"&&P===null)throw new i({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return en({cron_id:j,name:r,created_by:v,grantee:b,sub:w.sub,grant_id:w.grant_id,scopes:w.scopes,origin_run_id:w.origin_run_id,isolated:q,command:o,fire_at:c,recur:u,tz:typeof n.tz=="string"?n.tz:null,expires_at:l,anchor_node:f,cancel_on_wake:W,cwd:Q,env_json:p,profile:P,scope:x,run_timeout_s:$,overlap:m,on_output:s,sink:K,tier:N}),{status:201,body:k(y(j),null)}}t(qn,"handleArm");function jn(e){const n=E(e);return{status:200,body:(n===null?T({all:!0}):[...T({profile:n}),...T({global:!0})].sort((o,c)=>o.fire_at<c.fire_at?-1:o.fire_at>c.fire_at?1:0)).filter(o=>U(e,o)).map(o=>k(o,J(o)))}}t(jn,"handleList");function J(e){return G(e.cron_id)[0]??null}t(J,"lastRunOf");function O(e){const n=e.params.cronId,r=y(n);if(r===null||!U(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(!M(r,E(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(O,"requireCron");function I(e,n){const r=Y(e);if(r!==null&&n.grantee!==r)throw new a(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");function Pn(e){const n=O(e),r=G(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(Z)}}}t(Pn,"handleShow");function Tn(e){const n=I(e,O(e));F(n.cron_id,"paused");const r=y(n.cron_id);return{status:200,body:k(r,J(r))}}t(Tn,"handlePause");function An(e){const n=I(e,O(e));F(n.cron_id,"active");const r=y(n.cron_id);return{status:200,body:k(r,J(r))}}t(An,"handleResume");async function En(e){const n=I(e,O(e));if(n.run_state==="running")throw new a(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await hn(n,{outOfBand:!0});if(r===null)throw new a(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:Z(r)}}t(En,"handleRun");function Un(e){const n=new Date().toISOString();return{status:200,body:{unparked:on(n,Y(e)),at:n}}}t(Un,"handlePoke");function Jn(e){const n=e.params.cronId,r=y(n);if(r!==null&&!U(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!M(r,E(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),c=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,u=o!==null&&c&&mn(n,o);return rn(n,{preserveRunningProcess:u}),{status:204}}t(Jn,"handleCancel");const ee=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:qn},{method:"GET",pattern:"/v1/crons",handler:jn},{method:"GET",pattern:"/v1/crons/:cronId",handler:Pn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:Tn},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:An},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Un},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:En},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:Jn}];export{ee as cronRoutes};
1
+ var X=Object.defineProperty;var t=(e,n)=>X(e,"name",{value:n,configurable:!0});import{randomUUID as nn}from"node:crypto";import{relative as en,resolve as z,sep as B}from"node:path";import{ApiError as a}from"../../../api/errors.js";import{armCron as rn,cancelCron as on,getCron as y,listCrons as T,listCronRuns as G,pokeHeldCrons as tn,setCronState as F}from"../../../core/canvas/crons.js";import{getNode as h}from"../../../core/canvas/canvas.js";import{appSpace as sn,cronFolder as an}from"../../../core/layout.js";import{appProcessesSandboxed as cn,cwdIsMounted as ln}from"../../../core/spaces/process.js";import{activeGrant as dn,activeGrantForApp as un,grantFor as pn,terminalGrant as fn}from"../../../core/grants/mirror.js";import{readConfig as _n}from"../../../core/config.js";import{assertScope as L}from"../../../core/scopes.js";import{InputError as i}from"../../../core/io.js";import{nextSlotAfter as hn}from"../../../core/wake.js";import{executeCron as mn,markSelfCanceledCronRun as gn}from"../../cron-run.js";import{toCronDTO as k,toCronRunDTO as Z}from"../map.js";import{appOf as H,assertCeiling as wn,canReadNode as bn}from"../router.js";import{checkCronBirth as vn,checkCronFire as yn}from"../../cron/authority.js";import{requireUsableProfile as kn}from"./profiles.js";function A(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new i({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}t(A,"requireString");function O(e,n,r,o){if(e==null)return o;if(typeof e=="string"&&r.includes(e))return e;throw new i({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}t(O,"requireEnum");function Sn(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new i({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(h(n)===null)throw new i({error:"sink_node_not_found",message:`sink node ${n} does not exist on the canvas`,field:"sink",next:"Name a live node id, or use spawn:<kind> for a delivery that outlives any one node."});return JSON.stringify({node:n})}if(e.startsWith("spawn:")){const n=e.slice(6).trim();if(n==="")throw new i({error:"bad_sink",message:"spawn sink requires a node kind",field:"sink",next:'Use sink "spawn:<kind>" (e.g. spawn:general).'});return JSON.stringify({spawn:n})}throw new i({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}t(Sn,"parseSinkSpec");const $n=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function M(e,n){if(!$n.test(e)||Number.isNaN(Date.parse(e)))throw new i({error:`bad_${n}`,message:`"${e}" is not a UTC ISO instant`,field:n,next:'Send a resolved UTC instant like "2026-06-07T09:00:00.000Z" (the client resolves the time grammar).'});return e}t(M,"requireUtcIso");function On(e){try{hn(e,new Date)}catch(n){throw new i({error:"bad_recur",message:`recur is not advanceable: ${n instanceof Error?n.message:String(n)}`,field:"recur",next:'Send recur JSON the daemon can advance: {"every":"6h"} or {"cron":"0 9 * * *","tz":"America/New_York"}.'})}return e}t(On,"requireRecur");function Cn(e){let n;try{n=JSON.parse(e)}catch{throw new i({error:"bad_env_json",message:"env_json is not valid JSON",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'})}if(typeof n!="object"||n===null||Array.isArray(n))throw new i({error:"bad_env_json",message:"env_json must be a JSON object",field:"env_json",next:'Send a JSON object of string values, e.g. {"API_BASE":"https://x"}.'});for(const[r,o]of Object.entries(n))if(typeof o!="string")throw new i({error:"bad_env_json",message:`env_json value for "${r}" is not a string`,field:"env_json",next:"Every env value must be a string."});return e}t(Cn,"requireEnvJson");function E(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}t(E,"callerProfile");function Y(e,n){return n===null||e.scope==="global"||e.profile===n}t(Y,"inScope");function K(e){return e.principal.class==="person"?null:H(e.principal)}t(K,"callerApp");function U(e,n){return bn(e.principal,n.grantee,n.created_by??void 0)}t(U,"visibleTo");function In(e){return typeof e.anchor_node=="string"&&e.anchor_node!==""?e.anchor_node:null}t(In,"anchorNodeOf");function Nn(e,n,r,o,c,u){const l=typeof e.cwd=="string"&&e.cwd.trim()!==""?z(e.cwd):null;if(!cn()){if(l===null)throw new i({error:"missing_field",message:'cron arm request requires a non-empty "cwd"',field:"cwd",next:'Send a non-empty string "cwd".'});return l}const p=o!==null&&h(o)!==null?o:void 0,d=t(N=>ln(n,N,p,c,void 0,u),"reaches");if(l!==null&&d(l))return l;const s=sn(n).files;if(l!==null&&e.cwd_is_default!==!0)throw new a(400,"invalid_request",`cwd ${l} is not inside a directory this app's cron runs can see, so every run would fail to start; use a directory under ${s}`,{cwd:l,default:s},void 0,{param:"cwd"});const m=r===null?null:h(r)?.cwd??null;return m!==null&&d(m)?m:s}t(Nn,"resolveArmedCwd");function qn(e,n,r){const o=e.principal;if(o.class==="person"){const s=fn();return{sub:s?.sub??"user:local",grant_id:s?.id??null,scopes:null,origin_run_id:null}}if(o.class==="app"){const s=un(n);if(s===null)throw new a(401,"grant_revoked",`no active grant for ${n}`);const m=e.ceiling??o.scopes;return{sub:s.sub,grant_id:s.id,scopes:[...m??s.resolved],origin_run_id:null}}if(o.class==="cron"){const s=yn({grantee:o.app,sub:o.sub,grant_id:o.grant,scopes:o.scopes});if(!s.allowed)throw new a(s.reason==="scope_missing"?403:401,s.reason==="scope_missing"?"scope_missing":"grant_revoked",s.message);return{sub:o.sub,grant_id:o.grant,scopes:o.scopes===null?[...s.effectiveScopes]:[...o.scopes],origin_run_id:o.origin_run}}if(o.class!=="broker")throw new a(401,"unauthorized","this caller cannot arm a cron");const c=r??h(o.node),u=c?.grant_id??o.grant,l=c?.sub??o.sub,p=u!=null?dn(u):pn(n,l);if(p===null){if(n==="app:terminal"&&!_n("user").runtime?.issuer)return{sub:l,grant_id:u??null,scopes:c?.scopes??o.scopes,origin_run_id:c?.run_id??o.run};throw new a(401,"grant_revoked","the node grant is not active")}const d=c?.scopes??o.scopes;return{sub:p.sub,grant_id:p.id,scopes:d===null?[...p.resolved]:[...d],origin_run_id:c?.run_id??o.run}}t(qn,"armPrincipal");function jn(e){const n=e.body??{},r=A(n,"name"),o=A(n,"command"),c=M(A(n,"fire_at"),"fire_at"),u=typeof n.recur=="string"&&n.recur!==""?On(n.recur):null,l=typeof n.expires_at=="string"&&n.expires_at!==""?M(n.expires_at,"expires_at"):null,p=typeof n.env_json=="string"&&n.env_json!==""?Cn(n.env_json):null,d=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,s=O(n.on_output,"on_output",["silent","on-failure","always","on-change"],d?.startsWith("node:")?"on-change":"on-failure"),m=O(n.overlap,"overlap",["skip","queue","replace"],"skip"),N=O(n.tier,"tier",["normal","urgent","critical"],"normal"),R=s==="always"||s==="on-change";if(R&&d===null)throw new i({error:"sink_required",message:`on_output "${s}" delivers stdout every qualifying run \u2014 it needs a sink`,field:"sink",next:"Pass sink node:<node-id> (a living node's inbox), spawn:<kind> (birth a fresh node per delivery), or human (the humanloop inbox)."});if(!R&&d!==null)throw new i({error:"sink_unused",message:`on_output "${s}" never delivers stdout \u2014 a sink would be dead config`,field:"sink",next:'Drop the sink, or use on_output "always"/"on-change" if delivery is the point.'});const Q=d!==null?Sn(d):"{}";if(d?.startsWith("spawn:")&&(wn(e.ceiling,"crtr:act"),e.principal.class==="cron")){const _=vn({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes});if(!_.allowed)throw new a(_.reason==="scope_missing"?403:401,_.reason==="scope_missing"?"scope_missing":"grant_revoked",_.message)}const b=H(e.principal)??"app:terminal",v=e.principal.class==="broker"?e.principal.node:e.principal.class==="cron"?null:typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(e.principal.class==="broker"&&n.created_by!=null&&n.created_by!==v)throw new a(403,"permission_error","a broker may only arm a cron as its own node");const g=v===null?null:h(v);if(g!==null){if(g.grantee!==b)throw new i({error:"creator_app_mismatch",message:"creator node belongs to another app",field:"created_by",next:"Use a node of the calling app."});L(g,"crtr:schedule"),d?.startsWith("spawn:")&&L(g,"crtr:act")}if(n.isolated!==void 0&&typeof n.isolated!="boolean")throw new a(400,"invalid_request","isolated must be a boolean");if(g?.isolated&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated node cannot escape isolation");const D=g?.isolated===!0||e.principal.class==="cron"&&e.principal.isolated;if(D&&n.isolated===!1)throw new a(403,"isolated_run","a cron created by an isolated caller cannot escape isolation");const q=D||n.isolated===!0,w=qn(e,b,g),j=nn(),S=q?an(b,j):null;if(S!==null&&n.cwd_is_default!==!0){const _=en(S,typeof n.cwd=="string"&&n.cwd.trim()!==""?z(n.cwd):S);if(_===".."||_.startsWith(`..${B}`)||_.startsWith(B))throw new a(400,"invalid_request","an isolated cron must run in its own cron folder",{param:"cwd"})}const V=S??Nn(n,b,v,In(n),q,{sub:w.sub,grantId:w.grant_id,scopes:w.scopes}),f=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(f!==null&&h(f)!==null&&h(f).grantee!==b)throw new i({error:"anchor_app_mismatch",message:"anchor node belongs to another app",field:"anchor_node",next:"Use an anchor of the calling app."});if(f!==null&&h(f)===null)throw new i({error:"anchor_not_found",message:`anchor node ${f} does not exist on the canvas`,field:"anchor_node",next:"Name a live node id, or drop the anchor \u2014 an unanchored cron outlives every node."});const W=n.cancel_on_wake===!0;if(W&&f===null)throw new i({error:"cancel_on_wake_needs_anchor",message:"cancel_on_wake couples the cron to a node's dormancy \u2014 it requires an anchor node",field:"anchor_node",next:"Pass anchor_node with the node whose wake should cancel this cron."});const $=n.run_timeout_s??300;if(typeof $!="number"||!Number.isInteger($)||$<=0)throw new i({error:"bad_run_timeout",message:`run_timeout_s must be a positive integer (got ${String(n.run_timeout_s)})`,field:"run_timeout_s",next:"Send the timeout in whole seconds, e.g. 300."});const P=typeof n.profile=="string"&&n.profile!==""?kn(e,n.profile).profileId:null,x=O(n.scope,"scope",["profile","global"],P!==null?"profile":"global");if(x==="profile"&&P===null)throw new i({error:"scope_needs_profile",message:'scope "profile" names the profile the cron lists and cancels within \u2014 it requires a profile',field:"profile",next:'Send a profile, or use scope "global" for a canvas-home-wide cron.'});return rn({cron_id:j,name:r,created_by:v,grantee:b,sub:w.sub,grant_id:w.grant_id,scopes:w.scopes,origin_run_id:w.origin_run_id,isolated:q,command:o,fire_at:c,recur:u,tz:typeof n.tz=="string"?n.tz:null,expires_at:l,anchor_node:f,cancel_on_wake:W,cwd:V,env_json:p,profile:P,scope:x,run_timeout_s:$,overlap:m,on_output:s,sink:Q,tier:N}),{status:201,body:k(y(j),null)}}t(jn,"handleArm");function Pn(e){const n=E(e);return{status:200,body:(n===null?T({all:!0}):[...T({profile:n}),...T({global:!0})].sort((o,c)=>o.fire_at<c.fire_at?-1:o.fire_at>c.fire_at?1:0)).filter(o=>U(e,o)).map(o=>k(o,J(o)))}}t(Pn,"handleList");function J(e){return G(e.cron_id)[0]??null}t(J,"lastRunOf");function C(e){const n=e.params.cronId,r=y(n);if(r===null||!U(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(!Y(r,E(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}t(C,"requireCron");function I(e,n){const r=K(e);if(r!==null&&n.grantee!==r)throw new a(403,"other_app_run",`cron ${n.cron_id} belongs to another app`);return n}t(I,"requireOwnCron");function Tn(e){const n=C(e),r=G(n.cron_id);return{status:200,body:{cron:k(n,r[0]??null),runs:r.map(Z)}}}t(Tn,"handleShow");function An(e){const n=I(e,C(e));F(n.cron_id,"paused");const r=y(n.cron_id);return{status:200,body:k(r,J(r))}}t(An,"handlePause");function En(e){const n=I(e,C(e));F(n.cron_id,"active");const r=y(n.cron_id);return{status:200,body:k(r,J(r))}}t(En,"handleResume");async function Un(e){const n=I(e,C(e));if(n.run_state==="running")throw new a(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await mn(n,{outOfBand:!0});if(r===null)throw new a(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:Z(r)}}t(Un,"handleRun");function Jn(e){const n=new Date().toISOString();return{status:200,body:{unparked:tn(n,K(e)),at:n}}}t(Jn,"handlePoke");function Rn(e){const n=e.params.cronId,r=y(n);if(r!==null&&!U(e,r))throw new a(404,"cron_not_found",`no cron ${n}`);if(r!==null&&!Y(r,E(e)))throw new a(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);r!==null&&I(e,r);const o=e.query.get("run_id"),c=e.principal.class==="cron"?o===e.principal.execution&&n===e.principal.cron:!0,u=o!==null&&c&&gn(n,o);return on(n,{preserveRunningProcess:u}),{status:204}}t(Rn,"handleCancel");const re=[{method:"POST",pattern:"/v1/crons",scope:"crtr:schedule",handler:jn},{method:"GET",pattern:"/v1/crons",handler:Pn},{method:"GET",pattern:"/v1/crons/:cronId",handler:Tn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"crtr:schedule",handler:An},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"crtr:schedule",handler:En},{method:"POST",pattern:"/v1/crons/poke",scope:"crtr:schedule",handler:Jn},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"crtr:schedule",handler:Un},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"crtr:schedule",handler:Rn}];export{re as cronRoutes};
@@ -1 +1 @@
1
- var A=Object.defineProperty;var o=(e,t)=>A(e,"name",{value:t,configurable:!0});import{closeSync as _,constants as q,fstatSync as B,readSync as T}from"node:fs";import{isAbsolute as R,resolve as I}from"node:path";import{appSpace as L}from"../../../core/layout.js";import{ApiError as u}from"../../../api/index.js";import{notFound as b,usage as c}from"../../../core/errors.js";import{InputError as v}from"../../../core/io.js";import{RunError as p}from"../../../core/runs/operations.js";import{isBeneathRefusal as M,listBeneath as k,openBeneath as z,writeFileBeneath as F}from"../../../core/spaces/open-beneath.js";import{fileRoot as N}from"../../../core/spaces/permissions.js";import{nodeMayReach as P,runNodeIds as O,runPlace as D}from"../../../core/runs/confinement.js";import{resolvedScopes as w}from"../router.js";const m=512*1024,E=1024*1024,C=1e3;function h(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!R(e))throw c(`file ${t} path must be absolute`,{received:e});return e}o(h,"requireAbsolutePath");function $(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}o($,"parseEncoding");function d(e,t,r){if(e instanceof u||e instanceof v||e instanceof p)throw e;if(M(e))throw new p(400,"invalid_request",`${t} meets a symlink, leaves its permitted root, or is not a regular file`,"path");const n=e.code;throw n==="ENOENT"?b(`no such file: ${t}`,{received:t}):n==="EACCES"||n==="EPERM"?new u(403,"forbidden",`permission denied ${r} ${t}`):new u(500,"internal",`cannot ${r} ${t}: ${e instanceof Error?e.message:String(e)}`)}o(d,"mapFilesystemError");function S(e,t){const r=D(e);if(r?.isolated){const n=new Set(O(r.run));return{isolated:!0,reaches:o(i=>n.has(i),"reaches")}}return{isolated:!1,reaches:o(n=>P(e,t,n,t),"reaches")}}o(S,"runConfinement");function y(e,t,r){const n=e.principal,i=n.class==="person"?null:n.class==="app"?{app:n.app,resolved:n.scopes,launched:null}:n.class==="cron"?{app:n.app,resolved:w(n),launched:n.scopes}:{app:n.app,resolved:w(n),launched:n.scopes,run:S(n.node,n.app)},s=N(i,t,r);if("root"in s)return s.root;throw"hidden"in s?b(`no such file: ${t}`,{received:t}):new p(403,"scope_missing",s.missing?`missing scope ${s.missing}`:`no permission names ${t}`,void 0,void 0,s.missing??void 0)}o(y,"rootFor");function W(e,t,r){let n;try{n=z(e,t,q.O_RDONLY)}catch(i){return d(i,t,"reading")}try{const i=B(n);if(i.isDirectory())throw c(`file peek path is a directory: ${t}`,{received:t});const s=Buffer.allocUnsafe(Math.min(i.size,m)+1),l=T(n,s,0,s.length,0),g=l>m,a=s.subarray(0,Math.min(l,m)).toString(r);return{path:t,content:a,truncated:g}}catch(i){return d(i,t,"reading")}finally{_(n)}}o(W,"readHead");function Y(e){const t=h(e.query.get("path"),"peek");return{status:200,body:W(y(e,t,"read"),t,$(e.query.get("encoding")))}}o(Y,"handlePeek");function Z(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}o(Z,"isBase64");function j(e){if(typeof e!="object"||e===null||Array.isArray(e))throw c("file write requires an object body");const t=e,r=h(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw c("file write requires a string `content` body field");const n=$(t.encoding);if(n==="base64"&&!Z(t.content))throw c("base64 file content must be valid base64");return{path:r,content:t.content,encoding:n}}o(j,"parseWriteBody");function G(e,t,r){if(r.byteLength>E)throw new u(413,"file_too_large",`file content exceeds the ${E} byte limit`);try{return F(e,t,r,{mode:process.platform==="linux"?432:384,atomic:!0}),{path:t,bytes_written:r.byteLength}}catch(n){return d(n,t,"writing")}}o(G,"writeAtomically");function U(e){const{path:t,content:r,encoding:n}=j(e.body);return{status:200,body:G(y(e,t,"rw"),t,Buffer.from(r,n))}}o(U,"handleWrite");function X(e){if(e===null)return C;const t=Number(e);if(!Number.isInteger(t)||t<1)throw c("file list limit must be a positive integer",{received:e});return t}o(X,"parseLimit");function H(e){const t=h(e.query.get("path"),"list"),r=X(e.query.get("limit")),n=y(e,t,"read");let i;try{i=k(n,t,r+1)}catch(a){return d(a,t,"listing")}const s=e.principal;if(s.class==="broker"&&I(t)===L(s.app).nodes){const a=S(s.node,s.app);i=i.filter(({name:f})=>a.reaches(f))}const l=i.slice(0,r).map(({name:a,stat:f})=>({name:a,type:f.isFile()?"file":f.isDirectory()?"dir":"other",size:f.size,modified:f.mtime.toISOString()}));return{status:200,body:{path:t,entries:l,truncated:i.length>r}}}o(H,"handleList");const ce=[{method:"GET",pattern:"/v1/files/peek",scope:"crtr:act",handler:Y},{method:"POST",pattern:"/v1/files/write",scope:"crtr:act",handler:U},{method:"GET",pattern:"/v1/files/list",scope:"crtr:act",handler:H}];export{ce as fileRoutes};
1
+ var A=Object.defineProperty;var o=(e,t)=>A(e,"name",{value:t,configurable:!0});import{closeSync as _,constants as q,fstatSync as B,readSync as T}from"node:fs";import{isAbsolute as R,resolve as v}from"node:path";import{appSpace as I}from"../../../core/layout.js";import{ApiError as f}from"../../../api/index.js";import{notFound as g,usage as c}from"../../../core/errors.js";import{InputError as L}from"../../../core/io.js";import{RunError as p}from"../../../core/runs/operations.js";import{isBeneathRefusal as M,listBeneath as k,openBeneath as z,writeFileBeneath as F}from"../../../core/spaces/open-beneath.js";import{fileRoot as N}from"../../../core/spaces/permissions.js";import{nodeMayReach as P,runNodeIds as O,runPlace as D}from"../../../core/runs/confinement.js";import{resolvedScopes as w}from"../router.js";const m=512*1024,E=1024*1024,C=1e3;function h(e,t){if(e===null||e==="")throw c(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!R(e))throw c(`file ${t} path must be absolute`,{received:e});return e}o(h,"requireAbsolutePath");function $(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw c("encoding must be `utf8` or `base64`",{received:e})}o($,"parseEncoding");function u(e,t,r){if(e instanceof f||e instanceof L||e instanceof p)throw e;if(M(e))throw new p(400,"invalid_request",`${t} meets a symlink, leaves its permitted root, or is not a regular file`,"path");const n=e.code;throw n==="ENOENT"?g(`no such file: ${t}`,{received:t}):n==="EACCES"||n==="EPERM"?new f(403,"forbidden",`permission denied ${r} ${t}`):new f(500,"internal",`cannot ${r} ${t}: ${e instanceof Error?e.message:String(e)}`)}o(u,"mapFilesystemError");function S(e,t){const r=D(e);if(r?.isolated){const n=new Set(O(r.run));return{isolated:!0,reaches:o(i=>n.has(i),"reaches")}}return{isolated:!1,reaches:o(n=>P(e,t,n,t),"reaches")}}o(S,"runConfinement");function y(e,t,r){const n=e.principal;if(n.class==="forwarded"||n.class==="machine")throw new f(403,"permission_error","this operation is not available to the caller");const i=n.class==="person"?null:n.class==="app"?{app:n.app,resolved:n.scopes,launched:null}:n.class==="cron"?{app:n.app,resolved:w(n),launched:n.scopes}:{app:n.app,resolved:w(n),launched:n.scopes,run:S(n.node,n.app)},s=N(i,t,r);if("root"in s)return s.root;throw"hidden"in s?g(`no such file: ${t}`,{received:t}):new p(403,"scope_missing",s.missing?`missing scope ${s.missing}`:`no permission names ${t}`,void 0,void 0,s.missing??void 0)}o(y,"rootFor");function W(e,t,r){let n;try{n=z(e,t,q.O_RDONLY)}catch(i){return u(i,t,"reading")}try{const i=B(n);if(i.isDirectory())throw c(`file peek path is a directory: ${t}`,{received:t});const s=Buffer.allocUnsafe(Math.min(i.size,m)+1),d=T(n,s,0,s.length,0),b=d>m,a=s.subarray(0,Math.min(d,m)).toString(r);return{path:t,content:a,truncated:b}}catch(i){return u(i,t,"reading")}finally{_(n)}}o(W,"readHead");function Y(e){const t=h(e.query.get("path"),"peek");return{status:200,body:W(y(e,t,"read"),t,$(e.query.get("encoding")))}}o(Y,"handlePeek");function Z(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}o(Z,"isBase64");function j(e){if(typeof e!="object"||e===null||Array.isArray(e))throw c("file write requires an object body");const t=e,r=h(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw c("file write requires a string `content` body field");const n=$(t.encoding);if(n==="base64"&&!Z(t.content))throw c("base64 file content must be valid base64");return{path:r,content:t.content,encoding:n}}o(j,"parseWriteBody");function G(e,t,r){if(r.byteLength>E)throw new f(413,"file_too_large",`file content exceeds the ${E} byte limit`);try{return F(e,t,r,{mode:process.platform==="linux"?432:384,atomic:!0}),{path:t,bytes_written:r.byteLength}}catch(n){return u(n,t,"writing")}}o(G,"writeAtomically");function U(e){const{path:t,content:r,encoding:n}=j(e.body);return{status:200,body:G(y(e,t,"rw"),t,Buffer.from(r,n))}}o(U,"handleWrite");function X(e){if(e===null)return C;const t=Number(e);if(!Number.isInteger(t)||t<1)throw c("file list limit must be a positive integer",{received:e});return t}o(X,"parseLimit");function H(e){const t=h(e.query.get("path"),"list"),r=X(e.query.get("limit")),n=y(e,t,"read");let i;try{i=k(n,t,r+1)}catch(a){return u(a,t,"listing")}const s=e.principal;if(s.class==="broker"&&v(t)===I(s.app).nodes){const a=S(s.node,s.app);i=i.filter(({name:l})=>a.reaches(l))}const d=i.slice(0,r).map(({name:a,stat:l})=>({name:a,type:l.isFile()?"file":l.isDirectory()?"dir":"other",size:l.size,modified:l.mtime.toISOString()}));return{status:200,body:{path:t,entries:d,truncated:i.length>r}}}o(H,"handleList");const ce=[{method:"GET",pattern:"/v1/files/peek",scope:"crtr:act",handler:Y},{method:"POST",pattern:"/v1/files/write",scope:"crtr:act",handler:U},{method:"GET",pattern:"/v1/files/list",scope:"crtr:act",handler:H}];export{ce as fileRoutes};
@@ -1,2 +1,2 @@
1
- import type { RouteTable } from '../router.js';
1
+ import { type RouteTable } from '../router.js';
2
2
  export declare const humanRequestRoutes: RouteTable;
@@ -1 +1 @@
1
- var N=Object.defineProperty;var o=(e,t)=>N(e,"name",{value:t,configurable:!0});import{statSync as P}from"node:fs";import{isAbsolute as T}from"node:path";import{ApiError as s}from"../../../api/index.js";import{readDaemonActionById as C}from"../../../core/canvas/daemon-actions.js";import{isSafeNodeId as I}from"../../../core/canvas/paths.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as $}from"../../../core/scopes.js";import{resolvePageComponents as g}from"../../../core/config.js";import{resolveHumanAction as D}from"../../../core/human-actions.js";import{HUMAN_ACTION_NAME_PATTERN as x}from"../../../core/human/action-binding.js";import{createPageRequest as J,getRequest as F}from"../../../core/human/requests.js";import{preparePageSource as M}from"../../../core/human/page-authoring.js";import{publishPage as B,validatePageTicketResponses as G}from"../../../core/human/tickets.js";import{cancelHumanTicket as H,resolvePageTicket as L}from"../../human/finish.js";import{inboxPageProjection as U}from"./inbox-page.js";const K=1e3,q=200,m={placement:"inline",inbox:!0,reply:!1},v=["sessionName","askedBy","emittedAt","profileName","nodeId"];function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(c,"isPlainObject");function l(e,t,n){for(const r of Object.keys(e))if(!t.includes(r))throw new s(400,"invalid_request",`unexpected field in ${n}: ${r}`)}o(l,"requireNoExtraKeys");function i(e){return new s(400,"invalid_request",e)}o(i,"invalid");function w(e){if(!c(e))throw i("`page` must be an object");if(l(e,["dialect","source"],"`page`"),e.dialect!=="jsx"&&e.dialect!=="html")throw i('`page.dialect` must be "jsx" or "html"');if(typeof e.source!="string"||e.source.trim()==="")throw i("`page.source` must be the authored page text");return{dialect:e.dialect,source:e.source}}o(w,"validatePage");function S(e,t){const n=e??t;if(n!=="inline"&&n!=="panel")throw i('`delivery.placement` must be "inline" or "panel"');return n}o(S,"validatePlacement");function X(e,t){if(e===void 0)return m;if(!c(e))throw i("`delivery` must be an object");l(e,["placement","inbox","reply"],"`delivery`");const n=e.inbox??m.inbox,r=e.reply??m.reply;if(typeof n!="boolean")throw i("`delivery.inbox` must be boolean");if(typeof r!="boolean")throw i("`delivery.reply` must be boolean");if(r&&t===null)throw i("`delivery.reply` requires `requester_node_id`");return{placement:S(e.placement,m.placement),inbox:n,reply:r}}o(X,"validateCreateDelivery");function z(e,t){if(e===void 0)return t;if(!c(e))throw i("`delivery` must be an object");return l(e,["placement"],"`delivery`"),{...t,placement:S(e.placement,t.placement)}}o(z,"validateReplaceDelivery");function V(e){if(e===void 0)return;if(!c(e))throw i("`source` must be an object");l(e,v,"`source`");const t={};for(const n of v){const r=e[n];if(r!==void 0){if(typeof r!="string"||r==="")throw i(`\`source.${n}\` must be a nonempty string`);t[n]=r}}return t}o(V,"validateSource");function Y(e){if(typeof e!="string"||e==="")throw i("`creator_cwd` is required");if(!T(e))throw i("`creator_cwd` must be an absolute path");try{if(!P(e).isDirectory())throw i(`\`creator_cwd\` is not a directory: ${e}`)}catch(t){throw t instanceof s?t:i(`\`creator_cwd\` does not exist: ${e}`)}return e}o(Y,"validateCreatorCwd");function Q(e){if(e!==void 0){if(!c(e))throw i("`action` must be an object");if(l(e,["name","payload"],"`action`"),typeof e.name!="string"||!x.test(e.name))throw i("`action.name` must be a declared action name");return{name:e.name,payload:e.payload??null}}}o(Q,"validateActionSelection");function W(e){if(e==null)return null;if(typeof e!="string"||!I(e))throw i("`requester_node_id` must be a safe node id or null");return e}o(W,"validateRequesterNodeId");function Z(e){if(!c(e))throw i("request body is required");l(e,["page","delivery","source","requester_node_id","creator_cwd","action"],"the request body");const t=V(e.source),n=W(e.requester_node_id),r=Q(e.action);return{page:w(e.page),delivery:X(e.delivery,n),...t===void 0?{}:{source:t},requesterNodeId:n,creatorCwd:Y(e.creator_cwd),...r===void 0?{}:{action:r}}}o(Z,"validateCreateRequest");function ee(e,t){if(!c(e))throw i("request body is required");return l(e,["page","delivery"],"the request body"),{page:w(e.page),delivery:z(e.delivery,t)}}o(ee,"validateReplaceRequest");function _(e,t,n){if(e!==void 0){if(typeof e!="string"||e.trim()===""||e.length>n)throw i(`\`${t}\` must be nonempty after trim and at most ${n} characters`);return e}}o(_,"optionalBoundedString");function te(e){if(!c(e))throw i("request body is required");if(l(e,["responses","actor"],"the request body"),!c(e.responses))throw i("`responses` must be an object");const t=_(e.actor,"actor",q);return{responses:e.responses,...t===void 0?{}:{actor:t}}}o(te,"validateRespondRequest");function ne(e){if(e===void 0)return{};if(!c(e))throw i("request body must be an object");l(e,["reason","actor"],"the request body");const t=_(e.reason,"reason",K),n=_(e.actor,"actor",q);return{...t===void 0?{}:{reason:t},...n===void 0?{}:{actor:n}}}o(ne,"validateSettleRequest");function h(e){const t=e.params.request_id;if(typeof t!="string"||t==="")throw i("request id is required");return t}o(h,"requestId");function f(e){const t=F(e);if(t===null||t.kind!=="page")throw new s(404,"not_found","no request with that id");return t}o(f,"requirePageRequest");function A(e){try{const t=JSON.parse(e.specification_json);if(!c(t))throw new Error("not an object");return t}catch{throw new s(500,"internal","the request specification could not be read")}}o(A,"manifest");function re(e){if(e.result_json===null)return null;try{return JSON.parse(e.result_json)}catch{throw new s(500,"internal","the request result could not be read")}}o(re,"terminalResult");function oe(e){if(e.action_json===null)return null;try{const t=JSON.parse(e.action_json);if(t.schema!=="crtr.human-action-binding/v1"||typeof t.name!="string")throw new Error("invalid action");return t}catch{throw new s(500,"internal","the request action could not be read")}}o(oe,"frozenAction");function ie(e){return e.state!=="settled"?"pending":e.outcome==="answered"?"answered":e.outcome==="dismissed"?"dismissed":"canceled"}o(ie,"requestState");function ae(e){if(e.action_delivery_id===null)return{state:"none",attempt:0};const t=C(e.action_delivery_id);if(t===null)return{state:"none",attempt:0};const n=t.state==="acknowledged"?"accepted":t.state==="permanent_failed"?"permanent_failed":t.state==="handed_off"?"running":"pending",r={state:n,attempt:t.attempts};return n==="pending"&&t.nextAttemptAt!==null&&(r.next_attempt_at=new Date(t.nextAttemptAt).toISOString()),t.acknowledgedAt!==null&&(r.accepted_at=new Date(t.acknowledgedAt).toISOString()),t.failedAt!==null&&(r.permanent_failed_at=new Date(t.failedAt).toISOString()),t.lastFailure!==null&&(r.last_failure=t.lastFailure),r}o(ae,"deliveryDetail");function y(e){const t=A(e),n=re(e),r=ie(e),a={request_id:e.request_id,inbox_ticket_id:e.inbox_ticket_id,request_dir:e.request_dir,kind:e.kind,state:r,title:e.title,source:t.source??{},emitted_at:t.source?.emittedAt??e.created_at};e.subtitle!==null&&(a.subtitle=e.subtitle),e.kind==="page"&&(a.page=U(e)),n!==null&&(a.settled_at=e.settled_at??e.updated_at,n.kind==="page"&&r==="answered"&&(a.responses=n.responses),n.kind==="canceled"&&n.reason!==void 0&&(a.reason=n.reason),n.kind==="page"&&n.actor!==void 0&&(a.actor=n.actor),n.kind==="canceled"&&n.actor!==void 0&&(a.actor=n.actor));const u=oe(e);return u!==null&&(a.action={name:u.name,payload:u.payload},a.delivery=ae(e)),a}o(y,"requestToDTO");function k(e,t,n,r){try{return M({dialect:e,document:t,source:n,delivery:r,productKinds:g()})}catch(a){throw i(a instanceof Error?a.message:"the page could not be prepared")}}o(k,"prepareOrReject");function se(e){const t=Z(e.body),n=t.requesterNodeId??t.source?.nodeId;if(n!=null){const d=b(n);d!==null&&$(d,"crtr:act")}let r;if(t.action!==void 0){const d=D(t.action.name,t.creatorCwd);if("kind"in d){const E=d.kind==="unknown_action"?`no action named ${d.name} is declared from ${t.creatorCwd}`:`${d.name} is declared in ${d.configOrigin} but unusable: ${d.reason}`;throw new s(400,d.kind,E)}const p=n==null?null:b(n);if(n&&!p)throw new s(404,"not_found","action requester node not found");if(p&&e.principal.class!=="person"&&p.grantee!==e.principal.app)throw new s(403,"permission_error","action requester belongs to another app");r=JSON.stringify({schema:"crtr.human-action-binding/v1",name:t.action.name,payload:t.action.payload,creatorCwd:t.creatorCwd,configOrigin:d.configOrigin,argv:d.argv,cwd:d.cwd,app:p?.grantee??(e.principal.class==="broker"?e.principal.app:"app:terminal")})}const a=k(t.page.dialect,t.page.source,t.source,t.delivery),u=J({prepared:a,requester_node_id:t.requesterNodeId,...r===void 0?{}:{action_json:r}});return{status:201,body:{request_id:u.request_id,request_dir:u.request_dir,state:"pending",...t.action===void 0?{}:{action:{name:t.action.name}},delivery_state:"none"}}}o(se,"handleCreate");function de(e){return{status:200,body:y(f(h(e)))}}o(de,"handleGet");function ce(e){const t=f(h(e));if(t.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");const n=A(t),r=ee(e.body,n.delivery),a=k(r.page.dialect,r.page.source,n.source,r.delivery);try{B(t.request_dir,a)}catch(u){throw u instanceof Error&&u.message===`page request is not open: ${t.request_id}`?new s(409,"already_settled","the request already has a terminal result"):u}return{status:200,body:y(f(t.request_id))}}o(ce,"handleReplace");function O(e){return e instanceof s?e:e instanceof Error&&e.message.includes("unknown page request")?new s(404,"not_found","no request with that id"):e instanceof Error&&e.message==="page_has_no_response_lifecycle"?new s(409,"not_settleable","this request carries no response lifecycle"):new s(500,"internal","the request could not be settled")}o(O,"settlementError");function R(e){if(e.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");if(!e.settleable)throw new s(409,"not_settleable","this request carries no response lifecycle")}o(R,"requireSettleable");async function ue(e){const t=f(h(e)),n=te(e.body);R(t);try{G(t.request_dir,n.responses)}catch(r){throw i(r instanceof Error?r.message:"the response map is invalid")}try{await L(t.request_id,n.responses,n.actor===void 0?{}:{actor:n.actor})}catch(r){throw O(r)}return{status:200,body:y(f(t.request_id))}}o(ue,"handleRespond");async function j(e,t){const n=f(h(e)),r=ne(e.body);R(n);try{await H(n.request_id,{...r,disposition:t})}catch(a){throw O(a)}return{status:200,body:y(f(n.request_id))}}o(j,"settle");function le(){return{status:200,body:{components:[...g()]}}}o(le,"handleComponents");const je=[{method:"GET",pattern:"/v1/human/components",handler:o(()=>le(),"handler")},{method:"POST",pattern:"/v1/human/requests",scope:"crtr:act",handler:se},{method:"GET",pattern:"/v1/human/requests/:request_id",handler:de},{method:"POST",pattern:"/v1/human/requests/:request_id/replace",handler:ce},{method:"POST",pattern:"/v1/human/requests/:request_id/respond",handler:ue},{method:"POST",pattern:"/v1/human/requests/:request_id/dismiss",handler:o(e=>j(e,"dismissed"),"handler")},{method:"POST",pattern:"/v1/human/requests/:request_id/cancel",handler:o(e=>j(e,"canceled"),"handler")}];export{je as humanRequestRoutes};
1
+ var N=Object.defineProperty;var o=(e,t)=>N(e,"name",{value:t,configurable:!0});import{statSync as P}from"node:fs";import{isAbsolute as T}from"node:path";import{ApiError as s}from"../../../api/index.js";import{readDaemonActionById as C}from"../../../core/canvas/daemon-actions.js";import{isSafeNodeId as I}from"../../../core/canvas/paths.js";import{getNode as b}from"../../../core/canvas/canvas.js";import{assertScope as $}from"../../../core/scopes.js";import{resolvePageComponents as g}from"../../../core/config.js";import{resolveHumanAction as D}from"../../../core/human-actions.js";import{HUMAN_ACTION_NAME_PATTERN as x}from"../../../core/human/action-binding.js";import{createPageRequest as J,getRequest as F}from"../../../core/human/requests.js";import{preparePageSource as M}from"../../../core/human/page-authoring.js";import{publishPage as B,validatePageTicketResponses as G}from"../../../core/human/tickets.js";import{cancelHumanTicket as H,resolvePageTicket as L}from"../../human/finish.js";import{inboxPageProjection as U}from"./inbox-page.js";import{appOf as K}from"../router.js";const X=1e3,q=200,m={placement:"inline",inbox:!0,reply:!1},v=["sessionName","askedBy","emittedAt","profileName","nodeId"];function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(c,"isPlainObject");function l(e,t,n){for(const r of Object.keys(e))if(!t.includes(r))throw new s(400,"invalid_request",`unexpected field in ${n}: ${r}`)}o(l,"requireNoExtraKeys");function i(e){return new s(400,"invalid_request",e)}o(i,"invalid");function w(e){if(!c(e))throw i("`page` must be an object");if(l(e,["dialect","source"],"`page`"),e.dialect!=="jsx"&&e.dialect!=="html")throw i('`page.dialect` must be "jsx" or "html"');if(typeof e.source!="string"||e.source.trim()==="")throw i("`page.source` must be the authored page text");return{dialect:e.dialect,source:e.source}}o(w,"validatePage");function S(e,t){const n=e??t;if(n!=="inline"&&n!=="panel")throw i('`delivery.placement` must be "inline" or "panel"');return n}o(S,"validatePlacement");function z(e,t){if(e===void 0)return m;if(!c(e))throw i("`delivery` must be an object");l(e,["placement","inbox","reply"],"`delivery`");const n=e.inbox??m.inbox,r=e.reply??m.reply;if(typeof n!="boolean")throw i("`delivery.inbox` must be boolean");if(typeof r!="boolean")throw i("`delivery.reply` must be boolean");if(r&&t===null)throw i("`delivery.reply` requires `requester_node_id`");return{placement:S(e.placement,m.placement),inbox:n,reply:r}}o(z,"validateCreateDelivery");function V(e,t){if(e===void 0)return t;if(!c(e))throw i("`delivery` must be an object");return l(e,["placement"],"`delivery`"),{...t,placement:S(e.placement,t.placement)}}o(V,"validateReplaceDelivery");function Y(e){if(e===void 0)return;if(!c(e))throw i("`source` must be an object");l(e,v,"`source`");const t={};for(const n of v){const r=e[n];if(r!==void 0){if(typeof r!="string"||r==="")throw i(`\`source.${n}\` must be a nonempty string`);t[n]=r}}return t}o(Y,"validateSource");function Q(e){if(typeof e!="string"||e==="")throw i("`creator_cwd` is required");if(!T(e))throw i("`creator_cwd` must be an absolute path");try{if(!P(e).isDirectory())throw i(`\`creator_cwd\` is not a directory: ${e}`)}catch(t){throw t instanceof s?t:i(`\`creator_cwd\` does not exist: ${e}`)}return e}o(Q,"validateCreatorCwd");function W(e){if(e!==void 0){if(!c(e))throw i("`action` must be an object");if(l(e,["name","payload"],"`action`"),typeof e.name!="string"||!x.test(e.name))throw i("`action.name` must be a declared action name");return{name:e.name,payload:e.payload??null}}}o(W,"validateActionSelection");function Z(e){if(e==null)return null;if(typeof e!="string"||!I(e))throw i("`requester_node_id` must be a safe node id or null");return e}o(Z,"validateRequesterNodeId");function ee(e){if(!c(e))throw i("request body is required");l(e,["page","delivery","source","requester_node_id","creator_cwd","action"],"the request body");const t=Y(e.source),n=Z(e.requester_node_id),r=W(e.action);return{page:w(e.page),delivery:z(e.delivery,n),...t===void 0?{}:{source:t},requesterNodeId:n,creatorCwd:Q(e.creator_cwd),...r===void 0?{}:{action:r}}}o(ee,"validateCreateRequest");function te(e,t){if(!c(e))throw i("request body is required");return l(e,["page","delivery"],"the request body"),{page:w(e.page),delivery:V(e.delivery,t)}}o(te,"validateReplaceRequest");function _(e,t,n){if(e!==void 0){if(typeof e!="string"||e.trim()===""||e.length>n)throw i(`\`${t}\` must be nonempty after trim and at most ${n} characters`);return e}}o(_,"optionalBoundedString");function ne(e){if(!c(e))throw i("request body is required");if(l(e,["responses","actor"],"the request body"),!c(e.responses))throw i("`responses` must be an object");const t=_(e.actor,"actor",q);return{responses:e.responses,...t===void 0?{}:{actor:t}}}o(ne,"validateRespondRequest");function re(e){if(e===void 0)return{};if(!c(e))throw i("request body must be an object");l(e,["reason","actor"],"the request body");const t=_(e.reason,"reason",X),n=_(e.actor,"actor",q);return{...t===void 0?{}:{reason:t},...n===void 0?{}:{actor:n}}}o(re,"validateSettleRequest");function h(e){const t=e.params.request_id;if(typeof t!="string"||t==="")throw i("request id is required");return t}o(h,"requestId");function f(e){const t=F(e);if(t===null||t.kind!=="page")throw new s(404,"not_found","no request with that id");return t}o(f,"requirePageRequest");function A(e){try{const t=JSON.parse(e.specification_json);if(!c(t))throw new Error("not an object");return t}catch{throw new s(500,"internal","the request specification could not be read")}}o(A,"manifest");function oe(e){if(e.result_json===null)return null;try{return JSON.parse(e.result_json)}catch{throw new s(500,"internal","the request result could not be read")}}o(oe,"terminalResult");function ie(e){if(e.action_json===null)return null;try{const t=JSON.parse(e.action_json);if(t.schema!=="crtr.human-action-binding/v1"||typeof t.name!="string")throw new Error("invalid action");return t}catch{throw new s(500,"internal","the request action could not be read")}}o(ie,"frozenAction");function ae(e){return e.state!=="settled"?"pending":e.outcome==="answered"?"answered":e.outcome==="dismissed"?"dismissed":"canceled"}o(ae,"requestState");function se(e){if(e.action_delivery_id===null)return{state:"none",attempt:0};const t=C(e.action_delivery_id);if(t===null)return{state:"none",attempt:0};const n=t.state==="acknowledged"?"accepted":t.state==="permanent_failed"?"permanent_failed":t.state==="handed_off"?"running":"pending",r={state:n,attempt:t.attempts};return n==="pending"&&t.nextAttemptAt!==null&&(r.next_attempt_at=new Date(t.nextAttemptAt).toISOString()),t.acknowledgedAt!==null&&(r.accepted_at=new Date(t.acknowledgedAt).toISOString()),t.failedAt!==null&&(r.permanent_failed_at=new Date(t.failedAt).toISOString()),t.lastFailure!==null&&(r.last_failure=t.lastFailure),r}o(se,"deliveryDetail");function y(e){const t=A(e),n=oe(e),r=ae(e),a={request_id:e.request_id,inbox_ticket_id:e.inbox_ticket_id,request_dir:e.request_dir,kind:e.kind,state:r,title:e.title,source:t.source??{},emitted_at:t.source?.emittedAt??e.created_at};e.subtitle!==null&&(a.subtitle=e.subtitle),e.kind==="page"&&(a.page=U(e)),n!==null&&(a.settled_at=e.settled_at??e.updated_at,n.kind==="page"&&r==="answered"&&(a.responses=n.responses),n.kind==="canceled"&&n.reason!==void 0&&(a.reason=n.reason),n.kind==="page"&&n.actor!==void 0&&(a.actor=n.actor),n.kind==="canceled"&&n.actor!==void 0&&(a.actor=n.actor));const u=ie(e);return u!==null&&(a.action={name:u.name,payload:u.payload},a.delivery=se(e)),a}o(y,"requestToDTO");function k(e,t,n,r){try{return M({dialect:e,document:t,source:n,delivery:r,productKinds:g()})}catch(a){throw i(a instanceof Error?a.message:"the page could not be prepared")}}o(k,"prepareOrReject");function de(e){const t=ee(e.body),n=t.requesterNodeId??t.source?.nodeId;if(n!=null){const d=b(n);d!==null&&$(d,"crtr:act")}let r;if(t.action!==void 0){const d=D(t.action.name,t.creatorCwd);if("kind"in d){const E=d.kind==="unknown_action"?`no action named ${d.name} is declared from ${t.creatorCwd}`:`${d.name} is declared in ${d.configOrigin} but unusable: ${d.reason}`;throw new s(400,d.kind,E)}const p=n==null?null:b(n);if(n&&!p)throw new s(404,"not_found","action requester node not found");if(p&&e.principal.class!=="person"&&p.grantee!==K(e.principal))throw new s(403,"permission_error","action requester belongs to another app");r=JSON.stringify({schema:"crtr.human-action-binding/v1",name:t.action.name,payload:t.action.payload,creatorCwd:t.creatorCwd,configOrigin:d.configOrigin,argv:d.argv,cwd:d.cwd,app:p?.grantee??(e.principal.class==="broker"?e.principal.app:"app:terminal")})}const a=k(t.page.dialect,t.page.source,t.source,t.delivery),u=J({prepared:a,requester_node_id:t.requesterNodeId,...r===void 0?{}:{action_json:r}});return{status:201,body:{request_id:u.request_id,request_dir:u.request_dir,state:"pending",...t.action===void 0?{}:{action:{name:t.action.name}},delivery_state:"none"}}}o(de,"handleCreate");function ce(e){return{status:200,body:y(f(h(e)))}}o(ce,"handleGet");function ue(e){const t=f(h(e));if(t.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");const n=A(t),r=te(e.body,n.delivery),a=k(r.page.dialect,r.page.source,n.source,r.delivery);try{B(t.request_dir,a)}catch(u){throw u instanceof Error&&u.message===`page request is not open: ${t.request_id}`?new s(409,"already_settled","the request already has a terminal result"):u}return{status:200,body:y(f(t.request_id))}}o(ue,"handleReplace");function O(e){return e instanceof s?e:e instanceof Error&&e.message.includes("unknown page request")?new s(404,"not_found","no request with that id"):e instanceof Error&&e.message==="page_has_no_response_lifecycle"?new s(409,"not_settleable","this request carries no response lifecycle"):new s(500,"internal","the request could not be settled")}o(O,"settlementError");function R(e){if(e.state==="settled")throw new s(409,"already_settled","the request already has a terminal result");if(!e.settleable)throw new s(409,"not_settleable","this request carries no response lifecycle")}o(R,"requireSettleable");async function le(e){const t=f(h(e)),n=ne(e.body);R(t);try{G(t.request_dir,n.responses)}catch(r){throw i(r instanceof Error?r.message:"the response map is invalid")}try{await L(t.request_id,n.responses,n.actor===void 0?{}:{actor:n.actor})}catch(r){throw O(r)}return{status:200,body:y(f(t.request_id))}}o(le,"handleRespond");async function j(e,t){const n=f(h(e)),r=re(e.body);R(n);try{await H(n.request_id,{...r,disposition:t})}catch(a){throw O(a)}return{status:200,body:y(f(n.request_id))}}o(j,"settle");function fe(){return{status:200,body:{components:[...g()]}}}o(fe,"handleComponents");const Ne=[{method:"GET",pattern:"/v1/human/components",handler:o(()=>fe(),"handler")},{method:"POST",pattern:"/v1/human/requests",scope:"crtr:act",handler:de},{method:"GET",pattern:"/v1/human/requests/:request_id",handler:ce},{method:"POST",pattern:"/v1/human/requests/:request_id/replace",handler:ue},{method:"POST",pattern:"/v1/human/requests/:request_id/respond",handler:le},{method:"POST",pattern:"/v1/human/requests/:request_id/dismiss",handler:o(e=>j(e,"dismissed"),"handler")},{method:"POST",pattern:"/v1/human/requests/:request_id/cancel",handler:o(e=>j(e,"canceled"),"handler")}];export{Ne as humanRequestRoutes};
@@ -1,2 +1,2 @@
1
1
  var X=Object.defineProperty;var c=(t,e)=>X(t,"name",{value:e,configurable:!0});import{randomBytes as Y}from"node:crypto";import{claimNodeMail as Z,acknowledgeNodeMail as ee}from"../../../core/canvas/deliveries.js";import{nodeMailEntry as te}from"../../../core/feed/inbox.js";import{enqueueNodeMail as M,cancelPendingNodeMail as re}from"../../../core/feed/messages.js";import{reviveNode as B}from"../../../core/runtime/revive.js";import{BrokerUnreachableError as T}from"../../../core/runtime/broker-request.js";import{isBrokerLive as A}from"../../../core/runtime/model-swap.js";import{deliverLive as ne,interruptLive as oe}from"../../../core/runtime/interactive-deliver.js";import{isProfilePaused as ie}from"../../../core/profiles/manifest.js";import{getNode as $,withFreshTerminalGuard as ae}from"../../../core/canvas/canvas.js";import{getById as se,nodeObject as z,ownerForGrantee as de}from"../../../core/graph/objects.js";import{recordActedOn as ce}from"../../../core/graph/edges.js";import{recordWatch as ue}from"../../../core/graph/watches.js";import{publish as W}from"../../../core/graph/events.js";import{readerFor as le}from"../reader.js";import{withCanvasWrite as J}from"../../../core/canvas/db.js";import{getPendingStructuredRequestForNode as L}from"../../../core/canvas/structured-requests.js";import{cancelCronsOnWake as G}from"../../../core/canvas/crons.js";import{assertNotFinalized as C,assertFinalizedForReopen as fe,commitReopenResident as j}from"../../../core/runtime/reopen.js";import{admitLegacyStructuredOutput as me}from"../../structured-requests/sidecar.js";import{appendSituationalContext as F,formatSituationalProse as pe}from"../../../core/runtime/situational-context.js";import{formatDataCard as D}from"../../../shared/generated-context.js";import{hasNoNaturalCycle as ye}from"../../../core/runtime/revive-all.js";import{recordPendingParkActivity as he}from"../../park-pending.js";import{wantsExecution as ge}from"../../reconcilers/node-lifecycle/wants-execution.js";import{hasFreshGroundState as ve}from"../../../core/runtime/revive.js";import{CrtrError as be,notFound as q,usage as d}from"../../../core/errors.js";import{emitEvent as we}from"../../../core/events/emit.js";import{assertScope as _e}from"../../../core/scopes.js";import{InputError as O}from"../../../core/io.js";import{ApiError as ke}from"../../../api/index.js";import{ownerOnly as xe}from"../router.js";const Se=["critical","urgent","normal","deferred"];function U(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw d(`${e} must be an object { kind, facts?, body? }`);const i=t,r=i.kind;if(typeof r!="string"||r.trim()==="")throw d(`${e}.kind is required`);if(!r.includes(":"))throw d(`${e}.kind must be namespaced (contain ':'): ${r}`);const s={kind:r},n=i.facts;if(n!==void 0){if(typeof n!="object"||n===null||Array.isArray(n))throw d(`${e}.facts must be an object of string or number values`);const m={};for(const[l,f]of Object.entries(n)){if(typeof f!="string"&&typeof f!="number")throw d(`${e}.facts.${l} must be a string or number`);m[l]=f}s.facts=m}const o=i.body;if(o!==void 0){if(typeof o!="string")throw d(`${e}.body must be a string`);s.body=o}return s}c(U,"parseCardRequest");function H(t,e){try{return D(t.kind,t.facts??{},t.body??"")}catch(i){throw d(`${e}: ${i instanceof Error?i.message:String(i)}`)}}c(H,"renderCard");function $e(t){if(typeof t!="object"||t===null)throw d("a message requires a JSON body { body, tier?, fresh? }");const e=t,i=e.body,r=e.fresh===!0,s=e.tier,n=e.situational_context,o=e.output_schema,m=typeof i=="string"&&i.trim()!=="",l=typeof n=="string"&&n.trim()!=="",f=typeof o=="string"&&o.trim()!=="",w=e.situational_card,h=e.context_cards,v=w===void 0?void 0:U(w,"situational_card");if(h!==void 0&&!Array.isArray(h))throw d("context_cards must be an array of { kind, facts?, body? }");const p=h?.map((R,y)=>U(R,`context_cards[${y}]`));if(v!==void 0&&typeof n=="string"&&n!=="")throw d("situational_card and situational_context are mutually exclusive");const _=v!==void 0||p!==void 0&&p.length>0;if(!r&&!m&&!l&&!f&&!_)throw d("a message requires a non-empty body (or fresh: true, situational_context, a runtime card, or output_schema)");if(r&&p!==void 0&&p.length>0)throw d("context_cards cannot ride a fresh revive (no inbox entry carries them)");if(s!==void 0&&!Se.includes(s))throw d(`invalid tier: ${String(s)} (expected critical|urgent|normal|deferred)`);const u={body:typeof i=="string"?i:""};s!==void 0&&(u.tier=s),r&&(u.fresh=!0),e.reopen===!0&&(u.reopen=!0),typeof n=="string"&&(u.situational_context=n),v!==void 0&&(u.situational_card=v),p!==void 0&&(u.context_cards=p),typeof o=="string"&&(u.output_schema=o),typeof e.from=="string"&&(u.from=e.from);const b=e.delivery;if(b!==void 0&&b!=="interactive")throw d(`invalid delivery: ${String(b)} (expected interactive)`);if(b==="interactive"){if(r||u.situational_context!==void 0||u.output_schema!==void 0)throw d("interactive delivery supports a body, runtime cards, and reopen only (no fresh, situational_context, or output_schema)");if(!m)throw d("interactive delivery requires a non-empty body");if(u.tier==="deferred")throw d("interactive delivery cannot be deferred");u.delivery="interactive"}return u}c($e,"parseSendBody");function qe(t){try{return JSON.parse(t)}catch{throw d("output_schema must be a JSON schema string")}}c(qe,"parseSchema");function Ne(t){return t.split(`
2
- `)[0].slice(0,120)}c(Ne,"label");function K(t,e){return e===null?"":D("reopen-final-report",{report:`${t}/${e}`},"This node previously completed work. Read this final report before acting on the new instruction.")}c(K,"previousFinalReportCard");function Ae(t,e,i){if(i!==null&&i!=="human"&&se(t,i)?.type==="node"){const n=z(t,i);return{actor:n,senderNode:n}}if(i==="human")return{actor:de(t,"user:local"),senderNode:null};const r=le(t,e);return{actor:r.class==="person"?r.person:r.class==="app"?r.app:r.node,senderNode:r.class==="node"?r.node:null}}c(Ae,"messageActor");function Q(t,e,i,r,s,n){const o=z(t,i),{actor:m,senderNode:l}=Ae(t,e,r),f=s===null?{}:{pointer:s};ce(t,m,o),l!==null&&l.rowid!==o.rowid&&ue(t,l,o,"message",{endsOn:n&&s!==null?`result:${s}`:"final"}),W(t,{event:"messaged",object:o,actor:m,attrs:{},...f}),l!==null&&W(t,{event:"sent",object:l,actor:l,attrs:{to:o.id},...f})}c(Q,"recordMessage");function Ce(t,e){return new O({error:"deferred_no_natural_cycle",message:`${t} is ${e} with no natural next cycle ahead of it \u2014 a deferred message would never be delivered.`,next:"Use --tier normal, urgent, or critical to wake it now, or --reopen (with a body or --fresh) to re-task it first."})}c(Ce,"deferredNoNaturalCycle");function je(t){if(!ve(t))throw new ke(422,"unprocessable",`node ${t} has no goal/roadmap on disk \u2014 a fresh revive would resume amnesiac.`)}c(je,"assertRecoverableForFresh");function Fe(t,e){return t instanceof O?{error:t.payload.error,message:t.payload.message,next:t.payload.next,message_id:e}:t instanceof be?{error:t.code,message:t.message,message_id:e}:{error:"wake_failed",message:"the target could not be woken; see the daemon log",message_id:e}}c(Fe,"wakeFailure");async function Oe(t){const e=t.params.id,i=$(e);if(i===null)throw q(`unknown node: ${e}`,{received:e});const r=$e(t.body),s=r.tier??"normal",n=ie(i.profile_id),o=r.from??null;if(o!==null){const a=$(o);a!==null&&_e(a,"crtr:act")}const m=r.body.trim()!=="",l=r.situational_card!==void 0?H(r.situational_card,"situational_card"):r.situational_context!==void 0?pe(r.situational_context.trim()):"",f=l!=="",w=(r.context_cards??[]).map((a,g)=>H(a,`context_cards[${g}]`)),h=r.output_schema!==void 0&&r.output_schema.trim()!==""?qe(r.output_schema):null;let v=!1;const p=c(a=>{if(!(h===null||v)){if(L(e)!==null)throw new O({error:"structured_request_conflict",message:`node ${e} already has a pending structured request`,next:"Wait for the current request to settle before requesting another structured result."});me(e,"oneoff",h,a),v=!0}},"ensureSchema");if(r.fresh){const a=r.reopen?fe(e):null;r.reopen||C(e),je(e),p(),f&&F(e,l),r.reopen&&j(e,a);const g=await B(e,{resume:!1,capacity:"refuse"});return{status:201,body:{node_id:e,delivered:!1,revived:g.launch!==void 0}}}r.delivery==="interactive"&&!r.reopen&&C(e);let _=!1,u="";if(r.delivery==="interactive"&&r.reopen&&(u=K(e,j(e)),_=!0),r.delivery==="interactive"&&p(),r.delivery==="interactive"&&!n&&!i.run_id&&A(i))try{return f&&F(e,l),await ne(e,r.body,{cards:[...u===""?[]:[u],...f?[l]:[],...w],origin:o==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:[{...o===null?{}:{from:o},kind:"message",tier:s}]}}),J(g=>Q(g,t,e,o,null,!1)),G(e),{status:201,body:{node_id:e,delivered:!0,revived:!1,delivered_via:"engine"}}}catch(a){if(!(a instanceof T))throw a}const b=c(()=>{r.reopen||C(e);const a=Y(16).toString("hex"),g=c(S=>Q(S,t,e,o,a,h!==null),"withinEnqueue");p(a),f&&F(e,l),r.reopen&&!_&&(u=K(e,j(e)));const N=[...u===""?[]:[u],...w],I=N.length>0?{cards:N}:{};if(m||h!==null){const S=m?r.body:`A one-off structured-output request has been granted. Submit a result matching the requested schema with the \`submit_result\` tool (\`request_id: "${L(e)?.request_id??"<request-id>"}"\`) before you stop; after it is accepted, continue normally.`;return M(e,{senderNodeId:o,urgency:s,wake:1,kind:"message",messageId:a,withinEnqueue:g,label:Ne(S),bodyJson:{body:S,...f?{situational:!0}:{},...I}})}return M(e,{senderNodeId:o,urgency:s,wake:1,kind:"message",messageId:a,withinEnqueue:g,label:f?"(ambient context updated)":"(context delivered)",bodyJson:{...f?{situational:!0}:{},situationalOnly:!0,...I}})},"deliverImmediate"),y=c(()=>{if(s==="deferred"){const a=ae(e,ye,b);if(a.kind==="missing")throw q(`unknown node: ${e}`,{received:e});if(a.kind==="terminal")throw Ce(e,a.status);return a.result}return b()},"deliver")();y.urgency!=="deferred"&&J(()=>he(e));const k=$(e);if(k===null)throw q(`unknown node: ${e}`,{received:e});i.frozen_at!==null&&G(e);let P=!1,E=!1,x;if((r.reopen||k.lifecycle==="terminal"||!ge(k))&&y.urgency!=="deferred"&&!n&&!A(k))try{const a=await B(e,{resume:!0,capacity:"freeze"});P=a.launch!==void 0,E=a.outcome==="frozen"}catch(a){x=Fe(a,y.messageId),we({level:"error",event:"message.wake_failed",node_id:e,error:a,fields:{message_id:y.messageId,error_code:x.error}})}return{status:201,body:{node_id:e,delivered:!0,revived:P,...E?{not_revived_reason:"capacity_frozen"}:{},...x===void 0?{}:{wake_failed:x},delivered_at:y.createdAt,...y.urgency===s?{}:{delivered_tier:y.urgency},...r.delivery==="interactive"?{delivered_via:"inbox"}:{}}}}c(Oe,"handleMessage");async function Re(t){const e=t.params.id,i=$(e);if(i===null)throw q(`unknown node: ${e}`,{received:e});const r=re(e,{senderNodeId:"human",kind:"message"});let s=!1;if(A(i))try{s=await oe(e)!=="idle"}catch(o){if(!(o instanceof T))throw o}return{status:200,body:{node_id:e,outcome:s?"aborted_turn":r.length>0?"canceled_pending":"idle",aborted_turn:s,canceled_pending:r.length}}}c(Re,"handleInterrupt");function V(t){if(typeof t.body!="object"||t.body===null||Array.isArray(t.body))throw d("mail requires an object body");const e=t.body;if(typeof e.execution_id!="string"||e.execution_id.trim()==="")throw d("execution_id is required");return e}c(V,"mailBody");function Pe(t){const e=V(t),i=e.channel;if(i!=="wake"&&i!=="passive"&&i!=="deferred_run")throw d("channel must be wake, passive, or deferred_run");const r=Z(t.params.id,e.execution_id,i);return{status:200,body:{action:r.action,deliveries:r.deliveries.map(n=>({delivery_id:n.deliveryId,attempts:n.attempts,handoff:n.handoff,entry:{...te(n),seq:n.message.seq}}))}}}c(Pe,"handleMailClaim");function Ee(t){const e=V(t);if(!Array.isArray(e.deliveries))throw d("deliveries must be an array");const i=e.deliveries.map(n=>{if(typeof n!="object"||n===null||Array.isArray(n))throw d("each delivery must be an object");const o=n;if(typeof o.delivery_id!="string"||o.delivery_id==="")throw d("delivery_id is required");if(!Number.isSafeInteger(o.attempts)||o.attempts<1)throw d("attempts must be a positive integer");if(o.disposition!=="acknowledged"&&o.disposition!=="released")throw d("disposition must be acknowledged or released");return{delivery_id:o.delivery_id,attempts:o.attempts,disposition:o.disposition}}),r=ee(t.params.id,e.execution_id,i.map(n=>({deliveryId:n.delivery_id,attempts:n.attempts,disposition:n.disposition})));return{status:200,body:{action:r.action,results:r.results.map(n=>({delivery_id:n.deliveryId,outcome:n.outcome}))}}}c(Ee,"handleMailAcknowledge");const gt=xe([{method:"POST",pattern:"/v1/nodes/:id/mail/claim",handler:Pe},{method:"POST",pattern:"/v1/nodes/:id/mail/acknowledge",handler:Ee}]),vt=[{method:"POST",pattern:"/v1/nodes/:id/messages",scope:"crtr:act",handler:Oe},{method:"POST",pattern:"/v1/nodes/:id/interrupt",scope:"crtr:act",handler:Re}];export{gt as brokerMailRoutes,Oe as handleMessage,vt as messageRoutes};
2
+ `)[0].slice(0,120)}c(Ne,"label");function K(t,e){return e===null?"":D("reopen-final-report",{report:`${t}/${e}`},"This node previously completed work. Read this final report before acting on the new instruction.")}c(K,"previousFinalReportCard");function Ae(t,e,i){if(i!==null&&i!=="human"&&se(t,i)?.type==="node"){const n=z(t,i);return{actor:n,senderNode:n}}if(i==="human")return{actor:de(t,"user:local"),senderNode:null};const r=le(t,e);return{actor:r.class==="person"?r.person:r.class==="app"?r.app:r.node,senderNode:r.class==="node"?r.node:null}}c(Ae,"messageActor");function Q(t,e,i,r,s,n){const o=z(t,i),{actor:m,senderNode:l}=Ae(t,e,r),f=s===null?{}:{pointer:s};ce(t,m,o),l!==null&&l.rowid!==o.rowid&&ue(t,l,o,"message",{endsOn:n&&s!==null?`result:${s}`:"final"}),W(t,{event:"messaged",object:o,actor:m,attrs:{},...f}),l!==null&&W(t,{event:"sent",object:l,actor:l,attrs:{to:o.id},...f})}c(Q,"recordMessage");function Ce(t,e){return new O({error:"deferred_no_natural_cycle",message:`${t} is ${e} with no natural next cycle ahead of it \u2014 a deferred message would never be delivered.`,next:"Use --tier normal, urgent, or critical to wake it now, or --reopen (with a body or --fresh) to re-task it first."})}c(Ce,"deferredNoNaturalCycle");function je(t){if(!ve(t))throw new ke(422,"unprocessable",`node ${t} has no goal/roadmap on disk \u2014 a fresh revive would resume amnesiac.`)}c(je,"assertRecoverableForFresh");function Fe(t,e){return t instanceof O?{error:t.payload.error,message:t.payload.message,next:t.payload.next,message_id:e}:t instanceof be?{error:t.code,message:t.message,message_id:e}:{error:"wake_failed",message:"the target could not be woken; see the daemon log",message_id:e}}c(Fe,"wakeFailure");async function Oe(t){const e=t.params.id,i=$(e);if(i===null)throw q(`unknown node: ${e}`,{received:e});const r=$e(t.body),s=r.tier??"normal",n=ie(i.profile_id),o=t.principal.class==="forwarded"?null:r.from??null;if(o!==null){const a=$(o);a!==null&&_e(a,"crtr:act")}const m=r.body.trim()!=="",l=r.situational_card!==void 0?H(r.situational_card,"situational_card"):r.situational_context!==void 0?pe(r.situational_context.trim()):"",f=l!=="",w=(r.context_cards??[]).map((a,g)=>H(a,`context_cards[${g}]`)),h=r.output_schema!==void 0&&r.output_schema.trim()!==""?qe(r.output_schema):null;let v=!1;const p=c(a=>{if(!(h===null||v)){if(L(e)!==null)throw new O({error:"structured_request_conflict",message:`node ${e} already has a pending structured request`,next:"Wait for the current request to settle before requesting another structured result."});me(e,"oneoff",h,a),v=!0}},"ensureSchema");if(r.fresh){const a=r.reopen?fe(e):null;r.reopen||C(e),je(e),p(),f&&F(e,l),r.reopen&&j(e,a);const g=await B(e,{resume:!1,capacity:"refuse"});return{status:201,body:{node_id:e,delivered:!1,revived:g.launch!==void 0}}}r.delivery==="interactive"&&!r.reopen&&C(e);let _=!1,u="";if(r.delivery==="interactive"&&r.reopen&&(u=K(e,j(e)),_=!0),r.delivery==="interactive"&&p(),r.delivery==="interactive"&&!n&&!i.run_id&&A(i))try{return f&&F(e,l),await ne(e,r.body,{cards:[...u===""?[]:[u],...f?[l]:[],...w],origin:o==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:[{...o===null?{}:{from:o},kind:"message",tier:s}]}}),J(g=>Q(g,t,e,o,null,!1)),G(e),{status:201,body:{node_id:e,delivered:!0,revived:!1,delivered_via:"engine"}}}catch(a){if(!(a instanceof T))throw a}const b=c(()=>{r.reopen||C(e);const a=Y(16).toString("hex"),g=c(S=>Q(S,t,e,o,a,h!==null),"withinEnqueue");p(a),f&&F(e,l),r.reopen&&!_&&(u=K(e,j(e)));const N=[...u===""?[]:[u],...w],I=N.length>0?{cards:N}:{};if(m||h!==null){const S=m?r.body:`A one-off structured-output request has been granted. Submit a result matching the requested schema with the \`submit_result\` tool (\`request_id: "${L(e)?.request_id??"<request-id>"}"\`) before you stop; after it is accepted, continue normally.`;return M(e,{senderNodeId:o,urgency:s,wake:1,kind:"message",messageId:a,withinEnqueue:g,label:Ne(S),bodyJson:{body:S,...f?{situational:!0}:{},...I}})}return M(e,{senderNodeId:o,urgency:s,wake:1,kind:"message",messageId:a,withinEnqueue:g,label:f?"(ambient context updated)":"(context delivered)",bodyJson:{...f?{situational:!0}:{},situationalOnly:!0,...I}})},"deliverImmediate"),y=c(()=>{if(s==="deferred"){const a=ae(e,ye,b);if(a.kind==="missing")throw q(`unknown node: ${e}`,{received:e});if(a.kind==="terminal")throw Ce(e,a.status);return a.result}return b()},"deliver")();y.urgency!=="deferred"&&J(()=>he(e));const k=$(e);if(k===null)throw q(`unknown node: ${e}`,{received:e});i.frozen_at!==null&&G(e);let P=!1,E=!1,x;if((r.reopen||k.lifecycle==="terminal"||!ge(k))&&y.urgency!=="deferred"&&!n&&!A(k))try{const a=await B(e,{resume:!0,capacity:"freeze"});P=a.launch!==void 0,E=a.outcome==="frozen"}catch(a){x=Fe(a,y.messageId),we({level:"error",event:"message.wake_failed",node_id:e,error:a,fields:{message_id:y.messageId,error_code:x.error}})}return{status:201,body:{node_id:e,delivered:!0,revived:P,...E?{not_revived_reason:"capacity_frozen"}:{},...x===void 0?{}:{wake_failed:x},delivered_at:y.createdAt,...y.urgency===s?{}:{delivered_tier:y.urgency},...r.delivery==="interactive"?{delivered_via:"inbox"}:{}}}}c(Oe,"handleMessage");async function Re(t){const e=t.params.id,i=$(e);if(i===null)throw q(`unknown node: ${e}`,{received:e});const r=re(e,{senderNodeId:"human",kind:"message"});let s=!1;if(A(i))try{s=await oe(e)!=="idle"}catch(o){if(!(o instanceof T))throw o}return{status:200,body:{node_id:e,outcome:s?"aborted_turn":r.length>0?"canceled_pending":"idle",aborted_turn:s,canceled_pending:r.length}}}c(Re,"handleInterrupt");function V(t){if(typeof t.body!="object"||t.body===null||Array.isArray(t.body))throw d("mail requires an object body");const e=t.body;if(typeof e.execution_id!="string"||e.execution_id.trim()==="")throw d("execution_id is required");return e}c(V,"mailBody");function Pe(t){const e=V(t),i=e.channel;if(i!=="wake"&&i!=="passive"&&i!=="deferred_run")throw d("channel must be wake, passive, or deferred_run");const r=Z(t.params.id,e.execution_id,i);return{status:200,body:{action:r.action,deliveries:r.deliveries.map(n=>({delivery_id:n.deliveryId,attempts:n.attempts,handoff:n.handoff,entry:{...te(n),seq:n.message.seq}}))}}}c(Pe,"handleMailClaim");function Ee(t){const e=V(t);if(!Array.isArray(e.deliveries))throw d("deliveries must be an array");const i=e.deliveries.map(n=>{if(typeof n!="object"||n===null||Array.isArray(n))throw d("each delivery must be an object");const o=n;if(typeof o.delivery_id!="string"||o.delivery_id==="")throw d("delivery_id is required");if(!Number.isSafeInteger(o.attempts)||o.attempts<1)throw d("attempts must be a positive integer");if(o.disposition!=="acknowledged"&&o.disposition!=="released")throw d("disposition must be acknowledged or released");return{delivery_id:o.delivery_id,attempts:o.attempts,disposition:o.disposition}}),r=ee(t.params.id,e.execution_id,i.map(n=>({deliveryId:n.delivery_id,attempts:n.attempts,disposition:n.disposition})));return{status:200,body:{action:r.action,results:r.results.map(n=>({delivery_id:n.deliveryId,outcome:n.outcome}))}}}c(Ee,"handleMailAcknowledge");const gt=xe([{method:"POST",pattern:"/v1/nodes/:id/mail/claim",handler:Pe},{method:"POST",pattern:"/v1/nodes/:id/mail/acknowledge",handler:Ee}]),vt=[{method:"POST",pattern:"/v1/nodes/:id/messages",scope:"crtr:act",handler:Oe},{method:"POST",pattern:"/v1/nodes/:id/interrupt",scope:"crtr:act",handler:Re}];export{gt as brokerMailRoutes,Oe as handleMessage,vt as messageRoutes};