@north-light/cli 0.3.570 → 0.3.571

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.
@@ -258,11 +258,26 @@ function findCompatibleSibling(entryName, excludeId) {
258
258
  return best;
259
259
  }
260
260
 
261
+ const RELEASE_ENTRIES = { broker: 'dist/core/runtime/broker-cli.js', cli: 'dist/cli.js', daemon: 'dist/daemon/crtrd-cli.js', launcher: 'dist/launcher/main.js' };
262
+
263
+ /** A fleet sandbox runs its installed release tree itself. `scripts/sandbox-bootstrap.sh` marks a finished release with `.complete`, so a
264
+ * `crtr` loaded from that tree needs no environment variable to know it (an agent's shell may not carry the broker's env). Staging a
265
+ * generation there would repeat a ~20 s `npm pack` and `npm ci` in every sandbox, and its safety check refuses the sandbox's layout. */
266
+ function releaseRuntime(entryName) {
267
+ if (!existsSync(join(PACKAGE_ROOT, '.complete'))) return null;
268
+ const entry = RELEASE_ENTRIES[entryName];
269
+ const entryPath = join(PACKAGE_ROOT, entry ?? '');
270
+ if (entry === undefined || !existsSync(entryPath)) return failure(`release ${PACKAGE_ROOT} has no ${entryName} entry`);
271
+ return { ok: true, runtime: { manifest: null, entryPath, generationId: 'release' } };
272
+ }
273
+
261
274
  /** Resolve a runtime without changing process state or writing output. Falls
262
275
  * back to the newest ABI-compatible sibling generation when `selected` is
263
276
  * incompatible with the running Node; only fails outright when no compatible
264
277
  * generation exists at all. */
265
278
  export function tryResolveRuntime(entryName) {
279
+ const release = releaseRuntime(entryName);
280
+ if (release !== null) return release;
266
281
  const selected = readSelection();
267
282
  if (!selected.ok) return selected;
268
283
 
@@ -0,0 +1,126 @@
1
+ -- 008-import.sql — cycle 6 I-S (design-import "Data model"): the tables the import lane, the remote machine runner and the
2
+ -- sandbox import step share, and the third runner kind. One transaction, additions only: `schema_min_reader` is not touched
3
+ -- and stays 2, so the previous build (v0.3.569, schema 7) still opens a version-8 database. It never names the new tables.
4
+ -- Timestamps are text, as in 002; `*_json` columns are text holding JSON, as in 002; a lane's claim is `claimed_by` +
5
+ -- `lease_expires_at`, as on the cycle 5 lanes' rows.
6
+
7
+ -- ---------------------------------------------------------------- the remote machine runner is a third runner (design-import I13)
8
+ -- Widening a CHECK is rollback-safe (007 did the same for `limits_source_check`): the previous build never writes 'machine'.
9
+ ALTER TABLE executions DROP CONSTRAINT IF EXISTS executions_runner_check;
10
+ ALTER TABLE executions ADD CONSTRAINT executions_runner_check CHECK (runner IN ('process', 'blaxel', 'machine'));
11
+
12
+ -- ---------------------------------------------------------------- import runs
13
+
14
+ -- One row per attempt. A run is open in `uploading`, `loading` and `committed`; `done` closes it for good. At most one run per
15
+ -- source install id is open or `done` (a `failed` or `rolled_back` run does not count), which is the rule behind the API's
16
+ -- `already_imported` and `import_running` refusals.
17
+ CREATE TABLE import_runs (
18
+ run_id text PRIMARY KEY,
19
+ source_install_id text NOT NULL,
20
+ source_kind text NOT NULL CHECK (source_kind IN ('machine', 'hosted')),
21
+ person text NOT NULL,
22
+ release text NOT NULL,
23
+ state text NOT NULL CHECK (state IN ('uploading', 'loading', 'committed', 'done', 'failed', 'rolled_back')),
24
+ committed_at text,
25
+ step text,
26
+ counts_json text,
27
+ verify_json text,
28
+ failure_json text,
29
+ manifest_key text,
30
+ created_at text NOT NULL,
31
+ loaded_at text,
32
+ finished_at text,
33
+ claimed_by text,
34
+ lease_expires_at text
35
+ );
36
+ CREATE UNIQUE INDEX import_runs_one_per_install ON import_runs(source_install_id) WHERE state IN ('uploading', 'loading', 'committed', 'done');
37
+ -- The lane's SKIP LOCKED claim: `loading` rows, and `committed` rows whose lease expired (resume at verify).
38
+ CREATE INDEX import_runs_claim ON import_runs(state, lease_expires_at) WHERE state IN ('loading', 'committed');
39
+
40
+ -- The three identity-keyed tables' source keys → the keys the target assigned (`audit_rows.id`, `review_comment_events.seq`,
41
+ -- `app_users.uid`). Written in the copy transaction.
42
+ CREATE TABLE import_key_map (
43
+ source_install_id text NOT NULL,
44
+ table_name text NOT NULL,
45
+ source_key text NOT NULL,
46
+ target_key text NOT NULL,
47
+ PRIMARY KEY (source_install_id, table_name, source_key)
48
+ );
49
+
50
+ -- The primary keys each run inserted, per table (a compressed list): the authority for verify and rollback.
51
+ CREATE TABLE import_manifest (
52
+ run_id text NOT NULL REFERENCES import_runs(run_id),
53
+ table_name text NOT NULL,
54
+ inserted_keys bytea NOT NULL,
55
+ PRIMARY KEY (run_id, table_name)
56
+ );
57
+
58
+ -- The id map and path map a run persisted, so a later import that maps onto this run's rows is visible to rollback.
59
+ CREATE TABLE import_id_map (
60
+ run_id text NOT NULL REFERENCES import_runs(run_id),
61
+ kind text NOT NULL CHECK (kind IN ('object', 'grant', 'kind', 'path')),
62
+ source_value text NOT NULL,
63
+ target_value text NOT NULL,
64
+ PRIMARY KEY (run_id, kind, source_value)
65
+ );
66
+ CREATE INDEX import_id_map_target ON import_id_map(kind, target_value);
67
+
68
+ -- ---------------------------------------------------------------- the remote machine runner
69
+
70
+ -- One live link per machine environment; the API process holding it renews `lease_expires_at`, and a delete is fenced by `generation`.
71
+ CREATE TABLE machine_links (
72
+ environment text PRIMARY KEY REFERENCES environments(object) ON DELETE CASCADE,
73
+ generation bigint NOT NULL,
74
+ api_address text NOT NULL,
75
+ connected_at text NOT NULL,
76
+ lease_expires_at text NOT NULL
77
+ );
78
+
79
+ -- The machine credential (principal `machine:<environment id>`): one live credential per environment, stored as the sha256 hex of
80
+ -- the secret (as `node_tokens`). Issued once when a machine source's run is `done`; `crtr sys machine rotate` replaces the row;
81
+ -- rollback and the console set `revoked_at`, which closes the link within one heartbeat.
82
+ CREATE TABLE machine_credentials (
83
+ environment text PRIMARY KEY REFERENCES environments(object) ON DELETE CASCADE,
84
+ token_hash text NOT NULL UNIQUE,
85
+ issued_at text NOT NULL,
86
+ revoked_at text
87
+ );
88
+
89
+ -- A runner call to a machine: `pending` until the holder of the link claims it (`claimed_generation`, `lease_expires_at`), `sent`
90
+ -- once forwarded, then `done` or `failed` with `result_json`. Deleted a day after `finished_at`.
91
+ CREATE TABLE machine_calls (
92
+ call_id text PRIMARY KEY,
93
+ environment text NOT NULL REFERENCES environments(object) ON DELETE CASCADE,
94
+ op text NOT NULL CHECK (op IN ('start', 'wake', 'stop', 'status', 'delete', 'viewer', 'cron.run', 'cron.cancel')),
95
+ args_json text NOT NULL DEFAULT '{}',
96
+ state text NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'sent', 'done', 'failed')),
97
+ claimed_generation bigint,
98
+ lease_expires_at text,
99
+ result_json text,
100
+ created_at text NOT NULL,
101
+ finished_at text
102
+ );
103
+ -- The holder's SKIP LOCKED claim of an environment's open calls, and the lease sweep.
104
+ CREATE INDEX machine_calls_open ON machine_calls(environment, state, created_at) WHERE state IN ('pending', 'sent');
105
+ CREATE INDEX machine_calls_finished ON machine_calls(finished_at) WHERE finished_at IS NOT NULL;
106
+
107
+ -- ---------------------------------------------------------------- hosted running nodes' files
108
+
109
+ -- The archive a hosted running node's sandbox unpacks at its next start; `unpacked_at` is set when the start script reports it.
110
+ -- Storage maintenance deletes the blob 30 days after unpack.
111
+ CREATE TABLE import_node_files (
112
+ node text PRIMARY KEY REFERENCES nodes(node_id) ON DELETE CASCADE,
113
+ blob_key text NOT NULL,
114
+ unpacked_at text
115
+ );
116
+ CREATE INDEX import_node_files_unpacked ON import_node_files(unpacked_at) WHERE unpacked_at IS NOT NULL;
117
+
118
+ -- ---------------------------------------------------------------- the 30-day physical purge (I-8)
119
+
120
+ -- The purge's candidate scan (soft-deleted rows other than nodes, which 005's index and D100's pass cover) and its reference
121
+ -- checks on authorship and kind ownership.
122
+ CREATE INDEX objects_deleted ON objects(deleted_at) WHERE deleted_at IS NOT NULL AND type <> 'node';
123
+ CREATE INDEX revisions_author ON revisions(author) WHERE author IS NOT NULL;
124
+ CREATE INDEX object_kinds_owner ON object_kinds(owner);
125
+
126
+ INSERT INTO canvas_meta (key, value) VALUES ('schema_min_reader', '2') ON CONFLICT (key) DO NOTHING;
@@ -1,7 +1,7 @@
1
1
  /** The canvas schema version this build supports: the number of the highest delta in `schema-pg/` (the baseline
2
2
  * `schema-pg.sql` is version 1). `scripts/install-runtime.mjs` reads this module from a candidate generation to refuse
3
3
  * one below the live database's `schema_min_reader`. */
4
- export declare const CANVAS_SCHEMA_VERSION = 7;
4
+ export declare const CANVAS_SCHEMA_VERSION = 8;
5
5
  /** The Postgres schema holding the canvas tables: `CRTR_DATABASE_SCHEMA` (`crtr` in the cloud deploy). Unset or empty
6
6
  * means the role's own search_path (`public` on a Mac and on a VM). A name that is not a plain identifier is an error:
7
7
  * it is put in a `search_path` connection option. */
@@ -1 +1 @@
1
- var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=7;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
1
+ var m=Object.defineProperty;var r=(n,e)=>m(n,"name",{value:e,configurable:!0});const A=8;function _(n=process.env){const e=n.CRTR_DATABASE_SCHEMA?.trim();if(e){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(e))throw new Error(`CRTR_DATABASE_SCHEMA must be a plain identifier (letters, digits, underscore); got ${JSON.stringify(e)}`);return e}}r(_,"canvasDatabaseSchema");function h(n=process.env){return n.CRTR_SCHEMA_DATABASE_URL?.trim()||void 0}r(h,"canvasSchemaDatabaseUrl");function E(n=process.env){const e=_(n);return e===void 0?void 0:`-c search_path=${e}`}r(E,"canvasSearchPathOptions");async function v(n,e){const t=e===void 0?"canvas_meta":`"${e.replace(/"/g,'""')}".canvas_meta`;if((await n.query(`SELECT to_regclass('${t.replace(/'/g,"''")}') IS NOT NULL AS found`)).rows[0].found!==!0)return;const u=(await n.query(`SELECT key, value FROM ${t} WHERE key IN ('schema_version', 'schema_min_reader')`)).rows,o=r(c=>{const i=u.find(f=>f.key===c);if(i===void 0)return;const d=Number(i.value);if(!Number.isSafeInteger(d))throw new Error(`canvas_meta.${c} is not an integer: ${String(i.value)}`);return d},"value"),s=o("schema_version");return s===void 0?void 0:{version:s,minReader:o("schema_min_reader")??s}}r(v,"readStoredVersions");async function l(n,e,t){const a=await v(n,t);if(a!==void 0&&a.minReader>e)throw new Error(`refusing to select runtime with canvas schema ${e}: the live database requires readers at version ${a.minReader} or above (it is at ${a.version}); install a descendant build instead`)}r(l,"assertCandidateMayOpen");export{A as CANVAS_SCHEMA_VERSION,l as assertCandidateMayOpen,_ as canvasDatabaseSchema,h as canvasSchemaDatabaseUrl,E as canvasSearchPathOptions,v as readStoredVersions};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/cli",
3
- "version": "0.3.570",
3
+ "version": "0.3.571",
4
4
  "description": "crtr — agent runtime with memory, plugins, and marketplaces",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -102,10 +102,11 @@
102
102
  "typecheck": "tsc --noEmit && tsc -p src/doc-editor/tsconfig.json",
103
103
  "typecheck:pi-packages": "npx tsc -p src/builtin-pi-packages/pi-crtr-extensions/tsconfig.json",
104
104
  "test:runtime-packaging": "node --test scripts/runtime-smoke.test.mjs scripts/pack-args.test.mjs scripts/ensure-native-linux.test.mjs scripts/release-version.test.mjs scripts/sandbox-start.test.mjs",
105
+ "check:consumer-resolution": "node scripts/check-consumer-resolution.mjs",
105
106
  "contract:check": "node scripts/contract.mjs check",
106
107
  "contract:surface": "node scripts/contract.mjs generate",
107
108
  "docs:check-reference": "npm exec --workspace @north-light/docs -- tsx scripts/check-reference.ts",
108
- "test:contract": "node --conditions=crtr-src --import tsx/esm --test scripts/contract.test.mjs scripts/promote-sdk.test.mjs scripts/release-version.test.mjs scripts/sdk-docs-commands.test.mjs scripts/ci-image.test.mjs scripts/ci-miss-triage.test.mjs scripts/ci-test-pick.test.mjs",
109
+ "test:contract": "node --conditions=crtr-src --import tsx/esm --test scripts/contract.test.mjs scripts/promote-sdk.test.mjs scripts/release-version.test.mjs scripts/sdk-docs-commands.test.mjs scripts/ci-image.test.mjs scripts/ci-miss-triage.test.mjs scripts/ci-test-pick.test.mjs scripts/runtime-selector.test.mjs",
109
110
  "test:build-output": "node scripts/build-concurrency-test.mjs",
110
111
  "check:cli-no-canvas": "node scripts/check-cli-no-canvas.mjs",
111
112
  "check:cli-cold-start": "node scripts/check-cli-cold-start.mjs",
@@ -144,10 +145,14 @@
144
145
  },
145
146
  "license": "UNLICENSED",
146
147
  "dependencies": {
147
- "@north-light/identity": "0.3.570",
148
+ "@north-light/identity": "0.3.571",
149
+ "@earendil-works/chord": "1.1.0",
148
150
  "@earendil-works/pi-agent-core": "1.1.0",
149
151
  "@earendil-works/pi-ai": "1.1.0",
152
+ "@earendil-works/pi-codemode": "1.1.0",
150
153
  "@earendil-works/pi-coding-agent": "1.1.0",
154
+ "@earendil-works/pi-mcp": "1.1.0",
155
+ "@earendil-works/pi-telemetry": "1.1.0",
151
156
  "@earendil-works/pi-tui": "1.1.0",
152
157
  "@milkdown/core": "^7.22.3",
153
158
  "@milkdown/plugin-history": "^7.22.3",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@north-light/identity",
3
- "version": "0.3.570",
3
+ "version": "0.3.571",
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.570",
3
+ "version": "0.3.571",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@north-light/cli",
9
- "version": "0.3.570",
9
+ "version": "0.3.571",
10
10
  "hasInstallScript": true,
11
11
  "license": "UNLICENSED",
12
12
  "workspaces": [
@@ -14,9 +14,13 @@
14
14
  "apps/*"
15
15
  ],
16
16
  "dependencies": {
17
+ "@earendil-works/chord": "1.1.0",
17
18
  "@earendil-works/pi-agent-core": "1.1.0",
18
19
  "@earendil-works/pi-ai": "1.1.0",
20
+ "@earendil-works/pi-codemode": "1.1.0",
19
21
  "@earendil-works/pi-coding-agent": "1.1.0",
22
+ "@earendil-works/pi-mcp": "1.1.0",
23
+ "@earendil-works/pi-telemetry": "1.1.0",
20
24
  "@earendil-works/pi-tui": "1.1.0",
21
25
  "@milkdown/core": "^7.22.3",
22
26
  "@milkdown/plugin-history": "^7.22.3",
@@ -24,7 +28,7 @@
24
28
  "@milkdown/preset-gfm": "^7.22.3",
25
29
  "@milkdown/prose": "^7.22.3",
26
30
  "@milkdown/utils": "^7.22.3",
27
- "@north-light/identity": "0.3.570",
31
+ "@north-light/identity": "0.3.571",
28
32
  "cron-parser": "^5.6.0",
29
33
  "esbuild": "^0.27.7",
30
34
  "jose": "^6.2.12",
@@ -12455,17 +12459,17 @@
12455
12459
  },
12456
12460
  "packages/crouter-api": {
12457
12461
  "name": "@north-light/api",
12458
- "version": "0.3.570",
12462
+ "version": "0.3.571",
12459
12463
  "license": "UNLICENSED"
12460
12464
  },
12461
12465
  "packages/crouter-env-docker": {
12462
12466
  "name": "@north-light/env-docker",
12463
- "version": "0.3.570",
12467
+ "version": "0.3.571",
12464
12468
  "license": "UNLICENSED"
12465
12469
  },
12466
12470
  "packages/crouter-identity": {
12467
12471
  "name": "@north-light/identity",
12468
- "version": "0.3.570",
12472
+ "version": "0.3.571",
12469
12473
  "license": "UNLICENSED",
12470
12474
  "dependencies": {
12471
12475
  "jose": "^6.2.1"
@@ -12473,21 +12477,21 @@
12473
12477
  },
12474
12478
  "packages/crouter-plugin": {
12475
12479
  "name": "@north-light/plugin",
12476
- "version": "0.3.570",
12480
+ "version": "0.3.571",
12477
12481
  "license": "UNLICENSED",
12478
12482
  "dependencies": {
12479
- "@north-light/api": "0.3.570",
12480
- "@north-light/identity": "0.3.570"
12483
+ "@north-light/api": "0.3.571",
12484
+ "@north-light/identity": "0.3.571"
12481
12485
  }
12482
12486
  },
12483
12487
  "packages/crouter-sdk": {
12484
12488
  "name": "@north-light/sdk",
12485
- "version": "0.3.570",
12489
+ "version": "0.3.571",
12486
12490
  "license": "UNLICENSED",
12487
12491
  "dependencies": {
12488
- "@north-light/api": "0.3.570",
12489
- "@north-light/identity": "0.3.570",
12490
- "@north-light/plugin": "0.3.570",
12492
+ "@north-light/api": "0.3.571",
12493
+ "@north-light/identity": "0.3.571",
12494
+ "@north-light/plugin": "0.3.571",
12491
12495
  "jose": "^6.2.1"
12492
12496
  },
12493
12497
  "bin": {