@north-light/cli 0.3.439 → 0.3.440

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.
@@ -1,5 +1,5 @@
1
1
  import{createRequire as __cr}from'node:module';import{fileURLToPath as __fu}from'node:url';import{dirname as __dn}from'node:path';const require=__cr(import.meta.url),__filename=__fu(import.meta.url),__dirname=__dn(__filename);
2
- var JMn=Object.create;var gAe=Object.defineProperty;var XMn=Object.getOwnPropertyDescriptor;var ZMn=Object.getOwnPropertyNames;var e1n=Object.getPrototypeOf,t1n=Object.prototype.hasOwnProperty;var re=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,n)=>(typeof require<"u"?require:e)[n]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var h=(t,e)=>()=>(t&&(e=t(t=0)),e);var $=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),ar=(t,e)=>{for(var n in e)gAe(t,n,{get:e[n],enumerable:!0})},n1n=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ZMn(e))!t1n.call(t,i)&&i!==n&&gAe(t,i,{get:()=>e[i],enumerable:!(r=XMn(e,i))||r.enumerable});return t};var Jt=(t,e,n)=>(n=t!=null?JMn(e1n(t)):{},n1n(e||!t||!t.__esModule?gAe(n,"default",{value:t,enumerable:!0}):n,t));function yAe(t,e=!1){return t==="."&&e?!0:t.length>0&&!t.startsWith("/")&&!t.includes("..")&&!t.includes(":")&&!t.includes("\\")&&!/\s/.test(t)&&!t.split("/").some((n,r,i)=>n==="."||n===""&&r!==i.length-1)&&(!e||!t.endsWith("/"))}function AAe(t){if(t==="crtr:llm"||t==="crtr:act"||t==="crtr:schedule")return{kind:t.slice(5),value:t};if(t==="crtr:files:share")return{kind:"share",value:t};if(t==="crtr:memory:manage:user")return{kind:"memory",access:"manage",holder:"user"};let e=/^crtr:memory:(read|rw):user(?::(.+))?$/.exec(t);if(e&&(e[2]===void 0||yAe(e[2])))return{kind:"memory",access:e[1],holder:"user",path:e[2]};if(e=/^crtr:memory:(read|rw):app:([A-Za-z0-9_-]{1,64})(?::profile:([A-Za-z0-9_-]{1,64})(?::(.+))?)?$/.exec(t),e&&(e[4]===void 0||yAe(e[4])))return{kind:"memory",access:e[1],holder:`app:${e[2]}`,profile:e[3],path:e[4]};if(e=/^crtr:files:(read|rw):(user|app:[A-Za-z0-9_-]{1,64}):(.+)$/.exec(t),e&&yAe(e[3],!0))return{kind:"files",access:e[1],holder:e[2],path:e[3]};if(e=/^crtr:(conversations|objects):read:app:([A-Za-z0-9_-]{1,64})$/.exec(t),e)return{kind:e[1],value:t};if(e=/^crtr:peer:([A-Za-z0-9_-]{1,64})$/.exec(t),e)return{kind:"peer",value:t};if(e=/^([^:]+):([^:]+)$/.exec(t),e&&e[1]!=="crtr"&&Ktt.test(e[1])&&Ktt.test(e[2]))return{kind:"provider",value:t};throw new TypeError(`Unsupported scope: ${t}`)}function BV(t,e){let n=AAe(t);if(e&&(n.kind==="memory"||n.kind==="files")&&n.holder.startsWith("app:")&&n.holder!==e)throw new TypeError(`Unsupported scope: ${t}`);return t}function r1n(t,e){return t===void 0?!0:e===void 0?!1:t===e||t.endsWith("/")&&e.startsWith(t)}function ed(t,e){let n=AAe(t),r=AAe(e);return n.kind!==r.kind?!1:n.kind==="memory"&&r.kind==="memory"?n.holder===r.holder&&(n.access===r.access||n.access==="manage"||n.access==="rw"&&r.access==="read")&&(n.profile===void 0||n.profile===r.profile)&&r1n(n.path,r.path):n.kind==="files"&&r.kind==="files"?n.holder===r.holder&&(n.access==="rw"||r.access==="read")&&(n.path==="."||n.path===r.path||r.path.startsWith(`${n.path}/`)):t===e}var Ktt,eM=h(()=>{"use strict";Ktt=/^[A-Za-z0-9_-]{1,64}$/});import{lstatSync as i1n,readFileSync as o1n,readdirSync as s1n}from"node:fs";import{join as wAe}from"node:path";function qS(){return process.env.CRTR_NODE_ID}function a1n(t){for(let e of["broker-launch.json","cron-launch.json"]){let n=wAe(t,e);if(i1n(n,{throwIfNoEntry:!1})?.isFile())return n}}function c1n(){if(process.platform==="linux")try{let t="/run/crtr/nodes",e=new Set(o1n("/proc/self/mountinfo","utf8").split(`
2
+ var JMn=Object.create;var gAe=Object.defineProperty;var XMn=Object.getOwnPropertyDescriptor;var ZMn=Object.getOwnPropertyNames;var e1n=Object.getPrototypeOf,t1n=Object.prototype.hasOwnProperty;var re=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,n)=>(typeof require<"u"?require:e)[n]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var h=(t,e)=>()=>(t&&(e=t(t=0)),e);var $=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),ar=(t,e)=>{for(var n in e)gAe(t,n,{get:e[n],enumerable:!0})},n1n=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ZMn(e))!t1n.call(t,i)&&i!==n&&gAe(t,i,{get:()=>e[i],enumerable:!(r=XMn(e,i))||r.enumerable});return t};var Jt=(t,e,n)=>(n=t!=null?JMn(e1n(t)):{},n1n(e||!t||!t.__esModule?gAe(n,"default",{value:t,enumerable:!0}):n,t));function yAe(t,e=!1){return t==="."&&e?!0:t.length>0&&!t.startsWith("/")&&!t.includes("..")&&!t.includes(":")&&!t.includes("\\")&&!/\s/.test(t)&&!t.split("/").some((n,r,i)=>n==="."||n===""&&r!==i.length-1)&&(!e||!t.endsWith("/"))}function AAe(t){if(t==="crtr:llm"||t==="crtr:act"||t==="crtr:schedule")return{kind:t.slice(5),value:t};if(t==="crtr:files:share")return{kind:"share",value:t};if(t==="crtr:memory:manage:user")return{kind:"memory",access:"manage",holder:"user"};let e=/^crtr:memory:(read|rw):user(?::(.+))?$/.exec(t);if(e&&(e[2]===void 0||yAe(e[2])))return{kind:"memory",access:e[1],holder:"user",path:e[2]};if(e=/^crtr:memory:(read|rw):app:([A-Za-z0-9_-]{1,64})(?::profile:([A-Za-z0-9_-]{1,64})(?::(.+))?)?$/.exec(t),e&&(e[4]===void 0||yAe(e[4])))return{kind:"memory",access:e[1],holder:`app:${e[2]}`,profile:e[3],path:e[4]};if(e=/^crtr:files:(read|rw):(user|app:[A-Za-z0-9_-]{1,64}):(.+)$/.exec(t),e&&yAe(e[3],!0))return{kind:"files",access:e[1],holder:e[2],path:e[3]};if(e=/^crtr:(?:(conversations):read|(objects):(?:read|message|spawn)):app:([A-Za-z0-9_-]{1,64})$/.exec(t),e)return{kind:e[1]??e[2],value:t};if(e=/^crtr:peer:([A-Za-z0-9_-]{1,64})$/.exec(t),e)return{kind:"peer",value:t};if(e=/^([^:]+):([^:]+)$/.exec(t),e&&e[1]!=="crtr"&&Ktt.test(e[1])&&Ktt.test(e[2]))return{kind:"provider",value:t};throw new TypeError(`Unsupported scope: ${t}`)}function BV(t,e){let n=AAe(t);if(e&&(n.kind==="memory"||n.kind==="files")&&n.holder.startsWith("app:")&&n.holder!==e)throw new TypeError(`Unsupported scope: ${t}`);return t}function r1n(t,e){return t===void 0?!0:e===void 0?!1:t===e||t.endsWith("/")&&e.startsWith(t)}function ed(t,e){let n=AAe(t),r=AAe(e);return n.kind!==r.kind?!1:n.kind==="memory"&&r.kind==="memory"?n.holder===r.holder&&(n.access===r.access||n.access==="manage"||n.access==="rw"&&r.access==="read")&&(n.profile===void 0||n.profile===r.profile)&&r1n(n.path,r.path):n.kind==="files"&&r.kind==="files"?n.holder===r.holder&&(n.access==="rw"||r.access==="read")&&(n.path==="."||n.path===r.path||r.path.startsWith(`${n.path}/`)):t===e}var Ktt,eM=h(()=>{"use strict";Ktt=/^[A-Za-z0-9_-]{1,64}$/});import{lstatSync as i1n,readFileSync as o1n,readdirSync as s1n}from"node:fs";import{join as wAe}from"node:path";function qS(){return process.env.CRTR_NODE_ID}function a1n(t){for(let e of["broker-launch.json","cron-launch.json"]){let n=wAe(t,e);if(i1n(n,{throwIfNoEntry:!1})?.isFile())return n}}function c1n(){if(process.platform==="linux")try{let t="/run/crtr/nodes",e=new Set(o1n("/proc/self/mountinfo","utf8").split(`
3
3
  `).map(r=>r.split(" ")[4])),n=s1n(t,{withFileTypes:!0}).filter(r=>r.isDirectory()&&/^[a-zA-Z0-9][a-zA-Z0-9-]{0,127}$/.test(r.name)&&e.has(wAe(t,r.name))&&a1n(wAe(t,r.name))!==void 0);return n.length===1?n[0].name:void 0}catch{return}}function GS(){return c1n()!==void 0}function $V(){return process.env.CRTR_GRANTEE}function FV(){return process.env.CRTR_EXECUTION_ID}function xAe(){return process.env.CRTR_PROFILE_ID}function N0(){return process.env.CRTR_HOME}function Ytt(){let t=process.env.CRTR_PIDFILE;return t!==void 0&&t!==""?t:void 0}function Jtt(){return process.env.CRTR_NO_DAEMON_AUTOSTART==="1"}var X_i,Z_i,evi,tvi,cs=h(()=>{"use strict";X_i=5*6e4,Z_i=300*1e3,evi=60*1e3;tvi=5*6e4});import{existsSync as u1n,readFileSync as l1n}from"node:fs";import{dirname as Xtt,join as d1n,parse as p1n}from"node:path";import{fileURLToPath as f1n}from"node:url";function m1n(t,e){let n=Xtt(t),r=p1n(n).root;for(;;){let i=d1n(n,"package.json");if(u1n(i))try{if(JSON.parse(l1n(i,"utf8")).name===e)return n}catch{return null}if(n===r)return null;n=Xtt(n)}}function Ztt(){let t="@earendil-works/pi-coding-agent",e;try{e=f1n(import.meta.resolve(t))}catch(r){throw new Error(`crouter install is missing bundled ${t}; reinstall crouter. ${r.message}`)}let n=m1n(e,t);if(n===null)throw new Error(`could not locate bundled ${t} package root from ${e}`);return n}var bAe=h(()=>{"use strict"});var ent=h(()=>{"use strict";bAe();try{process.env.PI_PACKAGE_DIR=Ztt()}catch{}});function h1n(t){if(!(typeof process>"u"||!process.versions?.bun||Object.keys(process.env).length>0)){if(UV===null){UV=new Map;try{let{readFileSync:e}=re("node:fs"),n=e("/proc/self/environ","utf-8");for(let r of n.split("\0")){let i=r.indexOf("=");i>0&&UV.set(r.slice(0,i),r.slice(i+1))}}catch{}}return UV.get(t)}}function xn(t,e){return e?.[t]||(typeof process<"u"?process.env[t]:void 0)||h1n(t)||void 0}var UV,td=h(()=>{UV=null});function QV(t){return t.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function tnt(t){let e=QV(t.title),n=QV(t.heading),r=QV(t.message),i=t.details?QV(t.details):void 0;return`<!doctype html>
4
4
  <html lang="en">
5
5
  <head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.439",
3
+ "version": "0.3.440",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -137,7 +137,7 @@
137
137
  },
138
138
  "license": "UNLICENSED",
139
139
  "dependencies": {
140
- "@north-light/identity": "0.3.439",
140
+ "@north-light/identity": "0.3.440",
141
141
  "@earendil-works/pi-agent-core": "1.1.0",
142
142
  "@earendil-works/pi-ai": "1.1.0",
143
143
  "@earendil-works/pi-coding-agent": "1.1.0",
@@ -22,7 +22,7 @@ export interface IdTokenClaims extends JWTPayload {
22
22
  family_name?: string;
23
23
  picture?: string;
24
24
  }
25
- export type Scope = 'crtr:llm' | 'crtr:act' | 'crtr:schedule' | `crtr:memory:${'read' | 'rw'}:user${'' | `:${string}`}` | 'crtr:memory:manage:user' | `crtr:memory:${'read' | 'rw'}:app:${string}` | `crtr:files:${'read' | 'rw'}:${'user' | `app:${string}`}:${string}` | 'crtr:files:share' | `crtr:${'conversations' | 'objects'}:read:app:${string}` | `crtr:peer:${string}` | `${string}:${string}`;
25
+ export type Scope = 'crtr:llm' | 'crtr:act' | 'crtr:schedule' | `crtr:memory:${'read' | 'rw'}:user${'' | `:${string}`}` | 'crtr:memory:manage:user' | `crtr:memory:${'read' | 'rw'}:app:${string}` | `crtr:files:${'read' | 'rw'}:${'user' | `app:${string}`}:${string}` | 'crtr:files:share' | `crtr:${'conversations' | 'objects'}:read:app:${string}` | `crtr:objects:${'message' | 'spawn'}:app:${string}` | `crtr:peer:${string}` | `${string}:${string}`;
26
26
  /** App-memory form keys omit the app id. */
27
27
  export declare const ENFORCED: ReadonlySet<string>;
28
28
  export declare function parseScope(value: string, grantee?: string): Scope;
@@ -73,6 +73,34 @@ export declare function verifyAccessToken(options: {
73
73
  issuer: string;
74
74
  audience: string;
75
75
  }): Promise<AccessTokenClaims>;
76
+ /** The claims of a machine token: one machine of an account calling another (or the account relay). It names the
77
+ * person and the calling machine and carries no grant, so it authorizes nothing on its own. */
78
+ export interface MachineTokenClaims extends JWTPayload {
79
+ iss: string;
80
+ /** The single audience: a peer machine URL or the relay origin. */
81
+ aud: string;
82
+ /** The account's person, `user:<id>`. */
83
+ sub: string;
84
+ /** Always equal to `sub`. */
85
+ grantee: string;
86
+ client_id: string;
87
+ /** The calling machine, `runtime:<id>`. */
88
+ machine_id: string;
89
+ exp: number;
90
+ }
91
+ /**
92
+ * Verifies a machine token: ES256 with typ `at+jwt`, signature against `jwks`, `iss` = `issuer`, `aud` a string equal
93
+ * to `audience` (the sole audience), and `exp`. `sub` must be `user:<id>`, `grantee` must equal `sub`, `machine_id`
94
+ * must be `runtime:<id>` and `client_id` a non-empty string. A token carrying a `grant`, `rev` or `scope` claim is
95
+ * refused, so an app grant token never verifies here; `verifyAccessToken` requires an `app:` grantee, so a machine
96
+ * token never verifies there.
97
+ */
98
+ export declare function verifyMachineToken(options: {
99
+ token: string;
100
+ jwks: JSONWebKeySet | JwksCache;
101
+ issuer: string;
102
+ audience: string;
103
+ }): Promise<MachineTokenClaims>;
76
104
  /** One level of the outbound token's RFC 8693 `act` nesting: the actor, and the actor it acted for. */
77
105
  export interface OutboundActor {
78
106
  sub: string;
@@ -31,9 +31,10 @@ function selector(value) {
31
31
  match = /^crtr:files:(read|rw):(user|app:[A-Za-z0-9_-]{1,64}):(.+)$/.exec(value);
32
32
  if (match && validPath(match[3], true))
33
33
  return { kind: 'files', access: match[1], holder: match[2], path: match[3] };
34
- match = /^crtr:(conversations|objects):read:app:([A-Za-z0-9_-]{1,64})$/.exec(value);
34
+ // read, message and spawn are three unrelated scopes: `covers` is exact equality for them.
35
+ match = /^crtr:(?:(conversations):read|(objects):(?:read|message|spawn)):app:([A-Za-z0-9_-]{1,64})$/.exec(value);
35
36
  if (match)
36
- return { kind: match[1], value };
37
+ return { kind: (match[1] ?? match[2]), value };
37
38
  match = /^crtr:peer:([A-Za-z0-9_-]{1,64})$/.exec(value);
38
39
  if (match)
39
40
  return { kind: 'peer', value };
@@ -226,6 +227,28 @@ export async function verifyAccessToken(options) {
226
227
  throw new Error('Invalid access token claims');
227
228
  return payload;
228
229
  }
230
+ /**
231
+ * Verifies a machine token: ES256 with typ `at+jwt`, signature against `jwks`, `iss` = `issuer`, `aud` a string equal
232
+ * to `audience` (the sole audience), and `exp`. `sub` must be `user:<id>`, `grantee` must equal `sub`, `machine_id`
233
+ * must be `runtime:<id>` and `client_id` a non-empty string. A token carrying a `grant`, `rev` or `scope` claim is
234
+ * refused, so an app grant token never verifies here; `verifyAccessToken` requires an `app:` grantee, so a machine
235
+ * token never verifies there.
236
+ */
237
+ export async function verifyMachineToken(options) {
238
+ const header = decodeProtectedHeader(options.token);
239
+ if (header.typ !== 'at+jwt' || header.alg !== 'ES256')
240
+ throw new Error('Invalid machine token header');
241
+ const keys = await keySet(options.jwks, header.kid);
242
+ const { payload } = await jwtVerify(options.token, createLocalJWKSet(keys), {
243
+ issuer: options.issuer, audience: options.audience, algorithms: ['ES256'], typ: 'at+jwt', requiredClaims: ['exp'],
244
+ });
245
+ if (payload.aud !== options.audience || !validatePrincipal(payload.sub, 'user') || payload.grantee !== payload.sub ||
246
+ !validatePrincipal(payload.machine_id, 'runtime') || !nonEmptyString(payload.client_id) || typeof payload.exp !== 'number' ||
247
+ payload.grant !== undefined || payload.rev !== undefined || payload.scope !== undefined) {
248
+ throw new Error('Invalid machine token claims');
249
+ }
250
+ return payload;
251
+ }
229
252
  function nonEmptyString(value) {
230
253
  return typeof value === 'string' && value !== '';
231
254
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.439",
3
+ "version": "0.3.440",
4
4
  "description": "Northlight access-token verification and scope rules",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/runtime.lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.439",
3
+ "version": "0.3.440",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.439",
9
+ "version": "0.3.440",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -18,7 +18,7 @@
18
18
  "@earendil-works/pi-ai": "1.1.0",
19
19
  "@earendil-works/pi-coding-agent": "1.1.0",
20
20
  "@earendil-works/pi-tui": "1.1.0",
21
- "@north-light/identity": "0.3.439",
21
+ "@north-light/identity": "0.3.440",
22
22
  "cron-parser": "^5.6.0",
23
23
  "esbuild": "^0.27.7",
24
24
  "jose": "^6.2.12",
@@ -12095,17 +12095,17 @@
12095
12095
  },
12096
12096
  "packages/crouter-api": {
12097
12097
  "name": "@north-light/api",
12098
- "version": "0.3.439",
12098
+ "version": "0.3.440",
12099
12099
  "license": "UNLICENSED"
12100
12100
  },
12101
12101
  "packages/crouter-env-docker": {
12102
12102
  "name": "@north-light/env-docker",
12103
- "version": "0.3.439",
12103
+ "version": "0.3.440",
12104
12104
  "license": "UNLICENSED"
12105
12105
  },
12106
12106
  "packages/crouter-identity": {
12107
12107
  "name": "@north-light/identity",
12108
- "version": "0.3.439",
12108
+ "version": "0.3.440",
12109
12109
  "license": "UNLICENSED",
12110
12110
  "dependencies": {
12111
12111
  "jose": "^6.2.1"
@@ -12113,20 +12113,20 @@
12113
12113
  },
12114
12114
  "packages/crouter-plugin": {
12115
12115
  "name": "@north-light/plugin",
12116
- "version": "0.3.439",
12116
+ "version": "0.3.440",
12117
12117
  "license": "UNLICENSED",
12118
12118
  "dependencies": {
12119
- "@north-light/api": "0.3.439",
12120
- "@north-light/identity": "0.3.439"
12119
+ "@north-light/api": "0.3.440",
12120
+ "@north-light/identity": "0.3.440"
12121
12121
  }
12122
12122
  },
12123
12123
  "packages/crouter-sdk": {
12124
12124
  "name": "@north-light/sdk",
12125
- "version": "0.3.439",
12125
+ "version": "0.3.440",
12126
12126
  "license": "UNLICENSED",
12127
12127
  "dependencies": {
12128
- "@north-light/api": "0.3.439",
12129
- "@north-light/identity": "0.3.439",
12128
+ "@north-light/api": "0.3.440",
12129
+ "@north-light/identity": "0.3.440",
12130
12130
  "jose": "^6.2.1"
12131
12131
  },
12132
12132
  "bin": {