@north-light/cli 0.3.551 → 0.3.552
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +11 -0
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/charges.d.ts +122 -0
- package/dist/api/dto/charges.js +0 -0
- package/dist/api/dto/usage.d.ts +3 -3
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -1
- package/dist/api/routes.d.ts +3 -0
- package/dist/api/routes.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +10 -0
- package/dist/clients/attach/viewer.js +50 -50
- package/dist/commands/sys/spend.d.ts +1 -0
- package/dist/commands/sys/spend.js +3 -0
- package/dist/commands/sys.js +1 -1
- package/dist/core/canvas/canvas.js +4 -4
- package/dist/core/canvas/export-sqlite.js +2 -2
- package/dist/core/canvas/import-sqlite.js +1 -1
- package/dist/core/canvas/person.js +7 -16
- package/dist/core/canvas/schema-pg.sql +61 -0
- package/dist/core/canvas/types.d.ts +4 -0
- package/dist/core/config.js +1 -1
- package/dist/core/grants/mirror.d.ts +2 -1
- package/dist/core/grants/mirror.js +2 -2
- package/dist/core/ledger/backfill.d.ts +12 -0
- package/dist/core/ledger/backfill.js +22 -0
- package/dist/core/ledger/charges.d.ts +18 -0
- package/dist/core/ledger/charges.js +1 -0
- package/dist/core/ledger/check.d.ts +44 -0
- package/dist/core/ledger/check.js +7 -0
- package/dist/core/ledger/index.d.ts +9 -0
- package/dist/core/ledger/index.js +1 -0
- package/dist/core/ledger/limits.d.ts +15 -0
- package/dist/core/ledger/limits.js +3 -0
- package/dist/core/ledger/months.d.ts +6 -0
- package/dist/core/ledger/months.js +1 -0
- package/dist/core/ledger/payer.d.ts +34 -0
- package/dist/core/ledger/payer.js +1 -0
- package/dist/core/ledger/read.d.ts +44 -0
- package/dist/core/ledger/read.js +3 -0
- package/dist/core/ledger/totals.d.ts +19 -0
- package/dist/core/ledger/totals.js +4 -0
- package/dist/core/ledger/types.d.ts +66 -0
- package/dist/core/ledger/types.js +0 -0
- package/dist/core/llm/run.d.ts +17 -5
- package/dist/core/llm/run.js +2 -2
- package/dist/core/providers/call.d.ts +7 -1
- package/dist/core/providers/call.js +1 -1
- package/dist/core/providers/errors.js +1 -1
- package/dist/core/providers/receipts.d.ts +7 -1
- package/dist/core/providers/receipts.js +1 -3
- package/dist/core/runs/operations.d.ts +1 -0
- package/dist/core/runs/operations.js +7 -7
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/model-adapter.js +2 -2
- package/dist/core/runtime/broker/turn-cause.d.ts +7 -0
- package/dist/core/runtime/broker/turn-cause.js +1 -0
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/nodes.d.ts +2 -0
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/recycle.js +1 -1
- package/dist/core/runtime/reset.js +1 -1
- package/dist/core/runtime/revive.js +2 -2
- package/dist/core/runtime/spawn.d.ts +2 -0
- package/dist/core/runtime/spawn.js +2 -2
- package/dist/core/runtime/start-check.d.ts +15 -0
- package/dist/core/runtime/start-check.js +1 -0
- package/dist/core/runtime/warm-pool.js +1 -1
- package/dist/core/user-settings.d.ts +4 -0
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/app-listener.js +13 -13
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/charges.d.ts +3 -0
- package/dist/daemon/api/handlers/charges.js +1 -0
- package/dist/daemon/api/handlers/llm.d.ts +9 -5
- package/dist/daemon/api/handlers/llm.js +3 -3
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/handlers/providers.d.ts +2 -0
- package/dist/daemon/api/handlers/providers.js +1 -1
- package/dist/daemon/api/handlers/run-routes.js +1 -1
- package/dist/daemon/api/handlers/runs.d.ts +1 -1
- package/dist/daemon/api/handlers/runs.js +1 -1
- package/dist/daemon/api/operations.js +1 -1
- package/dist/daemon/api/router.d.ts +8 -0
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -1
- package/dist/daemon/cron/authority.d.ts +1 -1
- package/dist/daemon/cron-run.js +2 -2
- package/dist/daemon/crtrd.js +10 -10
- package/dist/daemon/fleet.js +4 -4
- package/dist/daemon/reconcilers/action-lane.js +1 -1
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +2 -0
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
- package/dist/types.d.ts +4 -0
- package/docs/sdk/release-notes.md +10 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
- package/dist/core/usage/ledger.d.ts +0 -49
- package/dist/core/usage/ledger.js +0 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
var B=Object.defineProperty;var
|
|
1
|
+
var B=Object.defineProperty;var l=(e,o)=>B(e,"name",{value:o,configurable:!0});import{killBashJobObject as E,recordBashJobObject as J}from"../../../core/spaces/stop.js";import{readerFor as P}from"../reader.js";import{join as $}from"node:path";import{createFileBeneath as g,existsBeneath as m,unlinkBeneath as A}from"../../../core/spaces/open-beneath.js";import{launchAppProcess as q,appProcessSandboxed as O}from"../../../core/spaces/process.js";import{BASH_JOB_SUPERVISOR_SCRIPT as N}from"../../../core/bash-job-supervisor.js";import{resolveAdmittedHostCommand as L}from"../../../core/runtime/spawn-env.js";import{requireCurrentExecution as _}from"./broker-recovery.js";import{isSafeNodeId as T}from"../../../core/canvas/paths.js";import{contextDir as v}from"../../../core/canvas/node-agent-paths.js";import{getNode as f}from"../../../core/canvas/canvas.js";import{isBrokerLive as D}from"../../../core/runtime/model-swap.js";import{assertNotFinalized as R}from"../../../core/runtime/reopen.js";import{reviveNode as C}from"../../../core/runtime/revive.js";import{checkStart as z}from"../../../core/runtime/start-check.js";import{activeBackgroundBashJobs as F,bashJobPaths as k,isBashJobProcessGroupAlive as M,readJobPgid as G,readJobPgidIdentity as H,isSafeBashJobId as I,readBashNiceness as Z,stopBackgroundBashJob as U}from"../../../core/bash-jobs.js";import{ApiError as i}from"../../../api/index.js";import{notFound as V}from"../../../core/errors.js";import{freshChildNodeExecPath as X}from"../../../core/runtime/branded-host.js";async function w(e){const o=e.params.id;if(o===void 0||!T(o))throw new i(400,"invalid_node_id",`invalid node id: ${JSON.stringify(o)}`);if(await f(o)===null)throw V(`unknown node: ${o}`,{received:o});const a=e.params.jobId;if(a!==void 0){if(!I(a))throw new i(400,"invalid_job_id",`invalid background job id: ${JSON.stringify(a)}`);return{nodeId:o,jobId:a,dir:await v(o)}}return{nodeId:o,dir:await v(o)}}l(w,"requireTarget");async function x(e){const{nodeId:o,dir:n}=await w(e);if(e.principal.class!=="broker"||e.principal.node!==o)throw new i(403,"permission_error","only this node\u2019s broker may start a job");const a=e.body;if(!a||typeof a!="object"||Array.isArray(a))throw new i(400,"invalid_request","job.start requires a JSON body");const d=a;await _(o,d.expected_execution_id);const r=d.job_id,s=d.cwd,p=d.env;if(typeof r!="string"||!I(r)||typeof s!="string"||!p||typeof p!="object"||Array.isArray(p)||Object.entries(p).some(([c,b])=>!/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)||typeof b!="string"))throw new i(400,"invalid_request","invalid job id, cwd or environment");const h=await f(o);if(await z({grantee:h.grantee??"app:terminal",sub:h.sub,grant_id:h.grant_id}),!O())throw new i(400,"invalid_request","job.start is only used with the root launcher");const t=k(n,r);if(!(m(t.root,t.jobRun)||m(t.root,t.jobBg))||![t.cmdSh,t.jobLog].every(c=>m(t.root,c)))throw new i(404,"job_not_found","job was not prepared by this node");const j=$(t.dir,"job.starting");try{g(t.root,j,"")}catch(c){throw c.code==="EEXIST"?new i(409,"job_already_started","job was already started"):c}let u;try{const c=p,b=L("bash",c,s),y=await f(o),S=await X();return u=await q({nodeId:o,app:y.grantee??"app:terminal",isolated:y.isolated,cwd:s,env:c,capture:!1,command:[b,"-c",N,"--",t.cmdSh,t.jobLog,t.jobExit,t.jobRun,t.jobDone,b,String(Z(n)),S]}),g(t.root,t.jobPgid,String(u.pid),384),g(t.root,t.jobPgidIdentity,`launcher:${u.pid}`,384),{status:201,body:{pid:u.pid}}}catch(c){if(u!==void 0)try{await u.stop()}catch(b){throw new AggregateError([c,b],`job ${r} launched but could not be recorded or stopped`)}try{A(t.root,j)}catch(b){if(b.code!=="ENOENT")throw b}throw c}}l(x,"handleStart");async function K(e){const{nodeId:o,dir:n}=await w(e),a=Date.now();return{status:200,body:F(n).map(r=>{const s=G(n,r.jobId)??null;return{job_id:r.jobId,command:r.command,purpose:r.purpose,started_at:new Date(r.startedAtMs).toISOString(),elapsed_ms:Math.max(0,a-r.startedAtMs),pgid:s,pgid_alive:s!==null&&M(s,H(n,r.jobId))}})}}l(K,"handleList");async function Q(e){const{nodeId:o,jobId:n,dir:a}=await w(e);if(n===void 0)throw new i(400,"invalid_request","a job id is required");const d=e.query.get("foreground")==="1";if(d){if(e.principal.class!=="broker"||e.principal.node!==o)throw new i(403,"permission_error","only this node\u2019s broker may abort its foreground job");await _(o,e.body?.expected_execution_id)}const r=await U(a,n,d,o);if(r.kind==="missing")throw new i(404,"job_not_found",`no background job ${n} for ${o}`);if(r.kind==="retired")throw new i(404,"job_not_found",`background job ${n} for ${o} is no longer running`,{log_path:r.logPath});if(r.kind==="not-stoppable")throw new i(409,"job_not_stoppable",`job ${n} recorded no trusted process identity`,{log_path:r.logPath});if(d)return{status:200,body:{node_id:o,job_id:n,signaled:r.signaled}};await R(o),await E(n,async h=>{const t=await P(h,e);return t.class==="person"?t.person:t.class==="app"?t.app:t.node});const s=await f(o);if(s!==null&&!D(s))try{await C(o,{resume:!0,capacity:"freeze"})}catch{}return{status:200,body:{node_id:o,job_id:n,signaled:r.signaled}}}l(Q,"handleStop");async function W(e){const{nodeId:o,jobId:n,dir:a}=await w(e);if(n===void 0)throw new i(400,"invalid_request","a job id is required");if(e.principal.class!=="broker"||e.principal.node!==o)throw new i(403,"permission_error","only this node\u2019s broker may record its job");const d=e.body,r=k(a,n);if(typeof d?.command!="string"||d.job_dir!==r.dir||d.log_path!==r.jobLog)throw new i(400,"invalid_request",`job.background requires command, job_dir ${r.dir} and log_path ${r.jobLog}`);if(!m(r.root,r.jobBg))throw new i(404,"job_not_found",`job ${n} of ${o} is not backgrounded`);const s=d.command;return{status:201,body:{id:(await J(o,n,{command:s,jobDir:r.dir,logPath:r.jobLog})).id}}}l(W,"handleBackground");const _o=[{method:"POST",pattern:"/v1/nodes/:id/jobs/:jobId/background",handler:W},{method:"POST",pattern:"/v1/nodes/:id/jobs/start",handler:x},{method:"GET",pattern:"/v1/nodes/:id/jobs",handler:K},{method:"DELETE",pattern:"/v1/nodes/:id/jobs/:jobId",handler:Q}];export{_o as bashJobRoutes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var b=Object.defineProperty;var o=(n,e)=>b(n,"name",{value:e,configurable:!0});import{ApiError as l}from"../../../api/errors.js";import{personIdAsync as w}from"../../../core/canvas/person.js";import{assertReference as v,deleteLimit as _,listCharges as q,listLimits as y,monthTotals as R,parseUsageMonth as E,upsertLimit as L}from"../../../core/ledger/index.js";import{ownerOnly as T}from"../router.js";import{integer as $}from"./runs.js";import{invalidRequest as N,objectBody as O}from"./validate.js";function k(n){const{principal:e}=n;if(e.class==="app"||e.class==="broker")return e.app;if(e.class!=="person")throw new l(403,"owner_only","charges are read by the person, a node or an app")}o(k,"granteeOf");async function F(n){const e=k(n),r=n.query.get("under"),t=n.query.get("after"),s=n.query.get("month"),i=r===null||s!==null?E(s):void 0,{charges:d,next:p}=await q({...i===void 0?{}:{month:i},...e===void 0?{}:{grantee:e},...r===null?{}:{under:v(r)},...t===null?{}:{after:t},limit:$(n.query.get("limit"),"limit",1,1e4,1e3)}),a=e===void 0&&r===null?await R(i):void 0;return{...i===void 0?{}:{month:i},charges:d,next:p,...a===void 0?{}:{totals:a}}}o(F,"listRoute");const h=["bill","subscription","key","endpoint","take","legacy"],f="person:";function g(n){const e=n.params.id??"";if(!e.startsWith(f)||e.length===f.length||e.length>128)throw new l(400,"invalid_request",`a person limit id starts with \`${f}\`; received ${e}`,{violations:[{field:"id",problem:`must start with ${f}`,received:e}]});return e}o(g,"idOf");function c(n,e,r){const t=n[e];if(t==null)return t;if(typeof t!="string"||t===""){r.push({field:e,problem:"must be a non-empty string or null",received:t});return}return t}o(c,"nullableString");async function G(n){const e=g(n),r=[],t=O(n.body,["payer","usd_per_month","funding","grantee","provider","sub"],r),s=t.payer;(typeof s!="string"||!/^(user|app):.+/.test(s))&&r.push({field:"payer",problem:"must be user:<id> or app:<id>",...s===void 0?{}:{received:s}});const i=t.usd_per_month;(typeof i!="number"||!Number.isFinite(i)||i<0)&&r.push({field:"usd_per_month",problem:"must be a number of at least 0",...i===void 0?{}:{received:i}});const d=c(t,"funding",r);typeof d=="string"&&!h.includes(d)&&r.push({field:"funding",problem:`must be one of ${h.join(", ")}`,received:d});const p=c(t,"grantee",r),a=c(t,"provider",r),m=c(t,"sub",r);r.length>0&&N("spend limit request",r);const u=t;if(u.payer.startsWith("user:")&&u.payer!==await w())throw new l(403,"owner_only","a person sets limits only on their own payer or on an app",{payer:u.payer});return await L({id:e,payer:u.payer,usd_per_month:u.usd_per_month,source:"person",...d===void 0?{}:{funding:d},...p===void 0?{}:{grantee:p},...a===void 0?{}:{provider:a},...m===void 0?{}:{sub:m}})}o(G,"setRoute");async function I(n){const e=g(n);if((await y({source:"person"})).find(t=>t.id===e)===void 0||!await _(e))throw new l(404,"not_found",`no person limit ${e}`);return{id:e,removed:!0}}o(I,"removeRoute");const B=[{method:"GET",pattern:"/v1/charges",handler:o(async n=>({status:200,body:await F(n)}),"handler")}],C=T([{method:"GET",pattern:"/v1/spend-limits",handler:o(async()=>({status:200,body:{limits:await y()}}),"handler")},{method:"PUT",pattern:"/v1/spend-limits/:id",handler:o(async n=>({status:200,body:await G(n)}),"handler")},{method:"DELETE",pattern:"/v1/spend-limits/:id",handler:o(async n=>({status:200,body:await I(n)}),"handler")}]);export{B as chargeRoutes,C as spendLimitRoutes};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { RouteTable } from '../router.js';
|
|
2
|
+
import { type LlmSpend } from '../../../core/llm/run.js';
|
|
2
3
|
import { RunError } from '../../../core/runs/operations.js';
|
|
3
4
|
import { type SocketPrincipal } from '../server.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/** Who a socket caller's model call is attributed to: a broker's node to its grant's app, the person (and a local scoped token
|
|
6
|
+
* on an unjoined runtime) to the person's own agent. Limits are `limits` rows the ledger check reads; nothing here carries a cap. */
|
|
7
|
+
export declare function spendTarget(principal: SocketPrincipal | undefined, node?: {
|
|
8
|
+
node_id: string;
|
|
9
|
+
run_id?: string | null;
|
|
10
|
+
}): Promise<LlmSpend>;
|
|
11
|
+
/** `cap_exhausted` names the limit that refused, what had been spent against it, when the count starts over and what the person can do
|
|
12
|
+
* (the ledger's refusal carries `user_action` only for a limit the person can act on; an operator limit has none). */
|
|
9
13
|
export declare function capFields(error: RunError): Record<string, unknown>;
|
|
10
14
|
export declare const llmRoutes: RouteTable;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}:void 0,
|
|
3
|
-
`),{status:200}}if(r instanceof
|
|
1
|
+
var k=Object.defineProperty;var s=(t,e)=>k(t,"name",{value:e,configurable:!0});import{randomUUID as y}from"node:crypto";import{MODEL_CONTEXT_BODY_MAX_BYTES as x}from"../../../core/llm/body-limits.js";import{llmCallerOf as g,parseLlmRequest as O,runLlm as R}from"../../../core/llm/run.js";import{RunError as u}from"../../../core/runs/operations.js";import{errorCodes as S}from"../../../api/error-codes.js";import{getNode as T}from"../../../core/canvas/canvas.js";import{socketPrincipal as l}from"../server.js";import{activeGrant as E,activeGrantForApp as h,grantFor as I,grantsSource as w,payerOfGrant as N}from"../../../core/grants/mirror.js";import{parseUsageMonth as G,usageFor as L}from"../../../core/ledger/index.js";function b(t,e){if(e==="broker"||e==="person"||e===void 0||!t||typeof t!="object"||Array.isArray(t)||!("rate_limit"in t))return t;const{rate_limit:o,...a}=t;return a}s(b,"operatorDetails");const M="app:terminal";async function v(t,e){if(t?.class==="broker")return{grantee:t.app,sub:t.sub,node:t.node,run:e?.run_id??t.run};if(t?.class==="app"){const o=await w();if(o===null||o==="local")return{grantee:t.app};let a;try{a=await h(t.app)}catch{throw new u(401,"grant_revoked",`${t.app} has several active grants; a local token cannot name which user's cap applies`)}if(!a)throw new u(401,"grant_revoked",`${t.app} has no active grant`);return{grantee:t.app,sub:a.sub}}return{grantee:M}}s(v,"spendTarget");async function $(t){if(t?.class==="broker"){if(t.app==="app:terminal")return;const e=t.grant?await E(t.grant):await I(t.app,t.sub);if(!e&&await N(t.grant)==="app")throw new u(401,"grant_revoked",`${t.app}'s app-paid grant is no longer active`);return g(e&&e.grantee===t.app?e:null)}if(t?.class==="app"){const e=await w();return e===null||e==="local"?void 0:g(await h(t.app))}}s($,"llmCaller");function q(t){const e=S[t.code];return e?e.type:t.status===429?"rate_limit_error":t.status<500?"invalid_request_error":"server_error"}s(q,"errorType");function j(t){if(t.code!=="cap_exhausted")return{};const e=t.details??{};return{...e.limit!==void 0?{limit:e.limit}:{},...typeof e.usd=="number"?{usd:e.usd}:{},...typeof e.spend_cap=="number"?{spend_cap:e.spend_cap}:{},...typeof e.usd_equivalent=="number"?{usd_equivalent:e.usd_equivalent}:{},...typeof e.reset_at=="string"?{reset_at:e.reset_at}:{},...typeof e.user_action=="string"?{user_action:e.user_action}:{}}}s(j,"capFields");const z=[{method:"POST",pattern:"/v1/llm",scope:"crtr:llm",maxBodyBytes:x,handler:s(async({body:t,res:e,req:o,causedBy:a})=>{const i=y();e.setHeader("Request-Id",i);const p=new AbortController,_=s(()=>p.abort(),"abort");e.on("close",_);let f;try{const r=O(t),n=l(o),d=n?.class==="broker"?await T(n.node):null;if(n?.class==="broker"&&!d)throw new u(401,"unauthorized","node not found");const c={...await v(n,d??void 0),request:i},A=await R(r,p.signal,r.stream?m=>{typeof m.served_model=="string"&&(f=m.served_model),e.headersSent||e.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache"}),e.write(`${JSON.stringify(m)}
|
|
2
|
+
`)}:void 0,d?{cwd:d.cwd,profileId:d.profile_id??null}:void 0,c,await $(n),a);return r.stream?(e.headersSent||e.writeHead(200,{"content-type":"application/x-ndjson; charset=utf-8","cache-control":"no-cache"}),e.end(),{status:200}):{status:200,body:A.completion}}catch(r){if(e.headersSent){const n=r instanceof u?r:new u(503,"upstream_model_unavailable","model stream failed"),d=n.details&&typeof n.details=="object"&&!Array.isArray(n.details)?n.details:{},c=b(f!==void 0&&typeof d.served_model!="string"?{...d,served_model:f}:n.details,l(o)?.class);return e.end(`${JSON.stringify({error:{code:n.code,message:n.message,...c===void 0?{}:{details:c},type:"server_error",origin:"runtime",retryable:n.retryable,request_id:i}})}
|
|
3
|
+
`),{status:200}}if(r instanceof u){const n=b(r.details,l(o)?.class);return r.code==="all_routes_exhausted"&&e.setHeader("Retry-After",String(r.details.retry_after_s)),{status:r.status,body:{error:{code:r.code,message:r.message,...n===void 0?{}:{details:n},type:q(r),...r.param?{param:r.param}:{},origin:"runtime",retryable:r.retryable,request_id:i,...r.code==="all_routes_exhausted"?{retry_after_s:r.details.retry_after_s}:{},...j(r)}}}}throw r}finally{e.off("close",_)}},"handler")},{method:"GET",pattern:"/v1/usage",handler:s(async({req:t})=>{const e=y(),o={"Request-Id":e};try{const a=await v(l(t));return{status:200,headers:o,body:await L(a.grantee,G(new URL(t.url??"/","http://runtime.local").searchParams.get("month")))}}catch(a){if(!(a instanceof u))throw a;return{status:a.status,headers:o,body:{error:{code:a.code,message:a.message,type:q(a),...a.param?{param:a.param}:{},origin:"runtime",retryable:!1,request_id:e}}}}},"handler")}];export{j as capFields,z as llmRoutes,v as spendTarget};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var ce=Object.defineProperty;var u=(t,e)=>ce(t,"name",{value:e,configurable:!0});import{randomBytes as ue}from"node:crypto";import{claimNodeMail as le,acknowledgeNodeMail as fe}from"../../../core/canvas/deliveries.js";import{nodeMailEntry as me}from"../../../core/feed/inbox.js";import{enqueueNodeMail as D,cancelPendingNodeMail as pe}from"../../../core/feed/messages.js";import{reviveNode as L}from"../../../core/runtime/revive.js";import{BrokerUnreachableError as U}from"../../../core/runtime/broker-request.js";import{isBrokerLive as R}from"../../../core/runtime/model-swap.js";import{deliverLive as ye,interruptLive as he}from"../../../core/runtime/interactive-deliver.js";import{isProfilePaused as we}from"../../../core/profiles/manifest.js";import{getNode as O,withFreshTerminalGuard as be}from"../../../core/canvas/canvas.js";import{getById as G,nodeObject as F,ownerForGrantee as ge}from"../../../core/graph/objects.js";import{recordActedOn as ve}from"../../../core/graph/edges.js";import{recordWatch as H}from"../../../core/graph/watches.js";import{publish as V}from"../../../core/graph/events.js";import{readerFor as K}from"../reader.js";import{canRead as _e}from"../../../core/graph/access.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{getPendingStructuredRequestForNode as Q}from"../../../core/canvas/structured-requests.js";import{consumeDeadlineOnWake as X}from"../../../core/feed/messages.js";import{assertNotFinalized as M,assertFinalizedForReopen as ke,commitReopenResident as
|
|
2
|
-
`)[0].slice(0,120)}u(
|
|
1
|
+
var ce=Object.defineProperty;var u=(t,e)=>ce(t,"name",{value:e,configurable:!0});import{randomBytes as ue}from"node:crypto";import{claimNodeMail as le,acknowledgeNodeMail as fe}from"../../../core/canvas/deliveries.js";import{nodeMailEntry as me}from"../../../core/feed/inbox.js";import{enqueueNodeMail as D,cancelPendingNodeMail as pe}from"../../../core/feed/messages.js";import{reviveNode as L}from"../../../core/runtime/revive.js";import{BrokerUnreachableError as U}from"../../../core/runtime/broker-request.js";import{isBrokerLive as R}from"../../../core/runtime/model-swap.js";import{deliverLive as ye,interruptLive as he}from"../../../core/runtime/interactive-deliver.js";import{isProfilePaused as we}from"../../../core/profiles/manifest.js";import{getNode as O,withFreshTerminalGuard as be}from"../../../core/canvas/canvas.js";import{getById as G,nodeObject as F,ownerForGrantee as ge}from"../../../core/graph/objects.js";import{recordActedOn as ve}from"../../../core/graph/edges.js";import{recordWatch as H}from"../../../core/graph/watches.js";import{publish as V}from"../../../core/graph/events.js";import{readerFor as K}from"../reader.js";import{canRead as _e}from"../../../core/graph/access.js";import{withCanvasWrite as k}from"../../../core/canvas/db.js";import{getPendingStructuredRequestForNode as Q}from"../../../core/canvas/structured-requests.js";import{consumeDeadlineOnWake as X}from"../../../core/feed/messages.js";import{assertNotFinalized as M,assertFinalizedForReopen as ke,commitReopenResident as P}from"../../../core/runtime/reopen.js";import{admitLegacyStructuredOutput as Se}from"../../structured-requests/sidecar.js";import{assertOutputSchemaAccepted as $e}from"../../../core/runtime/structured-output.js";import{appendSituationalContext as E,formatSituationalProse as Ne}from"../../../core/runtime/situational-context.js";import{formatDataCard as Y}from"../../../shared/generated-context.js";import{hasNoNaturalCycle as je}from"../../../core/runtime/revive-all.js";import{recordPendingParkActivity as Ae}from"../../park-pending.js";import{wantsExecution as qe}from"../../reconcilers/node-lifecycle/wants-execution.js";import{hasFreshGroundState as Oe}from"../../../core/runtime/revive.js";import{CrtrError as Ce,notFound as C,usage as c}from"../../../core/errors.js";import{isStartRefusal as xe}from"../../../core/runtime/start-check.js";import{emitEvent as Re}from"../../../core/events/emit.js";import{assertScope as Fe}from"../../../core/scopes.js";import{InputError as I}from"../../../core/io.js";import{ApiError as Me}from"../../../api/index.js";import{describeObject as Pe,stubOf as Ee}from"../../../core/peer/stubs.js";import{machineNameOf as Z}from"../../../core/peer/names.js";import{callerNode as Ie,callerStub as Te,forwardVerb as ee,relayError as te,remoteStub as Be,unknownError as re,withMachine as ne}from"../../peer/forward.js";import{ownerOnly as ze}from"../router.js";import{personId as We}from"../../../core/canvas/person.js";const Je=["critical","urgent","normal","deferred"];function oe(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw c(`${e} must be an object { kind, facts?, body? }`);const a=t,n=a.kind;if(typeof n!="string"||n.trim()==="")throw c(`${e}.kind is required`);if(!n.includes(":"))throw c(`${e}.kind must be namespaced (contain ':'): ${n}`);const r={kind:n},o=a.facts;if(o!==void 0){if(typeof o!="object"||o===null||Array.isArray(o))throw c(`${e}.facts must be an object of string or number values`);const d={};for(const[l,m]of Object.entries(o)){if(typeof m!="string"&&typeof m!="number")throw c(`${e}.facts.${l} must be a string or number`);d[l]=m}r.facts=d}const s=a.body;if(s!==void 0){if(typeof s!="string")throw c(`${e}.body must be a string`);r.body=s}return r}u(oe,"parseCardRequest");function ae(t,e){try{return Y(t.kind,t.facts??{},t.body??"")}catch(a){throw c(`${e}: ${a instanceof Error?a.message:String(a)}`)}}u(ae,"renderCard");function De(t){if(typeof t!="object"||t===null)throw c("a message requires a JSON body { body, tier?, fresh? }");const e=t,a=e.body,n=e.fresh===!0,r=e.tier,o=e.situational_context,s=e.output_schema,d=typeof a=="string"&&a.trim()!=="",l=typeof o=="string"&&o.trim()!=="",m=typeof s=="string"&&s.trim()!=="",v=e.situational_card,S=e.context_cards,b=v===void 0?void 0:oe(v,"situational_card");if(S!==void 0&&!Array.isArray(S))throw c("context_cards must be an array of { kind, facts?, body? }");const p=S?.map((N,$)=>oe(N,`context_cards[${$}]`));if(b!==void 0&&typeof o=="string"&&o!=="")throw c("situational_card and situational_context are mutually exclusive");const w=b!==void 0||p!==void 0&&p.length>0;if(!n&&!d&&!l&&!m&&!w)throw c("a message requires a non-empty body (or fresh: true, situational_context, a runtime card, or output_schema)");if(n&&p!==void 0&&p.length>0)throw c("context_cards cannot ride a fresh revive (no inbox entry carries them)");if(r!==void 0&&!Je.includes(r))throw c(`invalid tier: ${String(r)} (expected critical|urgent|normal|deferred)`);const f={body:typeof a=="string"?a:""};r!==void 0&&(f.tier=r),n&&(f.fresh=!0),e.reopen===!0&&(f.reopen=!0),typeof o=="string"&&(f.situational_context=o),b!==void 0&&(f.situational_card=b),p!==void 0&&(f.context_cards=p),typeof s=="string"&&(f.output_schema=s),typeof e.from=="string"&&(f.from=e.from);const y=e.delivery;if(y!==void 0&&y!=="interactive")throw c(`invalid delivery: ${String(y)} (expected interactive)`);if(y==="interactive"){if(n||f.situational_context!==void 0||f.output_schema!==void 0)throw c("interactive delivery supports a body, runtime cards, and reopen only (no fresh, situational_context, or output_schema)");if(!d)throw c("interactive delivery requires a non-empty body");if(f.tier==="deferred")throw c("interactive delivery cannot be deferred");f.delivery="interactive"}return f}u(De,"parseSendBody");function Le(t){let e;try{e=JSON.parse(t)}catch{throw c("output_schema must be a JSON schema string")}if(typeof e!="object"||e===null||Array.isArray(e))throw c("output_schema must be a JSON schema object");return e}u(Le,"parseSchema");function Ue(t){return t.split(`
|
|
2
|
+
`)[0].slice(0,120)}u(Ue,"label");function ie(t,e){return e===null?"":Y("reopen-final-report",{report:`${t}/${e}`},"This node previously completed work. Read this final report before acting on the new instruction.")}u(ie,"previousFinalReportCard");async function Ge(t,e,a){if(a!==null&&a!=="human"&&(await G(t,a))?.type==="node"){const o=await F(t,a);return{actor:o,senderNode:o}}if(a==="human")return{actor:await ge(t,We()),senderNode:null};const n=await K(t,e);return{actor:n.class==="person"?n.person:n.class==="app"?n.app:n.node,senderNode:n.class==="node"?n.node:null}}u(Ge,"messageActor");async function se(t,e,a,n,r,o){const s=await F(t,a),{actor:d,senderNode:l}=await Ge(t,e,n),m=r===null?{}:{pointer:r};await ve(t,d,s),l!==null&&l.id!==s.id&&await H(t,l,s,"message",{endsOn:o&&r!==null?`result:${r}`:"final"}),await V(t,{event:"messaged",object:s,actor:d,attrs:{},...m}),l!==null&&await V(t,{event:"sent",object:l,actor:l,attrs:{to:s.id},...m})}u(se,"recordMessage");function He(t,e){return new I({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."})}u(He,"deferredNoNaturalCycle");async function Ve(t){if(!await Oe(t))throw new Me(422,"unprocessable",`node ${t} has no goal/roadmap on disk \u2014 a fresh revive would resume amnesiac.`)}u(Ve,"assertRecoverableForFresh");function Ke(t,e){return t instanceof I?{error:t.payload.error,message:t.payload.message,next:t.payload.next,message_id:e}:t instanceof Ce?{error:t.code,message:t.message,message_id:e}:xe(t)?{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}}u(Ke,"wakeFailure");async function Qe(t,e){const a=typeof t.body=="object"&&t.body!==null&&!Array.isArray(t.body)?{...t.body}:t.body;typeof a=="object"&&a!==null&&delete a.from;const n=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/messages`,body:a});if(n.kind==="local")return null;if(n.kind==="unknown")throw re(e,n.unreachable);if(n.status>=400)throw te(n.status,n.body);return await k(async r=>{const o=await Ie(r,t);if(o===null)return;const s=n.body?.object,d=await Be(r,e,"node",n.machine.id,s);await H(r,o,d,"message",{endsOn:"final"})}),{status:n.status,body:ne(n.body,n.machine)}}u(Qe,"forwardMessage");async function Xe(t){const e=await Ye(t);if(t.principal.class!=="forwarded"||typeof e.body!="object"||e.body===null)return e;const a=await k(async n=>{const r=await F(n,t.params.id);return await _e(n,await K(n,t),r)?await Pe(n,r):{id:r.id,type:r.type,kind:null,name:r.id,owner:null}});return{...e,body:{...e.body,object:a}}}u(Xe,"handleMessage");async function Ye(t){const e=t.params.id,a=await Qe(t,e);if(a!==null)return a;const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=De(t.body),o=r.tier??"normal",s=we(n.profile_id),d=t.principal.class==="forwarded"?await k(async i=>(await Te(i,t))?.id??null):r.from??null;if(d!==null){const i=await O(d);i!==null&&Fe(i,"crtr:act")}const l=d===null?null:await k(async i=>{const h=await G(i,d);return h===null?null:(await Ee(i,h))?.home_machine??null}),m=l===null?void 0:`${d} on ${Z(l)}`,v=t.principal.class==="broker"?t.principal.node:null,S=l===null?{}:{from_machine:Z(l)},b=r.body.trim()!=="",p=r.situational_card!==void 0?ae(r.situational_card,"situational_card"):r.situational_context!==void 0?Ne(r.situational_context.trim()):"",w=p!=="",f=(r.context_cards??[]).map((i,h)=>ae(i,`context_cards[${h}]`)),y=r.output_schema!==void 0&&r.output_schema.trim()!==""?Le(r.output_schema):null;y!==null&&await $e(y);let N=!1;const $=u(async i=>{if(!(y===null||N)){if(await Q(e)!==null)throw new I({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."});await Se(e,"oneoff",y,i),N=!0}},"ensureSchema");if(r.fresh){const i=r.reopen?await ke(e):null;r.reopen||await M(e),await Ve(e),await $(),w&&E(e,p),r.reopen&&await P(e,i,v);const h=await L(e,{resume:!1,capacity:"refuse"});return{status:201,body:{node_id:e,delivered:!1,revived:h.launch!==void 0}}}r.delivery==="interactive"&&!r.reopen&&await M(e);let T=!1,_="";if(r.delivery==="interactive"&&r.reopen&&(_=ie(e,await P(e,void 0,v)),T=!0),r.delivery==="interactive"&&await $(),r.delivery==="interactive"&&!s&&!n.run_id&&R(n))try{return w&&E(e,p),await ye(e,r.body,{cards:[..._===""?[]:[_],...w?[p]:[],...f],origin:d==="human"?{type:"human"}:{type:"runtime",kind:"inbox",entries:[{...d===null?{}:{from:d},...m===void 0?{}:{fromName:m},kind:"message",tier:o}]}}),await k(async h=>await se(h,t,e,d,null,!1)),await X(e),{status:201,body:{node_id:e,delivered:!0,revived:!1,delivered_via:"engine"}}}catch(i){if(!(i instanceof U))throw i}const B=u(async()=>{r.reopen||await M(e);const i=ue(16).toString("hex"),h=u(async q=>await se(q,t,e,d,i,y!==null),"withinEnqueue");await $(i),w&&E(e,p),r.reopen&&!T&&(_=ie(e,await P(e,void 0,v)));const x=[..._===""?[]:[_],...f],J=x.length>0?{cards:x}:{};if(b||y!==null){const q=b?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: "${(await Q(e))?.request_id??"<request-id>"}"\`) before you stop; after it is accepted, continue normally.`;return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:Ue(q),bodyJson:{body:q,...S,...w?{situational:!0}:{},...J}})}return await D(e,{senderNodeId:d,...m===void 0?{}:{senderName:m},urgency:o,wake:1,kind:"message",messageId:i,withinEnqueue:h,label:w?"(ambient context updated)":"(context delivered)",bodyJson:{...w?{situational:!0}:{},situationalOnly:!0,...J}})},"deliverImmediate"),g=await u(async()=>{if(o==="deferred"){const i=await be(e,je,B);if(i.kind==="missing")throw C(`unknown node: ${e}`,{received:e});if(i.kind==="terminal")throw He(e,i.status);return i.result}return await B()},"deliver")();g.urgency!=="deferred"&&await k(async()=>Ae(e));const j=await O(e);if(j===null)throw C(`unknown node: ${e}`,{received:e});n.frozen_at!==null&&await X(e);let z=!1,W=!1,A;if((r.reopen||j.lifecycle==="terminal"||!await qe(j))&&g.urgency!=="deferred"&&!s&&!R(j))try{const i=await L(e,{resume:!0,capacity:"freeze"});z=i.launch!==void 0,W=i.outcome==="frozen"}catch(i){A=Ke(i,g.messageId),Re({level:"error",event:"message.wake_failed",node_id:e,error:i,fields:{message_id:g.messageId,error_code:A.error}})}return{status:201,body:{node_id:e,delivered:!0,revived:z,...W?{not_revived_reason:"capacity_frozen"}:{},...A===void 0?{}:{wake_failed:A},delivered_at:g.createdAt,...g.urgency===o?{}:{delivered_tier:g.urgency},...r.delivery==="interactive"?{delivered_via:"inbox"}:{}}}}u(Ye,"handleMessageHere");async function Ze(t){const e=t.params.id,a=await ee(t,e,{method:"POST",path:`/v1/nodes/${encodeURIComponent(e)}/interrupt`,body:t.body});if(a.kind==="unknown")throw re(e,a.unreachable);if(a.kind==="remote"){if(a.status>=400)throw te(a.status,a.body);return{status:a.status,body:ne(a.body,a.machine)}}const n=await O(e);if(n===null)throw C(`unknown node: ${e}`,{received:e});const r=await pe(e,{senderNodeId:"human",kind:"message"});let o=!1;if(R(n))try{o=await he(e)!=="idle"}catch(d){if(!(d instanceof U))throw d}return{status:200,body:{node_id:e,outcome:o?"aborted_turn":r.length>0?"canceled_pending":"idle",aborted_turn:o,canceled_pending:r.length}}}u(Ze,"handleInterrupt");function de(t){if(typeof t.body!="object"||t.body===null||Array.isArray(t.body))throw c("mail requires an object body");const e=t.body;if(typeof e.execution_id!="string"||e.execution_id.trim()==="")throw c("execution_id is required");return e}u(de,"mailBody");async function et(t){const e=de(t),a=e.channel;if(a!=="wake"&&a!=="passive"&&a!=="deferred_run")throw c("channel must be wake, passive, or deferred_run");const n=await le(t.params.id,e.execution_id,a);return{status:200,body:{action:n.action,deliveries:n.deliveries.map(o=>({delivery_id:o.deliveryId,attempts:o.attempts,handoff:o.handoff,entry:{...me(o),seq:o.message.seq}}))}}}u(et,"handleMailClaim");async function tt(t){const e=de(t);if(!Array.isArray(e.deliveries))throw c("deliveries must be an array");const a=e.deliveries.map(o=>{if(typeof o!="object"||o===null||Array.isArray(o))throw c("each delivery must be an object");const s=o;if(typeof s.delivery_id!="string"||s.delivery_id==="")throw c("delivery_id is required");if(!Number.isSafeInteger(s.attempts)||s.attempts<1)throw c("attempts must be a positive integer");if(s.disposition!=="acknowledged"&&s.disposition!=="released")throw c("disposition must be acknowledged or released");return{delivery_id:s.delivery_id,attempts:s.attempts,disposition:s.disposition}}),n=await fe(t.params.id,e.execution_id,a.map(o=>({deliveryId:o.delivery_id,attempts:o.attempts,disposition:o.disposition})));return{status:200,body:{action:n.action,results:n.results.map(o=>({delivery_id:o.deliveryId,outcome:o.outcome}))}}}u(tt,"handleMailAcknowledge");const Ut=ze([{method:"POST",pattern:"/v1/nodes/:id/mail/claim",handler:et},{method:"POST",pattern:"/v1/nodes/:id/mail/acknowledge",handler:tt}]),Gt=[{method:"POST",pattern:"/v1/nodes/:id/messages",scope:"crtr:act",handler:Xe},{method:"POST",pattern:"/v1/nodes/:id/interrupt",scope:"crtr:act",handler:Ze}];export{Ut as brokerMailRoutes,Xe as handleMessage,Gt as messageRoutes};
|
|
@@ -54,6 +54,8 @@ export interface ProviderRequest {
|
|
|
54
54
|
requestId: string;
|
|
55
55
|
traceId?: string;
|
|
56
56
|
signal: AbortSignal;
|
|
57
|
+
/** The `Crtr-Caused-By` value the call arrived with (`message:<id>`, `request:<id>`, `cron_run:<id>`); absent when none. */
|
|
58
|
+
causedBy?: string;
|
|
57
59
|
}
|
|
58
60
|
export interface ProviderInput {
|
|
59
61
|
params: Record<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var R=Object.defineProperty;var i=(e,r)=>R(e,"name",{value:r,configurable:!0});import{randomUUID as I}from"node:crypto";import{readConfig as $}from"../../../core/config.js";import{activeGrantForApp as O,grantFor as q,localGrants as w}from"../../../core/grants/mirror.js";import{getNode as
|
|
1
|
+
var R=Object.defineProperty;var i=(e,r)=>R(e,"name",{value:r,configurable:!0});import{randomUUID as I}from"node:crypto";import{readConfig as $}from"../../../core/config.js";import{activeGrantForApp as O,grantFor as q,localGrants as w}from"../../../core/grants/mirror.js";import{getNode as G}from"../../../core/canvas/canvas.js";import{scopeAllowed as N}from"../../../core/scopes.js";import{ProviderError as u,providerErrorBody as T,scopeMissing as F}from"../../../core/providers/errors.js";import{callProvider as x,providerTools as C}from"../../../core/providers/call.js";import{providerIdsFromGrants as j,providerInstallState as k}from"../../../core/providers/bundles.js";import{providerRootClaims as B,resolveProviderRef as D}from"../../../core/providers/roots.js";import{listInstalledPlugins as M}from"../../../core/resolver.js";import{listAudit as U,writeProviderAudit as z}from"../../../core/providers/audit.js";import{emitEvent as b}from"../../../core/events/emit.js";import{resolvedScopes as _}from"../router.js";const we=new Set(["provider.tools","provider.call","audit.list"]);async function L(e){if(e.class==="forwarded"){if(e.actor.kind==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null,origin:{node:null,run:null,machine:e.via}};const r=await q(e.actor.app,e.sub);if(!r)throw new u("grant_revoked",`app ${e.actor.app} has no active grant`);return{class:"app",sub:e.sub,grantee:e.actor.app,client_id:null,resolved:r.resolved,nodeScopes:e.actor.node_scopes,run_id:null,node_id:null,origin:{node:e.actor.node,run:e.actor.place?.run??null,machine:e.via}}}if(e.class==="machine")throw new u("permission_error","this operation is not available to the caller");if(e.class==="person")return{class:"person",sub:null,grantee:null,client_id:null,resolved:null,nodeScopes:null,run_id:null,node_id:null};if(e.class==="app"){const r=await O(e.app);if(!r)throw new u("grant_revoked",`app ${e.app} has no active grant`);return{class:"app",sub:r.sub,grantee:e.app,client_id:null,resolved:r.resolved,nodeScopes:e.scopes,run_id:null,node_id:null}}return e.class==="cron"?{class:"app",sub:e.sub,grantee:e.app,client_id:null,resolved:await _(e),nodeScopes:e.scopes,run_id:e.origin_run,node_id:null}:{class:"broker",sub:e.sub,grantee:e.app,client_id:e.client_id,resolved:await _(e),nodeScopes:e.scopes,run_id:e.run??(await G(e.node))?.run_id??null,node_id:e.node}}i(L,"callerFromRequest");function be(e){return{class:"app",sub:e.sub,grantee:e.grantee,client_id:e.client_id,resolved:e.resolved,nodeScopes:null,run_id:null,node_id:null}}i(be,"callerFromApp");const V=/^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64})$/;function Z(e){const r=V.exec(e);return r!==null&&r[1]!=="crtr"}i(Z,"isProviderScope");const g=new WeakMap;function W(e){let r=g.get(e);return r===void 0&&(r=new Set(e),g.set(e,r)),r}i(W,"heldSet");function y(e,r){return e===null?!0:Z(r)?W(e).has(r):N(e,r)}i(y,"holds");function S(e,r){return e.class==="person"?!0:y(e.resolved??[],r)&&y(e.nodeScopes,r)}i(S,"callerAllows");function _e(e,r,t){const o=`${r}:${t}`;if(!S(e,o))throw F(o)}i(_e,"requireGroup");function J(){return!!$("user").runtime?.issuer}i(J,"providerOperationsEnabled");const X=/^[A-Za-z0-9_-]{1,64}$/;function m(e,r){return new u("invalid_request",e,{param:r})}i(m,"invalid");function A(e){if(typeof e!="string"||!X.test(e)||e==="crtr")throw m("provider must be a provider id","provider");return e}i(A,"parseProvider");function H(e){if(!e||typeof e!="object"||Array.isArray(e))throw new u("invalid_request","a JSON object is required");const r=e,t=A(r.provider),o=r.tool;if(typeof o!="string"||!o.trim())throw m("tool must be a non-empty string","tool");const a=r.arguments??{};if(!a||typeof a!="object"||Array.isArray(a))throw m("arguments must be an object","arguments");const d=r.version;if(d!==void 0&&(typeof d!="number"||!Number.isInteger(d)||d<1))throw m("version must be an integer >= 1","version");const n=r.approval;if(n!==void 0&&(typeof n!="string"||!n))throw m("approval must be a question id","approval");const l=r.machine;if(l!==void 0&&(typeof l!="string"||!l.trim()))throw m("machine must be a machine id","machine");return{provider:t,tool:o.trim(),arguments:a,...d===void 0?{}:{version:d},...n===void 0?{}:{approval:n},...l===void 0?{}:{machine:l.trim()}}}i(H,"parseCallInput");async function K(e,r=new URLSearchParams){const t=i((n,l,v)=>{const f=r.get(n);if(f===null)return l;if(!/^\d+$/.test(f)||Number(f)>v||Number(f)<1)throw m(`invalid ${n}`,n);return Number(f)},"numeric"),o=r.has("after")?t("after",0,Number.MAX_SAFE_INTEGER):void 0,a=t("limit",100,1e3),d=Object.fromEntries(["operation","outcome","run_id","since","until"].filter(n=>r.has(n)).map(n=>[n,r.get(n)]));return{status:200,body:await U(e.class==="person"?null:e.grantee,{after:o,limit:a,...d})}}i(K,"auditList");async function Q(e,r,t){let o=r;const a=performance.now(),d=M("user"),{skipped:n}=B(d),l=new Set(d.filter(s=>s.enabled&&s.manifest.provider?.id===s.name&&!n.has(s.name)).map(s=>s.name)),v=j(await w()).filter(s=>!n.has(s)),f=[...new Set([...l,...v])].sort();if(o!==void 0){const s=D(o);if(s===null)throw new u("not_found",`provider "${o}" is not available`);o=s}if(o!==void 0&&!f.includes(o))throw new u("not_found",`provider "${o}" is not available`);const E=o===void 0?f:[o],P=e.class==="person"?(await w()).flatMap(s=>s.status==="active"?s.resolved_scopes.map(({scope:c})=>c):[]):e.resolved??[],p=await Promise.all(E.map(async s=>{if(l.has(s))return C(e,s);if(!P.some(h=>h.startsWith(`${s}:`)&&S(e,h)))return null;const c=k(s);return{provider:s,display_name:s,status:c.status,...c.error?{error:c.error}:{},groups:[],commands:[]}})).then(s=>s.filter(c=>c!==null));if(b({level:"debug",event:"api.provider.tools",duration_ms:performance.now()-a,fields:{caller:e.class,provider:o??null,providers:p.length}}),o!==void 0){if(p.length===0)throw new u("not_found",`provider "${o}" is not available`);return{status:200,body:p[0]}}return{status:200,body:{providers:p}}}i(Q,"providerTools");async function Y(e,r,t){return x(e,r,t)}i(Y,"providerCall");async function ee(e,r,t,o){if(!J())throw new u("not_found","provider operations need a directory-connected runtime");if(e==="audit.list")return await K(r,t.query);if(e==="provider.tools")return Q(r,t.params.provider===void 0?void 0:A(t.params.provider),o);if(e==="provider.call"){let a;try{a=H(t.body)}catch(d){const n=t.body&&typeof t.body=="object"&&!Array.isArray(t.body)?t.body:{};throw await z({request_id:o.requestId,caller:r,provider:typeof n.provider=="string"?n.provider:"",tool:typeof n.tool=="string"?n.tool:"",outcome:`refused:${d instanceof u?d.code:"invalid_request"}`}),d}return Y(r,a,o)}throw new u("not_found","unknown operation")}i(ee,"handleProviderOperation");function re(e){const r=new AbortController,t=i(()=>{e.writableFinished||r.abort()},"abort");return e.on("close",t),{signal:r.signal,release:i(()=>e.off("close",t),"release")}}i(re,"abortOnClose");function oe(e,r,t,o){const a=T(e,r,t,o??void 0);return a.status>=500&&!(e instanceof u)&&b({event:"api.provider.failed",level:"error",error:e instanceof Error?e:new Error(String(e)),fields:{request_id:r}}),a}i(oe,"providerFailure");const ge=[{method:"GET",pattern:"/v1/audit",name:"audit.list"},{method:"GET",pattern:"/v1/providers/tools",name:"provider.tools"},{method:"GET",pattern:"/v1/providers/:provider/tools",name:"provider.tools"},{method:"POST",pattern:"/v1/provider/call",name:"provider.call"}].map(({method:e,pattern:r,name:t})=>({method:e,pattern:r,handler:i(async o=>{const a=I(),{signal:d,release:n}=re(o.res);let l=null;try{return l=await L(o.principal),{...await ee(t,l,{params:o.params,body:o.body,query:new URL(o.req.url??"/","http://runtime.local").searchParams},{requestId:a,signal:d,...o.causedBy===void 0?{}:{causedBy:o.causedBy}}),headers:{"Request-Id":a}}}catch(v){return oe(v,a,!1,l?.run_id)}finally{n()}},"handler")}));export{we as PROVIDER_OPERATIONS,re as abortOnClose,S as callerAllows,be as callerFromApp,L as callerFromRequest,ee as handleProviderOperation,H as parseCallInput,A as parseProvider,oe as providerFailure,J as providerOperationsEnabled,ge as providerRoutes,_e as requireGroup};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var s=Object.defineProperty;var u=(e,r)=>s(e,"name",{value:r,configurable:!0});import{randomUUID as t}from"node:crypto";import{cursor as m,streamRunEvents as i}from"../app-listener.js";import{handleRunOperation as o}from"./runs.js";import{OPERATIONS as d}from"../operations.js";const q=d.filter(e=>["run.list","run.get","run.events","trace.get","run.message","run.interrupt","run.cancel","run.delete"].includes(e.name)).map(e=>({method:e.method,pattern:e.path,handler:u(async r=>{const n=r.params.run_id;if(e.name==="run.events"){const a=m(r.req,r.query);return await i(r.req,r.res,n,r.principal,a,t(),"ndjson"),{status:200}}return o(e,r.principal,n,r.query,r.body,r.req.headers["idempotency-key"],r.causedBy)},"handler")}));export{q as runRoutes};
|
|
@@ -3,7 +3,7 @@ import { type RequestPrincipal } from '../router.js';
|
|
|
3
3
|
import type { AppOperation } from '../operations.js';
|
|
4
4
|
declare function integer(value: string | null, param: string, min: number, max: number, fallback: number): number;
|
|
5
5
|
export { integer };
|
|
6
|
-
export declare function handleRunOperation(operation: AppOperation, principal: AppPrincipal | RequestPrincipal, id: string | undefined, query: URLSearchParams, body: unknown, key: string | undefined): Promise<{
|
|
6
|
+
export declare function handleRunOperation(operation: AppOperation, principal: AppPrincipal | RequestPrincipal, id: string | undefined, query: URLSearchParams, body: unknown, key: string | undefined, causedBy?: string): Promise<{
|
|
7
7
|
status: number;
|
|
8
8
|
body: unknown;
|
|
9
9
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var S=Object.defineProperty;var w=(o,t)=>S(o,"name",{value:t,configurable:!0});import{callerGrant as g}from"../principal.js";import{grantVersion as b,payerOfGrant as O}from"../../../core/grants/mirror.js";import{waitGrantChange as $}from"../../../core/grants/watch.js";import{RunError as n,idempotencyKeyOf as v,cancelRun as N,deleteRun as A,interruptRun as j,listRuns as x,messageRun as B,renameRun as J,requestedScopes as z,startRun as C,traceRun as E,waitRun as G}from"../../../core/runs/operations.js";import{canReadRun as y,requireRun as l,runCallerApp as K}from"../router.js";import{waitSeconds as q}from"../long-poll-wait.js";import{outputSchemaProblem as T}from"../../../core/runtime/structured-output.js";function f(o){if(!o||typeof o!="object"||Array.isArray(o))throw new n(400,"invalid_request","a JSON object is required");return o}w(f,"object");function k(o,t,i,d,u){if(o===null)return u;if(!/^(0|[1-9]\d*)$/.test(o)||!Number.isSafeInteger(Number(o))||Number(o)<i||Number(o)>d)throw new n(400,"invalid_request",`invalid ${t}`,t);return Number(o)}w(k,"integer");const L=256*1024;async function Y(o,t,i,d,u,_,p){switch(o.name){case"grant.get":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=q("grant.get",d),s=d.get("since");if(s!==null&&!/^[A-Za-z0-9_-]{1,64}$/.test(s))throw new n(400,"invalid_request","invalid since","since");let a=await g(t);return e&&s!==null&&b(a)===s&&(await $(t.grant,e),a=await g(t)),{status:200,body:{sub:t.sub,grantee:t.grantee,client_id:t.client_id,grant:t.grant,rev:a.rev,resolved_scopes:a.resolved_scopes,spend_cap:a.spend_cap,exp:t.exp,payer:await O(t.grant),dependencies:a.dependencies??{},version:b(a)}}}case"run.start":{if("class"in t)throw new n(403,"permission_error","an app token is required");const e=f(u),s=["mode","parent","fork_from","node_id","env","tools","system_prompt"];for(const r of s)if(e[r]!==void 0)throw new n(403,"owner_only",`${r} is owner-only`,r);const a=["payer","model_policy","kind"];for(const r of a)if(e[r]!==void 0)throw new n(400,"invalid_request",`${r} is not available`,r);for(const r of Object.keys(e))if(!["prompt","scopes","metadata","profile","isolated","cwd","worktree","output_schema","message","model","deadline"].includes(r))throw new n(400,"invalid_request",`unknown field ${r}`,r);if(e.model!==void 0&&(typeof e.model!="string"||!e.model.trim()))throw new n(400,"invalid_request","model must be a non-empty string","model");if(e.deadline!==void 0&&(typeof e.deadline!="string"||!e.deadline.trim()))throw new n(400,"invalid_request","deadline must be a duration string such as 10m","deadline");if(e.profile!==void 0&&(typeof e.profile!="string"||!e.profile))throw new n(400,"invalid_request","profile must be a profile id","profile");if(e.isolated!==void 0&&typeof e.isolated!="boolean")throw new n(400,"invalid_request","isolated must be a boolean","isolated");if(e.cwd!==void 0&&(e.isolated!==!0||typeof e.cwd!="string"||!e.cwd))throw new n(400,"invalid_request","cwd is available only for an isolated run and must be an absolute path inside its root folder","cwd");if(e.worktree!==void 0&&e.isolated!==!0)throw new n(400,"invalid_request","worktree is not available","worktree");if(e.isolated===!0&&e.worktree!==void 0)throw new n(400,"invalid_request","worktree is unavailable for an isolated run","worktree");if(typeof e.prompt!="string"||!e.prompt.trim())throw new n(400,"invalid_request","prompt is required","prompt");if(e.message!==void 0&&(typeof e.message!="string"||!e.message.trim()))throw new n(400,"invalid_request","message must be a non-empty string","message");const m=e.metadata===void 0?{}:f(e.metadata);if(Buffer.byteLength(JSON.stringify(m))>4096)throw new n(400,"invalid_request","metadata exceeds 4 KB","metadata");let c;if(e.output_schema!==void 0){const r=e.output_schema;if(!r||typeof r!="object"||Array.isArray(r))throw new n(400,"invalid_request","output_schema must be a JSON Schema object","output_schema");if(Buffer.byteLength(JSON.stringify(r))>L)throw new n(400,"invalid_request","output_schema exceeds 256 KB","output_schema");const h=await T(r);if(h!==null)throw new n(400,"invalid_request",`output_schema is not usable: ${h}`,"output_schema");c=r}const R=z(t.resolved,e.scopes,t.grantee);return{status:201,body:await C(t,{prompt:e.prompt,scopes:R,metadata:m,profile:e.profile,isolated:e.isolated,cwd:e.cwd,...e.model===void 0?{}:{model:e.model},...e.deadline===void 0?{}:{deadline:e.deadline},...c===void 0?{}:{output_schema:c},...e.message===void 0?{}:{message:e.message},...p===void 0?{}:{causedBy:p}},v(_))}}case"run.list":{const e=d.get("status")??void 0;if(e&&!["running","waiting_on_user","idle","settled"].includes(e))throw new n(400,"invalid_request","invalid status","status");const s=d.get("app");if(s!==null&&!/^(?:app:)?[a-z0-9][a-z0-9-]*$/.test(s))throw new n(400,"invalid_request","invalid app","app");const a=s===null?K(t):s.startsWith("app:")?s:`app:${s}`;if(a!==null&&!await y(t,a))throw new n(404,"not_found","run not found");return{status:200,body:await x(a,{status:e,after:d.get("after")??void 0,limit:k(d.get("limit"),"limit",1,100,50),..."class"in t&&(t.class==="broker"||t.class==="cron")&&a!==null?{visible:w(async m=>await y(t,a,m),"visible")}:{}})}}case"run.get":{const e=q("run.get",d),s=await l(t,i),a=await G(i,s.grantee,e);return await l(t,i),{status:200,body:a}}case"run.message":{const e=f(u);if(typeof e.message!="string"||!e.message.trim())throw new n(400,"invalid_request","message is required","message");if(e.start_turn!==void 0&&typeof e.start_turn!="boolean")throw new n(400,"invalid_request","start_turn must be a boolean","start_turn");return{status:202,body:await B(await l(t,i,!0),e.message,void 0,e.start_turn,v(_))}}case"run.interrupt":{const e=f(u===void 0?{}:u);if(e.children!==void 0&&e.children!=="stop"&&e.children!=="continue")throw new n(400,"invalid_request","invalid children","children");return{status:200,body:await j(await l(t,i,!0),e.children)}}case"run.cancel":{const e=f(u===void 0?{}:u);if(e.keep_schedules!==void 0&&typeof e.keep_schedules!="boolean")throw new n(400,"invalid_request","invalid keep_schedules","keep_schedules");return{status:200,body:await N(await l(t,i,!0),{keepSchedules:e.keep_schedules})}}case"run.rename":{const e=f(u);for(const s of Object.keys(e))if(s!=="name")throw new n(400,"invalid_request",`unknown field ${s}`,s);return{status:200,body:await J(await l(t,i,!0),e.name)}}case"run.delete":{const{uploads_pending:e}=await A(await l(t,i,!0,!0));return{status:200,body:{run_id:i,uploads_pending:e}}}case"trace.get":{const e=d.get("format")??void 0;if(e&&e!=="entries")throw new n(400,"invalid_request","unknown trace format","format");const s=await l(t,i);return{status:200,body:await E(i,s.grantee,e)}}default:throw new n(404,"not_found","unknown operation")}}w(Y,"handleRunOperation");export{Y as handleRunOperation,k as integer};
|