@north-light/crouter 0.3.331 → 0.3.332
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/builtin-memory/crouter-plugin/README.md +3 -6
- package/dist/builtin-memory/crouter-plugin/bundles-and-memory.md +2 -4
- package/dist/builtin-memory/crouter-plugin/commands.md +3 -6
- package/dist/builtin-memory/crouter-plugin/deploying.md +3 -6
- package/dist/builtin-memory/crouter-plugin/errors.md +2 -5
- package/dist/builtin-memory/crouter-plugin/getting-started.md +2 -5
- package/dist/builtin-memory/crouter-plugin/output.md +1 -3
- package/dist/builtin-memory/crouter-plugin/parameters.md +3 -4
- package/dist/builtin-memory/crouter-sdk/README.md +3 -4
- package/dist/builtin-memory/crouter-sdk/bash.md +2 -3
- package/dist/builtin-memory/crouter-sdk/client.md +3 -4
- package/dist/builtin-memory/crouter-sdk/docker.md +2 -4
- package/dist/builtin-memory/crouter-sdk/errors.md +9 -4
- package/dist/builtin-memory/crouter-sdk/files.md +2 -3
- package/dist/builtin-memory/crouter-sdk/getting-started.md +19 -6
- package/dist/builtin-memory/crouter-sdk/memory.md +3 -4
- package/dist/builtin-memory/crouter-sdk/migration.md +2 -6
- package/dist/builtin-memory/crouter-sdk/nodes.md +3 -3
- package/dist/builtin-memory/crouter-sdk/resources.md +2 -4
- package/dist/builtin-memory/crouter-sdk/streaming.md +3 -4
- package/dist/clients/attach/viewer.js +773 -776
- package/dist/commands/sys/connect.js +3 -3
- package/dist/core/runtime/spawn.d.ts +3 -1
- package/dist/core/runtime/spawn.js +2 -2
- package/dist/core/scopes.d.ts +4 -2
- package/dist/core/scopes.js +1 -1
- package/dist/core/secrets.d.ts +11 -0
- package/dist/core/secrets.js +2 -2
- package/dist/daemon/api/bridge.d.ts +4 -0
- package/dist/daemon/api/bridge.js +2 -2
- package/dist/daemon/api/handlers/attach.d.ts +1 -1
- package/dist/daemon/api/handlers/attach.js +1 -1
- package/dist/daemon/api/handlers/bash.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.d.ts +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +1 -1
- package/dist/daemon/api/handlers/broker-recovery.d.ts +1 -1
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/canvas.js +4 -4
- package/dist/daemon/api/handlers/crons.d.ts +1 -1
- package/dist/daemon/api/handlers/crons.js +1 -1
- package/dist/daemon/api/handlers/daemon.d.ts +1 -1
- package/dist/daemon/api/handlers/daemon.js +1 -1
- package/dist/daemon/api/handlers/files.js +1 -1
- package/dist/daemon/api/handlers/focus.d.ts +1 -1
- package/dist/daemon/api/handlers/focus.js +1 -1
- package/dist/daemon/api/handlers/human-requests.js +1 -1
- package/dist/daemon/api/handlers/memory.js +1 -1
- package/dist/daemon/api/handlers/messages.d.ts +1 -1
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/handlers/model-config.d.ts +1 -1
- package/dist/daemon/api/handlers/model-config.js +1 -1
- package/dist/daemon/api/handlers/modelauth.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/profiles.js +1 -1
- package/dist/daemon/api/handlers/reports.js +1 -1
- package/dist/daemon/api/handlers/reviews.js +1 -1
- package/dist/daemon/api/handlers/worktree.d.ts +1 -1
- package/dist/daemon/api/handlers/worktree.js +1 -1
- package/dist/daemon/api/router.d.ts +20 -1
- package/dist/daemon/api/router.js +1 -1
- package/dist/daemon/api/server.js +1 -11
- package/docs/plugin/README.md +5 -0
- package/docs/plugin/bundles-and-memory.md +5 -0
- package/docs/plugin/commands.md +5 -0
- package/docs/plugin/deploying.md +5 -0
- package/docs/plugin/errors.md +5 -0
- package/docs/plugin/getting-started.md +5 -0
- package/docs/plugin/output.md +5 -0
- package/docs/plugin/parameters.md +5 -0
- package/docs/sdk/README.md +5 -0
- package/docs/sdk/bash.md +5 -0
- package/docs/sdk/client.md +6 -1
- package/docs/sdk/docker.md +5 -0
- package/docs/sdk/errors.md +12 -0
- package/docs/sdk/files.md +5 -0
- package/docs/sdk/getting-started.md +21 -1
- package/docs/sdk/memory.md +5 -0
- package/docs/sdk/migration.md +5 -0
- package/docs/sdk/nodes.md +6 -1
- package/docs/sdk/resources.md +5 -0
- package/docs/sdk/streaming.md +5 -0
- package/package.json +3 -2
- package/runtime.lock.json +6570 -506
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)}
|
|
3
|
-
`);const{loginProvider:r,openProviderLoginRuntime:
|
|
1
|
+
var _=Object.defineProperty;var o=(e,t)=>_(e,"name",{value:t,configurable:!0});import{randomBytes as $}from"node:crypto";import x from"node:path";import{defineLeaf as R}from"../../core/command.js";import{readConfig as w,updateConfig as g}from"../../core/config.js";import{emit as S,InputError as d,isJsonOutput as A}from"../../core/io.js";import{renderResult as L}from"../../core/render.js";import{addScopedApiToken as E,getApiToken as y,setApiToken as I}from"../../core/secrets.js";import{SCOPE_VOCABULARY as k,validateScopes as O}from"../../core/scopes.js";import{envCrtrdTcp as D,envCrtrdToken as b,envNodeId as N}from"../../shared/env.js";import{shellQuote as c}from"../../shared/shell-quote.js";import{cliClient as v}from"../api-client.js";const q="127.0.0.1:8787",T=3e4,M=100;function C(){return $(32).toString("hex")}o(C,"token");function U(e){if(e===void 0)return;const t=String(e).split(",").map(n=>n.trim()).filter(n=>n!=="");if(t.length===0)throw new d({error:"empty_scopes",message:"--scopes names no scope",received:e,field:"scopes",next:`Pass a comma-separated list from ${k.join(", ")}, or prefix:operand; omit the flag for the owner token.`});return O(t)}o(U,"parseScopes");function j(e){A()?S(e):process.stdout.write(L(e,H.help)+`
|
|
2
|
+
`)}o(j,"printCredentials");function W(e){const t=e.lastIndexOf(":");return t===-1||Number(e.slice(t+1))!==0?e:`${e.slice(0,t+1)}8787`}o(W,"fixedTcpPort");async function B(){const e=v(),t=Date.now()+T;let n;for(;Date.now()<t;){try{const r=await e.status();if(r.listeners.tcp!==null)return r}catch(r){n=r}await new Promise(r=>setTimeout(r,M))}throw new d({error:"listener_unavailable",message:`the successor did not expose a TCP listener within ${T}ms${n instanceof Error?`: ${n.message}`:""}`,next:"Run `crtr sys daemon status` to inspect the daemon, then retry `crtr sys connect`."})}o(B,"awaitTcpListener");async function P(e,t){const n=await e.getModelAuthReadiness(t);if(n.credential==="ready")return;process.stderr.write(`Logging in to ${n.provider} for ${n.model}.
|
|
3
|
+
`);const{loginProvider:r,openProviderLoginRuntime:f,terminalAuthInteraction:i}=await import("./provider-login.js");await r(await f(),n.provider,i());const s=await e.getModelAuthReadiness(t);if(s.credential!=="ready")throw new d({error:"provider_login_incomplete",message:`provider ${s.provider} credential is ${s.credential} after login`,next:`Run \`${F(t)}\` again and complete the provider login.`})}o(P,"ensureProviderCredential");function F(e){return["crtr sys connect",e.profile===void 0?void 0:`--profile ${c(e.profile)}`,e.cwd===void 0?void 0:`--cwd ${c(e.cwd)}`,e.kind===void 0?void 0:`--kind ${c(e.kind)}`,e.model===void 0?void 0:`--model ${c(e.model)}`].filter(t=>t!==void 0).join(" ")}o(F,"connectCommand");const H=R({name:"connect",description:"enable browser access to this daemon",whenToUse:"an application needs a browser-reachable daemon URL and bearer token; this enables the local TCP listener and reports the credentials to paste into that application. Add --scopes when that application should hold less than the owner.",help:{name:"sys connect",summary:"enable authenticated browser access and log in to the selected model provider when needed; a handover reports browser credentials before it begins",params:[{kind:"flag",name:"scopes",type:"string",required:!1,constraint:`Comma-separated list from ${k.join(", ")}, or prefix:operand. Mints a NEW bearer token whose list is the ceiling for every scope-gated daemon operation it performs and every node it creates; owner-only operations (daemon restart, attach, broker internals) refuse it. Omit for the owner token, which has no ceiling.`},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile setting selecting the provider a root launch with these settings would use."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Directory setting selecting the provider a root launch with these settings would use. Default: this directory."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Node kind setting selecting the provider a root launch with these settings would use."},{kind:"flag",name:"model",type:"string",required:!1,constraint:"Model setting selecting the provider a root launch with these settings would use."}],output:[{name:"base_url",type:"string",required:!0,constraint:"The HTTP URL configured for the TCP listener."},{name:"token",type:"string",required:!0,constraint:"The bearer token accepted by that listener: the owner token, or with --scopes the newly minted scoped token. Paste it only into the application that needs daemon access."}],outputKind:"object",effects:["Stores a missing listener address in user settings and a missing owner bearer token in the 0600 user secrets store.","With --scopes, appends a new scoped token to that secrets store; there is no revoke verb yet, so removing one means editing the store by hand.","When the listener is not already up with both settings present, or a scoped token was minted (the daemon reads tokens only at boot), asks the daemon to hand over to a successor.","After the listener is available, resolves the provider selected by these params (default: this directory) and runs its existing OAuth login flow when that provider has no usable credential."]},run:o(async e=>{const t={profile:e.profile,cwd:x.resolve(e.cwd??process.cwd()),kind:e.kind,model:e.model},n=U(e.scopes),r=N(),i=w("user").api.tcp,s=y("user"),a=v(),h=await a.status();i===""?g("user",u=>{u.api.tcp=q}):Number(i.slice(i.lastIndexOf(":")+1))===0&&g("user",u=>{u.api.tcp=W(i)});const l=D()??w("user").api.tcp;if(Number(l.slice(l.lastIndexOf(":")+1))===0)throw new d({error:"dynamic_tcp_port",message:"CRTRD_TCP must use a fixed port for `crtr sys connect`",next:"Set CRTRD_TCP to a host and fixed port, then run `crtr sys connect` again."});if(n===void 0&&i!==""&&s!==void 0&&h.listeners.tcp!==null)return await P(a,t),{base_url:`http://${h.listeners.tcp}`,token:b()??s};s===void 0&&I(C(),"user");const m=b()??y("user");if(m===void 0)throw new Error("TCP listener cannot start without a bearer token");let p=m;n!==void 0&&(p=C(),E({token:p,scopes:n,created:new Date().toISOString()},"user")),j({base_url:`http://${l}`,token:p}),await a.restartDaemon(),await B(),await P(a,t),r!==void 0&&await a.sendMessage(r,{body:"Browser access is ready.",tier:"urgent",from:r})},"run")});export{H as sysConnectLeaf};
|
|
@@ -140,5 +140,7 @@ export declare function spawnChild(opts: SpawnChildOpts): Promise<SpawnChildResu
|
|
|
140
140
|
export declare function invokeBirthLifecycleHooks(meta: NodeMeta): Promise<void>;
|
|
141
141
|
/** Create an independent, idle root from a node's conversation and durable
|
|
142
142
|
* context. Runtime/graph state is intentionally not inherited. */
|
|
143
|
-
|
|
143
|
+
/** Fork `sourceId` into a fresh root. `scopes` is the fork's allow-list; omit
|
|
144
|
+
* for a root with no ceiling. */
|
|
145
|
+
export declare function forkNode(sourceId: string, scopes?: string[]): Promise<SpawnChildResult>;
|
|
144
146
|
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var Q=Object.defineProperty;var
|
|
1
|
+
var Q=Object.defineProperty;var c=(e,o)=>Q(e,"name",{value:o,configurable:!0});import{accessSync as W,constants as A,cpSync as Y,readdirSync as D,existsSync as Z,rmSync as ee}from"node:fs";import{isAbsolute as ne,resolve as oe,join as g}from"node:path";import{spawnNode as re,currentNodeContext as ie,rootOfSpine as te,newNodeId as de,preflightNodeId as ae}from"./nodes.js";import{resolveProfileOperand as se}from"../profiles/manifest.js";import{selectProfileForCwd as ce,selectProfileForCwdReadOnly as le}from"../profiles/select.js";import{buildLaunchSpecAsync as M,buildPiArgv as fe}from"./launch.js";import{createManagedWorktreeAsync as ue,rollbackManagedWorktreeAsync as me}from"../worktree-mutation-async.js";import{usage as f,brokerLaunchFailed as E}from"../errors.js";import{writeGoal as he}from"./kickoff.js";import{appendSituationalContext as we,formatSituationalProse as pe}from"./situational-context.js";import{hasRoadmap as ke,seedRoadmap as _e}from"./roadmap.js";import{buildWakeBearings as ye}from"./bearings.js";import{encodeKickoffOrigin as ve,KICKOFF_ORIGIN_ENV as ge}from"./stamp/protocol.js";import{beginExecution as be,canonicalSessionFile as I,contextDir as B,crtrHome as xe,findNodeBySessionFile as Fe,getNode as w,fullName as H,jobDir as Oe,nodeDir as Ce,recordExecutionPid as Se,abortExecution as P,reportsDir as $e,setFrozen as Ee}from"../canvas/index.js";import{boundFleet as Re,brokerThresholdsForDaemon as De}from"./fleet.js";import{emitEvent as Ie}from"../events/emit.js";import{openViewerWindow as Be,focusOf as Pe,windowOfPane as je}from"./placement.js";import{waitForBrokerViewSocket as Ne}from"./placement-tmux.js";import{transition as b}from"./lifecycle.js";import{beginBootFaultAttempt as Te}from"../canvas/recovery.js";import{piSessionsRoot as Le}from"./pi-vendored.js";import{headlessBrokerHost as j}from"./host.js";import{ensureDaemon as We}from"../../daemon/manage.js";import{readConfig as Ae}from"../config.js";import{modelRequestFromConfig as Me,unregisteredConcreteModel as He}from"../model-routes.js";import{openModelRegistry as Ke}from"./model-registry.js";import{MODEL_SPEC_NEXT as ze}from"./model-selection.js";import{initializeForkContextExposure as qe}from"../substrate/injected-store.js";import{discoverLifecycleHookRegistry as Ue}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as Ve}from"../command-hooks/transport/exec-lifecycle.js";function x(e){return{detail:{schema:"crtr.node-outcome-detail/v1",message:e instanceof Error?e.message:String(e)}}}c(x,"launchFailureOutcome");function Xe(e,o,r){try{return j.launch(e,o,r)}catch(i){try{b(e,"crash",{reason:"launch_failed",outcome:x(i)})}catch{}const d=i.message;throw E(d.startsWith("[broker]")?d:`[broker] refusing to launch ${e}: ${d}`,"Inspect the cause above, fix the launch recipe or environment, then retry.")}}c(Xe,"launchBrokerOrCrash");function Ge(e,o){try{b(e,"crash",{reason:"launch_failed",outcome:x(o)})}catch{}try{j.teardown(e)}catch{}}c(Ge,"crashAndTearDown");function Je(e){const o=Le();if(!Z(o))throw f(`--fork-from '${e}': no pi sessions store at ${o} to resolve it from \u2014 pass a node id or an absolute .jsonl path.`,{next:"Pass a live node id, an absolute .jsonl session path, or drop --fork-from."});const r=[];for(const a of D(o,{withFileTypes:!0})){if(!a.isDirectory())continue;const l=g(o,a.name);for(const s of D(l)){if(!s.endsWith(".jsonl"))continue;const F=s.slice(0,-6),u=F.slice(F.lastIndexOf("_")+1);r.push({id:u,path:g(l,s)})}}const i=r.filter(a=>a.id===e),d=i.length>0?i:r.filter(a=>a.id.startsWith(e));if(d.length===0)throw f(`--fork-from '${e}': no pi session with that id found.`,{next:"Pass a live node id, a real session uuid/prefix, or an absolute .jsonl session path."});if(d.length>1)throw f(`--fork-from '${e}': ambiguous \u2014 ${d.length} sessions match that id prefix; pass the full id or the .jsonl path.`,{next:"Pass the full session uuid or an absolute .jsonl path to disambiguate."});return I(d[0].path)}c(Je,"resolveSessionUuid");function K(e){const o=e.trim();if(o==="")throw f("--fork-from requires a node id, session file, or session uuid.",{next:"Pass a live node id, an absolute .jsonl session path, or a session uuid/prefix."});if(o.includes("/")||o.endsWith(".jsonl"))return I(ne(o)?o:oe(process.cwd(),o));const r=w(o);if(r!==null){const i=r.pi_session_file;if(i==null||i==="")throw f(`node ${o} has no pi session yet \u2014 it has not captured a session FILE to fork from (the broker forks a .jsonl path, not a bare id).`,{next:"Pick a node that has captured a session, or drop --fork-from."});return I(i)}return Je(o)}c(K,"resolveForkSource");async function Qe(e,o,r,i={}){return e===null?null:e!==void 0&&e!==""?se(e).profileId:o!==null?w(o)?.profile_id??null:i.readOnlySelection===!0?le(r):ce(r)}c(Qe,"resolveProfileId");function z(e){const o=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return Me(e.model,o,e.modelExact===!0)}c(z,"requestFromLaunch");async function q(e,o,r){if(e===void 0||e.kind==="logical")return;const i=He(e,r??await Ke(),o);if(i!==void 0)throw f(`model '${i.request.spec}' is not registered in this daemon.`,{received:i.request.spec,next:ze})}c(q,"assertLaunchModelRegistered");function Ye(e,o,r){const i=e??o??null;if(!r&&i===null)throw new Error("spawnChild requires a calling node (CRTR_NODE_ID) or an explicit parent");return i}c(Ye,"resolveSpawner");async function U(e,o,r){try{We()}catch{}const i=ie(),d=e.root===!0,a=Ye(e.parent,i.nodeId??null,d),l=e.mode??"base",s=e.rootLifecycle??(d?"resident":"terminal"),F=d&&s==="resident",u=e.worktree===!0,p=u?e.nodeId??de():e.nodeId;e.nodeId!==void 0&&ae(e.nodeId);const k=await Qe(e.profile,a,e.cwd),N=(await M(e.kind,l,{lifecycle:s,hasManager:!d,model:e.model,modelOverride:e.model!==void 0,cwd:e.cwd,profileId:k})).launch;await q(z(N),{cwd:e.cwd,profileId:k});const O=e.forkFrom!==void 0?K(e.forkFrom):void 0,X=O===void 0?null:Fe(O)?.node_id??null;let m,C=e.cwd;try{u&&(m=await ue(e.worktreeCwd??e.cwd,p,e.worktreeBase),C=m.path);const _=u?(await M(e.kind,l,{lifecycle:s,hasManager:!d,model:e.model,modelOverride:e.model!==void 0,cwd:C,profileId:k})).launch:N;u&&await q(z(_),{cwd:C,profileId:k});const n=await re({kind:e.kind,mode:l,lifecycle:s,cwd:C,name:e.name??e.kind,description:e.description,parent:d?null:a,creator:e.creator,spawnedBy:d?a??void 0:void 0,profile_id:k,...e.scopes===void 0?{}:{scopes:e.scopes},forkFrom:e.forkFrom,forkSourceFile:O,modelOverride:e.model===void 0?void 0:_.model,...m!==void 0?{managedWorktree:m}:{},...p!==void 0?{nodeId:p}:{},...e.reviewBinding===void 0?{}:{reviewBinding:e.reviewBinding},...e.deadlineAt===void 0?{}:{deadlineAt:e.deadlineAt},...e.outcomeDelivery===void 0?{}:{outcomeDelivery:e.outcomeDelivery},launch:_});if(r?.(n.node_id),e.forkFrom!==void 0)try{qe(n.node_id,X)}catch(t){throw b(n.node_id,"crash",{reason:"launch_failed",outcome:x(t)}),t}if(he(n.node_id,e.prompt),e.situationalContext!==void 0&&we(n.node_id,pe(e.situationalContext)),o!==void 0)try{o(n.node_id)}catch(t){throw b(n.node_id,"crash",{reason:"launch_failed",outcome:x(t)}),t}const R=e.prompt!=="",G=`${R&&e.wakeOrigin!==void 0?`${ye(e.wakeOrigin)}
|
|
2
2
|
|
|
3
|
-
`:""}${e.prompt}`;
|
|
3
|
+
`:""}${e.prompt}`;l==="orchestrator"&&!ke(n.node_id)&&_e(n.node_id,{goal:e.prompt.trim()}),await Ze(n);const h=fe(n,{prompt:G,forkFrom:O});if(h.env={...h.env,CRTR_SUBTREE:te(n.node_id)},R){const t=e.wakeOrigin!==void 0?{type:"runtime",kind:"wake",wake:e.wakeOrigin}:{type:"runtime",kind:"kickoff",...n.spawned_by!=null&&n.spawned_by!==""?{spawnedBy:n.spawned_by}:{}};h.env[ge]=ve(t)}const S=Re();if(!S.reserveSlot(n.node_id))return Ee(n.node_id,new Date().toISOString()),Ie({level:"info",event:"broker.frozen",node_id:n.node_id,fields:{live_count:S.size(),hard_cap:De().automaticReviveCap}}),{node:w(n.node_id),frozen:!0,window:null,session:null};const J=Te(n.node_id),y=be(n.node_id,{daemonEpoch:S.epoch()});h.env={...h.env,CRTR_EXECUTION_ID:y};let $;try{$=Xe(n.node_id,h,{cwd:n.cwd,name:H(n),resuming:!1})}catch(t){throw P(y,{reason:"launch_failed"}),t}finally{S.releaseReservation(n.node_id)}if($.pid===null){const t=new Error(`broker host returned no pid for ${n.node_id}`);throw P(y,{reason:"launch_failed"}),b(n.node_id,"crash",{reason:"launch_failed",outcome:x(t)}),E(`failed to launch the broker engine for ${n.node_id} (${n.name}) \u2014 the node was not started.`,`Inspect canonical event diagnostics with \`crtr sys logs --node ${n.node_id}\`, then verify the pi engine can start in this environment.`)}if(Se(y,n.node_id,$.pid)==="shutdown")throw P(y,{reason:"superseded"}),j.teardown(n.node_id),E(`broker execution was superseded for ${n.node_id}`,"Retry spawning the node.");if(F&&e.readiness==="await"&&!await Ne(n.node_id,$.exited)){const t=J.explanation();throw Ge(n.node_id,new Error(t??`broker view socket never bound for ${n.node_id}`)),E(t!==null?`the root broker ${n.node_id} could not start \u2014 ${t}`:`the root broker ${n.node_id} never bound its view socket \u2014 it was not started.`,t!==null?"Resolve the cause named above, then retry.":`The broker engine exited before binding its view socket. Inspect canonical event diagnostics with \`crtr sys logs --node ${n.node_id}\`, then retry.`)}let T=null,L=null;if(!d&&a!==null&&Ae("user").auto_open_child_viewers)try{const t=Pe(a);if(t!==null&&t.pane!==null&&t.pane!==""){const v=Be(n.node_id,t.session??"",{name:H(n),cwd:e.cwd,besidePane:t.pane,waitForBroker:!0,initialWhip:R});v!==null&&v.pane!==null&&v.pane!==""&&(L=v.session,T=je(v.pane))}}catch{}return{node:w(n.node_id),frozen:!1,window:T,session:L}}catch(_){throw m!==void 0&&p!==void 0&&w(p)===null&&await me(m),_}}c(U,"spawnChildPrepared");async function Pn(e){return U(e)}c(Pn,"spawnChild");async function Ze(e){const o=Ue(e.cwd,e.profile_id).plans.get("node:start")??[];await Ve(o,{node:{id:e.node_id,name:e.name,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,cwd:e.cwd,nodeDir:Ce(e.node_id),contextDir:B(e.node_id),jobDir:Oe(e.node_id),reportsDir:$e(e.node_id)},runtime:{isBirth:!0,canvasHome:xe(),profile:e.profile_id??null}})}c(Ze,"invokeBirthLifecycleHooks");const V=new Set(["initial-prompt.md","yield-message.md","result.json"]);async function jn(e,o){const r=w(e);if(r===null)throw f(`unknown node: ${e}`,{received:e,next:"List nodes and pass the id of a node with a captured pi session."});const i=K(e),d=B(e);try{W(i,A.R_OK),W(d,A.R_OK)}catch(a){throw new Error(`cannot fork ${e}: its conversation or context is unreadable \u2014 ${a.message}`)}return U({kind:r.kind,mode:"base",cwd:r.cwd,prompt:"",root:!0,readiness:"await",forkFrom:e,profile:r.profile_id,model:r.model_override??void 0,...o===void 0?{}:{scopes:o}},a=>{const l=B(a);for(const s of D(d,{withFileTypes:!0}))V.has(s.name)||Y(g(d,s.name),g(l,s.name),{recursive:!0,dereference:!1,force:!0});for(const s of V)ee(g(l,s),{recursive:!0,force:!0})})}c(jn,"forkNode");export{q as assertLaunchModelRegistered,jn as forkNode,Ze as invokeBirthLifecycleHooks,K as resolveForkSource,Qe as resolveProfileId,Ye as resolveSpawner,Pn as spawnChild,U as spawnChildPrepared};
|
package/dist/core/scopes.d.ts
CHANGED
|
@@ -8,8 +8,10 @@ export declare function assertScope(meta: {
|
|
|
8
8
|
node_id: string;
|
|
9
9
|
scopes?: readonly string[] | null;
|
|
10
10
|
}, scope: string): void;
|
|
11
|
+
/** `holder` names who lacks the scope in prose — `node <id>` for a node's own
|
|
12
|
+
* allow-list, `the bearer token` for a scoped API token's ceiling. */
|
|
11
13
|
export declare class ScopeDeniedError extends Error {
|
|
12
|
-
readonly
|
|
14
|
+
readonly holder: string;
|
|
13
15
|
readonly scope: string;
|
|
14
|
-
constructor(
|
|
16
|
+
constructor(holder: string, scope: string);
|
|
15
17
|
}
|
package/dist/core/scopes.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var p=Object.defineProperty;var t=(r,e)=>p(r,"name",{value:e,configurable:!0});import{scopeAllowed as n}from"../shared/env.js";import{InputError as c}from"./io.js";const d=["ask","llm","act","schedule","memory:read","memory:write","net"],s=new Set(d),i=/^[a-z][a-z0-9-]*:[^\s]+$/;function
|
|
1
|
+
var p=Object.defineProperty;var t=(r,e)=>p(r,"name",{value:e,configurable:!0});import{scopeAllowed as n}from"../shared/env.js";import{InputError as c}from"./io.js";const d=["ask","llm","act","schedule","memory:read","memory:write","net"],s=new Set(d),i=/^[a-z][a-z0-9-]*:[^\s]+$/;function E(r){if(!Array.isArray(r)||r.some(e=>typeof e!="string"||!s.has(e)&&!i.test(e))){const e=Array.isArray(r)?r.find(o=>typeof o!="string"||!s.has(o)&&!i.test(o)):r;throw new c({error:"unknown_scope",message:`unknown scope: ${String(e)}`,received:e,field:"scopes",next:"Use a runtime scope name or a parameterized scope in prefix:operand form."})}return[...r]}t(E,"validateScopes");function A(r,e){if(!n(r.scopes,e))throw new a(`node ${r.node_id}`,e)}t(A,"assertScope");class a extends Error{static{t(this,"ScopeDeniedError")}holder;scope;constructor(e,o){super(`${e} does not hold the ${o} scope`),this.holder=e,this.scope=o,this.name="ScopeDeniedError"}}export{d as SCOPE_VOCABULARY,a as ScopeDeniedError,A as assertScope,n as scopeAllowed,E as validateScopes};
|
package/dist/core/secrets.d.ts
CHANGED
|
@@ -9,6 +9,15 @@ import type { Scope } from '../types.js';
|
|
|
9
9
|
export interface SecretsStore {
|
|
10
10
|
relayTokens: Record<string, string>;
|
|
11
11
|
apiToken?: string;
|
|
12
|
+
scopedApiTokens?: ScopedApiToken[];
|
|
13
|
+
}
|
|
14
|
+
/** A bearer token minted by `crtr sys connect --scopes` whose `scopes` list is
|
|
15
|
+
* the ceiling for every scope-gated daemon operation the holder performs. The
|
|
16
|
+
* owner token (`apiToken`) has no ceiling. */
|
|
17
|
+
export interface ScopedApiToken {
|
|
18
|
+
token: string;
|
|
19
|
+
scopes: string[];
|
|
20
|
+
created: string;
|
|
12
21
|
}
|
|
13
22
|
export declare function readSecrets(scope: Scope): SecretsStore;
|
|
14
23
|
/** Writes secrets.json directly (never via `fs-utils.writeJson`, which has no
|
|
@@ -21,6 +30,8 @@ export declare function setRelayToken(ref: string, token: string, scope: Scope):
|
|
|
21
30
|
export declare function deleteRelayToken(ref: string, scope: Scope): void;
|
|
22
31
|
export declare function getApiToken(scope: Scope): string | undefined;
|
|
23
32
|
export declare function setApiToken(token: string, scope: Scope): void;
|
|
33
|
+
export declare function listScopedApiTokens(scope: Scope): ScopedApiToken[];
|
|
34
|
+
export declare function addScopedApiToken(entry: ScopedApiToken, scope: Scope): void;
|
|
24
35
|
/** A short, non-reversible display fingerprint of a raw token — safe to print
|
|
25
36
|
* in `canvas config list`/`show` output. NEVER derive this from anything
|
|
26
37
|
* that could round-trip back to the token, and never log/return the raw
|
package/dist/core/secrets.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,{mode:384}),
|
|
1
|
+
var k=Object.defineProperty;var t=(e,o)=>k(e,"name",{value:o,configurable:!0});import{createHash as T}from"node:crypto";import{existsSync as f,readFileSync as u,writeFileSync as y,chmodSync as d}from"node:fs";import{join as l}from"node:path";import{scopeRoot as A,requireScopeRoot as S}from"./scope.js";import{ensureDir as m}from"./fs-utils.js";const x="secrets.json";function a(e){return l(e,x)}t(a,"secretsPathFor");function s(e){const o=A(e);if(!o)return{relayTokens:{}};const n=a(o);if(!f(n))return{relayTokens:{}};try{const r=JSON.parse(u(n,"utf8")),c=Array.isArray(r.scopedApiTokens)?r.scopedApiTokens.filter(i=>typeof i?.token=="string"&&i.token!==""&&Array.isArray(i.scopes)&&typeof i.created=="string"):[];return{relayTokens:r.relayTokens??{},...typeof r.apiToken=="string"&&r.apiToken!==""?{apiToken:r.apiToken}:{},...c.length>0?{scopedApiTokens:c}:{}}}catch{return{relayTokens:{}}}}t(s,"readSecrets");function p(e,o){const n=S(e);m(n);const r=a(n);y(r,JSON.stringify(o,null,2)+`
|
|
2
|
+
`,{mode:384}),d(r,384)}t(p,"writeSecrets");function w(e,o){return s(o).relayTokens[e]}t(w,"getRelayToken");function J(e,o,n){const r=s(n);r.relayTokens[e]=o,p(n,r)}t(J,"setRelayToken");function N(e,o){const n=s(o);delete n.relayTokens[e],p(o,n)}t(N,"deleteRelayToken");function O(e){return s(e).apiToken}t(O,"getApiToken");function q(e,o){const n=s(o);n.apiToken=e,p(o,n)}t(q,"setApiToken");function C(e){return s(e).scopedApiTokens??[]}t(C,"listScopedApiTokens");function D(e,o){const n=s(o);n.scopedApiTokens=[...n.scopedApiTokens??[],e],p(o,n)}t(D,"addScopedApiToken");function H(e){return T("sha256").update(e).digest("hex").slice(0,12)}t(H,"fingerprint");export{D as addScopedApiToken,N as deleteRelayToken,H as fingerprint,O as getApiToken,w as getRelayToken,C as listScopedApiTokens,s as readSecrets,q as setApiToken,J as setRelayToken,p as writeSecrets};
|
|
@@ -37,6 +37,10 @@ export interface RemoteAttachHeartbeat {
|
|
|
37
37
|
noteActivity: () => void;
|
|
38
38
|
}
|
|
39
39
|
export declare function startRemoteAttachHeartbeat(ws: WebSocket, intervalMs?: number, quietMs?: number): RemoteAttachHeartbeat;
|
|
40
|
+
/** Refuse an upgrade PRE-handshake: write a plain HTTP error (uniform API
|
|
41
|
+
* `ErrorBody`, spec §8) to the raw socket and destroy it. Never throws — a
|
|
42
|
+
* reset peer is swallowed. Shared with server.ts's auth and startup gates. */
|
|
43
|
+
export declare function refuseUpgrade(socket: Duplex, status: number, statusText: string, code: string, message: string): void;
|
|
40
44
|
/** Handle the HTTP upgrade for `GET /v1/nodes/{id}/attach`. A-9 matches the
|
|
41
45
|
* route, extracts `{id}`, and calls this with the raw upgrade triplet; this
|
|
42
46
|
* owns the WS handshake (its own noServer `wss` above) AND the spec §5.3
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var A=Object.defineProperty;var c=(r,e)=>A(r,"name",{value:e,configurable:!0});import{createConnection as E}from"node:net";import{WebSocketServer as _}from"ws";import{isSafeNodeId as g,viewSocketPath as b}from"../../core/canvas/paths.js";import{getNode as w}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as S,CLIENT_READ_CAPS as $,FrameDecoder as R,FrameOverflowError as T}from"../../core/runtime/broker-protocol.js";import{classify as v}from"../../core/fault-classifier.js";import{clearFault as h,recordFault as p}from"../../core/canvas/recovery.js";import{decideAttachRevive as
|
|
1
|
+
var A=Object.defineProperty;var c=(r,e)=>A(r,"name",{value:e,configurable:!0});import{createConnection as E}from"node:net";import{WebSocketServer as _}from"ws";import{isSafeNodeId as g,viewSocketPath as b}from"../../core/canvas/paths.js";import{getNode as w}from"../../core/canvas/index.js";import{BROKER_READ_CAPS as S,CLIENT_READ_CAPS as $,FrameDecoder as R,FrameOverflowError as T}from"../../core/runtime/broker-protocol.js";import{classify as v}from"../../core/fault-classifier.js";import{clearFault as h,recordFault as p}from"../../core/canvas/recovery.js";import{decideAttachRevive as x}from"../../core/runtime/revive.js";import{probeViewSocket as C,waitForBrokerViewSocket as P}from"../../core/runtime/view-socket.js";const B=new _({noServer:!0,maxPayload:S.maxLineBytes}),N=25e3,O=6e4;function F(r,e=N,s=O){let t=!0,a=!1,n=Date.now();const f=c(()=>{t=!0},"acknowledge"),u=c(()=>{a||(a=!0,clearInterval(m),r.off("pong",f))},"stop");r.on("pong",f);const m=setInterval(()=>{if(Date.now()-n>s){t=!0;return}if(!t){u(),r.terminate();return}t=!1;try{r.ping()}catch{u(),r.terminate()}},e);return m.unref(),{stop:u,noteActivity:c(()=>{n=Date.now()},"noteActivity")}}c(F,"startRemoteAttachHeartbeat");function k(r){const e=Buffer.from(r,"utf8");return e.byteLength<=123?r:e.subarray(0,123).toString("utf8")}c(k,"clampReason");function d(r,e,s,t,a){const n=JSON.stringify({error:{code:t,message:a}}),f=`HTTP/1.1 ${e} ${s}\r
|
|
2
2
|
content-type: application/json; charset=utf-8\r
|
|
3
3
|
content-length: ${Buffer.byteLength(n)}\r
|
|
4
4
|
connection: close\r
|
|
5
5
|
\r
|
|
6
6
|
`+n;try{r.end(f)}catch{try{r.destroy()}catch{}}}c(d,"refuseUpgrade");function U(r){if(new URL(r.url??"/","http://127.0.0.1").searchParams.get("revive")==="0")return!0;const s=r.headers["x-crtr-attach-revive"];return(Array.isArray(s)?s[0]:s)==="0"}c(U,"reviveOptedOut");function D(r){return new URL(r.url??"/","http://127.0.0.1").searchParams.get("wakeParked")==="1"}c(D,"wakeParkedRequested");function L(r,e){if(!g(e)){try{r.close(1008,k(`invalid node id: ${e}`))}catch{}return}if(w(e)===null){try{r.close(1011,k(`no node ${e}`))}catch{}return}h(e,{link:"viewer\u2194crtrd"});const s=b(e),t=E(s),a=new R($);let n=!1,f=!1,u;t.on("connect",()=>{h(e,{link:"relay\u2194broker"})});const m=c((o,l)=>{if(!n){if(n=!0,u?.stop(),!t.destroyed)try{t.destroy()}catch{}try{r.close(o,k(l))}catch{}}},"teardown");u=F(r),t.on("data",o=>{u?.noteActivity();let l;try{l=a.push(o)}catch(i){const y=i instanceof T?`broker sent an oversized frame (${i.message})`:`failed to decode a broker frame for ${e}`;m(1009,y);return}for(const i of l)r.readyState===r.OPEN&&r.send(JSON.stringify(i))}),t.on("error",o=>{const l=v("relay\u2194broker",o);p(e,{link:"relay\u2194broker",op:"bridge socket",kind:l.kind,retry:{disposition:"manual"},message:o.message});const i=o.code==="ECONNREFUSED"||o.code==="ENOENT"?`no running broker for ${e}`:`view socket error for ${e}: ${o.message}`;m(1011,i)}),t.on("close",o=>{n||o||(h(e,{link:"relay\u2194broker"}),m(1e3,"broker socket closed"))}),r.on("close",(o,l)=>{const i=l.toString("utf8");if(n){o===1e3&&h(e,{link:"viewer\u2194crtrd"});return}if(o===1e3)h(e,{link:"viewer\u2194crtrd"});else if(!f){f=!0;const y=v("viewer\u2194crtrd",{code:o,reason:i});p(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:y.kind,retry:{disposition:"auto"},message:i||`remote attach closed (${o})`})}m(o,i||`remote attach closed (${o})`)}),r.on("error",()=>{if(!f){f=!0;const o=v("viewer\u2194crtrd",{code:1006,reason:"remote attach websocket error"});p(e,{link:"viewer\u2194crtrd",op:"remote attach websocket",kind:o.kind,retry:{disposition:"auto"},message:"remote attach websocket error"})}m(1011,"remote attach websocket error")}),r.on("message",o=>{if(n||t.destroyed)return;u?.noteActivity();const i=(Array.isArray(o)?Buffer.concat(o):Buffer.isBuffer(o)?o:Buffer.from(new Uint8Array(o))).toString("utf8"),y=i.endsWith(`
|
|
7
7
|
`)?i:i+`
|
|
8
|
-
`;try{t.write(y)}catch{}})}c(L,"bridgeConnection");async function X(r,e,s,t){e.on("error",()=>{});try{if(!g(t)){d(e,400,"Bad Request","invalid_node_id",`invalid node id: ${t}`);return}if(w(t)===null){d(e,404,"Not Found","node_not_found",`no node ${t}`);return}if(U(r)){if(!await
|
|
8
|
+
`;try{t.write(y)}catch{}})}c(L,"bridgeConnection");async function X(r,e,s,t){e.on("error",()=>{});try{if(!g(t)){d(e,400,"Bad Request","invalid_node_id",`invalid node id: ${t}`);return}if(w(t)===null){d(e,404,"Not Found","node_not_found",`no node ${t}`);return}if(U(r)){if(!await C(b(t),250)){d(e,409,"Conflict","node_dormant",`node ${t} is dormant and revive was opted out (?revive=0)`);return}}else{let a;try{const n=await x(t,{resume:!0,capacity:"refuse"},D(r));if(n.parked){d(e,409,"Conflict","node_dormant",`node ${t} is parked; attach with ?wakeParked=1 to wake it`);return}a=n.result.launch?.exited}catch(n){const f=n instanceof Error?n.message:String(n);d(e,409,"Conflict","attach_revive_failed",`attach revive failed: ${f}`);return}if(!await P(t,a)){d(e,409,"Conflict","broker_not_ready",`broker view socket for ${t} did not become listenable after revive`);return}}if(e.destroyed)return;B.handleUpgrade(r,e,s,a=>L(a,t))}catch(a){const n=a instanceof Error?a.message:String(a);d(e,500,"Internal Server Error","internal",`attach upgrade failed: ${n}`)}}c(X,"handleAttachUpgrade");export{N as REMOTE_ATTACH_HEARTBEAT_MS,O as REMOTE_ATTACH_QUIET_MS,X as handleAttachUpgrade,d as refuseUpgrade,F as startRemoteAttachHeartbeat};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RouteTable } from '../router.js';
|
|
2
2
|
export declare const attachRoutes: RouteTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var u=(t,e)=>c(t,"name",{value:e,configurable:!0});import{getNode as m}from"../../../core/canvas/canvas.js";import{decideAttachRevive as k}from"../../../core/runtime/revive.js";import{viewSocketPath as l}from"../../../core/canvas/paths.js";import{waitForBrokerViewSocket as v}from"../../../core/runtime/placement-tmux.js";import{notFound as p,usage as n}from"../../../core/errors.js";function
|
|
1
|
+
var c=Object.defineProperty;var u=(t,e)=>c(t,"name",{value:e,configurable:!0});import{getNode as m}from"../../../core/canvas/canvas.js";import{decideAttachRevive as k}from"../../../core/runtime/revive.js";import{viewSocketPath as l}from"../../../core/canvas/paths.js";import{waitForBrokerViewSocket as v}from"../../../core/runtime/placement-tmux.js";import{notFound as p,usage as n}from"../../../core/errors.js";import{ownerOnly as w}from"../router.js";function h(t){if(t==null)return{resume:!0,revive:!0,wakeParked:!0};if(typeof t!="object")throw n("attach body must be a JSON object { resume?, revive?, wakeParked? }");const e=t.resume,r=t.revive,o=t.wakeParked;if(e!==void 0&&typeof e!="boolean")throw n("resume must be a boolean");if(r!==void 0&&typeof r!="boolean")throw n("revive must be a boolean");if(o!==void 0&&typeof o!="boolean")throw n("wakeParked must be a boolean");return{resume:e??!0,revive:r??!0,wakeParked:o??!0}}u(h,"parseAttachBody");async function b(t){const e=t.params.id;if(m(e)===null)throw p(`unknown node: ${e}`,{received:e});const{resume:r,revive:o,wakeParked:f}=h(t.body);let i=!1,d=!1,s=!1;if(o){const a=await k(e,{resume:r,capacity:"refuse"},f);s=a.parked,a.parked||(i=a.result.launch!==void 0,d=a.result.resumed,i&&await v(e))}return{status:200,body:{node_id:e,socket_path:l(e),revived:i,resumed:d,parked:s}}}u(b,"handleAttach");const F=w([{method:"POST",pattern:"/v1/nodes/:id/attach",handler:b}]);export{F as attachRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var m=Object.defineProperty;var d=(r,t)=>m(r,"name",{value:t,configurable:!0});import{statSync as u}from"node:fs";import{isAbsolute as f}from"node:path";import{ApiError as a}from"../../../api/index.js";import{buildOperationalEnvBase as l}from"../../../core/runtime/spawn-env.js";import{runCommandOnce as p}from"../../../core/runtime/run-command.js";import{readProfileEnvVars as w}from"../../../core/profiles/env-store.js";import{usage as s}from"../../../core/errors.js";const _=60,c=600,y=256*1024;function b(r){if(typeof r!="object"||r===null||Array.isArray(r))throw s("bash requires an object body");const t=r;if(typeof t.command!="string"||t.command==="")throw s("bash requires a non-empty `command`");if(typeof t.cwd!="string"||!f(t.cwd))throw new a(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 a(404,"cwd_not_found",`cwd not found: ${t.cwd}`)}const e=t.timeout_s??_;if(typeof e!="number"||!Number.isFinite(e)||e<=0||e>c)throw new a(422,"invalid_timeout",`timeout_s must be greater than 0 and at most ${c}`);if(t.profile!==void 0&&typeof t.profile!="string")throw s("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 s("env must be an object of string values");n={};for(const[i,o]of Object.entries(t.env)){if(typeof o!="string")throw s("env must be an object of string values");n[i]=o}}return{command:t.command,cwd:t.cwd,timeout_s:e,env:n,profile:t.profile}}d(b,"parseRequest");async function T(r){const t=b(r.body),e=t.profile??null,n=l({targetCwd:t.cwd,targetProfileId:e}),i={...n,...w(e),...t.env,...e===null?{}:{CRTR_PROFILE_ID:e},CRTR_ADMITTED_HOST_PATH:n.CRTR_ADMITTED_HOST_PATH},o=await p({command:t.command,cwd:t.cwd,env:i,timeoutMs:t.timeout_s*1e3,headBytes:y}).completion;if(o.processError!==null)throw o.processError;return{status:200,body:{exit_code:o.timedOut?null:o.exitCode,signal:o.signal,stdout:o.stdout,stdout_truncated:o.stdoutTruncated,stderr:o.stderr,stderr_truncated:o.stderrTruncated,timed_out:o.timedOut,duration_ms:o.durationMs}}}d(T,"handleBash");const C=[{method:"POST",pattern:"/v1/bash",handler:T}];export{C as bashRoutes};
|
|
1
|
+
var m=Object.defineProperty;var d=(r,t)=>m(r,"name",{value:t,configurable:!0});import{statSync as u}from"node:fs";import{isAbsolute as f}from"node:path";import{ApiError as a}from"../../../api/index.js";import{buildOperationalEnvBase as l}from"../../../core/runtime/spawn-env.js";import{runCommandOnce as p}from"../../../core/runtime/run-command.js";import{readProfileEnvVars as w}from"../../../core/profiles/env-store.js";import{usage as s}from"../../../core/errors.js";const _=60,c=600,y=256*1024;function b(r){if(typeof r!="object"||r===null||Array.isArray(r))throw s("bash requires an object body");const t=r;if(typeof t.command!="string"||t.command==="")throw s("bash requires a non-empty `command`");if(typeof t.cwd!="string"||!f(t.cwd))throw new a(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 a(404,"cwd_not_found",`cwd not found: ${t.cwd}`)}const e=t.timeout_s??_;if(typeof e!="number"||!Number.isFinite(e)||e<=0||e>c)throw new a(422,"invalid_timeout",`timeout_s must be greater than 0 and at most ${c}`);if(t.profile!==void 0&&typeof t.profile!="string")throw s("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 s("env must be an object of string values");n={};for(const[i,o]of Object.entries(t.env)){if(typeof o!="string")throw s("env must be an object of string values");n[i]=o}}return{command:t.command,cwd:t.cwd,timeout_s:e,env:n,profile:t.profile}}d(b,"parseRequest");async function T(r){const t=b(r.body),e=t.profile??null,n=l({targetCwd:t.cwd,targetProfileId:e}),i={...n,...w(e),...t.env,...e===null?{}:{CRTR_PROFILE_ID:e},CRTR_ADMITTED_HOST_PATH:n.CRTR_ADMITTED_HOST_PATH},o=await p({command:t.command,cwd:t.cwd,env:i,timeoutMs:t.timeout_s*1e3,headBytes:y}).completion;if(o.processError!==null)throw o.processError;return{status:200,body:{exit_code:o.timedOut?null:o.exitCode,signal:o.signal,stdout:o.stdout,stdout_truncated:o.stdoutTruncated,stderr:o.stderr,stderr_truncated:o.stderrTruncated,timed_out:o.timedOut,duration_ms:o.durationMs}}}d(T,"handleBash");const C=[{method:"POST",pattern:"/v1/bash",scope:"act",handler:T}];export{C as bashRoutes};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RouteTable } from '../router.js';
|
|
2
2
|
export declare const brokerOperationRoutes: RouteTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var F=Object.defineProperty;var r=(t,e)=>F(t,"name",{value:e,configurable:!0});import{canonicalSessionFile as A,findNodeBySessionFile as P,getNode as k,isWarmSpare as T,listNodeIdentities as I,recordSettledTurn as R,setPresence as w,subscriptionsOf as $,updateNode as g,updateNodeTelemetry as q}from"../../../core/canvas/canvas.js";import{recordExecutionPid as C}from"../../../core/canvas/executions.js";import{bindSession as j,readLiveSession as E,transferSession as M}from"../../../core/canvas/sessions.js";import{isSafeNodeId as D}from"../../../core/canvas/paths.js";import{isPidAlive as L}from"../../../core/canvas/pid.js";import{editorLabel as N}from"../../../core/canvas/labels.js";import{assembleNodeSubject as G}from"../../../core/substrate/subject.js";import{withCanvasWrite as l}from"../../../core/canvas/db.js";import{notFound as v,usage as s}from"../../../core/errors.js";import{hasUnseenWakeMail as O}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as J}from"../../../core/runtime/kickoff.js";import{transition as h}from"../../../core/runtime/lifecycle.js";import{focusOf as b,tearDownNode as x}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as W}from"../../../core/runtime/broker/client-registry.js";import{evaluateStop as U}from"../../../core/runtime/stop-guard.js";import{clearFault as m,recordFault as V}from"../../../core/canvas/recovery.js";import{updateModelRecipe as Y}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as z}from"../../review/finish.js";import{emitEvent as H}from"../../../core/events/emit.js";import{consumePendingPark as K,recordPendingParkActivity as Q}from"../../park-pending.js";import{shouldParkAfterSummary as X}from"../../park-activity.js";import{requireCurrentExecution as f}from"./broker-recovery.js";function p(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw s(`${e} requires a JSON body`);return t}r(p,"objectBody");function Z(t){const e=p(t,"broker session-bound");if(typeof e.executionId!="string"||e.executionId==="")throw s("executionId must be a non-empty string");if(typeof e.piSessionId!="string"||e.piSessionId==="")throw s("piSessionId must be a non-empty string");if(e.sessionFile!==null&&typeof e.sessionFile!="string")throw s("sessionFile must be a string or null");if(!Number.isSafeInteger(e.pid)||e.pid<=0)throw s("pid must be a positive safe integer");if(e.reason!==null&&typeof e.reason!="string")throw s("reason must be a string or null");if(!Array.isArray(e.reviewBoundaryIds)||!e.reviewBoundaryIds.every(n=>typeof n=="string"))throw s("reviewBoundaryIds must be an array of strings");return e}r(Z,"parseSessionBoundBody");function ee(t){const e=p(t,"broker settle");if(typeof e.stopReason!="string")throw s("stopReason must be a string");for(const n of["backgroundJobsRunning","pushedFinal"])if(typeof e[n]!="boolean")throw s(`${n} must be a boolean`);return e}r(ee,"parseSettleBody");function te(t){const e=p(t,"broker park-complete");if(e.outcome!=="completed"&&e.outcome!=="failed")throw s("outcome must be completed or failed");return e}r(te,"parseParkCompleteBody");function ne(t){const e=p(t,"broker telemetry");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw s("expected_execution_id must be a non-empty string");if(typeof e.tokens_in!="number"||!Number.isFinite(e.tokens_in)||e.tokens_in<0)throw s("tokens_in must be a non-negative finite number");if(e.context_tokens!==null&&(typeof e.context_tokens!="number"||!Number.isFinite(e.context_tokens)||e.context_tokens<0))throw s("context_tokens must be a non-negative finite number or null");if(e.last_activity!==null&&typeof e.last_activity!="string")throw s("last_activity must be a string or null");if(typeof e.updated_at!="string"||Number.isNaN(Date.parse(e.updated_at)))throw s("updated_at must be an ISO timestamp");return{...e,updated_at:new Date(e.updated_at).toISOString()}}r(ne,"parseTelemetryBody");function ie(t){const e=p(t,"broker model");if(typeof e.spec!="string"||e.spec.trim()==="")throw s("spec must be a non-empty string");if(e.pinnedOverride!==void 0&&typeof e.pinnedOverride!="boolean")throw s("pinnedOverride must be a boolean when present");if(typeof e.userSelected!="boolean")throw s("userSelected must be a boolean");return e}r(ie,"parseModelCommitBody");function _(t){const e=k(t);if(e===null)throw v(`unknown node: ${t}`,{received:t});return e}r(_,"requireNode");function y(t){return{node_id:t.node_id,name:t.name,...t.description===void 0?{}:{description:t.description},...t.title===void 0?{}:{title:t.title},...t.icon===void 0?{}:{icon:t.icon},kind:t.kind,mode:t.mode,lifecycle:t.lifecycle,status:t.status,cwd:t.cwd,parent:t.parent??null,fork_from:t.fork_from??null,profile_id:t.profile_id??null,...t.managed_worktree===void 0?{}:{managed_worktree:t.managed_worktree===null?null:{state:t.managed_worktree.state,...t.managed_worktree.cleanup===void 0?{}:{cleanup:t.managed_worktree.cleanup},path:t.managed_worktree.path,branch:t.managed_worktree.branch,base_ref:t.managed_worktree.base_ref,base_sha:t.managed_worktree.base_sha}},...t.review_binding===void 0?{}:{review_binding:t.review_binding??null},intent:t.intent??null,...t.persona_ack===void 0?{}:{persona_ack:t.persona_ack},created:t.created}}r(y,"extensionNode");function oe(t){const e=_(t),n=[],o=new Set([t]);let i=e.parent??null;for(;i!==null&&i!==""&&!o.has(i);){o.add(i);const c=k(i);if(c===null)break;n.unshift(y(c)),i=c.parent??null}const a=$(t).map(c=>k(c.node_id)).filter(c=>c!==null&&c.kind!=="human").sort((c,S)=>c.status===S.status?0:c.status==="active"?-1:S.status==="active"?1:0).map(y),u=typeof e.fork_source_file=="string"?P(e.fork_source_file):null,d=G(t);if(d===null)throw v(`unknown node: ${t}`,{received:t});return{node:y(e),warm_spare:T(t),ancestors:n,children:a,fork_source:u===null?null:y(u),subject:d,report_nodes:I()}}r(oe,"extensionState");function B(t,e){return t?.kind===e.kind&&t?.mode===e.mode&&t?.lifecycle===e.lifecycle}r(B,"samePersona");function re(t){const e=p(t,"broker generated-name");if(e.kind!=="initial"&&e.kind!=="recap")throw s("kind must be initial or recap");if(typeof e.description!="string"||typeof e.title!="string"||typeof e.icon!="string")throw s("description, title, and icon must be strings");if(e.kind==="initial")return e;const n=e.expected;if(typeof n!="object"||n===null||Array.isArray(n)||["name","description","title","icon","kind"].some(o=>typeof n[o]!="string"))throw s("recap expected must contain name, description, title, icon, and kind strings");return e}r(re,"parseGeneratedNameBody");function se(t){const e=p(t,"broker persona-ack"),n=r(o=>{const i=e[o];if(typeof i!="object"||i===null||Array.isArray(i)||typeof i.kind!="string"||i.mode!=="base"&&i.mode!=="orchestrator"||i.lifecycle!=="terminal"&&i.lifecycle!=="resident")throw s(`${o} must be a persona state`);return i},"parse");return{expected_execution_id:e.expected_execution_id,from:n("from"),to:n("to")}}r(se,"parsePersonaAckBody");function ae(t){const e=t.params.id,n=Z(t.body),o=n.sessionFile===null?null:A(n.sessionFile);return{status:200,body:l(()=>{const a=_(e);if(C(n.executionId,e,n.pid)==="shutdown")return{action:"shutdown"};const u=a.review_binding?.review_id;if(u!==void 0&&!n.reviewBoundaryIds.includes(u))return{action:"shutdown"};if(n.reason==="new"&&a.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&a.fork_from!=null&&a.pi_session_id==null&&o===null)return{action:"shutdown"};const d=o===null?null:P(o);if(d!==null&&d.node_id!==e){if(L(d.pi_pid))return V(e,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${o}: it is owned by live node ${d.node_id}`}),{action:"shutdown"};const c=E(d.node_id);if(c===null||M({fromNodeId:d.node_id,fromBindingId:c.bindingId,toNodeId:e,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})!=="transferred")return{action:"shutdown"}}else if(j(e,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(J(e),a.intent==="refresh"&&h(e,"revive")),{action:"none"}})}}r(ae,"handleSessionBound");function de(t,e){const n=k(t);if(n===null){if(e.pushedFinal)return{action:"shutdown"};throw v(`unknown node: ${t}`,{received:t})}if(R(t,new Date().toISOString()),e.stopReason==="quit")return(n.status==="active"||n.status==="idle")&&n.intent===null&&(h(t,"finish"),m(t),b(t)!==null&&x(t),w(t,{pane:null,tmux_session:null,window:null})),{action:"shutdown"};if(e.stopReason!=="stop"&&e.stopReason!=="length")return m(t,{link:"daemon\u2192node"}),n.intent==="refresh"?{action:"shutdown"}:{action:"stay_dormant"};const o={pushedFinal:e.pushedFinal};if(o.pushedFinal||n.status==="done")return m(t),b(t)!==null&&x(t),w(t,{pane:null,tmux_session:null,window:null}),{action:"shutdown"};if(n.intent==="refresh")return m(t),{action:"shutdown"};const i=U(t,o,e.backgroundJobsRunning);return m(t),i.action==="reprompt"?{action:"reprompt",prompt:i.message}:i.reason==="finished"?O(t)?{action:"stay_dormant"}:{action:"shutdown"}:i.reason!=="awaiting"&&i.reason!=="scheduled"?{action:"stay_dormant"}:b(t)!==null||W(t)||O(t)?{action:"stay_dormant"}:(h(t,"release"),{action:"shutdown"})}r(de,"settleBroker");function ue(t){const e=t.params.id,n=ee(t.body);f(e,n.expected_execution_id);const o=l(()=>de(e,n));return o.action!=="reprompt"&&z(e),{status:200,body:o}}r(ue,"handleSettle");function ce(t){const e=t.params.id,n=te(t.body);return f(e,n.expected_execution_id),{status:200,body:l(()=>{const i=K(e);if(i===void 0)return{action:"stay_dormant"};const a=_(e);return X(e,a,i)?(m(e),h(e,"park"),b(e)!==null&&x(e),w(e,{pane:null,tmux_session:null,window:null}),H({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...D(e)?{node_id:e}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}})}}r(ce,"handleParkComplete");function le(t){const e=t.params.id;return f(e,p(t.body,"broker park-activity").expected_execution_id),{status:200,body:l(()=>({activity:Q(e)?"recorded":"none"}))}}r(le,"handleParkActivity");function pe(t){const e=t.params.id,n=ne(t.body);return f(e,n.expected_execution_id),l(()=>{q(e,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at})}),{status:204}}r(pe,"handleTelemetry");function fe(t){const e=t.params.id,n=ie(t.body);return f(e,n.expected_execution_id),{status:200,body:{modelOverride:l(()=>Y(_(e),n.spec,n.pinnedOverride,n.userSelected)).model_override}}}r(fe,"handleModelCommit");function me(t){return{status:200,body:oe(t.params.id)}}r(me,"handleExtensionState");function _e(t){const e=t.params.id,n=re(t.body);return f(e,n.expected_execution_id),{status:200,body:l(()=>{const i=_(e);if(n.kind==="initial"){if((i.description??"").trim()!=="")return{applied:!1};const d=g(e,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:N(d),description:d.description??"",title:d.title??"",icon:d.icon??""}}const a=n.expected;if(i.name!==a.name||(i.description??"")!==a.description||(i.title??"")!==a.title||(i.icon??"")!==a.icon||i.kind!==a.kind)return{applied:!1};const u=g(e,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:N(u),description:u.description??"",title:u.title??"",icon:u.icon??""}})}}r(_e,"handleGeneratedName");function ke(t){const e=t.params.id,n=se(t.body);return f(e,n.expected_execution_id),{status:200,body:l(()=>{const i=_(e),a={kind:i.kind,mode:i.mode,lifecycle:i.lifecycle},u=i.persona_ack,d=u===void 0?a:{kind:u.kind??i.kind,mode:u.mode,lifecycle:u.lifecycle};return!B(a,n.to)||!B(d,n.from)?{applied:!1}:(g(e,{persona_ack:n.to}),{applied:!0})})}}r(ke,"handlePersonaAck");const De=[{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:ae},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:ue},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:ce},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:le},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:pe},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:fe},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:me},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:_e},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:ke}];export{De as brokerOperationRoutes};
|
|
1
|
+
var F=Object.defineProperty;var r=(t,e)=>F(t,"name",{value:e,configurable:!0});import{canonicalSessionFile as A,findNodeBySessionFile as P,getNode as k,isWarmSpare as T,listNodeIdentities as I,recordSettledTurn as R,setPresence as w,subscriptionsOf as $,updateNode as g,updateNodeTelemetry as q}from"../../../core/canvas/canvas.js";import{recordExecutionPid as C}from"../../../core/canvas/executions.js";import{bindSession as j,readLiveSession as E,transferSession as M}from"../../../core/canvas/sessions.js";import{isSafeNodeId as D}from"../../../core/canvas/paths.js";import{isPidAlive as L}from"../../../core/canvas/pid.js";import{editorLabel as N}from"../../../core/canvas/labels.js";import{assembleNodeSubject as G}from"../../../core/substrate/subject.js";import{withCanvasWrite as l}from"../../../core/canvas/db.js";import{notFound as v,usage as s}from"../../../core/errors.js";import{hasUnseenWakeMail as O}from"../../../core/canvas/deliveries.js";import{clearYieldMessage as J}from"../../../core/runtime/kickoff.js";import{transition as h}from"../../../core/runtime/lifecycle.js";import{focusOf as b,tearDownNode as x}from"../../../core/runtime/placement.js";import{hasAttachedBrokerViewers as W}from"../../../core/runtime/broker/client-registry.js";import{evaluateStop as U}from"../../../core/runtime/stop-guard.js";import{clearFault as f,recordFault as V}from"../../../core/canvas/recovery.js";import{updateModelRecipe as Y}from"../../../core/runtime/model-swap.js";import{completeCompanionRequestedSubmit as z}from"../../review/finish.js";import{emitEvent as H}from"../../../core/events/emit.js";import{consumePendingPark as K,recordPendingParkActivity as Q}from"../../park-pending.js";import{shouldParkAfterSummary as X}from"../../park-activity.js";import{ownerOnly as Z}from"../router.js";import{requireCurrentExecution as m}from"./broker-recovery.js";function p(t,e){if(typeof t!="object"||t===null||Array.isArray(t))throw s(`${e} requires a JSON body`);return t}r(p,"objectBody");function ee(t){const e=p(t,"broker session-bound");if(typeof e.executionId!="string"||e.executionId==="")throw s("executionId must be a non-empty string");if(typeof e.piSessionId!="string"||e.piSessionId==="")throw s("piSessionId must be a non-empty string");if(e.sessionFile!==null&&typeof e.sessionFile!="string")throw s("sessionFile must be a string or null");if(!Number.isSafeInteger(e.pid)||e.pid<=0)throw s("pid must be a positive safe integer");if(e.reason!==null&&typeof e.reason!="string")throw s("reason must be a string or null");if(!Array.isArray(e.reviewBoundaryIds)||!e.reviewBoundaryIds.every(n=>typeof n=="string"))throw s("reviewBoundaryIds must be an array of strings");return e}r(ee,"parseSessionBoundBody");function te(t){const e=p(t,"broker settle");if(typeof e.stopReason!="string")throw s("stopReason must be a string");for(const n of["backgroundJobsRunning","pushedFinal"])if(typeof e[n]!="boolean")throw s(`${n} must be a boolean`);return e}r(te,"parseSettleBody");function ne(t){const e=p(t,"broker park-complete");if(e.outcome!=="completed"&&e.outcome!=="failed")throw s("outcome must be completed or failed");return e}r(ne,"parseParkCompleteBody");function ie(t){const e=p(t,"broker telemetry");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw s("expected_execution_id must be a non-empty string");if(typeof e.tokens_in!="number"||!Number.isFinite(e.tokens_in)||e.tokens_in<0)throw s("tokens_in must be a non-negative finite number");if(e.context_tokens!==null&&(typeof e.context_tokens!="number"||!Number.isFinite(e.context_tokens)||e.context_tokens<0))throw s("context_tokens must be a non-negative finite number or null");if(e.last_activity!==null&&typeof e.last_activity!="string")throw s("last_activity must be a string or null");if(typeof e.updated_at!="string"||Number.isNaN(Date.parse(e.updated_at)))throw s("updated_at must be an ISO timestamp");return{...e,updated_at:new Date(e.updated_at).toISOString()}}r(ie,"parseTelemetryBody");function oe(t){const e=p(t,"broker model");if(typeof e.spec!="string"||e.spec.trim()==="")throw s("spec must be a non-empty string");if(e.pinnedOverride!==void 0&&typeof e.pinnedOverride!="boolean")throw s("pinnedOverride must be a boolean when present");if(typeof e.userSelected!="boolean")throw s("userSelected must be a boolean");return e}r(oe,"parseModelCommitBody");function _(t){const e=k(t);if(e===null)throw v(`unknown node: ${t}`,{received:t});return e}r(_,"requireNode");function y(t){return{node_id:t.node_id,name:t.name,...t.description===void 0?{}:{description:t.description},...t.title===void 0?{}:{title:t.title},...t.icon===void 0?{}:{icon:t.icon},kind:t.kind,mode:t.mode,lifecycle:t.lifecycle,status:t.status,cwd:t.cwd,parent:t.parent??null,fork_from:t.fork_from??null,profile_id:t.profile_id??null,...t.managed_worktree===void 0?{}:{managed_worktree:t.managed_worktree===null?null:{state:t.managed_worktree.state,...t.managed_worktree.cleanup===void 0?{}:{cleanup:t.managed_worktree.cleanup},path:t.managed_worktree.path,branch:t.managed_worktree.branch,base_ref:t.managed_worktree.base_ref,base_sha:t.managed_worktree.base_sha}},...t.review_binding===void 0?{}:{review_binding:t.review_binding??null},intent:t.intent??null,...t.persona_ack===void 0?{}:{persona_ack:t.persona_ack},created:t.created}}r(y,"extensionNode");function re(t){const e=_(t),n=[],o=new Set([t]);let i=e.parent??null;for(;i!==null&&i!==""&&!o.has(i);){o.add(i);const c=k(i);if(c===null)break;n.unshift(y(c)),i=c.parent??null}const a=$(t).map(c=>k(c.node_id)).filter(c=>c!==null&&c.kind!=="human").sort((c,S)=>c.status===S.status?0:c.status==="active"?-1:S.status==="active"?1:0).map(y),u=typeof e.fork_source_file=="string"?P(e.fork_source_file):null,d=G(t);if(d===null)throw v(`unknown node: ${t}`,{received:t});return{node:y(e),warm_spare:T(t),ancestors:n,children:a,fork_source:u===null?null:y(u),subject:d,report_nodes:I()}}r(re,"extensionState");function B(t,e){return t?.kind===e.kind&&t?.mode===e.mode&&t?.lifecycle===e.lifecycle}r(B,"samePersona");function se(t){const e=p(t,"broker generated-name");if(e.kind!=="initial"&&e.kind!=="recap")throw s("kind must be initial or recap");if(typeof e.description!="string"||typeof e.title!="string"||typeof e.icon!="string")throw s("description, title, and icon must be strings");if(e.kind==="initial")return e;const n=e.expected;if(typeof n!="object"||n===null||Array.isArray(n)||["name","description","title","icon","kind"].some(o=>typeof n[o]!="string"))throw s("recap expected must contain name, description, title, icon, and kind strings");return e}r(se,"parseGeneratedNameBody");function ae(t){const e=p(t,"broker persona-ack"),n=r(o=>{const i=e[o];if(typeof i!="object"||i===null||Array.isArray(i)||typeof i.kind!="string"||i.mode!=="base"&&i.mode!=="orchestrator"||i.lifecycle!=="terminal"&&i.lifecycle!=="resident")throw s(`${o} must be a persona state`);return i},"parse");return{expected_execution_id:e.expected_execution_id,from:n("from"),to:n("to")}}r(ae,"parsePersonaAckBody");function de(t){const e=t.params.id,n=ee(t.body),o=n.sessionFile===null?null:A(n.sessionFile);return{status:200,body:l(()=>{const a=_(e);if(C(n.executionId,e,n.pid)==="shutdown")return{action:"shutdown"};const u=a.review_binding?.review_id;if(u!==void 0&&!n.reviewBoundaryIds.includes(u))return{action:"shutdown"};if(n.reason==="new"&&a.parent==null)return{action:"relaunch_root"};if(n.reason!=="new"&&a.fork_from!=null&&a.pi_session_id==null&&o===null)return{action:"shutdown"};const d=o===null?null:P(o);if(d!==null&&d.node_id!==e){if(L(d.pi_pid))return V(e,{link:"daemon\u2192node",op:"session-file-ownership",kind:"protocol",retry:{disposition:"fatal"},message:`refusing to bind ${o}: it is owned by live node ${d.node_id}`}),{action:"shutdown"};const c=E(d.node_id);if(c===null||M({fromNodeId:d.node_id,fromBindingId:c.bindingId,toNodeId:e,executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})!=="transferred")return{action:"shutdown"}}else if(j(e,{executionId:n.executionId,sessionId:n.piSessionId,transcriptPath:o})==="shutdown")return{action:"shutdown"};return n.reason!=="new"&&(J(e),a.intent==="refresh"&&h(e,"revive")),{action:"none"}})}}r(de,"handleSessionBound");function ue(t,e){const n=k(t);if(n===null){if(e.pushedFinal)return{action:"shutdown"};throw v(`unknown node: ${t}`,{received:t})}if(R(t,new Date().toISOString()),e.stopReason==="quit")return(n.status==="active"||n.status==="idle")&&n.intent===null&&(h(t,"finish"),f(t),b(t)!==null&&x(t),w(t,{pane:null,tmux_session:null,window:null})),{action:"shutdown"};if(e.stopReason!=="stop"&&e.stopReason!=="length")return f(t,{link:"daemon\u2192node"}),n.intent==="refresh"?{action:"shutdown"}:{action:"stay_dormant"};const o={pushedFinal:e.pushedFinal};if(o.pushedFinal||n.status==="done")return f(t),b(t)!==null&&x(t),w(t,{pane:null,tmux_session:null,window:null}),{action:"shutdown"};if(n.intent==="refresh")return f(t),{action:"shutdown"};const i=U(t,o,e.backgroundJobsRunning);return f(t),i.action==="reprompt"?{action:"reprompt",prompt:i.message}:i.reason==="finished"?O(t)?{action:"stay_dormant"}:{action:"shutdown"}:i.reason!=="awaiting"&&i.reason!=="scheduled"?{action:"stay_dormant"}:b(t)!==null||W(t)||O(t)?{action:"stay_dormant"}:(h(t,"release"),{action:"shutdown"})}r(ue,"settleBroker");function ce(t){const e=t.params.id,n=te(t.body);m(e,n.expected_execution_id);const o=l(()=>ue(e,n));return o.action!=="reprompt"&&z(e),{status:200,body:o}}r(ce,"handleSettle");function le(t){const e=t.params.id,n=ne(t.body);return m(e,n.expected_execution_id),{status:200,body:l(()=>{const i=K(e);if(i===void 0)return{action:"stay_dormant"};const a=_(e);return X(e,a,i)?(f(e),h(e,"park"),b(e)!==null&&x(e),w(e,{pane:null,tmux_session:null,window:null}),H({level:n.outcome==="completed"?"info":"warn",event:"broker.park.complete",...D(e)?{node_id:e}:{},outcome:n.outcome==="completed"?"succeeded":"failed"}),{action:"shutdown"}):{action:"stay_dormant"}})}}r(le,"handleParkComplete");function pe(t){const e=t.params.id;return m(e,p(t.body,"broker park-activity").expected_execution_id),{status:200,body:l(()=>({activity:Q(e)?"recorded":"none"}))}}r(pe,"handleParkActivity");function me(t){const e=t.params.id,n=ie(t.body);return m(e,n.expected_execution_id),l(()=>{q(e,{tokensIn:n.tokens_in,contextTokens:n.context_tokens,lastActivity:n.last_activity,updatedAt:n.updated_at})}),{status:204}}r(me,"handleTelemetry");function fe(t){const e=t.params.id,n=oe(t.body);return m(e,n.expected_execution_id),{status:200,body:{modelOverride:l(()=>Y(_(e),n.spec,n.pinnedOverride,n.userSelected)).model_override}}}r(fe,"handleModelCommit");function _e(t){return{status:200,body:re(t.params.id)}}r(_e,"handleExtensionState");function ke(t){const e=t.params.id,n=se(t.body);return m(e,n.expected_execution_id),{status:200,body:l(()=>{const i=_(e);if(n.kind==="initial"){if((i.description??"").trim()!=="")return{applied:!1};const d=g(e,{description:n.description,...n.title.trim()===""?{}:{title:n.title},...n.icon.trim()===""?{}:{icon:n.icon}});return{applied:!0,editorLabel:N(d),description:d.description??"",title:d.title??"",icon:d.icon??""}}const a=n.expected;if(i.name!==a.name||(i.description??"")!==a.description||(i.title??"")!==a.title||(i.icon??"")!==a.icon||i.kind!==a.kind)return{applied:!1};const u=g(e,{description:n.description,title:n.title.trim()===""?void 0:n.title,icon:n.icon.trim()===""?void 0:n.icon});return{applied:!0,editorLabel:N(u),description:u.description??"",title:u.title??"",icon:u.icon??""}})}}r(ke,"handleGeneratedName");function he(t){const e=t.params.id,n=ae(t.body);return m(e,n.expected_execution_id),{status:200,body:l(()=>{const i=_(e),a={kind:i.kind,mode:i.mode,lifecycle:i.lifecycle},u=i.persona_ack,d=u===void 0?a:{kind:u.kind??i.kind,mode:u.mode,lifecycle:u.lifecycle};return!B(a,n.to)||!B(d,n.from)?{applied:!1}:(g(e,{persona_ack:n.to}),{applied:!0})})}}r(he,"handlePersonaAck");const Ge=Z([{method:"POST",pattern:"/v1/nodes/:id/broker/session-bound",handler:de},{method:"POST",pattern:"/v1/nodes/:id/broker/settle",handler:ce},{method:"POST",pattern:"/v1/nodes/:id/broker/park-complete",handler:le},{method:"POST",pattern:"/v1/nodes/:id/broker/park-activity",handler:pe},{method:"POST",pattern:"/v1/nodes/:id/broker/telemetry",handler:me},{method:"POST",pattern:"/v1/nodes/:id/broker/model",handler:fe},{method:"GET",pattern:"/v1/nodes/:id/broker/extension-state",handler:_e},{method:"POST",pattern:"/v1/nodes/:id/broker/generated-name",handler:ke},{method:"POST",pattern:"/v1/nodes/:id/broker/persona-ack",handler:he}]);export{Ge as brokerOperationRoutes};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RouteTable } from '../router.js';
|
|
2
2
|
export declare function requireCurrentExecution(nodeId: string, expectedExecutionId: unknown): void;
|
|
3
3
|
export declare const brokerRecoveryRoutes: RouteTable;
|
|
4
4
|
export declare const nodeFaultRoutes: RouteTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var k=Object.defineProperty;var i=(r,e)=>k(r,"name",{value:e,configurable:!0});import{ApiError as w}from"../../../api/errors.js";import{getNode as E}from"../../../core/canvas/canvas.js";import{liveExecutionForNode as I}from"../../../core/canvas/executions.js";import{admitEpisode as S,clearEpisode as T,clearFault as y,closeTurn as q,invalidateEpisode as R,openTurn as m,parseProviderError as F,readEpisode as A,readFault as h,recordFault as v,recordPendingEpisode as N,recordTransportPoisonVerdict as P,recordTurnProgress as O,reopenEpisode as j}from"../../../core/canvas/recovery.js";import{notFound as D,usage as n}from"../../../core/errors.js";import{ownerOnly as b}from"../router.js";const l=new Set(["pi\u2192provider","viewer\u2194broker","relay\u2194broker","viewer\u2194crtrd","daemon\u2192node","crtr\u2192pi"]),L=new Set(["rate-limit","overloaded","connection","auth","protocol","context-overflow","other","wedged","model-not-found"]),B=new Set(["auto","manual","fatal"]),$=new Set(["sdk","daemon","client"]);function d(r,e){if(typeof r!="object"||r===null||Array.isArray(r))throw n(`${e} requires a JSON object`);return r}i(d,"objectBody");function a(r){if(E(r)===null)throw D(`unknown node: ${r}`,{received:r})}i(a,"requireNode");function c(r,e){if(typeof e!="string"||e==="")throw n("expected_execution_id must be a non-empty string");const t=I(r);if(t===null||t.executionId!==e)throw new w(409,"stale_execution","broker execution is no longer current")}i(c,"requireCurrentExecution");function g(r){if(typeof r!="object"||r===null||Array.isArray(r))throw n("retry must be an object");const e=r;if(!B.has(e.disposition))throw n("retry.disposition must be auto, manual, or fatal");if(e.by!==void 0&&!$.has(e.by))throw n("retry.by must be sdk, daemon, or client");for(const t of["attempt","max"])if(e[t]!==void 0&&(!Number.isSafeInteger(e[t])||e[t]<0))throw n(`retry.${t} must be a non-negative safe integer`);if(e.nextAt!==void 0&&typeof e.nextAt!="string")throw n("retry.nextAt must be a string");return e}i(g,"parseRetry");function f(r){const e=d(r,"fault input");if(!l.has(e.link))throw n("fault link is invalid");if(typeof e.op!="string"||e.op==="")throw n("fault op must be a non-empty string");if(!L.has(e.kind))throw n("fault kind is invalid");if(typeof e.message!="string")throw n("fault message must be a string");if(e.operation_id!==void 0&&(typeof e.operation_id!="string"||e.operation_id===""))throw n("operation_id must be a non-empty string");if(e.since!==void 0&&typeof e.since!="string")throw n("since must be a string");if(e.anchor_entry_id!==void 0&&(typeof e.anchor_entry_id!="string"||e.anchor_entry_id===""))throw n("anchor_entry_id must be a non-empty string");const t=e.provider_error===void 0?void 0:F(e.provider_error);if(e.provider_error!==void 0&&t===void 0)throw n("provider_error is invalid");return{...e,...t===void 0?{}:{provider_error:t},retry:g(e.retry)}}i(f,"parseFaultInput");function _(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,...r.operation_id===void 0?{}:{operation_id:r.operation_id},...r.since===void 0?{}:{since:r.since},...r.anchor_entry_id===void 0?{}:{anchorEntryId:r.anchor_entry_id},...r.provider_error===void 0?{}:{providerError:r.provider_error}}}i(_,"faultInput");function p(r){return{fault_id:r.faultId,link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,since:r.since,operation_id:r.operation_id,...r.anchorEntryId===void 0?{}:{anchor_entry_id:r.anchorEntryId},...r.providerError===void 0?{}:{provider_error:r.providerError}}}i(p,"faultDto");function x(r){const e=h(r),t=A(r);return{fault:e===null?null:p(e),episode:t===null?null:{state:t.state,session_id:t.sessionId,fault:p(t.fault)}}}i(x,"recoveryState");function C(r){const e=d(r,"broker turn"),t=e.event;if(t!=="started"&&t!=="progress"&&t!=="closed"&&t!=="interrupted"&&t!=="exit-diagnostic")throw n("event is invalid");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw n("expected_execution_id must be a non-empty string");if(t==="closed"){if(e.end_reason!=="settled"&&e.end_reason!=="teardown-aborted")throw n("closed turn requires end_reason settled or teardown-aborted")}else if(e.end_reason!==void 0)throw n("end_reason is valid only for closed turns");if(t==="exit-diagnostic"){const o=d(e.exit_diagnostic,"exit_diagnostic"),s=d(o.transport_poison,"exit_diagnostic.transport_poison");if(o.schema!=="crtr.execution-exit-diagnostic/v1"||!Number.isSafeInteger(s.consecutive_passes)||s.consecutive_passes<1||!Array.isArray(s.routes)||s.routes.length===0||!s.routes.every(u=>typeof u=="string"&&u!==""))throw n("exit_diagnostic is invalid")}else if(e.exit_diagnostic!==void 0)throw n("exit_diagnostic is valid only for exit-diagnostic turns");return e}i(C,"parseTurn");function K(r){const e=r.params.id;a(e);const t=C(r.body);c(e,t.expected_execution_id);let o;switch(t.event){case"started":o=m(t.expected_execution_id,{reason:"agent-start"});break;case"interrupted":o=m(t.expected_execution_id,{reason:"prompt-unsettled"});break;case"progress":o=O(t.expected_execution_id);break;case"closed":o=q(t.expected_execution_id,{endReason:t.end_reason});break;case"exit-diagnostic":{const u=t.exit_diagnostic.transport_poison;o=P(t.expected_execution_id,{consecutivePasses:u.consecutive_passes,routes:u.routes});break}}return{status:200,body:{applied:o}}}i(K,"handleTurn");function U(r){const e=d(r,"provider retry");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw n("expected_execution_id must be a non-empty string");if(!["record-pending","admit","reopen","invalidate","clear"].includes(e.op))throw n("provider retry op is invalid");if(typeof e.session_id!="string"||e.session_id==="")throw n("session_id must be a non-empty string");if(e.op==="record-pending"&&(e.input=f(e.input)),e.op==="admit"){if(typeof e.fault_id!="string"||e.fault_id==="")throw n("admit requires fault_id");e.retry=g(e.retry)}return e}i(U,"parseProviderRetry");function Y(r){const e=r.params.id;a(e);const t=U(r.body);c(e,t.expected_execution_id);let o;switch(t.op){case"record-pending":o=N(e,{sessionId:t.session_id,input:_(t.input)})!==null;break;case"admit":o=S(e,{sessionId:t.session_id,faultId:t.fault_id,retry:t.retry});break;case"reopen":o=j(e,{sessionId:t.session_id});break;case"invalidate":o=R(e,{sessionId:t.session_id});break;case"clear":o=T(e,{sessionId:t.session_id});break}return{status:200,body:{applied:o,episode:x(e).episode}}}i(Y,"handleProviderRetry");function G(r){const e=d(r,"broker fault");if(typeof e.expected_execution_id!="string"||e.expected_execution_id==="")throw n("expected_execution_id must be a non-empty string");if(e.op!=="record"&&e.op!=="clear")throw n("fault op must be record or clear");if(e.op==="record"&&(e.input=f(e.input)),e.op==="clear"&&e.link!==void 0&&!l.has(e.link))throw n("fault link is invalid");if(e.preserve_episode!==void 0&&typeof e.preserve_episode!="boolean")throw n("preserve_episode must be boolean");return e}i(G,"parseBrokerFault");function J(r){const e=r.params.id;a(e);const t=G(r.body);c(e,t.expected_execution_id);const o=t.op==="record"?v(e,_(t.input)):(y(e,{...t.link===void 0?{}:{link:t.link},...t.preserve_episode===void 0?{}:{preserveEpisode:t.preserve_episode}}),h(e));return{status:200,body:{fault:o===null?null:p(o)}}}i(J,"handleBrokerFault");function V(r){const e=r.params.id;return a(e),c(e,r.query.get("expected_execution_id")),{status:200,body:x(e)}}i(V,"handleRecovery");function W(r){const e=r.params.id;a(e);const t=v(e,_(f(r.body)));return{status:200,body:{fault:t===null?null:p(t)}}}i(W,"handleNodeFaultRecord");function z(r){const e=r.params.id;a(e);const t=r.query.get("link");if(t!==null&&!l.has(t))throw n("fault link is invalid");const o=r.query.get("preserve_episode");if(o!==null&&o!=="true"&&o!=="false")throw n("preserve_episode must be true or false");return{status:200,body:{cleared:y(e,{...t===null?{}:{link:t},...o===null?{}:{preserveEpisode:o==="true"}})}}}i(z,"handleNodeFaultClear");const te=b([{method:"POST",pattern:"/v1/broker/:id/turn",handler:K},{method:"POST",pattern:"/v1/broker/:id/provider-retry",handler:Y},{method:"POST",pattern:"/v1/broker/:id/fault",handler:J},{method:"GET",pattern:"/v1/broker/:id/recovery",handler:V}]),ne=b([{method:"POST",pattern:"/v1/nodes/:id/fault",handler:W},{method:"DELETE",pattern:"/v1/nodes/:id/fault",handler:z}]);export{te as brokerRecoveryRoutes,ne as nodeFaultRoutes,c as requireCurrentExecution};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var P=Object.defineProperty;var u=(t,e)=>P(t,"name",{value:e,configurable:!0});import{graphNodes as I,pruneNodes as K,pruneToLimit as A,rosterEdges as C,rosterNodes as D}from"../../../core/canvas/canvas.js";import{activeBackgroundBashJobs as q}from"../../../core/bash-jobs.js";import{contextDir as H}from"../../../core/canvas/paths.js";import{nowIso as k}from"../../../core/fs-utils.js";import{isPidPresent as J}from"../../../core/canvas/pid.js";import{dashboardRowsAllFromSource as z,enrichRowsFromSource as Q}from"../../../core/canvas/render-source.js";import{localCanvasSource as M}from"../../../core/canvas/source.js";import{ticketCountsForNodes as j,ticketsAcrossCanvas as U}from"../../../core/canvas/attention.js";import{buildCorpus as Y,groupCorpus as V,resolveRef as W}from"../../../core/canvas/history.js";import{listFocuses as R,reapEmptyNodes as X}from"../../../core/runtime/placement.js";import{notFound as Z,usage as p}from"../../../core/errors.js";import{toGraphNodeDTO as ee}from"../map.js";function te(){return{status:200,body:{items:U().map(e=>({node_id:e.node_id,name:e.name,cwd:e.cwd,count:e.count,subjects:e.subjects}))}}}u(te,"handleAttention");function ne(t){const e=t.body;if(e===null||typeof e!="object"||!Array.isArray(e.node_ids))throw p("attention count request requires node_ids: string[]");const n=e.node_ids;if(!n.every(s=>typeof s=="string"))throw p("attention count request node_ids must contain only strings");return{status:200,body:{counts:j(n)}}}u(ne,"handleAttentionCounts");function B(t){return Buffer.from(String(t),"utf8").toString("base64")}u(B,"encodeCursor");function
|
|
2
|
-
`);if(e.length>0){const i=s.findIndex(d=>{const o=d.toLowerCase();return e.some(c=>o.includes(c))});if(i!==-1)return s.slice(i,i+n).map(d=>d.trim()).filter(d=>d!=="").join(" \u23CE ")}const a=s.map(i=>i.trim()).filter(i=>i!=="").slice(0,n).join(" \u23CE ");return a!==""?a:t.title}u(oe,"snippet");function re(t,e,n){const r=[...t];return e==="relevance"&&n!==void 0?r.sort((s,a)=>(n.get(a.ref)??0)-(n.get(s.ref)??0)||a.tsMs-s.tsMs||s.ref.localeCompare(a.ref)):e==="oldest"?r.sort((s,a)=>s.tsMs-a.tsMs||s.ref.localeCompare(a.ref)):r.sort((s,a)=>a.tsMs-s.tsMs||s.ref.localeCompare(a.ref)),r}u(re,"sortArtifacts");function se(t){if(t==null)return{};if(typeof t!="object")throw p("history search body must be a JSON object");return t}u(se,"parseSearchBody");function ie(t){if(t===null||typeof t!="object")throw p("history grep body must be a JSON object");const e=t;if(typeof e.pattern!="string"||e.pattern==="")throw p("history grep body requires a non-empty pattern");return e}u(ie,"parseGrepBody");function $(t){const e={cwd:t.cwd,allCwds:t.all_cwds===!0,under:t.under,nodes:t.nodes},n={types:t.types,reportKind:t.report_kind,kinds:t.kinds,statuses:t.statuses,sinceMs:t.since_ms,untilMs:t.until_ms,origins:t.origins,roles:t.roles,from:t.from};return Y(e,n)}u($,"buildCorpusFromQuery");function de(t){if(t==null)return{};if(typeof t!="object")throw p("history stats body must be a JSON object");return t}u(de,"parseStatsBody");const
|
|
3
|
-
`).forEach((y,b)=>{a.test(y)&&i.push({ref:f.ref,node:`${f.nodeName} (${f.nodeId})`,ts:f.ts,line:b+1,text:y.trim()})});i.sort((f,m)=>f.ref.localeCompare(m.ref)||f.line-m.line);const d=i.slice(s,s+n),o=s+n<i.length?B(s+n):null;return{status:200,body:{hits:d,next_cursor:o,total:i.length}}}u(ce,"handleHistoryGrep");function ue(t){const e=se(t.body),n=(e.query??"").trim(),r=e.weigh_body===!0,s=e.full===!0,a=Math.max(1,e.snippet_lines??1),i=Math.min(100,Math.max(1,e.limit??20)),d=$(e),o=e.cursor!==void 0?
|
|
1
|
+
var P=Object.defineProperty;var u=(t,e)=>P(t,"name",{value:e,configurable:!0});import{graphNodes as I,pruneNodes as K,pruneToLimit as A,rosterEdges as C,rosterNodes as D}from"../../../core/canvas/canvas.js";import{activeBackgroundBashJobs as q}from"../../../core/bash-jobs.js";import{contextDir as H}from"../../../core/canvas/paths.js";import{nowIso as k}from"../../../core/fs-utils.js";import{isPidPresent as J}from"../../../core/canvas/pid.js";import{dashboardRowsAllFromSource as z,enrichRowsFromSource as Q}from"../../../core/canvas/render-source.js";import{localCanvasSource as M}from"../../../core/canvas/source.js";import{ticketCountsForNodes as j,ticketsAcrossCanvas as U}from"../../../core/canvas/attention.js";import{buildCorpus as Y,groupCorpus as V,resolveRef as W}from"../../../core/canvas/history.js";import{listFocuses as R,reapEmptyNodes as X}from"../../../core/runtime/placement.js";import{notFound as Z,usage as p}from"../../../core/errors.js";import{toGraphNodeDTO as ee}from"../map.js";function te(){return{status:200,body:{items:U().map(e=>({node_id:e.node_id,name:e.name,cwd:e.cwd,count:e.count,subjects:e.subjects}))}}}u(te,"handleAttention");function ne(t){const e=t.body;if(e===null||typeof e!="object"||!Array.isArray(e.node_ids))throw p("attention count request requires node_ids: string[]");const n=e.node_ids;if(!n.every(s=>typeof s=="string"))throw p("attention count request node_ids must contain only strings");return{status:200,body:{counts:j(n)}}}u(ne,"handleAttentionCounts");function B(t){return Buffer.from(String(t),"utf8").toString("base64")}u(B,"encodeCursor");function O(t){const e=Number(Buffer.from(t,"base64").toString("utf8"));if(!Number.isInteger(e)||e<0)throw p(`invalid cursor: ${t}`);return e}u(O,"decodeCursor");function oe(t,e,n){const s=t.loadBody().split(`
|
|
2
|
+
`);if(e.length>0){const i=s.findIndex(d=>{const o=d.toLowerCase();return e.some(c=>o.includes(c))});if(i!==-1)return s.slice(i,i+n).map(d=>d.trim()).filter(d=>d!=="").join(" \u23CE ")}const a=s.map(i=>i.trim()).filter(i=>i!=="").slice(0,n).join(" \u23CE ");return a!==""?a:t.title}u(oe,"snippet");function re(t,e,n){const r=[...t];return e==="relevance"&&n!==void 0?r.sort((s,a)=>(n.get(a.ref)??0)-(n.get(s.ref)??0)||a.tsMs-s.tsMs||s.ref.localeCompare(a.ref)):e==="oldest"?r.sort((s,a)=>s.tsMs-a.tsMs||s.ref.localeCompare(a.ref)):r.sort((s,a)=>a.tsMs-s.tsMs||s.ref.localeCompare(a.ref)),r}u(re,"sortArtifacts");function se(t){if(t==null)return{};if(typeof t!="object")throw p("history search body must be a JSON object");return t}u(se,"parseSearchBody");function ie(t){if(t===null||typeof t!="object")throw p("history grep body must be a JSON object");const e=t;if(typeof e.pattern!="string"||e.pattern==="")throw p("history grep body requires a non-empty pattern");return e}u(ie,"parseGrepBody");function $(t){const e={cwd:t.cwd,allCwds:t.all_cwds===!0,under:t.under,nodes:t.nodes},n={types:t.types,reportKind:t.report_kind,kinds:t.kinds,statuses:t.statuses,sinceMs:t.since_ms,untilMs:t.until_ms,origins:t.origins,roles:t.roles,from:t.from};return Y(e,n)}u($,"buildCorpusFromQuery");function de(t){if(t==null)return{};if(typeof t!="object")throw p("history stats body must be a JSON object");return t}u(de,"parseStatsBody");const x=["origin","node","kind","day","type","role"];function ae(t){const e=de(t.body),n=e.group_by??"type";if(!x.includes(n))throw p(`--group-by must be one of: ${x.join(", ")}; received: ${String(e.group_by)}`);const r=$(e),s=V(r,n);return{status:200,body:{group_by:n,groups:s,total:s.reduce((i,d)=>i+d.count,0),scanned:r.length}}}u(ae,"handleHistoryStats");function ce(t){const e=ie(t.body),n=Math.min(100,Math.max(1,e.limit??20)),r=$(e),s=e.cursor!==void 0?O(e.cursor):0;let a;try{a=new RegExp(e.pattern,"i")}catch{throw p(`invalid regex pattern: ${e.pattern}`)}const i=[];for(const f of r)f.loadBody().split(`
|
|
3
|
+
`).forEach((y,b)=>{a.test(y)&&i.push({ref:f.ref,node:`${f.nodeName} (${f.nodeId})`,ts:f.ts,line:b+1,text:y.trim()})});i.sort((f,m)=>f.ref.localeCompare(m.ref)||f.line-m.line);const d=i.slice(s,s+n),o=s+n<i.length?B(s+n):null;return{status:200,body:{hits:d,next_cursor:o,total:i.length}}}u(ce,"handleHistoryGrep");function ue(t){const e=se(t.body),n=(e.query??"").trim(),r=e.weigh_body===!0,s=e.full===!0,a=Math.max(1,e.snippet_lines??1),i=Math.min(100,Math.max(1,e.limit??20)),d=$(e),o=e.cursor!==void 0?O(e.cursor):0,c=n.toLowerCase().split(/\s+/).filter(l=>l.length>0);let f=d,m;const y=e.sort??(c.length>0?"relevance":"recency");if(c.length>0&&y==="relevance"){m=new Map;const l=[];for(const h of d){const S=h.title.toLowerCase(),F=h.nodeName.toLowerCase(),G=h.nodeDesc.toLowerCase(),N=r?h.loadBody().toLowerCase():null;let _=0;for(const v of c)S.includes(v)&&(_+=10),F.includes(v)&&(_+=5),G.includes(v)&&(_+=3),N!==null&&N.includes(v)&&(_+=1);_>0&&(m.set(h.ref,_),l.push(h))}f=l}else c.length>0&&(f=d.filter(l=>{const h=`${l.title}
|
|
4
4
|
${l.nodeName}
|
|
5
5
|
${l.nodeDesc}${r?`
|
|
6
|
-
`+l.loadBody():""}`.toLowerCase();return c.some(S=>h.includes(S))}));const b=re(f,y,m),T=b.slice(o,o+i),E=o+i<b.length?B(o+i):null;return{status:200,body:{hits:T.map(l=>{const h={ref:l.ref,node:`${l.nodeName} (${l.nodeId})`,kind:l.nodeKind,source:l.source==="report"&&l.reportKind?`report:${l.reportKind}`:l.source,ts:l.ts};return y==="relevance"&&m!==void 0&&(h.score=m.get(l.ref)??0),s?h.body=l.loadBody():h.snippet=oe(l,c,a),h}),next_cursor:E,total:b.length}}}u(ue,"handleHistorySearch");function le(t){const e=(t.query.get("ref")??"").trim(),n=t.query.get("frontmatter")==="true";if(!e.includes(":"))throw p(`ref must be <node-id>:<relpath>; received: ${e}`,{received:e,next:"Get a valid ref from `crtr canvas history search` or `crtr node inspect artifacts <node-id>`."});let r;try{r=W(e)}catch(o){throw p(`ref rejected: ${o.message}`,{received:e,next:"Refs come verbatim from `crtr canvas history search`; do not hand-construct relpaths."})}if(r===null)throw Z(`no artifact for ref: ${e}`,{ref:e,next:"The node or file may not exist. Re-run `crtr canvas history search` for a current ref, or `crtr node inspect artifacts <node-id>` to list a node's artifacts."});const s=r.source==="report"&&r.reportKind?`report:${r.reportKind}`:r.source,i=(t.query.get("format")==="raw"||n)&&r.raw!==null?r.raw:r.body;return{status:200,body:{ref:e,node:`${r.nodeName} (${r.nodeId})`,source:s,ts:r.ts,content:i}}}u(le,"handleHistoryRead");function fe(t){for(const[r]of t.query)if(r!=="live")throw p(`unknown snapshot query parameter: ${r}`);const e=t.query.getAll("live");if(e.length>1)throw p("live must appear at most once");const n=e[0];if(n===void 0||n==="false"||n==="0")return!1;if(n==="true"||n==="1")return!0;throw p("live must be true or false")}u(fe,"snapshotLiveOnly");async function me(t){const e=new Set(R().map(o=>o.node_id)),n=await z(M,e,fe(t)),r=j(n.map(o=>o.node_id));await Q(M,n,r);const s=n.map(o=>{const c=o.srcRow,f=c?.host_kind??"tmux";return{node_id:o.node_id,name:o.name,title:o.handle??o.name,description:o.description??null,kind:o.kind,mode:o.mode,lifecycle:o.lifecycle??c?.lifecycle,status:o.status,cwd:o.cwd,profile_id:o.profile_id??null,parent:c?.parent??null,created:o.created,host_kind:f,enterable:f==="broker",attention_count:o.asks,...c?.cycles!=null?{cycles:c.cycles}:{},...o.mtimeMs!==void 0?{last_activity:new Date(o.mtimeMs).toISOString()}:{},ctx_tokens:o.ctx_tokens,streaming:o.streaming??!1,jobs_active:c!==void 0&&J(c.pi_pid)?q(H(o.node_id)).length:0,waiting_for:c?.waiting_for??null,hanging:o.hanging??null,viewed:o.viewed??!1,final_report:c?.final_report??null,finalized_at:c?.finalized_at??null}}),a=new Map;for(const o of C()){const c=a.get(o.from_id);c===void 0?a.set(o.from_id,[o.to_id]):c.push(o.to_id)}const i=Object.fromEntries(n.map(o=>[o.node_id,a.get(o.node_id)??[]]));return{status:200,body:{generated_at:k(),nodes:s,subscriptions:i}}}u(me,"handleSnapshot");function
|
|
6
|
+
`+l.loadBody():""}`.toLowerCase();return c.some(S=>h.includes(S))}));const b=re(f,y,m),T=b.slice(o,o+i),E=o+i<b.length?B(o+i):null;return{status:200,body:{hits:T.map(l=>{const h={ref:l.ref,node:`${l.nodeName} (${l.nodeId})`,kind:l.nodeKind,source:l.source==="report"&&l.reportKind?`report:${l.reportKind}`:l.source,ts:l.ts};return y==="relevance"&&m!==void 0&&(h.score=m.get(l.ref)??0),s?h.body=l.loadBody():h.snippet=oe(l,c,a),h}),next_cursor:E,total:b.length}}}u(ue,"handleHistorySearch");function le(t){const e=(t.query.get("ref")??"").trim(),n=t.query.get("frontmatter")==="true";if(!e.includes(":"))throw p(`ref must be <node-id>:<relpath>; received: ${e}`,{received:e,next:"Get a valid ref from `crtr canvas history search` or `crtr node inspect artifacts <node-id>`."});let r;try{r=W(e)}catch(o){throw p(`ref rejected: ${o.message}`,{received:e,next:"Refs come verbatim from `crtr canvas history search`; do not hand-construct relpaths."})}if(r===null)throw Z(`no artifact for ref: ${e}`,{ref:e,next:"The node or file may not exist. Re-run `crtr canvas history search` for a current ref, or `crtr node inspect artifacts <node-id>` to list a node's artifacts."});const s=r.source==="report"&&r.reportKind?`report:${r.reportKind}`:r.source,i=(t.query.get("format")==="raw"||n)&&r.raw!==null?r.raw:r.body;return{status:200,body:{ref:e,node:`${r.nodeName} (${r.nodeId})`,source:s,ts:r.ts,content:i}}}u(le,"handleHistoryRead");function fe(t){for(const[r]of t.query)if(r!=="live")throw p(`unknown snapshot query parameter: ${r}`);const e=t.query.getAll("live");if(e.length>1)throw p("live must appear at most once");const n=e[0];if(n===void 0||n==="false"||n==="0")return!1;if(n==="true"||n==="1")return!0;throw p("live must be true or false")}u(fe,"snapshotLiveOnly");async function me(t){const e=new Set(R().map(o=>o.node_id)),n=await z(M,e,fe(t)),r=j(n.map(o=>o.node_id));await Q(M,n,r);const s=n.map(o=>{const c=o.srcRow,f=c?.host_kind??"tmux";return{node_id:o.node_id,name:o.name,title:o.handle??o.name,description:o.description??null,kind:o.kind,mode:o.mode,lifecycle:o.lifecycle??c?.lifecycle,status:o.status,cwd:o.cwd,profile_id:o.profile_id??null,parent:c?.parent??null,created:o.created,host_kind:f,enterable:f==="broker",attention_count:o.asks,...c?.cycles!=null?{cycles:c.cycles}:{},...o.mtimeMs!==void 0?{last_activity:new Date(o.mtimeMs).toISOString()}:{},ctx_tokens:o.ctx_tokens,streaming:o.streaming??!1,jobs_active:c!==void 0&&J(c.pi_pid)?q(H(o.node_id)).length:0,waiting_for:c?.waiting_for??null,hanging:o.hanging??null,viewed:o.viewed??!1,final_report:c?.final_report??null,finalized_at:c?.finalized_at??null}}),a=new Map;for(const o of C()){const c=a.get(o.from_id);c===void 0?a.set(o.from_id,[o.to_id]):c.push(o.to_id)}const i=Object.fromEntries(n.map(o=>[o.node_id,a.get(o.node_id)??[]]));return{status:200,body:{generated_at:k(),nodes:s,subscriptions:i}}}u(me,"handleSnapshot");function L(){const t=D().map(n=>{const r=n.host_kind??"tmux";return{node_id:n.node_id,name:n.name,kind:n.kind,mode:n.mode,lifecycle:n.lifecycle,status:n.status,cwd:n.cwd,host_kind:r,enterable:r==="broker",parent:n.parent,created:n.created}}),e=C().map(n=>({from_id:n.from_id,to_id:n.to_id,active:n.active,created:n.created}));return{generated_at:k(),nodes:t,edges:e}}u(L,"buildRoster");let g=null,w=null;function pe(){return g===null?(g=Promise.resolve().then(()=>L()).finally(()=>{g=null}),g):(w===null&&(w=g.then(()=>L()).finally(()=>{w=null})),w)}u(pe,"rosterCoalesced");async function he(){return{status:200,body:await pe()}}u(he,"handleRoster");function ye(){return{status:200,body:{generated_at:k(),nodes:I().map(ee),edges:C().map(e=>({from_id:e.from_id,to_id:e.to_id,active:e.active,created:e.created})),focused_node_ids:R().map(e=>e.node_id)}}}u(ye,"handleGraph");function _e(t){if(t==null)return{};if(typeof t!="object")throw p("prune body must be a JSON object");const e=t,n=u(f=>{const m=e[f];if(m!==void 0){if(typeof m!="number"||!Number.isFinite(m))throw p(`${f} must be a number`);return m}},"num"),r=u(f=>{const m=e[f];if(m!==void 0){if(typeof m!="boolean")throw p(`${f} must be a boolean`);return m}},"bool"),s={},a=n("limit");a!==void 0&&(s.limit=a);const i=n("ttl_days");i!==void 0&&(s.ttl_days=i);const d=r("include_stale");d!==void 0&&(s.include_stale=d);const o=r("empty");o!==void 0&&(s.empty=o);const c=r("dry_run");return c!==void 0&&(s.dry_run=c),s}u(_e,"parsePruneBody");function be(t){const e=_e(t.body),n=e.ttl_days??14,r=e.dry_run??!1;let s;return e.limit!==void 0?s=A({maxNodes:e.limit,dryRun:r}).pruned.map(d=>({node_id:d.node_id,status:d.status,created:d.created})):e.empty===!0?s=X({dryRun:r}).map(d=>({node_id:d,status:"empty",created:""})):s=K({ttlDays:n,dryRun:r,includeStale:e.include_stale??!1}).pruned.map(d=>({node_id:d.node_id,status:d.status,created:d.created})),{status:200,body:{pruned:s,dry_run:r,ttl_days:n}}}u(be,"handlePrune");const Te=[{method:"GET",pattern:"/v1/canvas/attention",handler:te},{method:"POST",pattern:"/v1/canvas/attention/counts",handler:ne},{method:"POST",pattern:"/v1/canvas/history/search",handler:ue},{method:"POST",pattern:"/v1/canvas/history/grep",handler:ce},{method:"GET",pattern:"/v1/canvas/history/read",handler:le},{method:"POST",pattern:"/v1/canvas/history/stats",handler:ae},{method:"GET",pattern:"/v1/canvas/snapshot",handler:me},{method:"GET",pattern:"/v1/canvas/roster",handler:he},{method:"GET",pattern:"/v1/canvas/graph",handler:ye},{method:"POST",pattern:"/v1/canvas/prune",ownerOnly:!0,handler:be}];export{Te as canvasRoutes};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RouteTable } from '../router.js';
|
|
2
2
|
export declare const cronRoutes: RouteTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var z=Object.defineProperty;var o=(e,n)=>z(e,"name",{value:n,configurable:!0});import{randomUUID as B}from"node:crypto";import{ApiError as d}from"../../../api/errors.js";import{armCron as G,cancelCron as L,getCron as l,listCrons as y,listCronRuns as j,pokeHeldCrons as Z,setCronState as q}from"../../../core/canvas/crons.js";import{getNode as k}from"../../../core/canvas/canvas.js";import{assertScope as
|
|
1
|
+
var z=Object.defineProperty;var o=(e,n)=>z(e,"name",{value:n,configurable:!0});import{randomUUID as B}from"node:crypto";import{ApiError as d}from"../../../api/errors.js";import{armCron as G,cancelCron as L,getCron as l,listCrons as y,listCronRuns as j,pokeHeldCrons as Z,setCronState as q}from"../../../core/canvas/crons.js";import{getNode as k}from"../../../core/canvas/canvas.js";import{assertScope as C}from"../../../core/scopes.js";import{InputError as t}from"../../../core/io.js";import{nextSlotAfter as H}from"../../../core/wake.js";import{executeCron as Y,markSelfCanceledCronRun as F}from"../../cron-run.js";import{toCronDTO as f,toCronRunDTO as N}from"../map.js";import{assertCeiling as K}from"../router.js";function h(e,n){const r=e[n];if(typeof r!="string"||r.trim()==="")throw new t({error:"missing_field",message:`cron arm request requires a non-empty "${n}"`,field:n,next:`Send a non-empty string "${n}".`});return r}o(h,"requireString");function m(e,n,r,s){if(e==null)return s;if(typeof e=="string"&&r.includes(e))return e;throw new t({error:`bad_${n}`,message:`"${String(e)}" is not a valid ${n}`,field:n,next:`Use one of: ${r.join(", ")}.`})}o(m,"requireEnum");function M(e){if(e==="human")return JSON.stringify({human:!0});if(e.startsWith("node:")){const n=e.slice(5).trim();if(n==="")throw new t({error:"bad_sink",message:"node sink requires a node id",field:"sink",next:'Use sink "node:<node-id>".'});if(k(n)===null)throw new t({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 t({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 t({error:"bad_sink",message:`"${e}" is not a valid sink`,field:"sink",next:"Use node:<node-id>, spawn:<kind>, or human."})}o(M,"parseSinkSpec");const Q=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;function T(e,n){if(!Q.test(e)||Number.isNaN(Date.parse(e)))throw new t({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}o(T,"requireUtcIso");function V(e){try{H(e,new Date)}catch(n){throw new t({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}o(V,"requireRecur");function X(e){let n;try{n=JSON.parse(e)}catch{throw new t({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 t({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,s]of Object.entries(n))if(typeof s!="string")throw new t({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}o(X,"requireEnvJson");function b(e){const n=e.query.get("profile");return n!==null&&n.trim()!==""?n.trim():null}o(b,"callerProfile");function E(e,n){return n===null||e.scope==="global"||e.profile===n}o(E,"inScope");function nn(e){const n=e.body??{},r=h(n,"name"),s=h(n,"command"),a=T(h(n,"fire_at"),"fire_at"),P=h(n,"cwd"),A=typeof n.recur=="string"&&n.recur!==""?V(n.recur):null,J=typeof n.expires_at=="string"&&n.expires_at!==""?T(n.expires_at,"expires_at"):null,U=typeof n.env_json=="string"&&n.env_json!==""?X(n.env_json):null,i=typeof n.sink=="string"&&n.sink.trim()!==""?n.sink.trim():null,c=m(n.on_output,"on_output",["silent","on-failure","always","on-change"],i?.startsWith("node:")?"on-change":"on-failure"),R=m(n.overlap,"overlap",["skip","queue","replace"],"skip"),D=m(n.tier,"tier",["normal","urgent","critical"],"normal"),x=c==="always"||c==="on-change";if(x&&i===null)throw new t({error:"sink_required",message:`on_output "${c}" 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(!x&&i!==null)throw new t({error:"sink_unused",message:`on_output "${c}" 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 W=i!==null?M(i):"{}";i?.startsWith("spawn:")&&K(e.ceiling,"act");const g=typeof n.created_by=="string"&&n.created_by!==""?n.created_by:null;if(g!==null){const v=k(g);v!==null&&(C(v,"schedule"),i?.startsWith("spawn:")&&C(v,"act"))}const u=typeof n.anchor_node=="string"&&n.anchor_node!==""?n.anchor_node:null;if(u!==null&&k(u)===null)throw new t({error:"anchor_not_found",message:`anchor node ${u} 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 O=n.cancel_on_wake===!0;if(O&&u===null)throw new t({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 p=n.run_timeout_s??300;if(typeof p!="number"||!Number.isInteger(p)||p<=0)throw new t({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 w=typeof n.profile=="string"&&n.profile!==""?n.profile:null,$=m(n.scope,"scope",["profile","global"],w!==null?"profile":"global");if($==="profile"&&w===null)throw new t({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.'});const I=B();return G({cron_id:I,name:r,created_by:g,command:s,fire_at:a,recur:A,tz:typeof n.tz=="string"?n.tz:null,expires_at:J,anchor_node:u,cancel_on_wake:O,cwd:P,env_json:U,profile:w,scope:$,run_timeout_s:p,overlap:R,on_output:c,sink:W,tier:D}),{status:201,body:f(l(I),null)}}o(nn,"handleArm");function en(e){const n=b(e);return{status:200,body:(n===null?y({all:!0}):[...y({profile:n}),...y({global:!0})].sort((s,a)=>s.fire_at<a.fire_at?-1:s.fire_at>a.fire_at?1:0)).map(s=>f(s,S(s)))}}o(en,"handleList");function S(e){return j(e.cron_id)[0]??null}o(S,"lastRunOf");function _(e){const n=e.params.cronId,r=l(n);if(r===null)throw new d(404,"cron_not_found",`no cron ${n}`);if(!E(r,b(e)))throw new d(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);return r}o(_,"requireCron");function rn(e){const n=_(e),r=j(n.cron_id);return{status:200,body:{cron:f(n,r[0]??null),runs:r.map(N)}}}o(rn,"handleShow");function on(e){const n=_(e);q(n.cron_id,"paused");const r=l(n.cron_id);return{status:200,body:f(r,S(r))}}o(on,"handlePause");function tn(e){const n=_(e);q(n.cron_id,"active");const r=l(n.cron_id);return{status:200,body:f(r,S(r))}}o(tn,"handleResume");async function sn(e){const n=_(e);if(n.run_state==="running")throw new d(409,"cron_running",`cron ${n.cron_id} has a run in flight \u2014 wait for it to settle`);const r=await Y(n,{outOfBand:!0});if(r===null)throw new d(409,"cron_running",`cron ${n.cron_id} was claimed by a concurrent run \u2014 check \`cron show\` for its result`);return{status:200,body:N(r)}}o(sn,"handleRun");function an(){const e=new Date().toISOString();return{status:200,body:{unparked:Z(e),at:e}}}o(an,"handlePoke");function cn(e){const n=e.params.cronId,r=l(n);if(r!==null&&!E(r,b(e)))throw new d(404,"cron_not_found",`no cron ${n} in this profile \u2014 it is scoped to another profile`);const s=e.query.get("run_id"),a=s!==null&&F(n,s);return L(n,{preserveRunningProcess:a}),{status:204}}o(cn,"handleCancel");const yn=[{method:"POST",pattern:"/v1/crons",scope:"schedule",handler:nn},{method:"GET",pattern:"/v1/crons",handler:en},{method:"GET",pattern:"/v1/crons/:cronId",handler:rn},{method:"POST",pattern:"/v1/crons/:cronId/pause",scope:"schedule",handler:on},{method:"POST",pattern:"/v1/crons/:cronId/resume",scope:"schedule",handler:tn},{method:"POST",pattern:"/v1/crons/poke",scope:"schedule",handler:an},{method:"POST",pattern:"/v1/crons/:cronId/run",scope:"schedule",handler:sn},{method:"DELETE",pattern:"/v1/crons/:cronId",scope:"schedule",handler:cn}];export{yn as cronRoutes};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RouteTable } from '../router.js';
|
|
2
2
|
export declare const daemonRoutes: RouteTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var i=Object.defineProperty;var s=(e,t)=>i(e,"name",{value:t,configurable:!0});import{toMigrateStateDTO as u}from"../map.js";import{boundDaemonControl as
|
|
1
|
+
var i=Object.defineProperty;var s=(e,t)=>i(e,"name",{value:t,configurable:!0});import{toMigrateStateDTO as u}from"../map.js";import{boundDaemonControl as a}from"../../control.js";import{ownerOnly as m}from"../router.js";function c(){const e=a();if(e===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot hand over"};const{graceMs:t}=e.requestHandover();return{status:202,body:{accepted:!0,predecessor_pid:process.pid,predecessor_epoch:e.epoch,grace_ms:t}}}s(c,"handleRestart");async function l(e){if(e.body!==void 0)throw{status:400,code:"invalid_request",message:"admission request must not include a body"};const t=a();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot admit normal work"};return{status:200,body:await t.admitStartup()}}s(l,"handleAdmit");async function y(e){const t=a();if(t===null)throw{status:503,code:"daemon_control_unavailable",message:"this API server is not hosted by a live daemon and cannot migrate its corpus"};const r=e.body;if(r!==void 0&&(typeof r!="object"||r===null||Array.isArray(r)))throw{status:400,code:"invalid_request",message:"migration request body must be an object"};const o=r??{};if(o.dry_run!==void 0&&typeof o.dry_run!="boolean")throw{status:400,code:"invalid_request",message:"dry_run must be a boolean"};if(o.dirs!==void 0&&(!Array.isArray(o.dirs)||o.dirs.some(d=>typeof d!="string")))throw{status:400,code:"invalid_request",message:"dirs must be an array of strings"};const n=await t.repairMigrations({dryRun:o.dry_run===!0,dirs:o.dirs??[]});return{status:200,body:u(n.result,{resumed:n.resumed,resumeError:n.resumeError})}}s(y,"handleMigrate");const v=m([{method:"POST",pattern:"/v1/daemon/restart",handler:s(e=>c(),"handler")},{method:"POST",pattern:"/v1/daemon/migrate",handler:y},{method:"POST",pattern:"/v1/daemon/admit",handler:l}]);export{v as daemonRoutes};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var E=Object.defineProperty;var c=(e,t)=>E(e,"name",{value:t,configurable:!0});import{closeSync as y,fsyncSync as S,fstatSync as T,lstatSync as $,mkdirSync as A,openSync as g,readSync as q,readdirSync as _,realpathSync as I,renameSync as L,statSync as N,unlinkSync as k,writeSync as z}from"node:fs";import{randomUUID as B}from"node:crypto";import{basename as M,dirname as F,isAbsolute as O,join as b}from"node:path";import{ApiError as d}from"../../../api/index.js";import{notFound as P,usage as u}from"../../../core/errors.js";import{InputError as D}from"../../../core/io.js";const m=512*1024,w=1024*1024,R=1e3;function h(e,t){if(e===null||e==="")throw u(`a file ${t} requires a \`path\` ${t==="write"?"body field":"query param"}`);if(!O(e))throw u(`file ${t} path must be absolute`,{received:e});return e}c(h,"requireAbsolutePath");function p(e){if(e==null)return"utf8";if(e==="utf8"||e==="base64")return e;throw u("encoding must be `utf8` or `base64`",{received:e})}c(p,"parseEncoding");function l(e,t,n){if(e instanceof d||e instanceof D)throw e;const r=e.code;throw r==="ENOENT"?P(`no such file: ${t}`,{received:t}):r==="EACCES"||r==="EPERM"?new d(403,"forbidden",`permission denied ${n} ${t}`):new d(500,"internal",`cannot ${n} ${t}: ${e instanceof Error?e.message:String(e)}`)}c(l,"mapFilesystemError");function U(e,t){let n;try{n=g(e,"r")}catch(r){return l(r,e,"reading")}try{const r=T(n);if(r.isDirectory())throw u(`file peek path is a directory: ${e}`,{received:e});const f=Buffer.allocUnsafe(Math.min(r.size,m)+1),o=q(n,f,0,f.length,0),i=o>m,s=f.subarray(0,Math.min(o,m)).toString(t);return{path:e,content:s,truncated:i}}catch(r){return l(r,e,"reading")}finally{y(n)}}c(U,"readHead");function j(e){const t=h(e.query.get("path"),"peek");return{status:200,body:U(t,p(e.query.get("encoding")))}}c(j,"handlePeek");function C(e){return e.length%4===0&&/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}c(C,"isBase64");function W(e){if(typeof e!="object"||e===null||Array.isArray(e))throw u("file write requires an object body");const t=e,n=h(typeof t.path=="string"?t.path:null,"write");if(typeof t.content!="string")throw u("file write requires a string `content` body field");const r=p(t.encoding);if(r==="base64"&&!C(t.content))throw u("base64 file content must be valid base64");return{path:n,content:t.content,encoding:r}}c(W,"parseWriteBody");function Z(e){try{if(!$(e).isSymbolicLink())return e}catch(t){return t.code!=="ENOENT"?l(t,e,"resolving"):e}try{return I(e)}catch(t){return l(t,e,"resolving")}}c(Z,"writeTarget");function v(e,t){if(t.byteLength>w)throw new d(413,"file_too_large",`file content exceeds the ${w} byte limit`);const n=Z(e),r=F(n);try{A(r,{recursive:!0})}catch(i){return l(i,e,"creating parent directory for")}const f=b(r,`.${M(e)}.${B()}.tmp`);let o;try{o=g(f,"wx",384);let i=0;for(;i<t.length;)i+=z(o,t,i,t.length-i);return S(o),y(o),o=void 0,L(f,n),{path:e,bytes_written:t.byteLength}}catch(i){return l(i,e,"writing")}finally{o!==void 0&&y(o);try{k(f)}catch(i){if(i.code!=="ENOENT")throw i}}}c(v,"writeAtomically");function G(e){const{path:t,content:n,encoding:r}=W(e.body);return{status:200,body:v(t,Buffer.from(n,r))}}c(G,"handleWrite");function X(e){if(e===null)return R;const t=Number(e);if(!Number.isInteger(t)||t<1)throw u("file list limit must be a positive integer",{received:e});return t}c(X,"parseLimit");function Y(e){const t=h(e.query.get("path"),"list"),n=X(e.query.get("limit"));let r;try{r=_(t,{withFileTypes:!0}).sort((s,a)=>s.name.localeCompare(a.name))}catch(s){return l(s,t,"listing")}const f=r.slice(0,n),o=[];try{for(const s of f){const a=N(b(t,s.name));o.push({name:s.name,type:a.isFile()?"file":a.isDirectory()?"dir":"other",size:a.size,modified:a.mtime.toISOString()})}}catch(s){return l(s,t,"listing")}return{status:200,body:{path:t,entries:o,truncated:r.length>n}}}c(Y,"handleList");const te=[{method:"GET",pattern:"/v1/files/peek",scope:"act",handler:j},{method:"POST",pattern:"/v1/files/write",scope:"act",handler:G},{method:"GET",pattern:"/v1/files/list",scope:"act",handler:Y}];export{te as fileRoutes};
|