@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
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Tx } from "../store/types.js";
|
|
2
2
|
import type { BootstrapPrincipal } from "./types.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
tokenHash: string;
|
|
7
|
-
idempotencyKey: string | null;
|
|
8
|
-
createdAt: string;
|
|
9
|
-
}
|
|
3
|
+
export type { PrincipalRow } from "../store/types.js";
|
|
4
|
+
/** Principal id of the Studio key derived from the admin key. */
|
|
5
|
+
export declare const STUDIO_PRINCIPAL_ID = "studio";
|
|
10
6
|
/**
|
|
11
|
-
* Writes the application principal and idempotency key in
|
|
7
|
+
* Writes the application principal and idempotency key in the caller's transaction, plus
|
|
8
|
+
* principal `studio` when the bootstrap carries a Studio credential hash.
|
|
12
9
|
* Exported for WS-B's `TenantStore.create` (A3).
|
|
13
10
|
*/
|
|
14
|
-
export declare function bootstrapPrincipal(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
export declare function bootstrapPrincipal(t: Tx, bootstrap: BootstrapPrincipal, now?: Date): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the stored bootstrap, Studio and derived principals equal `bootstrap`. A retried
|
|
14
|
+
* create names the same derived principals, so each must be stored with its hash.
|
|
15
|
+
*/
|
|
16
|
+
export declare function bootstrapPrincipalMatches(t: Tx, bootstrap: BootstrapPrincipal): Promise<boolean>;
|
|
@@ -1,44 +1,56 @@
|
|
|
1
|
+
/** Principal id of the Studio key derived from the admin key. */
|
|
2
|
+
export const STUDIO_PRINCIPAL_ID = "studio";
|
|
1
3
|
/**
|
|
2
|
-
* Writes the application principal and idempotency key in
|
|
4
|
+
* Writes the application principal and idempotency key in the caller's transaction, plus
|
|
5
|
+
* principal `studio` when the bootstrap carries a Studio credential hash.
|
|
3
6
|
* Exported for WS-B's `TenantStore.create` (A3).
|
|
4
7
|
*/
|
|
5
|
-
export function bootstrapPrincipal(
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
db.prepare(`INSERT INTO principals(id, role, token_hash, idempotency_key, created_at)
|
|
10
|
-
VALUES(?, 'application', ?, ?, ?)`).run(bootstrap.principalId, bootstrap.credentialHash, bootstrap.idempotencyKey, now);
|
|
11
|
-
db.exec("COMMIT");
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
db.exec("ROLLBACK");
|
|
15
|
-
throw error;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export function findPrincipalByTokenHash(db, tokenHash) {
|
|
19
|
-
const row = db
|
|
20
|
-
.prepare(`SELECT id, role, token_hash, idempotency_key, created_at
|
|
21
|
-
FROM principals WHERE token_hash = ?`)
|
|
22
|
-
.get(tokenHash);
|
|
23
|
-
if (!row)
|
|
24
|
-
return undefined;
|
|
25
|
-
return {
|
|
26
|
-
id: String(row.id),
|
|
8
|
+
export async function bootstrapPrincipal(t, bootstrap, now = new Date()) {
|
|
9
|
+
const createdAt = now.toISOString();
|
|
10
|
+
await t.insertPrincipal({
|
|
11
|
+
id: bootstrap.principalId,
|
|
27
12
|
role: "application",
|
|
28
|
-
tokenHash:
|
|
29
|
-
idempotencyKey:
|
|
30
|
-
createdAt
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
tokenHash: bootstrap.credentialHash,
|
|
14
|
+
idempotencyKey: bootstrap.idempotencyKey,
|
|
15
|
+
createdAt,
|
|
16
|
+
});
|
|
17
|
+
if (bootstrap.studioCredentialHash)
|
|
18
|
+
await t.insertPrincipal({
|
|
19
|
+
id: STUDIO_PRINCIPAL_ID,
|
|
20
|
+
role: "application",
|
|
21
|
+
tokenHash: bootstrap.studioCredentialHash,
|
|
22
|
+
idempotencyKey: null,
|
|
23
|
+
createdAt,
|
|
24
|
+
});
|
|
25
|
+
for (const principal of bootstrap.derivedPrincipals ?? [])
|
|
26
|
+
await t.insertPrincipal({
|
|
27
|
+
id: principal.id,
|
|
28
|
+
role: "application",
|
|
29
|
+
tokenHash: principal.credentialHash,
|
|
30
|
+
idempotencyKey: null,
|
|
31
|
+
createdAt,
|
|
32
|
+
});
|
|
38
33
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Whether the stored bootstrap, Studio and derived principals equal `bootstrap`. A retried
|
|
36
|
+
* create names the same derived principals, so each must be stored with its hash.
|
|
37
|
+
*/
|
|
38
|
+
export async function bootstrapPrincipalMatches(t, bootstrap) {
|
|
39
|
+
const row = await t.principalById(bootstrap.principalId);
|
|
40
|
+
if (!row ||
|
|
41
|
+
row.role !== "application" ||
|
|
42
|
+
row.tokenHash !== bootstrap.credentialHash ||
|
|
43
|
+
row.idempotencyKey !== bootstrap.idempotencyKey)
|
|
44
|
+
return false;
|
|
45
|
+
const studio = await t.principalById(STUDIO_PRINCIPAL_ID);
|
|
46
|
+
if ((studio?.role === "application" ? studio.tokenHash : undefined) !==
|
|
47
|
+
bootstrap.studioCredentialHash)
|
|
48
|
+
return false;
|
|
49
|
+
for (const principal of bootstrap.derivedPrincipals ?? []) {
|
|
50
|
+
const stored = await t.principalById(principal.id);
|
|
51
|
+
if (stored?.role !== "application" ||
|
|
52
|
+
stored.tokenHash !== principal.credentialHash)
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
44
56
|
}
|
|
@@ -2,5 +2,5 @@ import type { Quarantine } from "./types.js";
|
|
|
2
2
|
/** Typed quarantine raised while opening a Tenant Runtime (A7). */
|
|
3
3
|
export declare class QuarantineError extends Error {
|
|
4
4
|
readonly quarantine: Quarantine;
|
|
5
|
-
constructor(code: Quarantine["code"], message: string, repair?: string
|
|
5
|
+
constructor(code: Quarantine["code"], message: string, repair?: string);
|
|
6
6
|
}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
/** Typed quarantine raised while opening a Tenant Runtime (A7). */
|
|
2
2
|
export class QuarantineError extends Error {
|
|
3
3
|
quarantine;
|
|
4
|
-
constructor(code, message, repair = "
|
|
4
|
+
constructor(code, message, repair = "nylo tenant status") {
|
|
5
5
|
super(message);
|
|
6
6
|
this.name = "QuarantineError";
|
|
7
|
-
this.quarantine = {
|
|
8
|
-
code,
|
|
9
|
-
message,
|
|
10
|
-
repair,
|
|
11
|
-
...(extra?.lockPath === undefined ? {} : { lockPath: extra.lockPath }),
|
|
12
|
-
...(extra?.lockPid === undefined ? {} : { lockPid: extra.lockPid }),
|
|
13
|
-
};
|
|
7
|
+
this.quarantine = { code, message, repair };
|
|
14
8
|
}
|
|
15
9
|
}
|
|
@@ -3,8 +3,6 @@ import type { Quarantine } from "./types.js";
|
|
|
3
3
|
export declare class QuarantineError extends Error implements Quarantine {
|
|
4
4
|
readonly code: Quarantine["code"];
|
|
5
5
|
readonly repair: string;
|
|
6
|
-
readonly lockPath?: string;
|
|
7
|
-
readonly lockPid?: number;
|
|
8
6
|
constructor(fields: Quarantine);
|
|
9
7
|
toQuarantine(): Quarantine;
|
|
10
8
|
}
|
|
@@ -13,15 +11,9 @@ export declare function asQuarantine(error: unknown): Quarantine | undefined;
|
|
|
13
11
|
/** CLI-facing repair strings for every quarantine code (B9). */
|
|
14
12
|
export declare function repairFor(code: Quarantine["code"], detail?: {
|
|
15
13
|
tenantId?: string;
|
|
16
|
-
lockPath?: string;
|
|
17
|
-
lockPid?: number;
|
|
18
|
-
migrationPath?: string;
|
|
19
14
|
}): string;
|
|
20
15
|
export declare function quarantine(code: Quarantine["code"], message: string, detail?: {
|
|
21
16
|
tenantId?: string;
|
|
22
|
-
lockPath?: string;
|
|
23
|
-
lockPid?: number;
|
|
24
|
-
migrationPath?: string;
|
|
25
17
|
}): QuarantineError;
|
|
26
18
|
/** Conflict when create bootstrap material does not match an existing Tenant (HTTP 409). */
|
|
27
19
|
export declare class TenantConflictError extends Error {
|
|
@@ -2,26 +2,14 @@
|
|
|
2
2
|
export class QuarantineError extends Error {
|
|
3
3
|
code;
|
|
4
4
|
repair;
|
|
5
|
-
lockPath;
|
|
6
|
-
lockPid;
|
|
7
5
|
constructor(fields) {
|
|
8
6
|
super(fields.message);
|
|
9
7
|
this.name = "QuarantineError";
|
|
10
8
|
this.code = fields.code;
|
|
11
9
|
this.repair = fields.repair;
|
|
12
|
-
if (fields.lockPath !== undefined)
|
|
13
|
-
this.lockPath = fields.lockPath;
|
|
14
|
-
if (fields.lockPid !== undefined)
|
|
15
|
-
this.lockPid = fields.lockPid;
|
|
16
10
|
}
|
|
17
11
|
toQuarantine() {
|
|
18
|
-
return {
|
|
19
|
-
code: this.code,
|
|
20
|
-
message: this.message,
|
|
21
|
-
repair: this.repair,
|
|
22
|
-
...(this.lockPath !== undefined ? { lockPath: this.lockPath } : {}),
|
|
23
|
-
...(this.lockPid !== undefined ? { lockPid: this.lockPid } : {}),
|
|
24
|
-
};
|
|
12
|
+
return { code: this.code, message: this.message, repair: this.repair };
|
|
25
13
|
}
|
|
26
14
|
}
|
|
27
15
|
export function isQuarantineError(error) {
|
|
@@ -43,28 +31,20 @@ export function asQuarantine(error) {
|
|
|
43
31
|
export function repairFor(code, detail = {}) {
|
|
44
32
|
const id = detail.tenantId ? ` ${detail.tenantId}` : "";
|
|
45
33
|
switch (code) {
|
|
46
|
-
case "locked":
|
|
47
|
-
return (`nylorun tenant status${id}` +
|
|
48
|
-
(detail.lockPath !== undefined && detail.lockPid !== undefined
|
|
49
|
-
? ` — stop pid ${detail.lockPid} holding ${detail.lockPath}, or remove a stale lock`
|
|
50
|
-
: " — resolve the .runtime-lock conflict"));
|
|
51
34
|
case "kek-missing":
|
|
52
|
-
return `
|
|
35
|
+
return `nylo tenant status${id} — restore vault-kek for this Tenant (ciphertext cannot be opened without it)`;
|
|
53
36
|
case "corrupt":
|
|
54
|
-
return `
|
|
37
|
+
return `nylo tenant status${id} — restore the Tenant from backup`;
|
|
55
38
|
case "schema-too-new":
|
|
56
|
-
return `
|
|
57
|
-
`${id ? ` — Tenant${id} needs a newer Host` : " — Host schema is older than this Tenant"}`;
|
|
39
|
+
return `nylo tenant status${id} — the Tenant was migrated by a newer Runtime; run that version or newer`;
|
|
58
40
|
case "migration-failed":
|
|
59
|
-
return
|
|
60
|
-
? `restore ${detail.migrationPath} over the Tenant files, then nylorun tenant status${id}`
|
|
61
|
-
: `nylorun tenant status${id} — restore from .migration/ snapshot, then retry`;
|
|
41
|
+
return `nylo tenant status${id} — inspect the Runtime log for the failed migration, fix it, then restart the Runtime`;
|
|
62
42
|
case "envelope-invalid":
|
|
63
|
-
return `
|
|
43
|
+
return `nylo tenant status${id} — the Tenant envelope is missing or invalid; restore the Tenant from backup`;
|
|
64
44
|
case "open-timeout":
|
|
65
|
-
return `
|
|
45
|
+
return `nylo tenant status${id} — open timed out; inspect the Runtime log and Postgres, then retry`;
|
|
66
46
|
case "open-failed":
|
|
67
|
-
return `
|
|
47
|
+
return `nylo tenant status${id} — inspect Tenant logs and repair before the Host retries open`;
|
|
68
48
|
}
|
|
69
49
|
}
|
|
70
50
|
export function quarantine(code, message, detail = {}) {
|
|
@@ -72,8 +52,6 @@ export function quarantine(code, message, detail = {}) {
|
|
|
72
52
|
code,
|
|
73
53
|
message,
|
|
74
54
|
repair: repairFor(code, detail),
|
|
75
|
-
...(detail.lockPath !== undefined ? { lockPath: detail.lockPath } : {}),
|
|
76
|
-
...(detail.lockPid !== undefined ? { lockPid: detail.lockPid } : {}),
|
|
77
55
|
});
|
|
78
56
|
}
|
|
79
57
|
/** Conflict when create bootstrap material does not match an existing Tenant (HTTP 409). */
|
package/dist/tenant/reset.d.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ResetScope, SessionStore } from "../store/types.js";
|
|
2
2
|
import type { ExecutorRegistry } from "../core/executors.js";
|
|
3
3
|
import type { SandboxManager } from "../sandbox/manager.js";
|
|
4
4
|
import type { TenantPaths } from "./types.js";
|
|
5
|
-
export type ResetScope
|
|
5
|
+
export type { ResetScope } from "../store/types.js";
|
|
6
6
|
export interface ResetTenantContext {
|
|
7
|
-
store:
|
|
7
|
+
store: SessionStore;
|
|
8
8
|
registry: ExecutorRegistry;
|
|
9
9
|
sandbox: SandboxManager;
|
|
10
10
|
paths: TenantPaths;
|
|
11
|
-
/** Clear in-memory session observers after
|
|
11
|
+
/** Clear in-memory session observers after the store wipe. */
|
|
12
12
|
clearSessionState: () => void;
|
|
13
13
|
/** Clear executor SSE streams when registrations are removed. */
|
|
14
14
|
clearExecutorStreams: () => void;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Reset Tenant durable state for `scope` (A17).
|
|
18
|
-
* Caller must have already drained (`activeWork`).
|
|
19
|
-
* transaction, then filesystem rename+delete for sandboxes /
|
|
18
|
+
* Caller must have already drained (`activeWork`). Deletes store state in one
|
|
19
|
+
* transaction (`Tx.reset`), then does the filesystem rename+delete for sandboxes /
|
|
20
|
+
* log as needed. The host vault (model credentials), principals and settings stay.
|
|
20
21
|
*/
|
|
21
22
|
export declare function resetTenant(ctx: ResetTenantContext, scope: ResetScope): Promise<void>;
|
package/dist/tenant/reset.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
const SESSION_TABLES = [
|
|
4
|
-
"sessions",
|
|
5
|
-
"commands",
|
|
6
|
-
"checkpoints",
|
|
7
|
-
"effects",
|
|
8
|
-
"actions",
|
|
9
|
-
];
|
|
10
3
|
/**
|
|
11
4
|
* Rename `dir` to a sibling, delete the sibling, recreate an empty `dir`.
|
|
12
5
|
* Failure before rename leaves the original intact; after rename the empty
|
|
@@ -35,41 +28,11 @@ function replaceDirectory(dir) {
|
|
|
35
28
|
throw error;
|
|
36
29
|
}
|
|
37
30
|
}
|
|
38
|
-
function clearSessionTables(store) {
|
|
39
|
-
for (const table of SESSION_TABLES) {
|
|
40
|
-
store.db.prepare(`DELETE FROM ${table}`).run();
|
|
41
|
-
}
|
|
42
|
-
store.db.prepare("DELETE FROM events").run();
|
|
43
|
-
}
|
|
44
|
-
function clearSandboxRows(store) {
|
|
45
|
-
store.db.prepare("DELETE FROM sandboxes").run();
|
|
46
|
-
}
|
|
47
|
-
function clearDefinitions(store) {
|
|
48
|
-
store.db.prepare("DELETE FROM definitions").run();
|
|
49
|
-
}
|
|
50
|
-
function clearExecutors(store, registry) {
|
|
51
|
-
for (const row of registry.list())
|
|
52
|
-
registry.remove(row.agentId);
|
|
53
|
-
store.db.prepare("DELETE FROM executors").run();
|
|
54
|
-
}
|
|
55
|
-
/** Delete user-scoped vaults; keep the host vault (model credentials). */
|
|
56
|
-
function clearUserVaults(store) {
|
|
57
|
-
const userVaults = store.db
|
|
58
|
-
.prepare(`SELECT id FROM vaults WHERE scope != 'host'`)
|
|
59
|
-
.all();
|
|
60
|
-
for (const vault of userVaults) {
|
|
61
|
-
store.db
|
|
62
|
-
.prepare(`DELETE FROM vault_credentials WHERE vault_id=?`)
|
|
63
|
-
.run(vault.id);
|
|
64
|
-
store.db.prepare(`DELETE FROM vaults WHERE id=?`).run(vault.id);
|
|
65
|
-
}
|
|
66
|
-
// Drop audit / idempotency rows that referenced user vaults only when all.
|
|
67
|
-
// Keep host-model idempotency so seed replay stays stable.
|
|
68
|
-
}
|
|
69
31
|
/**
|
|
70
32
|
* Reset Tenant durable state for `scope` (A17).
|
|
71
|
-
* Caller must have already drained (`activeWork`).
|
|
72
|
-
* transaction, then filesystem rename+delete for sandboxes /
|
|
33
|
+
* Caller must have already drained (`activeWork`). Deletes store state in one
|
|
34
|
+
* transaction (`Tx.reset`), then does the filesystem rename+delete for sandboxes /
|
|
35
|
+
* log as needed. The host vault (model credentials), principals and settings stay.
|
|
73
36
|
*/
|
|
74
37
|
export async function resetTenant(ctx, scope) {
|
|
75
38
|
const clearSessions = scope === "sessions" || scope === "all";
|
|
@@ -78,17 +41,10 @@ export async function resetTenant(ctx, scope) {
|
|
|
78
41
|
if (clearSandboxes) {
|
|
79
42
|
await ctx.sandbox.reconcile(() => false);
|
|
80
43
|
}
|
|
81
|
-
ctx.store.tx(() =>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
clearSandboxRows(ctx.store);
|
|
86
|
-
if (clearAll) {
|
|
87
|
-
clearDefinitions(ctx.store);
|
|
88
|
-
clearExecutors(ctx.store, ctx.registry);
|
|
89
|
-
clearUserVaults(ctx.store);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
44
|
+
await ctx.store.tx((t) => t.reset(scope));
|
|
45
|
+
if (clearAll)
|
|
46
|
+
for (const row of ctx.registry.list())
|
|
47
|
+
ctx.registry.remove(row.agentId);
|
|
92
48
|
if (clearSessions)
|
|
93
49
|
ctx.clearSessionState();
|
|
94
50
|
if (clearAll)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant administration and vault routes: `/v1/tenant` (status, reset, config seed, host
|
|
3
|
+
* model and providers, sandbox report) and `/v1/vaults` (vaults and credentials).
|
|
4
|
+
* A reset that deletes sessions abandons their streams (a session created again gets a new
|
|
5
|
+
* incarnation) and collects them afterwards; it never deletes the Tenant's basin.
|
|
6
|
+
*
|
|
7
|
+
* Later waves: Wave 2 / X changes what reset clears once work leaves process memory.
|
|
8
|
+
*/
|
|
9
|
+
import type { IncomingMessage } from "node:http";
|
|
10
|
+
import type { AuthScope, TenantContext } from "./context.js";
|
|
11
|
+
export declare function dispatchTenant(ctx: TenantContext, scope: AuthScope, method: string | undefined, path: string[], request: IncomingMessage): Promise<unknown>;
|
|
12
|
+
export declare function dispatchVault(ctx: TenantContext, scope: AuthScope, method: string | undefined, path: string[], url: URL, request: IncomingMessage): Promise<unknown>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { CreateCredentialRequestSchema, CreateVaultRequestSchema, PutHostModelRequestSchema, ResetTenantRequestSchema, RotateCredentialRequestSchema, SeedTenantConfigRequestSchema, SelectHostModelRequestSchema, } from "@nylorun/core/contracts";
|
|
2
|
+
import { hostModelCatalog } from "../model/catalog.js";
|
|
3
|
+
import { resetTenant } from "./reset.js";
|
|
4
|
+
import { buildTenantStatus, seedTenantConfig } from "./status.js";
|
|
5
|
+
import { ownerOf } from "./auth.js";
|
|
6
|
+
import { fail, readBody } from "./http.js";
|
|
7
|
+
import { clearExecutorStreams, clearObservers } from "./live.js";
|
|
8
|
+
import { requestStreamCollection, sessionStreamsAbandoned, streamsStatus, } from "./streams.js";
|
|
9
|
+
import { clearWork, drain } from "./scheduler.js";
|
|
10
|
+
import { usesFixtureModel } from "./model-setting.js";
|
|
11
|
+
export async function dispatchTenant(ctx, scope, method, path, request) {
|
|
12
|
+
const { vault } = ctx;
|
|
13
|
+
// A subject reaches only the routes `authorize` allowed its scopes.
|
|
14
|
+
if (scope.kind === "executor") {
|
|
15
|
+
await vault.reject(path.join("/"));
|
|
16
|
+
fail(403, "Application credential required");
|
|
17
|
+
}
|
|
18
|
+
if (path.length === 2 && method === "GET")
|
|
19
|
+
return await buildTenantStatus({
|
|
20
|
+
envelope: ctx.envelope,
|
|
21
|
+
config: ctx.config,
|
|
22
|
+
store: ctx.store,
|
|
23
|
+
registry: ctx.registry,
|
|
24
|
+
vault,
|
|
25
|
+
sandbox: ctx.sandbox,
|
|
26
|
+
closing: ctx.closing || ctx.closed,
|
|
27
|
+
modelConfigured: !ctx.useVaultModel ||
|
|
28
|
+
(await ctx.store.tx((t) => usesFixtureModel(t))) ||
|
|
29
|
+
(await vault.getHostModel()).configured,
|
|
30
|
+
executorStreams: ctx.live.executorStreams,
|
|
31
|
+
...(ctx.stuckInvocations
|
|
32
|
+
? { stuckInvocations: ctx.stuckInvocations }
|
|
33
|
+
: {}),
|
|
34
|
+
streamsStatus: () => streamsStatus(ctx),
|
|
35
|
+
});
|
|
36
|
+
if (path[2] === "reset" && path.length === 3 && method === "POST") {
|
|
37
|
+
const body = ResetTenantRequestSchema.parse(await readBody(request));
|
|
38
|
+
await drain(ctx, body.activeWork, 30_000);
|
|
39
|
+
// The deleted sessions' streams are abandoned: a session created again with the same id,
|
|
40
|
+
// during or after the reset, gets a new incarnation and starts at sequence 0. The pending
|
|
41
|
+
// collection is recorded first, so a crash before it runs leaves it to the sweep.
|
|
42
|
+
const sessionsReset = body.scope !== "sandboxes";
|
|
43
|
+
if (sessionsReset)
|
|
44
|
+
await requestStreamCollection(ctx);
|
|
45
|
+
await resetTenant({
|
|
46
|
+
store: ctx.store,
|
|
47
|
+
registry: ctx.registry,
|
|
48
|
+
sandbox: ctx.sandbox,
|
|
49
|
+
paths: ctx.config.paths,
|
|
50
|
+
clearSessionState: () => {
|
|
51
|
+
clearWork(ctx);
|
|
52
|
+
clearObservers(ctx.live);
|
|
53
|
+
},
|
|
54
|
+
clearExecutorStreams: () => clearExecutorStreams(ctx.live),
|
|
55
|
+
}, body.scope);
|
|
56
|
+
if (sessionsReset)
|
|
57
|
+
sessionStreamsAbandoned(ctx);
|
|
58
|
+
// Reset leaves the Tenant open for new work.
|
|
59
|
+
ctx.closing = false;
|
|
60
|
+
return { ok: true };
|
|
61
|
+
}
|
|
62
|
+
if (path[2] === "config" &&
|
|
63
|
+
path[3] === "seed" &&
|
|
64
|
+
path.length === 4 &&
|
|
65
|
+
method === "PUT") {
|
|
66
|
+
const body = SeedTenantConfigRequestSchema.parse(await readBody(request));
|
|
67
|
+
return await seedTenantConfig({ store: ctx.store, vault }, body);
|
|
68
|
+
}
|
|
69
|
+
if (path[2] === "models" && path.length === 3 && method === "GET")
|
|
70
|
+
return hostModelCatalog();
|
|
71
|
+
if (path[2] === "sandbox" && path.length === 3 && method === "GET")
|
|
72
|
+
return ctx.sandbox.report();
|
|
73
|
+
if (path[2] === "providers" && path.length === 3 && method === "GET")
|
|
74
|
+
return vault.listHostProviders();
|
|
75
|
+
if (path[2] === "model" && path.length === 3 && method === "GET")
|
|
76
|
+
return vault.getHostModel();
|
|
77
|
+
if (path[2] === "model" && path.length === 3 && method === "PUT")
|
|
78
|
+
return vault.putHostModel(PutHostModelRequestSchema.parse(await readBody(request)));
|
|
79
|
+
if (path[2] === "model" &&
|
|
80
|
+
path[3] === "selection" &&
|
|
81
|
+
path.length === 4 &&
|
|
82
|
+
method === "PUT")
|
|
83
|
+
return vault.selectHostModel(SelectHostModelRequestSchema.parse(await readBody(request)));
|
|
84
|
+
fail(404, "Route not found");
|
|
85
|
+
}
|
|
86
|
+
export async function dispatchVault(ctx, scope, method, path, url, request) {
|
|
87
|
+
const { vault } = ctx;
|
|
88
|
+
if (scope.kind === "executor") {
|
|
89
|
+
await vault.reject(path.join("/"));
|
|
90
|
+
fail(403, "Application credential required");
|
|
91
|
+
}
|
|
92
|
+
// Acting for a subject: only the subject's own vaults, and never on another's behalf.
|
|
93
|
+
const owner = ownerOf(scope);
|
|
94
|
+
if (path.length === 2 && method === "POST") {
|
|
95
|
+
const body = CreateVaultRequestSchema.parse(await readBody(request));
|
|
96
|
+
if (owner !== undefined && body.ownerUserId !== owner)
|
|
97
|
+
fail(403, "ownerUserId must be the subject");
|
|
98
|
+
return vault.createVault(body);
|
|
99
|
+
}
|
|
100
|
+
if (path.length === 2 && method === "GET") {
|
|
101
|
+
const ownerUserId = url.searchParams.get("ownerUserId") ??
|
|
102
|
+
owner ??
|
|
103
|
+
fail(400, "ownerUserId is required");
|
|
104
|
+
if (owner !== undefined && ownerUserId !== owner)
|
|
105
|
+
fail(403, "ownerUserId must be the subject");
|
|
106
|
+
return { vaults: await vault.listVaults(ownerUserId) };
|
|
107
|
+
}
|
|
108
|
+
const vaultId = path[2];
|
|
109
|
+
if (!vaultId)
|
|
110
|
+
fail(404, "Vault not found");
|
|
111
|
+
if (owner !== undefined)
|
|
112
|
+
await vault.assertOwner(vaultId, owner);
|
|
113
|
+
if (path.length === 3 && method === "GET")
|
|
114
|
+
return vault.getVault(vaultId);
|
|
115
|
+
if (path.length === 3 && method === "DELETE")
|
|
116
|
+
return vault.deleteVault(vaultId);
|
|
117
|
+
if (path[3] !== "credentials")
|
|
118
|
+
fail(404, "Route not found");
|
|
119
|
+
if (path.length === 4 && method === "POST") {
|
|
120
|
+
const body = CreateCredentialRequestSchema.parse(await readBody(request));
|
|
121
|
+
return vault.createCredential(vaultId, body);
|
|
122
|
+
}
|
|
123
|
+
if (path.length === 4 && method === "GET")
|
|
124
|
+
return { credentials: await vault.listCredentials(vaultId) };
|
|
125
|
+
const credentialId = path[4];
|
|
126
|
+
if (!credentialId)
|
|
127
|
+
fail(404, "Credential not found");
|
|
128
|
+
if (path.length === 5 && method === "GET")
|
|
129
|
+
return vault.getCredential(vaultId, credentialId);
|
|
130
|
+
if (path.length === 5 && method === "POST") {
|
|
131
|
+
const body = RotateCredentialRequestSchema.parse(await readBody(request));
|
|
132
|
+
return vault.rotateCredential(vaultId, credentialId, body);
|
|
133
|
+
}
|
|
134
|
+
if (path.length === 5 && method === "DELETE")
|
|
135
|
+
return vault.deleteCredential(vaultId, credentialId);
|
|
136
|
+
fail(404, "Route not found");
|
|
137
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Tenant HTTP router: authenticates the bearer token, matches `/v1/...` routes and
|
|
3
|
+
* delegates to the service modules (commands, actions, sessions, live, routes-tenant). It
|
|
4
|
+
* maps typed errors to responses and owns no business logic of its own. History, SSE and
|
|
5
|
+
* executor work streams read Durable Streams (`live.ts`).
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
|
+
import { type TenantContext } from "./context.js";
|
|
9
|
+
export declare function handle(ctx: TenantContext, request: IncomingMessage, response: ServerResponse, _url?: URL): Promise<void>;
|