@nylorun/runtime 0.10.0-beta → 0.12.0-beta
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/CHANGELOG.md +86 -0
- package/README.md +53 -51
- package/dist/adapters/execution/restate.d.ts +130 -0
- package/dist/adapters/execution/restate.js +442 -0
- package/dist/adapters/sandbox/virtual.js +1 -1
- package/dist/adapters/streams/s2.d.ts +18 -0
- package/dist/adapters/streams/s2.js +361 -0
- package/dist/core/executors.d.ts +4 -1
- package/dist/core/executors.js +20 -1
- package/dist/core/flow-host.d.ts +93 -132
- package/dist/core/flow-host.js +282 -547
- package/dist/core/sandbox-routes.d.ts +13 -4
- package/dist/core/sandbox-routes.js +14 -12
- package/dist/execution/memory.d.ts +54 -0
- package/dist/execution/memory.js +217 -0
- package/dist/execution/types.d.ts +125 -0
- package/dist/execution/types.js +52 -0
- package/dist/host/config-for.d.ts +2 -1
- package/dist/host/config-for.js +3 -21
- package/dist/host/config.d.ts +0 -8
- package/dist/host/create-host.d.ts +36 -1
- package/dist/host/create-host.js +51 -24
- package/dist/host/execution.d.ts +61 -0
- package/dist/host/execution.js +69 -0
- package/dist/host/http.d.ts +4 -0
- package/dist/host/http.js +6 -0
- package/dist/host/index.d.ts +3 -1
- package/dist/host/index.js +2 -0
- package/dist/host/main.js +106 -47
- package/dist/host/stack-config.d.ts +72 -0
- package/dist/host/stack-config.js +193 -0
- package/dist/index.d.ts +1 -1
- package/dist/infra/database.d.ts +16 -0
- package/dist/infra/database.js +31 -0
- package/dist/infra/execution.d.ts +39 -0
- package/dist/infra/execution.js +68 -0
- package/dist/infra/index.d.ts +42 -0
- package/dist/infra/index.js +56 -0
- package/dist/infra/readiness.d.ts +33 -0
- package/dist/infra/readiness.js +53 -0
- package/dist/infra/streams.d.ts +17 -0
- package/dist/infra/streams.js +33 -0
- package/dist/infra/workers.d.ts +39 -0
- package/dist/infra/workers.js +31 -0
- package/dist/model/pi-model.d.ts +2 -2
- package/dist/model/pi-model.js +5 -5
- package/dist/sandbox/manager.d.ts +26 -8
- package/dist/sandbox/manager.js +95 -47
- package/dist/sandbox/select.d.ts +1 -1
- package/dist/sandbox/select.js +4 -5
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/store/canonical.d.ts +5 -0
- package/dist/store/canonical.js +17 -0
- package/dist/store/cursor.d.ts +9 -0
- package/dist/store/cursor.js +18 -0
- package/dist/store/memory.d.ts +57 -0
- package/dist/store/memory.js +645 -0
- package/dist/store/ownership.d.ts +19 -0
- package/dist/store/ownership.js +33 -0
- package/dist/store/postgres/connect.d.ts +25 -0
- package/dist/store/postgres/connect.js +25 -0
- package/dist/store/postgres/locking.d.ts +16 -0
- package/dist/store/postgres/locking.js +22 -0
- package/dist/store/postgres/migrations/001_initial.d.ts +27 -0
- package/dist/store/postgres/migrations/001_initial.js +196 -0
- package/dist/store/postgres/migrations/002_session_owner.d.ts +6 -0
- package/dist/store/postgres/migrations/002_session_owner.js +13 -0
- package/dist/store/postgres/migrations/index.d.ts +49 -0
- package/dist/store/postgres/migrations/index.js +76 -0
- package/dist/store/postgres/names.d.ts +11 -0
- package/dist/store/postgres/names.js +42 -0
- package/dist/store/postgres/store.d.ts +11 -0
- package/dist/store/postgres/store.js +797 -0
- package/dist/store/postgres/tenants.d.ts +90 -0
- package/dist/store/postgres/tenants.js +214 -0
- package/dist/store/types.d.ts +442 -0
- package/dist/store/types.js +10 -0
- package/dist/streams/basin.d.ts +23 -0
- package/dist/streams/basin.js +51 -0
- package/dist/streams/memory.d.ts +25 -0
- package/dist/streams/memory.js +124 -0
- package/dist/streams/relay.d.ts +37 -0
- package/dist/streams/relay.js +254 -0
- package/dist/streams/types.d.ts +115 -0
- package/dist/streams/types.js +86 -0
- package/dist/tenant/actions.d.ts +153 -0
- package/dist/tenant/actions.js +191 -0
- package/dist/tenant/advance.d.ts +15 -0
- package/dist/tenant/advance.js +439 -0
- package/dist/tenant/auth.d.ts +32 -0
- package/dist/tenant/auth.js +169 -0
- package/dist/tenant/commands.d.ts +3 -0
- package/dist/tenant/commands.js +393 -0
- package/dist/tenant/context.d.ts +147 -0
- package/dist/tenant/context.js +48 -0
- package/dist/tenant/effects.d.ts +20 -0
- package/dist/tenant/effects.js +564 -0
- package/dist/tenant/envelope.d.ts +0 -3
- package/dist/tenant/envelope.js +2 -34
- package/dist/tenant/ephemeral.d.ts +15 -4
- package/dist/tenant/ephemeral.js +106 -38
- package/dist/tenant/http.d.ts +38 -0
- package/dist/tenant/http.js +53 -0
- package/dist/tenant/live.d.ts +92 -0
- package/dist/tenant/live.js +290 -0
- package/dist/tenant/model-setting.d.ts +16 -0
- package/dist/tenant/model-setting.js +12 -0
- package/dist/tenant/module.d.ts +29 -8
- package/dist/tenant/module.js +165 -158
- package/dist/tenant/paths.d.ts +12 -8
- package/dist/tenant/paths.js +38 -12
- package/dist/tenant/principals.d.ts +12 -13
- package/dist/tenant/principals.js +50 -38
- package/dist/tenant/quarantine-error.d.ts +1 -1
- package/dist/tenant/quarantine-error.js +2 -8
- package/dist/tenant/quarantine.d.ts +0 -8
- package/dist/tenant/quarantine.js +8 -30
- package/dist/tenant/reset.d.ts +7 -6
- package/dist/tenant/reset.js +7 -51
- package/dist/tenant/routes-tenant.d.ts +12 -0
- package/dist/tenant/routes-tenant.js +137 -0
- package/dist/tenant/routes.d.ts +9 -0
- package/dist/tenant/routes.js +151 -0
- package/dist/tenant/runtime.d.ts +59 -58
- package/dist/tenant/runtime.js +243 -2201
- package/dist/tenant/scheduler.d.ts +25 -0
- package/dist/tenant/scheduler.js +84 -0
- package/dist/tenant/session.d.ts +60 -0
- package/dist/tenant/session.js +94 -0
- package/dist/tenant/sessions.d.ts +40 -0
- package/dist/tenant/sessions.js +163 -0
- package/dist/tenant/status.d.ts +13 -4
- package/dist/tenant/status.js +76 -41
- package/dist/tenant/store-memory.d.ts +9 -12
- package/dist/tenant/store-memory.js +79 -68
- package/dist/tenant/store-pg.d.ts +15 -0
- package/dist/tenant/store-pg.js +140 -0
- package/dist/tenant/streams.d.ts +126 -0
- package/dist/tenant/streams.js +419 -0
- package/dist/tenant/sweep.d.ts +15 -0
- package/dist/tenant/sweep.js +122 -0
- package/dist/tenant/transcript.d.ts +104 -0
- package/dist/tenant/transcript.js +58 -0
- package/dist/tenant/types.d.ts +101 -15
- package/dist/tenant/types.js +23 -1
- package/dist/tenant/worker.d.ts +84 -0
- package/dist/tenant/worker.js +189 -0
- package/dist/vault/service.d.ts +44 -31
- package/dist/vault/service.js +454 -360
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +15 -14
- package/dist/adapters/sandbox/microsandbox.d.ts +0 -2
- package/dist/adapters/sandbox/microsandbox.js +0 -238
- package/dist/core/store.d.ts +0 -32
- package/dist/core/store.js +0 -143
- package/dist/launcher/commands.d.ts +0 -24
- package/dist/launcher/commands.js +0 -260
- package/dist/launcher/ensure-config.d.ts +0 -16
- package/dist/launcher/ensure-config.js +0 -79
- package/dist/launcher/errors.d.ts +0 -12
- package/dist/launcher/errors.js +0 -22
- package/dist/launcher/host-config.d.ts +0 -17
- package/dist/launcher/host-config.js +0 -64
- package/dist/launcher/host-state.d.ts +0 -17
- package/dist/launcher/host-state.js +0 -61
- package/dist/launcher/lifecycle.d.ts +0 -66
- package/dist/launcher/lifecycle.js +0 -407
- package/dist/launcher/locks.d.ts +0 -26
- package/dist/launcher/locks.js +0 -117
- package/dist/launcher/logs.d.ts +0 -18
- package/dist/launcher/logs.js +0 -117
- package/dist/launcher/main.d.ts +0 -2
- package/dist/launcher/main.js +0 -66
- package/dist/launcher/output.d.ts +0 -11
- package/dist/launcher/output.js +0 -68
- package/dist/launcher/paths.d.ts +0 -20
- package/dist/launcher/paths.js +0 -36
- package/dist/launcher/probe.d.ts +0 -23
- package/dist/launcher/probe.js +0 -72
- package/dist/launcher/protocol.d.ts +0 -62
- package/dist/launcher/protocol.js +0 -1
- package/dist/launcher/schema-guard.d.ts +0 -6
- package/dist/launcher/schema-guard.js +0 -36
- package/dist/launcher/spawn.d.ts +0 -22
- package/dist/launcher/spawn.js +0 -85
- package/dist/launcher/status.d.ts +0 -6
- package/dist/launcher/status.js +0 -65
- package/dist/tenant/migration.d.ts +0 -16
- package/dist/tenant/migration.js +0 -116
- package/dist/tenant/schema.d.ts +0 -23
- package/dist/tenant/schema.js +0 -151
- package/dist/tenant/store-fs.d.ts +0 -16
- package/dist/tenant/store-fs.js +0 -205
package/dist/tenant/migration.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { DatabaseSync } from "node:sqlite";
|
|
4
|
-
import { TENANT_SCHEMA_VERSION, migrateTenantDatabase as defaultMigrate, schemaVersionOf as defaultSchemaVersionOf, } from "./schema.js";
|
|
5
|
-
import { writeEnvelopeFile } from "./envelope.js";
|
|
6
|
-
import { quarantine } from "./quarantine.js";
|
|
7
|
-
function stamp(now) {
|
|
8
|
-
return now.toISOString().replaceAll(":", "-");
|
|
9
|
-
}
|
|
10
|
-
function copyIfPresent(from, to) {
|
|
11
|
-
if (existsSync(from))
|
|
12
|
-
copyFileSync(from, to);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Checkpoint WAL, snapshot database + envelope + KEK, migrate, update envelope.
|
|
16
|
-
* On failure restore the snapshot, keep diagnostics, and throw migration-failed.
|
|
17
|
-
*/
|
|
18
|
-
export function migrateTenantWithSnapshot(paths, envelope, hooks = {}, now = new Date()) {
|
|
19
|
-
const schemaVersionOf = hooks.schemaVersionOf ?? defaultSchemaVersionOf;
|
|
20
|
-
const migrateTenantDatabase = hooks.migrateTenantDatabase ?? defaultMigrate;
|
|
21
|
-
const target = hooks.targetVersion ?? TENANT_SCHEMA_VERSION;
|
|
22
|
-
const db = new DatabaseSync(paths.database);
|
|
23
|
-
let from;
|
|
24
|
-
try {
|
|
25
|
-
from = schemaVersionOf(db);
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
db.close();
|
|
29
|
-
throw error;
|
|
30
|
-
}
|
|
31
|
-
if (from > target) {
|
|
32
|
-
db.close();
|
|
33
|
-
throw quarantine("schema-too-new", `Tenant schema version ${from} is newer than Host ${target}`, { tenantId: envelope.id });
|
|
34
|
-
}
|
|
35
|
-
if (from === target) {
|
|
36
|
-
db.close();
|
|
37
|
-
return envelope;
|
|
38
|
-
}
|
|
39
|
-
const dirName = `${from}-${target}-${stamp(now)}`;
|
|
40
|
-
mkdirSync(paths.migration, { recursive: true });
|
|
41
|
-
const snapshotDir = join(paths.migration, dirName);
|
|
42
|
-
mkdirSync(snapshotDir, { recursive: true });
|
|
43
|
-
try {
|
|
44
|
-
// Checkpoint WAL so the database file is self-contained (invariant 7).
|
|
45
|
-
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
46
|
-
db.close();
|
|
47
|
-
copyIfPresent(paths.database, join(snapshotDir, "tenant.sqlite"));
|
|
48
|
-
copyIfPresent(paths.envelope, join(snapshotDir, "tenant.json"));
|
|
49
|
-
copyIfPresent(paths.kek, join(snapshotDir, "vault-kek"));
|
|
50
|
-
// Side-car WAL/SHM if present.
|
|
51
|
-
copyIfPresent(`${paths.database}-wal`, join(snapshotDir, "tenant.sqlite-wal"));
|
|
52
|
-
copyIfPresent(`${paths.database}-shm`, join(snapshotDir, "tenant.sqlite-shm"));
|
|
53
|
-
const migrating = new DatabaseSync(paths.database);
|
|
54
|
-
let result;
|
|
55
|
-
try {
|
|
56
|
-
migrating.exec("BEGIN IMMEDIATE");
|
|
57
|
-
result = migrateTenantDatabase(migrating);
|
|
58
|
-
migrating.exec("COMMIT");
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
try {
|
|
62
|
-
migrating.exec("ROLLBACK");
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
/* ignore */
|
|
66
|
-
}
|
|
67
|
-
migrating.close();
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
migrating.close();
|
|
71
|
-
const updated = {
|
|
72
|
-
...envelope,
|
|
73
|
-
schemaVersion: result.to,
|
|
74
|
-
updatedAt: now.toISOString(),
|
|
75
|
-
};
|
|
76
|
-
writeEnvelopeFile(paths.envelope, updated);
|
|
77
|
-
return updated;
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
restoreSnapshot(paths, snapshotDir);
|
|
81
|
-
writeFileSync(join(snapshotDir, "failure.json"), `${JSON.stringify({
|
|
82
|
-
at: now.toISOString(),
|
|
83
|
-
message: error instanceof Error ? error.message : String(error),
|
|
84
|
-
}, null, 2)}\n`);
|
|
85
|
-
throw quarantine("migration-failed", error instanceof Error ? error.message : "migration failed", { tenantId: envelope.id, migrationPath: snapshotDir });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function restoreSnapshot(paths, snapshotDir) {
|
|
89
|
-
const dbSnap = join(snapshotDir, "tenant.sqlite");
|
|
90
|
-
const envSnap = join(snapshotDir, "tenant.json");
|
|
91
|
-
const kekSnap = join(snapshotDir, "vault-kek");
|
|
92
|
-
if (existsSync(dbSnap)) {
|
|
93
|
-
// Replace via temp rename so a partial copy cannot leave a half file.
|
|
94
|
-
const tmp = `${paths.database}.restore-tmp`;
|
|
95
|
-
copyFileSync(dbSnap, tmp);
|
|
96
|
-
renameSync(tmp, paths.database);
|
|
97
|
-
}
|
|
98
|
-
if (existsSync(envSnap)) {
|
|
99
|
-
const tmp = `${paths.envelope}.restore-tmp`;
|
|
100
|
-
copyFileSync(envSnap, tmp);
|
|
101
|
-
renameSync(tmp, paths.envelope);
|
|
102
|
-
}
|
|
103
|
-
if (existsSync(kekSnap)) {
|
|
104
|
-
const tmp = `${paths.kek}.restore-tmp`;
|
|
105
|
-
copyFileSync(kekSnap, tmp);
|
|
106
|
-
renameSync(tmp, paths.kek);
|
|
107
|
-
}
|
|
108
|
-
for (const side of ["-wal", "-shm"]) {
|
|
109
|
-
const snap = join(snapshotDir, `tenant.sqlite${side}`);
|
|
110
|
-
const dest = `${paths.database}${side}`;
|
|
111
|
-
if (existsSync(snap))
|
|
112
|
-
copyFileSync(snap, dest);
|
|
113
|
-
else if (existsSync(dest))
|
|
114
|
-
rmSync(dest, { force: true });
|
|
115
|
-
}
|
|
116
|
-
}
|
package/dist/tenant/schema.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
-
/** Tenant schema version (D4). v2 adds tenant_settings; v3 adds workflow links. */
|
|
3
|
-
export declare const TENANT_SCHEMA_VERSION = 3;
|
|
4
|
-
export declare function schemaVersionOf(db: DatabaseSync): number;
|
|
5
|
-
/**
|
|
6
|
-
* Migrates a Tenant database to `TENANT_SCHEMA_VERSION`.
|
|
7
|
-
* Carries vault-scope DDL as a migration step, not ad hoc (D3, D4).
|
|
8
|
-
*/
|
|
9
|
-
export declare function migrateTenantDatabase(db: DatabaseSync): {
|
|
10
|
-
from: number;
|
|
11
|
-
to: number;
|
|
12
|
-
};
|
|
13
|
-
export type SandboxBackendSetting = "auto" | "microsandbox" | "virtual";
|
|
14
|
-
/** Non-secret Tenant settings persisted for seed / configFor (A18). */
|
|
15
|
-
export declare function readTenantSetting(db: DatabaseSync, key: string): string | undefined;
|
|
16
|
-
export declare function writeTenantSetting(db: DatabaseSync, key: string, value: string): void;
|
|
17
|
-
/**
|
|
18
|
-
* Read non-secret Tenant configuration from the open database.
|
|
19
|
-
* Used by Host `configFor` at Integration I1.
|
|
20
|
-
*/
|
|
21
|
-
export declare function readTenantConfig(db: DatabaseSync): {
|
|
22
|
-
sandboxBackend?: SandboxBackendSetting;
|
|
23
|
-
};
|
package/dist/tenant/schema.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/** Tenant schema version (D4). v2 adds tenant_settings; v3 adds workflow links. */
|
|
2
|
-
export const TENANT_SCHEMA_VERSION = 3;
|
|
3
|
-
export function schemaVersionOf(db) {
|
|
4
|
-
const row = db.prepare("PRAGMA user_version").get();
|
|
5
|
-
return Number(row.user_version);
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Migrates a Tenant database to `TENANT_SCHEMA_VERSION`.
|
|
9
|
-
* Carries vault-scope DDL as a migration step, not ad hoc (D3, D4).
|
|
10
|
-
*/
|
|
11
|
-
export function migrateTenantDatabase(db) {
|
|
12
|
-
const from = schemaVersionOf(db);
|
|
13
|
-
if (from > TENANT_SCHEMA_VERSION) {
|
|
14
|
-
throw new Error(`Tenant schema version ${from} is newer than Host ${TENANT_SCHEMA_VERSION}`);
|
|
15
|
-
}
|
|
16
|
-
if (from === TENANT_SCHEMA_VERSION) {
|
|
17
|
-
return { from, to: from };
|
|
18
|
-
}
|
|
19
|
-
db.exec("BEGIN IMMEDIATE");
|
|
20
|
-
try {
|
|
21
|
-
if (from < 1) {
|
|
22
|
-
db.exec(`
|
|
23
|
-
CREATE TABLE IF NOT EXISTS definitions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
24
|
-
CREATE TABLE IF NOT EXISTS sessions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
25
|
-
CREATE TABLE IF NOT EXISTS commands(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
26
|
-
CREATE TABLE IF NOT EXISTS checkpoints(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
27
|
-
CREATE TABLE IF NOT EXISTS effects(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
28
|
-
CREATE TABLE IF NOT EXISTS actions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
29
|
-
CREATE TABLE IF NOT EXISTS sandboxes(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
30
|
-
CREATE TABLE IF NOT EXISTS events(
|
|
31
|
-
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
-
session_id TEXT NOT NULL,
|
|
33
|
-
body TEXT NOT NULL
|
|
34
|
-
);
|
|
35
|
-
CREATE INDEX IF NOT EXISTS events_session ON events(session_id, sequence);
|
|
36
|
-
CREATE TABLE IF NOT EXISTS vaults(
|
|
37
|
-
id TEXT PRIMARY KEY,
|
|
38
|
-
name TEXT NOT NULL,
|
|
39
|
-
owner_user_id TEXT NOT NULL,
|
|
40
|
-
metadata_json TEXT,
|
|
41
|
-
created_at TEXT NOT NULL,
|
|
42
|
-
scope TEXT NOT NULL DEFAULT 'user'
|
|
43
|
-
);
|
|
44
|
-
CREATE INDEX IF NOT EXISTS vaults_owner ON vaults(owner_user_id);
|
|
45
|
-
CREATE TABLE IF NOT EXISTS vault_credentials(
|
|
46
|
-
id TEXT PRIMARY KEY,
|
|
47
|
-
vault_id TEXT NOT NULL REFERENCES vaults(id) ON DELETE CASCADE,
|
|
48
|
-
name TEXT NOT NULL,
|
|
49
|
-
type TEXT NOT NULL,
|
|
50
|
-
binding_json TEXT NOT NULL,
|
|
51
|
-
expires_at TEXT,
|
|
52
|
-
created_at TEXT NOT NULL,
|
|
53
|
-
rotated_at TEXT,
|
|
54
|
-
kek_id TEXT NOT NULL,
|
|
55
|
-
nonce BLOB NOT NULL,
|
|
56
|
-
ciphertext BLOB NOT NULL,
|
|
57
|
-
wrapped_dek BLOB NOT NULL
|
|
58
|
-
);
|
|
59
|
-
CREATE INDEX IF NOT EXISTS vault_credentials_vault ON vault_credentials(vault_id);
|
|
60
|
-
CREATE TABLE IF NOT EXISTS vault_audit(
|
|
61
|
-
id TEXT PRIMARY KEY,
|
|
62
|
-
at TEXT NOT NULL,
|
|
63
|
-
actor TEXT NOT NULL,
|
|
64
|
-
action TEXT NOT NULL,
|
|
65
|
-
vault_id TEXT,
|
|
66
|
-
credential_id TEXT,
|
|
67
|
-
session_id TEXT,
|
|
68
|
-
target TEXT,
|
|
69
|
-
outcome TEXT NOT NULL
|
|
70
|
-
);
|
|
71
|
-
CREATE TABLE IF NOT EXISTS vault_idempotency(
|
|
72
|
-
id TEXT PRIMARY KEY,
|
|
73
|
-
body_hash TEXT NOT NULL,
|
|
74
|
-
response TEXT NOT NULL
|
|
75
|
-
);
|
|
76
|
-
CREATE TABLE IF NOT EXISTS executors(
|
|
77
|
-
agent_id TEXT PRIMARY KEY,
|
|
78
|
-
token_hash TEXT NOT NULL UNIQUE,
|
|
79
|
-
implementation_version TEXT NOT NULL,
|
|
80
|
-
manifest_hash TEXT,
|
|
81
|
-
principal_id TEXT,
|
|
82
|
-
created_at TEXT NOT NULL,
|
|
83
|
-
updated_at TEXT NOT NULL
|
|
84
|
-
);
|
|
85
|
-
CREATE TABLE IF NOT EXISTS principals(
|
|
86
|
-
id TEXT PRIMARY KEY,
|
|
87
|
-
role TEXT NOT NULL CHECK(role = 'application'),
|
|
88
|
-
token_hash TEXT NOT NULL UNIQUE,
|
|
89
|
-
idempotency_key TEXT,
|
|
90
|
-
created_at TEXT NOT NULL
|
|
91
|
-
);
|
|
92
|
-
`);
|
|
93
|
-
migrateVaultScope(db);
|
|
94
|
-
ensureExecutorPrincipalColumn(db);
|
|
95
|
-
}
|
|
96
|
-
if (from < 2) {
|
|
97
|
-
db.exec(`
|
|
98
|
-
CREATE TABLE IF NOT EXISTS tenant_settings(
|
|
99
|
-
key TEXT PRIMARY KEY,
|
|
100
|
-
value TEXT NOT NULL
|
|
101
|
-
);
|
|
102
|
-
`);
|
|
103
|
-
}
|
|
104
|
-
if (from < 3) {
|
|
105
|
-
db.exec(`
|
|
106
|
-
CREATE TABLE IF NOT EXISTS links(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
107
|
-
`);
|
|
108
|
-
}
|
|
109
|
-
db.exec(`PRAGMA user_version = ${TENANT_SCHEMA_VERSION}`);
|
|
110
|
-
db.exec("COMMIT");
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
db.exec("ROLLBACK");
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
return { from, to: TENANT_SCHEMA_VERSION };
|
|
117
|
-
}
|
|
118
|
-
/** Existing vault-scope step, now owned by the Tenant migration. */
|
|
119
|
-
function migrateVaultScope(db) {
|
|
120
|
-
const columns = db.prepare("PRAGMA table_info(vaults)").all();
|
|
121
|
-
if (!columns.some((column) => column.name === "scope"))
|
|
122
|
-
db.exec("ALTER TABLE vaults ADD COLUMN scope TEXT NOT NULL DEFAULT 'user'");
|
|
123
|
-
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS vaults_one_host ON vaults(scope) WHERE scope = 'host'");
|
|
124
|
-
}
|
|
125
|
-
/** Additive column for registration credential attribution (A6). */
|
|
126
|
-
function ensureExecutorPrincipalColumn(db) {
|
|
127
|
-
const columns = db.prepare("PRAGMA table_info(executors)").all();
|
|
128
|
-
if (!columns.some((column) => column.name === "principal_id"))
|
|
129
|
-
db.exec("ALTER TABLE executors ADD COLUMN principal_id TEXT");
|
|
130
|
-
}
|
|
131
|
-
/** Non-secret Tenant settings persisted for seed / configFor (A18). */
|
|
132
|
-
export function readTenantSetting(db, key) {
|
|
133
|
-
const row = db
|
|
134
|
-
.prepare("SELECT value FROM tenant_settings WHERE key=?")
|
|
135
|
-
.get(key);
|
|
136
|
-
return row ? String(row.value) : undefined;
|
|
137
|
-
}
|
|
138
|
-
export function writeTenantSetting(db, key, value) {
|
|
139
|
-
db.prepare(`INSERT INTO tenant_settings(key,value) VALUES(?,?)
|
|
140
|
-
ON CONFLICT(key) DO UPDATE SET value=excluded.value`).run(key, value);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Read non-secret Tenant configuration from the open database.
|
|
144
|
-
* Used by Host `configFor` at Integration I1.
|
|
145
|
-
*/
|
|
146
|
-
export function readTenantConfig(db) {
|
|
147
|
-
const raw = readTenantSetting(db, "sandbox.backend");
|
|
148
|
-
if (raw === "auto" || raw === "microsandbox" || raw === "virtual")
|
|
149
|
-
return { sandboxBackend: raw };
|
|
150
|
-
return {};
|
|
151
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { DatabaseSync } from "node:sqlite";
|
|
2
|
-
import type { BootstrapPrincipal, Logger, OpenTenantRuntime, TenantConfig, TenantStore } from "./types.js";
|
|
3
|
-
import type { MigrationHooks } from "./migration.js";
|
|
4
|
-
export interface FsTenantStoreOptions {
|
|
5
|
-
hostRoot: string;
|
|
6
|
-
openRuntime: OpenTenantRuntime;
|
|
7
|
-
configFor: (tenantId: string) => TenantConfig;
|
|
8
|
-
logger?: Logger;
|
|
9
|
-
migration?: MigrationHooks;
|
|
10
|
-
/** Writes the application principal into a fresh Tenant database. */
|
|
11
|
-
writeBootstrap?: (db: DatabaseSync, bootstrap: BootstrapPrincipal, now: Date) => void;
|
|
12
|
-
}
|
|
13
|
-
export declare function createFsTenantStore(options: FsTenantStoreOptions): TenantStore;
|
|
14
|
-
/** Claim the Tenant lock file (used by fake openRuntime in tests). */
|
|
15
|
-
export declare function claimTenantLock(lockPath: string, pid?: number): void;
|
|
16
|
-
export declare function releaseTenantLock(lockPath: string): void;
|
package/dist/tenant/store-fs.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { DatabaseSync } from "node:sqlite";
|
|
4
|
-
import { isTenantId } from "@nylorun/core/compatibility";
|
|
5
|
-
import { hostPaths, tenantPaths } from "./paths.js";
|
|
6
|
-
import { readEnvelopeFile, writeEnvelopeFile } from "./envelope.js";
|
|
7
|
-
import { migrateTenantWithSnapshot } from "./migration.js";
|
|
8
|
-
import { quarantine } from "./quarantine.js";
|
|
9
|
-
import { bootstrapPrincipal } from "./principals.js";
|
|
10
|
-
import { TENANT_SCHEMA_VERSION, migrateTenantDatabase as defaultMigrate, schemaVersionOf as defaultSchemaVersionOf, } from "./schema.js";
|
|
11
|
-
function defaultWriteBootstrap(db, bootstrap, _now) {
|
|
12
|
-
defaultMigrate(db);
|
|
13
|
-
bootstrapPrincipal(db, bootstrap);
|
|
14
|
-
}
|
|
15
|
-
function assertLiveLock(lockPath, tenantId) {
|
|
16
|
-
if (!existsSync(lockPath))
|
|
17
|
-
return;
|
|
18
|
-
let raw;
|
|
19
|
-
try {
|
|
20
|
-
raw = readFileSync(lockPath, "utf8").trim();
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
throw quarantine("corrupt", "could not read .runtime-lock", { tenantId });
|
|
24
|
-
}
|
|
25
|
-
const lockPid = Number(raw);
|
|
26
|
-
if (!Number.isSafeInteger(lockPid) || lockPid < 1) {
|
|
27
|
-
throw quarantine("locked", "invalid .runtime-lock contents", {
|
|
28
|
-
tenantId,
|
|
29
|
-
lockPath,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
process.kill(lockPid, 0);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
const err = error;
|
|
37
|
-
if (err.code === "ESRCH") {
|
|
38
|
-
rmSync(lockPath, { force: true });
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
// EPERM: process exists but we cannot signal it — treat as live owner.
|
|
42
|
-
if (err.code !== "EPERM")
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
throw quarantine("locked", "another process holds this Tenant lock", {
|
|
46
|
-
tenantId,
|
|
47
|
-
lockPath,
|
|
48
|
-
lockPid,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
function trashStamp(now) {
|
|
52
|
-
return now.toISOString().replaceAll(":", "-");
|
|
53
|
-
}
|
|
54
|
-
export function createFsTenantStore(options) {
|
|
55
|
-
const host = hostPaths(options.hostRoot);
|
|
56
|
-
mkdirSync(host.tenants, { recursive: true });
|
|
57
|
-
mkdirSync(host.trash, { recursive: true });
|
|
58
|
-
const writeBootstrap = options.writeBootstrap ?? defaultWriteBootstrap;
|
|
59
|
-
const migrationHooks = {
|
|
60
|
-
schemaVersionOf: options.migration?.schemaVersionOf ?? defaultSchemaVersionOf,
|
|
61
|
-
migrateTenantDatabase: options.migration?.migrateTenantDatabase ?? defaultMigrate,
|
|
62
|
-
targetVersion: options.migration?.targetVersion ?? TENANT_SCHEMA_VERSION,
|
|
63
|
-
};
|
|
64
|
-
return {
|
|
65
|
-
async enumerate() {
|
|
66
|
-
if (!existsSync(host.tenants))
|
|
67
|
-
return [];
|
|
68
|
-
return readdirSync(host.tenants, { withFileTypes: true })
|
|
69
|
-
.filter((d) => d.isDirectory() && isTenantId(d.name))
|
|
70
|
-
.map((d) => d.name);
|
|
71
|
-
},
|
|
72
|
-
async readEnvelope(id) {
|
|
73
|
-
const paths = tenantPaths(options.hostRoot, id);
|
|
74
|
-
return readEnvelopeFile(paths.envelope, id);
|
|
75
|
-
},
|
|
76
|
-
async create(envelope, bootstrap) {
|
|
77
|
-
const paths = tenantPaths(options.hostRoot, envelope.id);
|
|
78
|
-
if (existsSync(paths.root) || existsSync(paths.envelope)) {
|
|
79
|
-
return "exists";
|
|
80
|
-
}
|
|
81
|
-
const now = new Date();
|
|
82
|
-
try {
|
|
83
|
-
mkdirSync(paths.root, { recursive: true, mode: 0o700 });
|
|
84
|
-
for (const dir of [
|
|
85
|
-
paths.home,
|
|
86
|
-
paths.tmp,
|
|
87
|
-
paths.sandboxes,
|
|
88
|
-
paths.pluginData,
|
|
89
|
-
paths.logs,
|
|
90
|
-
paths.migration,
|
|
91
|
-
]) {
|
|
92
|
-
mkdirSync(dir, { recursive: true });
|
|
93
|
-
}
|
|
94
|
-
writeEnvelopeFile(paths.envelope, envelope);
|
|
95
|
-
const db = new DatabaseSync(paths.database);
|
|
96
|
-
try {
|
|
97
|
-
writeBootstrap(db, bootstrap, now);
|
|
98
|
-
}
|
|
99
|
-
finally {
|
|
100
|
-
db.close();
|
|
101
|
-
}
|
|
102
|
-
// Exclusive create marker for the lock file is owned by openRuntime (WS-A);
|
|
103
|
-
// we only ensure the parent exists.
|
|
104
|
-
return "created";
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
rmSync(paths.root, { recursive: true, force: true });
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
async bootstrapMatches(id, bootstrap) {
|
|
112
|
-
const paths = tenantPaths(options.hostRoot, id);
|
|
113
|
-
if (!existsSync(paths.database))
|
|
114
|
-
return false;
|
|
115
|
-
const db = new DatabaseSync(paths.database, { readOnly: true });
|
|
116
|
-
try {
|
|
117
|
-
const row = db
|
|
118
|
-
.prepare(`SELECT id, token_hash, idempotency_key FROM principals
|
|
119
|
-
WHERE role = 'application' LIMIT 1`)
|
|
120
|
-
.get();
|
|
121
|
-
if (!row)
|
|
122
|
-
return false;
|
|
123
|
-
return (row.id === bootstrap.principalId &&
|
|
124
|
-
row.token_hash === bootstrap.credentialHash &&
|
|
125
|
-
row.idempotency_key === bootstrap.idempotencyKey);
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
db.close();
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
async open(id) {
|
|
135
|
-
const paths = tenantPaths(options.hostRoot, id);
|
|
136
|
-
if (!existsSync(paths.root)) {
|
|
137
|
-
throw quarantine("open-failed", `Tenant directory missing`, {
|
|
138
|
-
tenantId: id,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
assertLiveLock(paths.lock, id);
|
|
142
|
-
let envelope;
|
|
143
|
-
try {
|
|
144
|
-
envelope = readEnvelopeFile(paths.envelope, id);
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
if (!existsSync(paths.database)) {
|
|
150
|
-
throw quarantine("corrupt", "tenant.sqlite is missing", {
|
|
151
|
-
tenantId: id,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
// Probe schema before openRuntime so schema-too-new never mutates.
|
|
155
|
-
const probe = new DatabaseSync(paths.database);
|
|
156
|
-
let version;
|
|
157
|
-
try {
|
|
158
|
-
version = migrationHooks.schemaVersionOf(probe);
|
|
159
|
-
}
|
|
160
|
-
finally {
|
|
161
|
-
probe.close();
|
|
162
|
-
}
|
|
163
|
-
const target = migrationHooks.targetVersion ?? TENANT_SCHEMA_VERSION;
|
|
164
|
-
if (version > target) {
|
|
165
|
-
throw quarantine("schema-too-new", `Tenant schema version ${version} is newer than Host ${target}`, { tenantId: id });
|
|
166
|
-
}
|
|
167
|
-
if (version < target) {
|
|
168
|
-
envelope = migrateTenantWithSnapshot(paths, envelope, migrationHooks);
|
|
169
|
-
}
|
|
170
|
-
const config = options.configFor(id);
|
|
171
|
-
return options.openRuntime({
|
|
172
|
-
...config,
|
|
173
|
-
tenantId: id,
|
|
174
|
-
paths: config.paths.root ? config.paths : paths,
|
|
175
|
-
});
|
|
176
|
-
},
|
|
177
|
-
async trash(id, now) {
|
|
178
|
-
const paths = tenantPaths(options.hostRoot, id);
|
|
179
|
-
if (!existsSync(paths.root))
|
|
180
|
-
return;
|
|
181
|
-
mkdirSync(host.trash, { recursive: true });
|
|
182
|
-
const dest = join(host.trash, `${id}-${trashStamp(now)}`);
|
|
183
|
-
renameSync(paths.root, dest);
|
|
184
|
-
},
|
|
185
|
-
async removePartial(id) {
|
|
186
|
-
const paths = tenantPaths(options.hostRoot, id);
|
|
187
|
-
if (existsSync(paths.root)) {
|
|
188
|
-
rmSync(paths.root, { recursive: true, force: true });
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
/** Claim the Tenant lock file (used by fake openRuntime in tests). */
|
|
194
|
-
export function claimTenantLock(lockPath, pid = process.pid) {
|
|
195
|
-
const fd = openSync(lockPath, "wx");
|
|
196
|
-
try {
|
|
197
|
-
writeFileSync(fd, String(pid));
|
|
198
|
-
}
|
|
199
|
-
finally {
|
|
200
|
-
closeSync(fd);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
export function releaseTenantLock(lockPath) {
|
|
204
|
-
rmSync(lockPath, { force: true });
|
|
205
|
-
}
|