@north-light/api 0.3.538 → 0.3.540
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/profiles.d.ts +2 -0
- package/dist/core/canvas/legacy-person.d.ts +4 -0
- package/dist/core/canvas/legacy-person.js +4 -0
- package/dist/core/scoped-state/db.js +11 -3
- package/dist/core/scoped-state/profiles.js +6 -5
- package/dist/core/scoped-state/schema.d.ts +4 -0
- package/dist/core/scoped-state/schema.js +5 -1
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/api/contract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
|
|
2
|
-
export declare const CONTRACT_LEVEL = "1.
|
|
2
|
+
export declare const CONTRACT_LEVEL = "1.28";
|
|
3
3
|
/** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
|
|
4
4
|
export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
|
|
5
5
|
/** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
|
package/dist/api/contract.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
//
|
|
13
13
|
// PURITY (spec §3.1): Node built-ins + `src/api/*` only.
|
|
14
14
|
/** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
|
|
15
|
-
export const CONTRACT_LEVEL = '1.
|
|
15
|
+
export const CONTRACT_LEVEL = '1.28';
|
|
16
16
|
/** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
|
|
17
17
|
export const REQUIRED_CONTRACT_HEADER = 'Crouter-Required-Contract';
|
|
18
18
|
/** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
|
|
@@ -112,6 +112,8 @@ export interface ProfileDTO {
|
|
|
112
112
|
created_at: string;
|
|
113
113
|
/** ISO timestamp of the most recent selection, or null if never selected. */
|
|
114
114
|
last_used_at: string | null;
|
|
115
|
+
/** The environment the profile's nodes run in: the stored pointer, or the install's machine environment when none is stored. */
|
|
116
|
+
environment_id?: string;
|
|
115
117
|
/** Absolute path of the profile's directory, `<app space>/profiles/<id>`: its settings root. The profile's documents live in the canvas, not here. */
|
|
116
118
|
profile_path: string;
|
|
117
119
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The person principal every install carried before it minted its own: read only by the one-shot that retires it and by the legacy SQLite/layout importers. Pure so CLI-reachable modules can import it. */
|
|
2
|
+
export declare const LEGACY_LOCAL_PERSON = "user:local";
|
|
3
|
+
/** The id of the local terminal grant an install carried before its grant became `terminal:<install id>`; nodes, tokens, crons and queued births may still reference it. */
|
|
4
|
+
export declare const LEGACY_LOCAL_TERMINAL_GRANT = "local:terminal";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The person principal every install carried before it minted its own: read only by the one-shot that retires it and by the legacy SQLite/layout importers. Pure so CLI-reachable modules can import it. */
|
|
2
|
+
export const LEGACY_LOCAL_PERSON = 'user:local';
|
|
3
|
+
/** The id of the local terminal grant an install carried before its grant became `terminal:<install id>`; nodes, tokens, crons and queued births may still reference it. */
|
|
4
|
+
export const LEGACY_LOCAL_TERMINAL_GRANT = 'local:terminal';
|
|
@@ -8,7 +8,7 @@ import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
|
|
|
8
8
|
import { syncDirectory, syncFile } from '../fs-utils.js';
|
|
9
9
|
import { layoutNeedsMigration } from '../layout.js';
|
|
10
10
|
import { providerStore, projectStore, userStore } from './paths.js';
|
|
11
|
-
import { createScopedSchema, ensureProviderModelCooldowns, PROVIDER_SCHEMA_VERSION, SCOPED_SCHEMA_VERSION } from './schema.js';
|
|
11
|
+
import { createScopedSchema, ensureProviderModelCooldowns, PROFILE_ENVIRONMENT_COLUMN, PROVIDER_SCHEMA_VERSION, SCOPED_SCHEMA_VERSION } from './schema.js';
|
|
12
12
|
const handles = new Map();
|
|
13
13
|
export const PROFILE_ID_SHAPE = /^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/;
|
|
14
14
|
export function writeOwnerRoot(db, key, root) {
|
|
@@ -27,6 +27,9 @@ function assertVersion(db, store) {
|
|
|
27
27
|
if (found !== expected)
|
|
28
28
|
throw new Error(`scoped SQL store ${store.path} requires an explicit conversion`);
|
|
29
29
|
}
|
|
30
|
+
function profilesHaveEnvironmentColumn(db) {
|
|
31
|
+
return db.prepare('PRAGMA table_info(profiles)').all().some(({ name }) => name === PROFILE_ENVIRONMENT_COLUMN);
|
|
32
|
+
}
|
|
30
33
|
function upgradeScopedStore(db, store) {
|
|
31
34
|
if (store.kind === 'provider') {
|
|
32
35
|
if (version(db) === PROVIDER_SCHEMA_VERSION) {
|
|
@@ -35,9 +38,12 @@ function upgradeScopedStore(db, store) {
|
|
|
35
38
|
}
|
|
36
39
|
return;
|
|
37
40
|
}
|
|
38
|
-
if (version(db)
|
|
41
|
+
if (version(db) > SCOPED_SCHEMA_VERSION)
|
|
42
|
+
return;
|
|
43
|
+
db.exec('PRAGMA busy_timeout = 5000');
|
|
44
|
+
if (version(db) === SCOPED_SCHEMA_VERSION && profilesHaveEnvironmentColumn(db))
|
|
39
45
|
return;
|
|
40
|
-
db.exec('
|
|
46
|
+
db.exec('BEGIN IMMEDIATE');
|
|
41
47
|
try {
|
|
42
48
|
if (version(db) === 1) {
|
|
43
49
|
const columns = db.prepare('PRAGMA table_info(profiles)').all();
|
|
@@ -64,6 +70,8 @@ function upgradeScopedStore(db, store) {
|
|
|
64
70
|
}
|
|
65
71
|
db.exec(`PRAGMA user_version = ${SCOPED_SCHEMA_VERSION}`);
|
|
66
72
|
}
|
|
73
|
+
if (version(db) === SCOPED_SCHEMA_VERSION && !profilesHaveEnvironmentColumn(db))
|
|
74
|
+
db.exec(`ALTER TABLE profiles ADD COLUMN ${PROFILE_ENVIRONMENT_COLUMN} TEXT`);
|
|
67
75
|
db.exec('COMMIT');
|
|
68
76
|
}
|
|
69
77
|
catch (error) {
|
|
@@ -2,12 +2,13 @@ import { prepared as cachedStatement } from '../sqlite-statements.js';
|
|
|
2
2
|
import { canonicalRoot, userStore } from './paths.js';
|
|
3
3
|
import { profileSpace } from '../layout.js';
|
|
4
4
|
import { openScopedStore, withScopedWrite } from './db.js';
|
|
5
|
+
import { LEGACY_LOCAL_PERSON } from '../canvas/legacy-person.js';
|
|
5
6
|
function store(root) { return userStore(root); }
|
|
6
7
|
function rowToProfile(row, projects) {
|
|
7
|
-
return { profileId: row.id, manifest: { schema_version: 1, name: row.name, projects, paused_at: row.paused_at, ...(row.default_kind === null ? {} : { default_kind: row.default_kind }), ...(row.grantee === null ? {} : { grantee: row.grantee }), ...(row.delivery_limits_json === '{}' ? {} : { delivery_limits: JSON.parse(row.delivery_limits_json) }), ...(Object.keys(JSON.parse(row.metadata_json)).length === 0 ? {} : { metadata: JSON.parse(row.metadata_json) }), created_at: row.created_at, last_used_at: row.last_used_at } };
|
|
8
|
+
return { profileId: row.id, manifest: { schema_version: 1, name: row.name, projects, paused_at: row.paused_at, ...(row.default_kind === null ? {} : { default_kind: row.default_kind }), ...(row.grantee === null ? {} : { grantee: row.grantee }), ...(row.environment_id === null ? {} : { environment_id: row.environment_id }), ...(row.delivery_limits_json === '{}' ? {} : { delivery_limits: JSON.parse(row.delivery_limits_json) }), ...(Object.keys(JSON.parse(row.metadata_json)).length === 0 ? {} : { metadata: JSON.parse(row.metadata_json) }), created_at: row.created_at, last_used_at: row.last_used_at } };
|
|
8
9
|
}
|
|
9
10
|
function get(db, id) {
|
|
10
|
-
const row = cachedStatement(db, 'SELECT id, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json FROM profiles WHERE id=?').get(id);
|
|
11
|
+
const row = cachedStatement(db, 'SELECT id, name, created_at, last_used_at, paused_at, default_kind, grantee, metadata_json, delivery_limits_json, environment_id FROM profiles WHERE id=?').get(id);
|
|
11
12
|
if (!row)
|
|
12
13
|
return null;
|
|
13
14
|
const projects = cachedStatement(db, 'SELECT path FROM profile_projects WHERE profile_id=? ORDER BY position').all(id);
|
|
@@ -30,7 +31,7 @@ export function pausedProfileIds(root) {
|
|
|
30
31
|
}
|
|
31
32
|
export function insertStoredProfile(root, profile) {
|
|
32
33
|
withScopedWrite(store(root), (db) => {
|
|
33
|
-
cachedStatement(db, 'INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json,delivery_limits_json) VALUES(
|
|
34
|
+
cachedStatement(db, 'INSERT INTO profiles(id,name,created_at,last_used_at,paused_at,default_kind,grantee,metadata_json,delivery_limits_json,environment_id) VALUES(?,?,?,?,?,?,?,?,?,?)').run(profile.profileId, profile.manifest.name, profile.manifest.created_at, profile.manifest.last_used_at, profile.manifest.paused_at, profile.manifest.default_kind ?? null, profile.manifest.grantee ?? null, JSON.stringify(profile.manifest.metadata ?? {}), JSON.stringify(profile.manifest.delivery_limits ?? {}), profile.manifest.environment_id ?? null);
|
|
34
35
|
cachedStatement(db, 'INSERT INTO scope_owners(owner_key,kind,root,profile_id) VALUES(?,?,?,?)').run(`profile:${profile.profileId}`, 'profile', canonicalRoot(profileSpace(profile.manifest.grantee ?? 'app:terminal', profile.profileId).dir), profile.profileId);
|
|
35
36
|
const insert = cachedStatement(db, 'INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)');
|
|
36
37
|
profile.manifest.projects.forEach((project, position) => insert.run(profile.profileId, project.path, position));
|
|
@@ -42,7 +43,7 @@ export function updateStoredProfile(root, id, mutate) {
|
|
|
42
43
|
if (!current)
|
|
43
44
|
return null;
|
|
44
45
|
const next = mutate(current.manifest);
|
|
45
|
-
cachedStatement(db, 'UPDATE profiles SET name=?, last_used_at=?, paused_at=?, default_kind=?, grantee=?, metadata_json=?, delivery_limits_json=? WHERE id=?').run(next.name, next.last_used_at, next.paused_at, next.default_kind ?? null, next.grantee ?? null, JSON.stringify(next.metadata ?? {}), JSON.stringify(next.delivery_limits ?? {}), id);
|
|
46
|
+
cachedStatement(db, 'UPDATE profiles SET name=?, last_used_at=?, paused_at=?, default_kind=?, grantee=?, metadata_json=?, delivery_limits_json=?, environment_id=? WHERE id=?').run(next.name, next.last_used_at, next.paused_at, next.default_kind ?? null, next.grantee ?? null, JSON.stringify(next.metadata ?? {}), JSON.stringify(next.delivery_limits ?? {}), next.environment_id ?? null, id);
|
|
46
47
|
cachedStatement(db, 'DELETE FROM profile_projects WHERE profile_id=?').run(id);
|
|
47
48
|
const insert = cachedStatement(db, 'INSERT INTO profile_projects(profile_id,path,position) VALUES(?,?,?)');
|
|
48
49
|
next.projects.forEach((project, position) => insert.run(id, project.path, position));
|
|
@@ -65,7 +66,7 @@ export function deleteStoredProfile(root, id) {
|
|
|
65
66
|
}
|
|
66
67
|
export function rewriteLocalProfileGrantees(root) {
|
|
67
68
|
withScopedWrite(store(root), (db) => {
|
|
68
|
-
cachedStatement(db, "UPDATE profiles SET grantee = 'app:terminal' WHERE grantee =
|
|
69
|
+
cachedStatement(db, "UPDATE profiles SET grantee = 'app:terminal' WHERE grantee = ?").run(LEGACY_LOCAL_PERSON);
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
export function readStoredDefaults(root) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
export declare const SCOPED_SCHEMA_VERSION = 5;
|
|
3
3
|
export declare const PROVIDER_SCHEMA_VERSION = 1;
|
|
4
|
+
/** `profiles.environment_id` (null = the machine environment) is additive to version 5: a store written before it gains the
|
|
5
|
+
* column on open (`upgradeScopedStore`), and an older generation sharing the store names its columns and never reads it, so
|
|
6
|
+
* the version stays 5 and a runtime rollback keeps opening the store. */
|
|
7
|
+
export declare const PROFILE_ENVIRONMENT_COLUMN = "environment_id";
|
|
4
8
|
/** Adds the model-cooldown table to a provider store that predates it; a no-op (no write) once present. */
|
|
5
9
|
export declare function ensureProviderModelCooldowns(db: DatabaseSync): void;
|
|
6
10
|
export declare function createScopedSchema(db: DatabaseSync, kind: 'user' | 'project' | 'provider'): void;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { prepared as cachedStatement } from '../sqlite-statements.js';
|
|
2
2
|
export const SCOPED_SCHEMA_VERSION = 5;
|
|
3
3
|
export const PROVIDER_SCHEMA_VERSION = 1;
|
|
4
|
+
/** `profiles.environment_id` (null = the machine environment) is additive to version 5: a store written before it gains the
|
|
5
|
+
* column on open (`upgradeScopedStore`), and an older generation sharing the store names its columns and never reads it, so
|
|
6
|
+
* the version stays 5 and a runtime rollback keeps opening the store. */
|
|
7
|
+
export const PROFILE_ENVIRONMENT_COLUMN = 'environment_id';
|
|
4
8
|
const SCOPE_SCHEMA = `
|
|
5
9
|
CREATE TABLE scope_owners (owner_key TEXT PRIMARY KEY, kind TEXT NOT NULL CHECK(kind IN ('user','profile','project')), root TEXT NOT NULL UNIQUE, profile_id TEXT UNIQUE);
|
|
6
|
-
CREATE TABLE profiles (id TEXT PRIMARY KEY, name TEXT NOT NULL, created_at TEXT NOT NULL, last_used_at TEXT, paused_at TEXT, default_kind TEXT, grantee TEXT, metadata_json TEXT NOT NULL CHECK(json_valid(metadata_json)), delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json)));
|
|
10
|
+
CREATE TABLE profiles (id TEXT PRIMARY KEY, name TEXT NOT NULL, created_at TEXT NOT NULL, last_used_at TEXT, paused_at TEXT, default_kind TEXT, grantee TEXT, metadata_json TEXT NOT NULL CHECK(json_valid(metadata_json)), delivery_limits_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(delivery_limits_json)), environment_id TEXT);
|
|
7
11
|
CREATE TABLE profile_projects (profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE, path TEXT NOT NULL, position INTEGER NOT NULL, PRIMARY KEY(profile_id,path), UNIQUE(profile_id,position));
|
|
8
12
|
-- Pre-canvas per-project memory caps awaiting the canvas import, which turns them into delivery_limits and deletes the rows.
|
|
9
13
|
CREATE TABLE legacy_project_caps (profile_id TEXT NOT NULL, path TEXT NOT NULL, memory TEXT NOT NULL CHECK(memory IN ('none','name','preview')), PRIMARY KEY(profile_id,path));
|
package/dist/types.d.ts
CHANGED
|
@@ -553,6 +553,8 @@ export interface ProfileManifest {
|
|
|
553
553
|
delivery_limits?: DeliveryLimits;
|
|
554
554
|
/** App that owns this profile; older terminal profiles have no grantee. */
|
|
555
555
|
grantee?: string;
|
|
556
|
+
/** The environment the profile's nodes run in; absent means the install's machine environment. */
|
|
557
|
+
environment_id?: string;
|
|
556
558
|
created_at: string;
|
|
557
559
|
last_used_at: string | null;
|
|
558
560
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.540",
|
|
4
4
|
"description": "Typed crtrd /v1 API contract — DTOs, route builders, the error contract, the CrtrClient, and the command-plugin manifest format. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/api/index.js",
|