@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 +1 @@
|
|
|
1
|
-
var g=Object.defineProperty;var
|
|
1
|
+
var g=Object.defineProperty;var a=(e,t)=>g(e,"name",{value:t,configurable:!0});import{toErrorBody as E}from"./map.js";import{emitEvent as O}from"../../core/events/emit.js";import{scopeAllowed as b,ScopeDeniedError as S}from"../../core/scopes.js";import{ApiError as _}from"../../api/errors.js";function x(e,t){if(!b(e,t))throw new S("the bearer token",t)}a(x,"assertCeiling");function B(){return new _(403,"owner_only","this operation is owner-only; a scoped token does not reach it")}a(B,"ownerOnlyError");function R(e){return e.map(t=>({...t,ownerOnly:!0}))}a(R,"ownerOnly");const h=4*1024*1024;class T{static{a(this,"Router")}routes=[];register(t){return this.routes.push({method:t.method.toUpperCase(),segments:f(t.pattern),handler:t.handler,ownerOnly:t.ownerOnly===!0,scope:t.scope}),this}registerAll(t){for(const n of t)this.register(n);return this}match(t,n){const s=t.toUpperCase(),r=f(n);for(const o of this.routes){if(o.method!==s)continue;const i=C(o.segments,r);if(i!==null)return{handler:o.handler,params:i,ownerOnly:o.ownerOnly,scope:o.scope}}return null}async handle(t,n,s){try{const r=new URL(t.url??"/","http://127.0.0.1"),o=(t.method??"GET").toUpperCase(),i=this.match(o,r.pathname);if(i===null){y(n,404,"not_found",`no route for ${o} ${r.pathname}`);return}if(i.ownerOnly&&s!==null)throw B();i.scope!==void 0&&x(s,i.scope);let c;try{c=await J(t)}catch(d){const u=d instanceof l?"payload_too_large":"invalid_request";y(n,u==="payload_too_large"?413:400,u,d.message);return}const m={method:o,path:r.pathname,params:i.params,query:r.searchParams,body:c,ceiling:s,req:t,res:n},w=await i.handler(m);U(n,w)}catch(r){const{status:o,body:i}=E(r);o>=500&&O({event:"api.request.failed",level:"error",error:r instanceof Error?r:new Error(String(r)),fields:{method:(t.method??"GET").toUpperCase(),path:t.url??""}}),p(n,o,i)}}}function f(e){return e.split("/").filter(t=>t.length>0)}a(f,"splitPath");function C(e,t){if(e.length!==t.length)return null;const n={};for(let s=0;s<e.length;s++){const r=e[s],o=t[s];if(r.startsWith(":"))n[r.slice(1)]=decodeURIComponent(o);else if(r!==o)return null}return n}a(C,"matchSegments");class l extends Error{static{a(this,"BodyTooLargeError")}}async function J(e){const t=[];let n=0;for await(const r of e){const o=r;if(n+=o.length,n>h)throw new l(`request body exceeds ${h} bytes`);t.push(o)}if(n===0)return;const s=Buffer.concat(t).toString("utf8").trim();if(s!=="")try{return JSON.parse(s)}catch{throw new Error("request body is not valid JSON")}}a(J,"readJsonBody");function U(e,t){if(e.headersSent)return;const n={...t.headers??{}};if(t.rawBody!==void 0){e.writeHead(t.status,n),e.end(t.rawBody);return}if(t.status===204||t.body===void 0){e.writeHead(t.status,n),e.end();return}n["content-type"]===void 0&&(n["content-type"]="application/json; charset=utf-8"),e.writeHead(t.status,n),e.end(JSON.stringify(t.body))}a(U,"writeResult");function p(e,t,n){if(e.headersSent){e.end();return}e.writeHead(t,{"content-type":"application/json; charset=utf-8"}),e.end(JSON.stringify(n))}a(p,"writeJson");function y(e,t,n,s){p(e,t,{error:{code:n,message:s}})}a(y,"writeError");export{T as Router,x as assertCeiling,R as ownerOnly,B as ownerOnlyError};
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
var
|
|
2
|
-
content-type: application/json; charset=utf-8\r
|
|
3
|
-
content-length: ${Buffer.byteLength(s)}\r
|
|
4
|
-
connection: close\r
|
|
5
|
-
\r
|
|
6
|
-
${s}`;try{e.end(a)}catch{e.destroy()}}i(Ce,"destroyUpgradeStartupBlocked");function Oe(e){const t=JSON.stringify(H),s=`HTTP/1.1 401 Unauthorized\r
|
|
7
|
-
content-type: application/json; charset=utf-8\r
|
|
8
|
-
content-length: ${Buffer.byteLength(t)}\r
|
|
9
|
-
connection: close\r
|
|
10
|
-
\r
|
|
11
|
-
${t}`;try{e.end(s)}catch{try{e.destroy()}catch{}}}i(Oe,"destroyUpgradeUnauthorized");function I(e){return new Promise(t=>{try{e.closeAllConnections()}catch{}e.close(()=>t())})}i(I,"closeServer");function fr(e={}){let t=null;const s=Re(()=>t),a=new U().registerAll(E).registerAll(k).registerAll(P),m=i((o,r)=>{const u=F()?.startupBlock()??null;return u===null||o==="GET"&&(r==="/healthz"||r==="/v1/status")||o==="POST"&&(r==="/v1/daemon/migrate"||r==="/v1/daemon/admit")||u.phase==="recovering"&&a.match(o,r)!==null?null:u},"startupBlockFor"),c=x(),A=i((o,r)=>{const u=i(n=>{g({event:"api.request.failed",level:"error",error:n instanceof Error?n:new Error(String(n))});try{r.headersSent||r.writeHead(500,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"internal",message:"internal error"}}))}catch{}},"internalFailure");let p;try{p=new URL(o.url??"/","http://127.0.0.1")}catch{try{r.writeHead(400,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"invalid_request",message:"request URL is invalid"}}))}catch{}return}try{const n=m((o.method??"GET").toUpperCase(),p.pathname);if(n!==null){we(r,n.message);return}s.handle(o,r).catch(u)}catch(n){u(n)}},"onRequest"),y=i((o,r,u)=>{r.on("error",()=>{});try{const p=new URL(o.url??"/","http://127.0.0.1"),n=m((o.method??"GET").toUpperCase(),p.pathname);if(n!==null){Ce(r,n.message);return}const l=ve(o.method??"GET",p.pathname);if(l===null){r.destroy();return}D(o,r,u,l).catch(()=>{try{r.destroy()}catch{}})}catch{try{r.destroy()}catch{}}},"onUpgrade"),B=i(o=>{o.on("error",r=>{g({event:"api.server.failed",level:"error",error:r})})},"bindErrorHandler"),f=w(A);f.on("upgrade",y);let v=!1,T,b;const $=new Promise((o,r)=>{T=o,b=r});f.once("listening",()=>{v=!0,T()}),f.on("error",o=>{g({event:"api.server.failed",level:"error",error:o}),v||b(o)});try{C(c)&&O(c)}catch{}f.listen(c,()=>{try{z(c,384)}catch(o){g({event:"api.socket.chmod_failed",level:"error",error:o instanceof Error?o:new Error(String(o))})}});let d;const h=e.tcp??L()??G("user").api.tcp;if(h!==void 0&&h!==""){const o=Te(h);if(o===null)g({event:"api.tcp.invalid",level:"error",error:new Error(`invalid CRTRD_TCP/--tcp spec: ${h}`)});else{const r=e.token??_()??J("user"),u=r===void 0?A:(n,l)=>{if((n.method??"GET").toUpperCase()==="OPTIONS"){l.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":"authorization, content-type","Access-Control-Allow-Methods":"GET, POST, PATCH, PUT, DELETE, OPTIONS","Access-Control-Max-Age":"600"}),l.end();return}if(!N(n,r)){Se(l);return}l.setHeader("Access-Control-Allow-Origin","*"),A(n,l)},p=r===void 0?y:(n,l,R)=>{if(!N(n,r)){Oe(l);return}y(n,l,R)};d=w(u),d.on("upgrade",p),B(d),d.once("listening",()=>{const n=d?.address();if(n!==null&&typeof n=="object"){const{address:l,port:R}=n;t=be(l,R)}}),d.listen(o.port,o.host)}}let S=!1;return{ready:$,tcpAddress:i(()=>t,"tcpAddress"),close:i(async()=>{if(!S){S=!0,await Promise.all([I(f),d!==void 0?I(d):Promise.resolve()]);try{C(c)&&O(c)}catch{}}},"close")}}i(fr,"createApiServer");export{fr as createApiServer};
|
|
1
|
+
var L=Object.defineProperty;var s=(t,o)=>L(t,"name",{value:o,configurable:!0});import{createServer as O}from"node:http";import{chmodSync as J,existsSync as U,unlinkSync as P}from"node:fs";import{apiSocketPath as D}from"../../core/canvas/paths.js";import{readConfig as F}from"../../core/config.js";import{getApiToken as M,listScopedApiTokens as $}from"../../core/secrets.js";import{emitEvent as A}from"../../core/events/emit.js";import{envCrtrdTcp as W,envCrtrdToken as Y}from"../../shared/env.js";import{Router as N}from"./router.js";import{handleAttachUpgrade as Z,refuseUpgrade as b}from"./bridge.js";import{boundDaemonControl as K}from"../control.js";import{attachRoutes as Q}from"./handlers/attach.js";import{bashJobRoutes as V}from"./handlers/bash-jobs.js";import{bashRoutes as X}from"./handlers/bash.js";import{brokerOperationRoutes as H}from"./handlers/broker-ops.js";import{brokerRecoveryRoutes as I,nodeFaultRoutes as q}from"./handlers/broker-recovery.js";import{canvasRoutes as ee}from"./handlers/canvas.js";import{chatInventoryRoutes as re}from"./handlers/chat-inventory.js";import{daemonRoutes as te}from"./handlers/daemon.js";import{fileRoutes as oe}from"./handlers/files.js";import{focusRoutes as ne}from"./handlers/focus.js";import{healthRoutes as ie}from"./handlers/health.js";import{humanRequestRoutes as se}from"./handlers/human-requests.js";import{inboxRoutes as le}from"./handlers/inbox.js";import{feedbackCommentRoutes as ce}from"./handlers/feedback-comments.js";import{memoryRoutes as ae}from"./handlers/memory.js";import{brokerMailRoutes as _,messageRoutes as ue}from"./handlers/messages.js";import{modelAuthRoutes as de}from"./handlers/modelauth.js";import{modelConfigRoutes as me}from"./handlers/model-config.js";import{nodeRoutes as pe}from"./handlers/nodes.js";import{nodeOutcomeRoutes as fe}from"./handlers/node-outcomes.js";import{nodeEventRoutes as ge}from"./handlers/node-events.js";import{profileRoutes as he}from"./handlers/profiles.js";import{prospectiveChatInventoryRoutes as Ae}from"./handlers/prospective-chat-inventory.js";import{reportRoutes as Re}from"./handlers/reports.js";import{reviewCommentRoutes as ve}from"./handlers/review-comments.js";import{reviewRoutes as ye}from"./handlers/reviews.js";import{subscriptionRoutes as be}from"./handlers/subscriptions.js";import{cronRoutes as we}from"./handlers/crons.js";import{worktreeRoutes as Te}from"./handlers/worktree.js";function Se(t){return new N().registerAll(ie(t)).registerAll(te).registerAll(pe).registerAll(fe).registerAll(ge).registerAll(V).registerAll(X).registerAll(H).registerAll(I).registerAll(q).registerAll(Te).registerAll(_).registerAll(ue).registerAll(Re).registerAll(Q).registerAll(ee).registerAll(oe).registerAll(ae).registerAll(ne).registerAll(be).registerAll(we).registerAll(he).registerAll(de).registerAll(me).registerAll(ye).registerAll(ve).registerAll(le).registerAll(se).registerAll(re).registerAll(Ae).registerAll(ce)}s(Se,"buildRouter");function Ce(t,o){if(t.toUpperCase()!=="GET")return null;const i=o.split("/").filter(f=>f.length>0);if(i.length!==4||i[0]!=="v1"||i[1]!=="nodes"||i[3]!=="attach")return null;const u=i[2];return u===void 0||u===""?null:decodeURIComponent(u)}s(Ce,"attachNodeId");function ke(t){const o=t.trim();if(o==="")return null;const i=o.lastIndexOf(":");if(i<0)return null;const u=o.slice(0,i),f=o.slice(i+1),a=Number(f);return!Number.isInteger(a)||a<0||a>65535?null:{host:u===""?"0.0.0.0":u,port:a}}s(ke,"parseTcp");function Ee(t,o){return`${t.includes(":")?`[${t}]`:t}:${o}`}s(Ee,"formatTcpAddress");const w={code:"unauthorized",message:"missing or invalid bearer token"},x={code:"owner_only",message:"attach is owner-only; a scoped token does not reach it"};function Oe(t){const o=new Map;for(const i of $("user"))o.set(i.token,i.scopes);return o.set(t,null),o}s(Oe,"resolveBearers");function z(t,o){const i=t.headers.authorization;if(!(typeof i!="string"||!i.startsWith("Bearer ")))return o.get(i.slice(7))}s(z,"authorize");function Ue(t){t.writeHead(401,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({error:w}))}s(Ue,"writeUnauthorized");function Pe(t,o){t.writeHead(503,{"content-type":"application/json; charset=utf-8"}),t.end(JSON.stringify({error:{code:"startup_blocked",message:o}}))}s(Pe,"writeStartupBlocked");function B(t){return new Promise(o=>{try{t.closeAllConnections()}catch{}t.close(()=>o())})}s(B,"closeServer");function Rr(t={}){let o=null;const i=Se(()=>o),u=new N().registerAll(H).registerAll(I).registerAll(_),f=s((e,r)=>{const c=K()?.startupBlock()??null;return c===null||e==="GET"&&(r==="/healthz"||r==="/v1/status")||e==="POST"&&(r==="/v1/daemon/migrate"||r==="/v1/daemon/admit")||c.phase==="recovering"&&u.match(e,r)!==null?null:c},"startupBlockFor"),a=D(),v=s((e,r,c)=>{const m=s(n=>{A({event:"api.request.failed",level:"error",error:n instanceof Error?n:new Error(String(n))});try{r.headersSent||r.writeHead(500,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"internal",message:"internal error"}}))}catch{}},"internalFailure");let p;try{p=new URL(e.url??"/","http://127.0.0.1")}catch{try{r.writeHead(400,{"content-type":"application/json; charset=utf-8"}),r.end(JSON.stringify({error:{code:"invalid_request",message:"request URL is invalid"}}))}catch{}return}try{const n=f((e.method??"GET").toUpperCase(),p.pathname);if(n!==null){Pe(r,n.message);return}i.handle(e,r,c).catch(m)}catch(n){m(n)}},"onRequest"),y=s((e,r,c)=>{r.on("error",()=>{});try{const m=new URL(e.url??"/","http://127.0.0.1"),p=f((e.method??"GET").toUpperCase(),m.pathname);if(p!==null){b(r,503,"Service Unavailable","startup_blocked",p.message);return}const n=Ce(e.method??"GET",m.pathname);if(n===null){r.destroy();return}Z(e,r,c,n).catch(()=>{try{r.destroy()}catch{}})}catch{try{r.destroy()}catch{}}},"onUpgrade"),G=s(e=>{e.on("error",r=>{A({event:"api.server.failed",level:"error",error:r})})},"bindErrorHandler"),h=O((e,r)=>v(e,r,null));h.on("upgrade",y);let T=!1,S,C;const j=new Promise((e,r)=>{S=e,C=r});h.once("listening",()=>{T=!0,S()}),h.on("error",e=>{A({event:"api.server.failed",level:"error",error:e}),T||C(e)});try{U(a)&&P(a)}catch{}h.listen(a,()=>{try{J(a,384)}catch(e){A({event:"api.socket.chmod_failed",level:"error",error:e instanceof Error?e:new Error(String(e))})}});let d;const R=t.tcp??W()??F("user").api.tcp;if(R!==void 0&&R!==""){const e=ke(R);if(e===null)A({event:"api.tcp.invalid",level:"error",error:new Error(`invalid CRTRD_TCP/--tcp spec: ${R}`)});else{const r=t.token??Y()??M("user"),c=r===void 0?void 0:Oe(r),m=c===void 0?(n,l)=>v(n,l,null):(n,l)=>{if((n.method??"GET").toUpperCase()==="OPTIONS"){l.writeHead(204,{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":"authorization, content-type","Access-Control-Allow-Methods":"GET, POST, PATCH, PUT, DELETE, OPTIONS","Access-Control-Max-Age":"600"}),l.end();return}const g=z(n,c);if(g===void 0){Ue(l);return}l.setHeader("Access-Control-Allow-Origin","*"),v(n,l,g)},p=c===void 0?y:(n,l,g)=>{const E=z(n,c);if(E===void 0){b(l,401,"Unauthorized",w.code,w.message);return}if(E!==null){b(l,403,"Forbidden",x.code,x.message);return}y(n,l,g)};d=O(m),d.on("upgrade",p),G(d),d.once("listening",()=>{const n=d?.address();if(n!==null&&typeof n=="object"){const{address:l,port:g}=n;o=Ee(l,g)}}),d.listen(e.port,e.host)}}let k=!1;return{ready:j,tcpAddress:s(()=>o,"tcpAddress"),close:s(async()=>{if(!k){k=!0,await Promise.all([B(h),d!==void 0?B(d):Promise.resolve()]);try{U(a)&&P(a)}catch{}}},"close")}}s(Rr,"createApiServer");export{Rr as createApiServer};
|
package/docs/plugin/README.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Plugin overview
|
|
3
|
+
description: Expose application operations as native crtr commands with a typed HTTP plugin.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Authoring crtr HTTP plugins
|
|
2
7
|
|
|
3
8
|
`@north-light/crouter-plugin` turns one TypeScript command tree into both a Fetch handler and the archive accepted by `crtr pkg plugin install --endpoint`. Use it when an application should expose typed operations as native `crtr` commands without maintaining `commands.json` or a separate HTTP route definition.
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Bundles and memory docs
|
|
3
|
+
description: Generate install archives and include agent-facing memory documents in a plugin.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Bundles and memory docs
|
|
2
7
|
|
|
3
8
|
`createFetchHandler` builds and serves the install archive automatically. Use `buildBundle` when you need to inspect or save the generated bytes during an application build. Pass the same mount path where the handler is served.
|
package/docs/plugin/commands.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Commands
|
|
3
|
+
description: Define plugin command trees, branches, leaves, and agent-facing descriptions.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Commands
|
|
2
7
|
|
|
3
8
|
`definePlugin` declares the top-level crtr command. `name` must be lowercase kebab case. `description`, `whenToUse`, and `summary` are required text for the plugin, every branch, and every leaf. Write them for an agent choosing a command: describe the concrete object or action, state when the command applies, and state the short outcome.
|
package/docs/plugin/deploying.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Deployment
|
|
3
|
+
description: Serve plugin Fetch handlers with authentication, mount paths, and archive compression.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Deployment
|
|
2
7
|
|
|
3
8
|
`createFetchHandler(plugin, options)` returns `(request: Request) => Promise<Response>`. Use it directly in Cloudflare Workers, Bun, Deno, and any framework route that accepts Fetch `Request` and `Response` objects. A framework with different request types needs only an adapter at its boundary; the package itself has no framework dependency.
|
package/docs/plugin/errors.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Errors and streaming
|
|
3
|
+
description: Report expected application errors and return NDJSON streams from command leaves.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Errors and streaming
|
|
2
7
|
|
|
3
8
|
Throw `LeafError` when an expected application error should be reported to crtr. Its `code` must be lowercase snake case and cannot be `internal`, `unknown_path`, `command_collision`, or `cli_protocol_error`. `status` defaults to `400` and must be an integer from `400` through `599`. Use `field`, `next`, and `received` when they make the fix clearer.
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting started
|
|
3
|
+
description: Install the plugin package, deploy a Fetch handler, and install its commands in crtr.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Getting started
|
|
2
7
|
|
|
3
8
|
Install `@north-light/crouter-plugin`, copy the complete TypeScript file in the [package README](../../packages/crouter-plugin/README.md), and deploy its default export at the URL crtr will reach. The application must provide `ACME_CRTR_TOKEN` to its handler and the machine running crtr must provide the same value.
|
package/docs/plugin/output.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Output fields
|
|
3
|
+
description: Declare typed output fields and validate handler results.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Output fields
|
|
2
7
|
|
|
3
8
|
Each leaf declares an `output` object. Field object keys are returned verbatim in the result object, so `appId` stays `appId`; unlike command and parameter keys, output keys are not converted to kebab case.
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Parameters and handler input
|
|
3
|
+
description: Declare command parameters and infer their handler input types.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Parameters and handler input
|
|
2
7
|
|
|
3
8
|
Parameter object keys become handler input keys. The generated manifest uses their kebab-case form: `appId` becomes `app-id`, while the handler receives `input.appId`.
|
package/docs/sdk/README.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SDK overview
|
|
3
|
+
description: Drive a crouter daemon from a Node or browser application with the typed SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# `@north-light/crouter-sdk`
|
|
2
7
|
|
|
3
8
|
The ESM-only client an application installs to drive a crouter daemon: create agent runs, watch streamed events, wait for typed results, read and write memory, and reach the rest of the daemon's `/v1` API.
|
package/docs/sdk/bash.md
CHANGED
package/docs/sdk/client.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Client construction
|
|
3
|
+
description: Configure local socket and remote HTTP connections, authentication, and request options.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Client construction
|
|
2
7
|
|
|
3
8
|
```ts
|
|
@@ -16,7 +21,7 @@ const customSocketClient = new Crouter({ socketPath: '/custom/path/crtrd.sock' }
|
|
|
16
21
|
|---|---|---|---|
|
|
17
22
|
| `baseURL` | `string` | `CRTR_BASE_URL`, else unset | `http(s)://host:port` of a daemon TCP listener. |
|
|
18
23
|
| `socketPath` | `string` | `CRTR_SOCKET`, else `${CRTR_HOME}/crtrd.sock`, else `~/.crouter/canvas/crtrd.sock` | Unix socket. Node only; throws in a browser. |
|
|
19
|
-
| `token` | `string` | `CRTRD_TOKEN` | Sent as `Authorization: Bearer <token>`. Ignored by a unix-socket daemon, which authenticates by filesystem permission. |
|
|
24
|
+
| `token` | `string` | `CRTRD_TOKEN` | Sent as `Authorization: Bearer <token>`. The owner token or a scoped token from `crtr sys connect`; a scoped token's ceiling is enforced per request (see [Getting started](./getting-started.md#a-token-that-holds-less-than-the-owner)). Ignored by a unix-socket daemon, which authenticates by filesystem permission. |
|
|
20
25
|
| `timeout` | `number` (ms) | `30_000` | Per-request wall clock. Does not apply to a stream. |
|
|
21
26
|
| `maxRetries` | `number` | `2` | Transient-failure retries. Never applied to `POST` or `PATCH` — see [Errors](./errors.md). |
|
|
22
27
|
| `defaultHeaders` | `Record<string, string>` | `{}` | Merged into every request. |
|
package/docs/sdk/docker.md
CHANGED
package/docs/sdk/errors.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Errors
|
|
3
|
+
description: Handle API and connection errors separately from settled agent outcomes.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Errors
|
|
2
7
|
|
|
3
8
|
## What throws and what does not
|
|
@@ -50,6 +55,13 @@ The subclasses add **no fields**. `status` and `code` on the base class already
|
|
|
50
55
|
|
|
51
56
|
The SDK validates path-segment identifiers before it makes a request. Invalid node ids, cron ids, bash-job ids, human-request ids, inbox ticket ids, provider names, and profile names throw `TypeError` locally. Node ids apply to core node calls, lifecycle calls, and nested node resources. `nodes.events()` returns a `NodeStream` synchronously, so its invalid-id `TypeError` rejects `stream.node`, `stream.finalOutcome()`, and iteration instead. File paths are not identifiers; invalid or relative file paths reach the daemon and return its mapped API error. `nodes.outcome(id, { wait })` separately throws `RangeError` when `wait` is not an integer from 0 through 25.
|
|
52
57
|
|
|
58
|
+
## Scoped tokens
|
|
59
|
+
|
|
60
|
+
| Condition | Class, status, and code |
|
|
61
|
+
|---|---|
|
|
62
|
+
| The bearer token's ceiling lacks the scope a route needs, or `nodes.create` asks for `scopes` outside it (`details.scopes` lists them) | `PermissionDeniedError`, 403 `scope_denied` |
|
|
63
|
+
| A scoped token reaches an owner-only route (daemon restart, attach, broker internals, canvas prune, profile pause/resume/delete, model credential install) | `PermissionDeniedError`, 403 `owner_only` |
|
|
64
|
+
|
|
53
65
|
## Memory requests
|
|
54
66
|
|
|
55
67
|
| Condition | Class, status, and code |
|
package/docs/sdk/files.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting started
|
|
3
|
+
description: Connect to a local or remote daemon and run your first agent with the SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Getting started
|
|
2
7
|
|
|
3
8
|
Phase 1.
|
|
@@ -44,7 +49,7 @@ if (outcome.kind === 'result') console.log(outcome.final_report_path);
|
|
|
44
49
|
|
|
45
50
|
## 4. A browser or a remote application
|
|
46
51
|
|
|
47
|
-
A process that is not on the daemon's machine — or a page in a browser, which has no unix sockets at all — reaches the daemon over TCP with a bearer token. The token is the owner credential: whoever holds it can drive the whole surface.
|
|
52
|
+
A process that is not on the daemon's machine — or a page in a browser, which has no unix sockets at all — reaches the daemon over TCP with a bearer token. There are two kinds. The **owner token** is the owner credential: whoever holds it can drive the whole surface. A **scoped token** (`crtr sys connect --scopes`) carries a list of scopes that caps what its holder and every run it creates may do.
|
|
48
53
|
|
|
49
54
|
### Turn the listener on, once
|
|
50
55
|
|
|
@@ -63,6 +68,20 @@ $ crtr --json sys connect
|
|
|
63
68
|
{"base_url":"http://127.0.0.1:8787","token":"<64-character bearer token>"}
|
|
64
69
|
```
|
|
65
70
|
|
|
71
|
+
### A token that holds less than the owner
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
crtr sys connect --scopes ask,memory:read
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This mints a new token whose scope list is a ceiling, appends it to the same secrets store, prints it in place of the owner token, and always hands the daemon over (it reads tokens once, at boot). The ceiling is checked on every request that carries the token:
|
|
78
|
+
|
|
79
|
+
- A scope-gated operation the ceiling lacks answers `403 scope_denied`. Creating, reviving, forking, messaging, or yielding a run needs `act`; creating a review or human request needs `ask`; arming or running a cron needs `schedule`; memory reads and writes need `memory:read` and `memory:write`. `bash` and `files` also need `act`: they run code and touch the host directly, which is more than any run does, and `files:<dir>` is recorded, not enforced, so it cannot narrow them.
|
|
80
|
+
- `nodes.create` with `scopes` outside the ceiling answers `403 scope_denied`; `details.scopes` lists the offending scopes. Omit `scopes` and the run gets the ceiling.
|
|
81
|
+
- Owner-only operations — daemon restart, the attach viewer, broker internals, canvas prune, profile pause/resume/delete, model credential install — answer `403 owner_only` to any scoped token.
|
|
82
|
+
|
|
83
|
+
What a scope does **not** do on a developer's machine: there is no OS sandbox. Scopes gate the daemon and the CLI; a run's own bash tool is ungated, and the daemon identifies a calling node by a field the caller declares, so a scope check bounds a well-behaved agent, not a hostile one. `llm`, `files:<dir>`, `net`, and provider scopes are recorded on the run but not enforced. There is no list or revoke verb yet; to retire a scoped token, remove it from the 0600 user secrets store and restart the daemon.
|
|
84
|
+
|
|
66
85
|
### Check setup before generating
|
|
67
86
|
|
|
68
87
|
Construct the client from the application's saved connection. On the first visit that value is absent, and `client.auth.status()` returns `'connect'` without a request. Once the user pastes the base URL and token printed by `crtr sys connect`, construct it again and call `status()` to check the selected provider.
|
|
@@ -153,6 +172,7 @@ An outcome is returned, never thrown — including a decline and a failure. Only
|
|
|
153
172
|
|
|
154
173
|
## Where to go next
|
|
155
174
|
|
|
175
|
+
- A complete local page that demonstrates streaming, structured output, and a plugin: [localhost SDK demo](../../examples/localhost-demo)
|
|
156
176
|
- Every constructor option and environment-variable fallback: [Client construction](./client.md)
|
|
157
177
|
- Checking the daemon connection and selected provider before a run: `client.auth.status()` above
|
|
158
178
|
- The full create-parameter table and the outcome union: [Nodes](./nodes.md)
|
package/docs/sdk/memory.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Memory
|
|
3
|
+
description: Read and write memory documents with an explicit target for every request.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# `client.memory`
|
|
2
7
|
|
|
3
8
|
Phase 3. `client.memory` reads and writes the memory documents that shape an agent run. Every call names its target; the daemon never uses its own working directory or environment to select memory.
|
package/docs/sdk/migration.md
CHANGED
package/docs/sdk/nodes.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Nodes
|
|
3
|
+
description: Create agent runs, wait for outcomes, and parse structured results.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# `client.nodes`
|
|
2
7
|
|
|
3
8
|
Shipped, except where a row says otherwise.
|
|
@@ -58,7 +63,7 @@ Wire fields are `snake_case`. Every `NodeCreateParams` property is optional; `pa
|
|
|
58
63
|
| `description` | `string` | Display description. |
|
|
59
64
|
| `parent` | node id | Graph placement. An external caller leaves this unset. |
|
|
60
65
|
| `creator` | node id | Graph placement. An external caller leaves this unset. |
|
|
61
|
-
| `scopes` | `string[]` | Per-run allow-list. Omit it to inherit every scope. In beta, `ask`, `act`, `schedule`, `memory:read`, and `memory:write` are enforced; `llm`, `files:<dir>`, `net`, provider groups, and peers are recorded because their performers are not available. |
|
|
66
|
+
| `scopes` | `string[]` | Per-run allow-list. Omit it to inherit every scope, or under a scoped token to receive that token's ceiling; a list outside the ceiling answers `403 scope_denied` with the offending scopes in `details.scopes`. In beta, `ask`, `act`, `schedule`, `memory:read`, and `memory:write` are enforced; `llm`, `files:<dir>`, `net`, provider groups, and peers are recorded because their performers are not available. See [scoped tokens](./getting-started.md#a-token-that-holds-less-than-the-owner). |
|
|
62
67
|
| `worktree` | `string \| boolean` | Create a managed git worktree for the run. |
|
|
63
68
|
| `fork_from` | `string` | Start from an existing conversation. |
|
|
64
69
|
| `no_kickoff` | `boolean` | Create the node without sending the first message. |
|
package/docs/sdk/resources.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Resource map
|
|
3
|
+
description: Find client namespaces, their daemon routes, and the raw request escape hatch.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Resource map
|
|
2
7
|
|
|
3
8
|
Every namespace exported by the client. Namespaces are camelCase. Verbs are `create`, `retrieve`, `list`, `update`, `delete`, and `cancel`, except where the product already has a literal name for the action (`fork`, `revive`, `promote`, `pause`, `poke`) — in which case the SDK uses that name.
|
package/docs/sdk/streaming.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Streaming
|
|
3
|
+
description: Follow assistant text, tool activity, reports, and outcomes as an agent runs.
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Streaming
|
|
2
7
|
|
|
3
8
|
Watch a run as it works: assistant text as it is produced, tool calls as they start and finish, reports as they are pushed, and the settled outcome.
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.332",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"workspaces": [
|
|
9
|
-
"packages/*"
|
|
9
|
+
"packages/*",
|
|
10
|
+
"apps/*"
|
|
10
11
|
],
|
|
11
12
|
"bin": {
|
|
12
13
|
"crtr": "bin/crtr",
|