@nylorun/runtime 0.17.0-beta → 0.19.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 +149 -0
- package/README.md +88 -58
- package/dist/adapters/execution/restate.d.ts +1 -3
- package/dist/adapters/execution/restate.js +4 -31
- package/dist/adapters/replication/pgoutput.js +1 -1
- package/dist/adapters/sandbox/local.js +1 -1
- package/dist/adapters/sandbox/virtual.js +1 -1
- package/dist/api/ag-ui/routes.js +1 -1
- package/dist/api/ag-ui/translate.d.ts +1 -3
- package/dist/api/ag-ui/translate.js +2 -17
- package/dist/api/components.d.ts +4871 -2614
- package/dist/api/components.js +12 -11
- package/dist/api/gate/routes.d.ts +7 -8
- package/dist/api/gate/routes.js +58 -52
- package/dist/api/http/app.d.ts +6 -0
- package/dist/api/http/app.js +18 -14
- package/dist/api/http/define.d.ts +14 -2
- package/dist/api/http/define.js +34 -15
- package/dist/api/http/routes/access.d.ts +3 -3
- package/dist/api/http/routes/access.js +18 -17
- package/dist/api/http/routes/artifacts.js +2 -4
- package/dist/api/http/routes/files.d.ts +8 -0
- package/dist/api/http/routes/files.js +77 -0
- package/dist/api/http/routes/keys.d.ts +10 -0
- package/dist/api/http/routes/keys.js +64 -0
- package/dist/api/http/routes/me.js +5 -2
- package/dist/api/http/routes/reads.d.ts +66 -0
- package/dist/api/http/routes/reads.js +127 -0
- package/dist/api/http/routes/sandboxes.js +7 -4
- package/dist/api/http/routes/sessions.js +25 -8
- package/dist/api/http/routes/tenant.js +15 -18
- package/dist/api/http/routes/vaults.d.ts +10 -9
- package/dist/api/http/routes/vaults.js +27 -23
- package/dist/api/openapi.d.ts +10 -11
- package/dist/api/openapi.js +291 -61
- package/dist/artifacts/links.js +1 -1
- package/dist/core/flow-host.d.ts +44 -23
- package/dist/core/flow-host.js +88 -106
- package/dist/core/provider.d.ts +4 -1
- package/dist/core/provider.js +6 -1
- package/dist/core/sandbox-routes.d.ts +0 -9
- package/dist/core/sandbox-routes.js +2 -21
- package/dist/execution/memory.d.ts +4 -5
- package/dist/execution/memory.js +11 -25
- package/dist/execution/types.d.ts +3 -33
- package/dist/execution/types.js +2 -12
- package/dist/gates/http-tool.d.ts +86 -0
- package/dist/gates/http-tool.js +197 -0
- package/dist/gates/mcp-handler.d.ts +3 -0
- package/dist/gates/mcp-handler.js +1 -3
- package/dist/gates/meter.d.ts +2 -2
- package/dist/gates/meter.js +6 -0
- package/dist/gates/tenant-vaults.d.ts +5 -9
- package/dist/gates/tool-calls.d.ts +7 -15
- package/dist/gates/tool-calls.js +4 -4
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +23 -47
- package/dist/gates/tool-contract.d.ts +27 -39
- package/dist/gates/tool-contract.js +30 -21
- package/dist/gates/tool-gate.d.ts +25 -24
- package/dist/gates/tool-gate.js +11 -7
- package/dist/harness/calls.d.ts +10 -4
- package/dist/harness/calls.js +15 -6
- package/dist/harness/executors.d.ts +1 -1
- package/dist/harness/executors.js +17 -12
- package/dist/harness/main.d.ts +2 -2
- package/dist/harness/main.js +5 -13
- package/dist/harness/service.d.ts +4 -5
- package/dist/harness/service.js +9 -2
- package/dist/harness-api/in-process.js +2 -2
- package/dist/harness-api/record.d.ts +6 -3
- package/dist/harness-api/record.js +65 -72
- package/dist/harness-api/server.d.ts +1 -9
- package/dist/harness-api/server.js +19 -17
- package/dist/harness-api/start.js +1 -5
- package/dist/harness-api/workspace.js +2 -3
- package/dist/harness-api/ws-server.js +1 -1
- package/dist/host/app.d.ts +2 -9
- package/dist/host/app.js +15 -25
- package/dist/host/config-for.d.ts +1 -5
- package/dist/host/config-for.js +0 -3
- package/dist/host/config.d.ts +0 -5
- package/dist/host/create-host.d.ts +3 -33
- package/dist/host/create-host.js +18 -94
- package/dist/host/environment.d.ts +0 -3
- package/dist/host/environment.js +0 -9
- package/dist/host/gates.d.ts +4 -1
- package/dist/host/gates.js +3 -2
- package/dist/host/http.d.ts +1 -2
- package/dist/host/http.js +1 -7
- package/dist/host/index.d.ts +2 -2
- package/dist/host/index.js +2 -2
- package/dist/host/main.js +27 -34
- package/dist/host/operate.d.ts +29 -0
- package/dist/host/operate.js +223 -0
- package/dist/host/stack-config.d.ts +12 -12
- package/dist/host/stack-config.js +5 -32
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/infra/workers.d.ts +1 -1
- package/dist/keys/keys.js +2 -2
- package/dist/management-openapi.json +6491 -0
- package/dist/mcp/connect.d.ts +8 -4
- package/dist/mcp/connect.js +6 -26
- package/dist/mcp/pool.d.ts +5 -9
- package/dist/mcp/pool.js +8 -19
- package/dist/mcp/snapshot.d.ts +8 -2
- package/dist/mcp/snapshot.js +29 -2
- package/dist/media.d.ts +1 -1
- package/dist/media.js +1 -1
- package/dist/model/pi-model.js +3 -0
- package/dist/openapi.json +4700 -12350
- package/dist/reads/access.d.ts +4 -0
- package/dist/reads/access.js +12 -0
- package/dist/reads/cursor.d.ts +11 -0
- package/dist/reads/cursor.js +80 -0
- package/dist/reads/types.d.ts +25 -0
- package/dist/reads/types.js +1 -0
- package/dist/sandbox/manager.d.ts +16 -0
- package/dist/sandbox/manager.js +40 -0
- package/dist/sandbox/session-sandbox.js +13 -3
- package/dist/sandbox/skills.d.ts +13 -0
- package/dist/sandbox/skills.js +46 -0
- package/dist/sandbox/types.d.ts +2 -2
- package/dist/store/ownership.d.ts +9 -0
- package/dist/store/ownership.js +13 -0
- package/dist/store/postgres/connect.d.ts +2 -0
- package/dist/store/postgres/connect.js +29 -0
- package/dist/store/postgres/drizzle/0011_key_roles.sql +3 -0
- package/dist/store/postgres/drizzle/0012_session_reads.sql +8 -0
- package/dist/store/postgres/drizzle/0013_definition_files.sql +16 -0
- package/dist/store/postgres/drizzle/0014_action_endpoints_removed.sql +2 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/reads.d.ts +3 -0
- package/dist/store/postgres/reads.js +272 -0
- package/dist/store/postgres/schema.d.ts +294 -489
- package/dist/store/postgres/schema.js +41 -76
- package/dist/store/postgres/store.js +51 -154
- package/dist/store/postgres/tenant.d.ts +34 -2
- package/dist/store/postgres/tenant.js +16 -32
- package/dist/store/types.d.ts +27 -96
- package/dist/store/types.js +0 -1
- package/dist/streams/control.d.ts +0 -2
- package/dist/streams/control.js +0 -5
- package/dist/streams/types.d.ts +1 -10
- package/dist/tenant/advance.d.ts +3 -3
- package/dist/tenant/advance.js +10 -7
- package/dist/tenant/auth.d.ts +14 -9
- package/dist/tenant/auth.js +24 -28
- package/dist/tenant/cause.d.ts +3 -3
- package/dist/tenant/cause.js +1 -1
- package/dist/tenant/commands.d.ts +2 -18
- package/dist/tenant/commands.js +35 -172
- package/dist/tenant/context.d.ts +14 -24
- package/dist/tenant/definition-files.d.ts +24 -0
- package/dist/tenant/definition-files.js +103 -0
- package/dist/tenant/effects.d.ts +18 -20
- package/dist/tenant/effects.js +56 -119
- package/dist/tenant/ephemeral.d.ts +7 -6
- package/dist/tenant/ephemeral.js +3 -11
- package/dist/tenant/host-token.js +1 -1
- package/dist/tenant/jwt.js +1 -1
- package/dist/tenant/module.d.ts +1 -4
- package/dist/tenant/module.js +3 -12
- package/dist/tenant/operator-keys.d.ts +34 -8
- package/dist/tenant/operator-keys.js +50 -18
- package/dist/tenant/outbound.d.ts +10 -1
- package/dist/tenant/outbound.js +44 -27
- package/dist/tenant/paths.d.ts +1 -1
- package/dist/tenant/paths.js +1 -3
- package/dist/tenant/principals.d.ts +11 -2
- package/dist/tenant/principals.js +27 -8
- package/dist/tenant/run-token.js +3 -3
- package/dist/tenant/runtime.d.ts +4 -6
- package/dist/tenant/runtime.js +19 -18
- package/dist/tenant/sandboxes.d.ts +4 -1
- package/dist/tenant/sandboxes.js +11 -0
- package/dist/tenant/scheduler.d.ts +1 -5
- package/dist/tenant/scheduler.js +2 -5
- package/dist/tenant/session-streams.d.ts +2 -1
- package/dist/tenant/session-streams.js +39 -5
- package/dist/tenant/session.d.ts +8 -41
- package/dist/tenant/session.js +0 -37
- package/dist/tenant/sessions.js +16 -8
- package/dist/tenant/signing-keys.d.ts +3 -3
- package/dist/tenant/signing-keys.js +5 -5
- package/dist/tenant/skill-tool.d.ts +20 -0
- package/dist/tenant/skill-tool.js +72 -0
- package/dist/tenant/slim.d.ts +1 -1
- package/dist/tenant/slim.js +1 -1
- package/dist/tenant/status.js +7 -12
- package/dist/tenant/store-pg.d.ts +15 -0
- package/dist/tenant/store-pg.js +7 -3
- package/dist/tenant/streams.d.ts +3 -10
- package/dist/tenant/streams.js +1 -24
- package/dist/tenant/sweep.js +0 -5
- package/dist/tenant/transcript.d.ts +2 -2
- package/dist/tenant/transcript.js +2 -2
- package/dist/tenant/types.d.ts +15 -22
- package/dist/tenant/worker.d.ts +1 -3
- package/dist/tenant/worker.js +3 -8
- package/dist/vault/sources.d.ts +7 -1
- package/dist/vault/sources.js +22 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/admin-openapi.json +0 -904
- package/dist/api/http/routes/actions.d.ts +0 -8
- package/dist/api/http/routes/actions.js +0 -75
- package/dist/api/http/routes/endpoints.d.ts +0 -7
- package/dist/api/http/routes/endpoints.js +0 -100
- package/dist/host/admin-api.d.ts +0 -30
- package/dist/host/admin-api.js +0 -140
- package/dist/mcp/stdio.d.ts +0 -17
- package/dist/mcp/stdio.js +0 -82
- package/dist/tenant/actions.d.ts +0 -14
- package/dist/tenant/actions.js +0 -29
- package/dist/tenant/delivery-token.d.ts +0 -41
- package/dist/tenant/delivery-token.js +0 -104
- package/dist/tenant/delivery.d.ts +0 -62
- package/dist/tenant/delivery.js +0 -481
- package/dist/tenant/endpoints.d.ts +0 -67
- package/dist/tenant/endpoints.js +0 -90
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nylorun-operate`: the Host's one-shot operator command, run inside the runtime container
|
|
3
|
+
* (`docker compose exec runtime nylorun-operate …`, `kubectl exec … -- nylorun-operate …`). It
|
|
4
|
+
* reads the Tenant's database from `NYLORUN_DATABASE_URL`, as the Host does, does one job and
|
|
5
|
+
* exits. Being able to run it is the authorization: whoever can exec into the container holds
|
|
6
|
+
* the installation. It never migrates or creates the Tenant: the Host does that.
|
|
7
|
+
*
|
|
8
|
+
* nylorun-operate status [--json]
|
|
9
|
+
* nylorun-operate keys list [--json]
|
|
10
|
+
* nylorun-operate keys put <id> [--role application|management] [--json]
|
|
11
|
+
* nylorun-operate keys rm <id> [--json]
|
|
12
|
+
*
|
|
13
|
+
* `status` says whether the Tenant is open and, when it is not, why: from the database (no
|
|
14
|
+
* Tenant yet, an old layout, a schema newer or older than this Runtime) and from the running
|
|
15
|
+
* Host's `/ready` on this container's listen port. It needs no key, so it works when the Tenant
|
|
16
|
+
* cannot open. `keys put` prints the new key once on stdout (with `--json`, the whole
|
|
17
|
+
* response). Exit codes: 0 done (`status`: the Tenant is open), 1 refused (an id, a role),
|
|
18
|
+
* 2 the Tenant cannot be opened (`status` still prints it), 64 usage.
|
|
19
|
+
*/
|
|
20
|
+
import { pathToFileURL } from "node:url";
|
|
21
|
+
import { HOST_PROTOCOL } from "@nylorun/core/compatibility";
|
|
22
|
+
import { createPostgresClient } from "../store/postgres/connect.js";
|
|
23
|
+
import { database } from "../store/postgres/db.js";
|
|
24
|
+
import { assertCurrentLayout, expectedSchemaVersion, readSchemaVersion, } from "../store/postgres/migrate.js";
|
|
25
|
+
import { createPostgresSessionStore } from "../store/postgres/store.js";
|
|
26
|
+
import { readTenantEnvelope } from "../store/postgres/tenant.js";
|
|
27
|
+
import { openError, TenantOpenError } from "../tenant/cause.js";
|
|
28
|
+
import { operatorKeys } from "../tenant/operator-keys.js";
|
|
29
|
+
import { RUNTIME_VERSION } from "../version.js";
|
|
30
|
+
import { DEFAULT_CONTAINER_LISTEN_PORT } from "./stack-config.js";
|
|
31
|
+
export const EXIT_REFUSED = 1;
|
|
32
|
+
export const EXIT_TENANT = 2;
|
|
33
|
+
export const EXIT_USAGE = 64;
|
|
34
|
+
const USAGE = `Usage:
|
|
35
|
+
nylorun-operate status [--json]
|
|
36
|
+
nylorun-operate keys list [--json]
|
|
37
|
+
nylorun-operate keys put <id> [--role application|management] [--json]
|
|
38
|
+
nylorun-operate keys rm <id> [--json]`;
|
|
39
|
+
class Exit extends Error {
|
|
40
|
+
code;
|
|
41
|
+
constructor(code, message) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.code = code;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Runs one command; resolves to the exit code. */
|
|
47
|
+
export async function runOperate(argv, io) {
|
|
48
|
+
try {
|
|
49
|
+
if (argv[0] === "status") {
|
|
50
|
+
const json = argv[1] === "--json";
|
|
51
|
+
if (argv.length > (json ? 2 : 1))
|
|
52
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
53
|
+
return await status(io, json);
|
|
54
|
+
}
|
|
55
|
+
const command = parse(argv);
|
|
56
|
+
const url = io.env.NYLORUN_DATABASE_URL;
|
|
57
|
+
if (!url)
|
|
58
|
+
throw new Exit(EXIT_TENANT, "NYLORUN_DATABASE_URL is not set: run this inside the runtime container");
|
|
59
|
+
const sql = createPostgresClient(url, { max: 1 });
|
|
60
|
+
try {
|
|
61
|
+
const keys = operatorKeys(await openStore(sql));
|
|
62
|
+
return await command(keys, io);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await sql.end({ timeout: 5 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error instanceof Exit) {
|
|
70
|
+
io.err(error.message);
|
|
71
|
+
return error.code;
|
|
72
|
+
}
|
|
73
|
+
io.err(`nylorun-operate failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
74
|
+
return EXIT_TENANT;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function parse(argv) {
|
|
78
|
+
const [group, verb, ...rest] = argv;
|
|
79
|
+
if (group !== "keys")
|
|
80
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
81
|
+
const json = rest.includes("--json");
|
|
82
|
+
const args = rest.filter((arg) => arg !== "--json");
|
|
83
|
+
switch (verb) {
|
|
84
|
+
case "list":
|
|
85
|
+
if (args.length > 0)
|
|
86
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
87
|
+
return async (keys, io) => {
|
|
88
|
+
const list = await keys.list();
|
|
89
|
+
if (json)
|
|
90
|
+
io.out(JSON.stringify({ keys: list }));
|
|
91
|
+
else
|
|
92
|
+
for (const key of list)
|
|
93
|
+
io.out(`${key.id}\t${key.role}\t${key.createdAt}`);
|
|
94
|
+
return 0;
|
|
95
|
+
};
|
|
96
|
+
case "put": {
|
|
97
|
+
const [id, ...flags] = args;
|
|
98
|
+
if (!id || id.startsWith("-"))
|
|
99
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
100
|
+
let role = "application";
|
|
101
|
+
if (flags.length > 0) {
|
|
102
|
+
const value = flags[0] === "--role" ? flags[1] : undefined;
|
|
103
|
+
if (flags.length !== 2 || (value !== "application" && value !== "management"))
|
|
104
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
105
|
+
role = value;
|
|
106
|
+
}
|
|
107
|
+
return async (keys, io) => {
|
|
108
|
+
const put = await keys.put(id, role);
|
|
109
|
+
if ("reason" in put)
|
|
110
|
+
throw new Exit(EXIT_REFUSED, put.message);
|
|
111
|
+
if (json)
|
|
112
|
+
io.out(JSON.stringify(put));
|
|
113
|
+
else {
|
|
114
|
+
io.out(put.key);
|
|
115
|
+
io.err(`${put.rotated ? "Rotated" : "Created"} ${put.role} key ${put.id}. It is shown this once.`);
|
|
116
|
+
}
|
|
117
|
+
return 0;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
case "rm": {
|
|
121
|
+
const [id] = args;
|
|
122
|
+
if (!id || args.length !== 1)
|
|
123
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
124
|
+
return async (keys, io) => {
|
|
125
|
+
const deleted = await keys.delete(id);
|
|
126
|
+
if (typeof deleted !== "boolean")
|
|
127
|
+
throw new Exit(EXIT_REFUSED, deleted.message);
|
|
128
|
+
if (json)
|
|
129
|
+
io.out(JSON.stringify({ id, deleted }));
|
|
130
|
+
else
|
|
131
|
+
io.err(deleted ? `Deleted key ${id}.` : `There is no key ${id}.`);
|
|
132
|
+
return 0;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
throw new Exit(EXIT_USAGE, USAGE);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function status(io, json) {
|
|
140
|
+
const tenant = await tenantStatus(io);
|
|
141
|
+
const report = {
|
|
142
|
+
version: RUNTIME_VERSION,
|
|
143
|
+
protocol: { min: HOST_PROTOCOL.min, max: HOST_PROTOCOL.max, features: [...HOST_PROTOCOL.features] },
|
|
144
|
+
tenant,
|
|
145
|
+
};
|
|
146
|
+
if (json)
|
|
147
|
+
io.out(JSON.stringify(report));
|
|
148
|
+
else {
|
|
149
|
+
io.out(`Runtime ${report.version}, protocol ${report.protocol.min}–${report.protocol.max}`);
|
|
150
|
+
const named = tenant.name ? `${tenant.name} (${tenant.id})` : "none";
|
|
151
|
+
io.out(`Tenant ${named}: ${tenant.state}${tenant.cause ? ` (${tenant.cause})` : ""}`);
|
|
152
|
+
if (tenant.message)
|
|
153
|
+
io.out(tenant.message);
|
|
154
|
+
}
|
|
155
|
+
return tenant.state === "open" ? 0 : EXIT_TENANT;
|
|
156
|
+
}
|
|
157
|
+
/** The Tenant from its database, then from the running Host's `/ready`. */
|
|
158
|
+
async function tenantStatus(io) {
|
|
159
|
+
const url = io.env.NYLORUN_DATABASE_URL;
|
|
160
|
+
if (!url)
|
|
161
|
+
return unavailable(null, "NYLORUN_DATABASE_URL is not set: run this inside the runtime container");
|
|
162
|
+
const sql = createPostgresClient(url, { max: 1 });
|
|
163
|
+
let envelope;
|
|
164
|
+
try {
|
|
165
|
+
const db = database(sql);
|
|
166
|
+
await assertCurrentLayout(db);
|
|
167
|
+
envelope = await readTenantEnvelope(db);
|
|
168
|
+
if (!envelope)
|
|
169
|
+
return unavailable(null, "The database holds no Tenant yet: the Runtime creates it when it starts");
|
|
170
|
+
const version = (await readSchemaVersion(db)) ?? 0;
|
|
171
|
+
const expected = expectedSchemaVersion();
|
|
172
|
+
if (version > expected)
|
|
173
|
+
throw openError("schema-too-new", `The Tenant's schema is at version ${version}, this Runtime's is ${expected}: it was migrated by a newer Runtime`);
|
|
174
|
+
if (version < expected)
|
|
175
|
+
return unavailable(envelope, `The Tenant's schema is at version ${version}, this Runtime's is ${expected}: the Runtime migrates it when it starts; if it is running, its log says why it did not`);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
if (error instanceof TenantOpenError)
|
|
179
|
+
return { ...unavailable(envelope ?? null, `${error.message}. Repair: ${error.repair}`), cause: error.code };
|
|
180
|
+
return unavailable(envelope ?? null, `The database cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
await sql.end({ timeout: 5 });
|
|
184
|
+
}
|
|
185
|
+
const port = io.env.NYLORUN_LISTEN_PORT ?? String(DEFAULT_CONTAINER_LISTEN_PORT);
|
|
186
|
+
const ready = `http://127.0.0.1:${port}/ready`;
|
|
187
|
+
let answer;
|
|
188
|
+
try {
|
|
189
|
+
const response = await (io.fetch ?? fetch)(ready, { signal: AbortSignal.timeout(5000) });
|
|
190
|
+
answer = (await response.json());
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return unavailable(envelope, `The Runtime is not answering at ${ready}: it is stopped or still starting`);
|
|
194
|
+
}
|
|
195
|
+
if (answer.checks?.tenant !== true)
|
|
196
|
+
return unavailable(envelope, "The Runtime is running but has not opened the Tenant: its log names the cause");
|
|
197
|
+
return { id: envelope.id, name: envelope.name, state: "open" };
|
|
198
|
+
}
|
|
199
|
+
function unavailable(envelope, message) {
|
|
200
|
+
return { id: envelope?.id ?? null, name: envelope?.name ?? null, state: "unavailable", message };
|
|
201
|
+
}
|
|
202
|
+
/** The Tenant's store, when the Host has created and migrated it to this Runtime's schema. */
|
|
203
|
+
async function openStore(sql) {
|
|
204
|
+
const db = database(sql);
|
|
205
|
+
const envelope = await readTenantEnvelope(db).catch((error) => {
|
|
206
|
+
throw new Exit(EXIT_TENANT, `The Tenant's database cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
|
+
});
|
|
208
|
+
if (!envelope)
|
|
209
|
+
throw new Exit(EXIT_TENANT, "The database holds no Tenant yet: start the Runtime first");
|
|
210
|
+
const version = await readSchemaVersion(db);
|
|
211
|
+
const expected = expectedSchemaVersion();
|
|
212
|
+
if (version !== expected)
|
|
213
|
+
throw new Exit(EXIT_TENANT, `The Tenant's schema is at version ${version ?? "none"}, this Runtime's is ${expected}: start the Runtime to migrate it, or run the Runtime's own image`);
|
|
214
|
+
return createPostgresSessionStore({ sql, tenantId: envelope.id, schemaVersion: expected });
|
|
215
|
+
}
|
|
216
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
217
|
+
const code = await runOperate(process.argv.slice(2), {
|
|
218
|
+
env: process.env,
|
|
219
|
+
out: (line) => process.stdout.write(`${line}\n`),
|
|
220
|
+
err: (line) => process.stderr.write(`${line}\n`),
|
|
221
|
+
});
|
|
222
|
+
process.exitCode = code;
|
|
223
|
+
}
|
|
@@ -123,7 +123,7 @@ export interface HarnessServiceConfig {
|
|
|
123
123
|
pod?: PodHostConfig;
|
|
124
124
|
/** `NYLORUN_GATES_URL`: the gates service its model and MCP calls cross, with run tokens. */
|
|
125
125
|
gatesUrl: string;
|
|
126
|
-
/** `NYLORUN_HARNESS_ROOT`: its sandboxes
|
|
126
|
+
/** `NYLORUN_HARNESS_ROOT`: its sandboxes. Default `/harness`. */
|
|
127
127
|
root: string;
|
|
128
128
|
/** `/health` on `127.0.0.1:<NYLORUN_HARNESS_HEALTH_PORT>` (default 4300). */
|
|
129
129
|
healthPort: number;
|
|
@@ -149,7 +149,7 @@ export interface StackConfig {
|
|
|
149
149
|
sandboxes?: SandboxesConfig;
|
|
150
150
|
/**
|
|
151
151
|
* Present when the process runs core or loop and `NYLORUN_GATES_URL` is set: its model calls,
|
|
152
|
-
* remote MCP calls and
|
|
152
|
+
* remote MCP calls and HTTP tool calls cross the gates service. Required for loop in
|
|
153
153
|
* container mode; outside a container (a development Host, tests) the loop may make them in
|
|
154
154
|
* its own process.
|
|
155
155
|
*/
|
|
@@ -172,19 +172,13 @@ export interface StackConfig {
|
|
|
172
172
|
listen?: ContainerListen;
|
|
173
173
|
endpoints: StackEndpoints;
|
|
174
174
|
/**
|
|
175
|
-
* The URL clients use to reach this Host (`NYLORUN_PUBLIC_URL`)
|
|
176
|
-
*
|
|
177
|
-
* means nothing outside the container.
|
|
175
|
+
* The URL clients use to reach this Host (`NYLORUN_PUBLIC_URL`). In container mode the bind
|
|
176
|
+
* address (`0.0.0.0:4000`) means nothing outside the container.
|
|
178
177
|
*/
|
|
179
178
|
publicUrl?: string;
|
|
180
179
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*/
|
|
184
|
-
operator?: ContainerListen;
|
|
185
|
-
/**
|
|
186
|
-
* How the Runtime may call Action endpoints: `NYLORUN_ENDPOINT_LOOPBACK=docker-host` (the local
|
|
187
|
-
* stack: `localhost` means the machine that runs Docker), `NYLORUN_ENDPOINT_PRIVATE`
|
|
180
|
+
* How the Runtime may call MCP servers and HTTP tools: `NYLORUN_ENDPOINT_LOOPBACK=docker-host`
|
|
181
|
+
* (the local stack: `localhost` means the machine that runs Docker), `NYLORUN_ENDPOINT_PRIVATE`
|
|
188
182
|
* (`allow` or `refuse`) and `NYLORUN_ENDPOINT_HTTP` (`allow` or `refuse`).
|
|
189
183
|
*/
|
|
190
184
|
delivery?: {
|
|
@@ -228,6 +222,12 @@ export interface TenantSettings {
|
|
|
228
222
|
* `trusted-issuers`), read once at boot (`host/main.ts`). Absent: no issuer is trusted.
|
|
229
223
|
*/
|
|
230
224
|
identityFile?: string;
|
|
225
|
+
/**
|
|
226
|
+
* `NYLORUN_MANAGEMENT_KEY_FILE`: a file holding a management key (64 hex characters), which
|
|
227
|
+
* the Host registers as the principal `bootstrap` at every start, replacing its key when the
|
|
228
|
+
* file changed (protocol 8). For installations where no one can run `nylorun-operate`.
|
|
229
|
+
*/
|
|
230
|
+
managementKeyFile?: string;
|
|
231
231
|
}
|
|
232
232
|
export declare class StackConfigError extends Error {
|
|
233
233
|
constructor(message: string);
|
|
@@ -225,30 +225,6 @@ function parseListen(env) {
|
|
|
225
225
|
const allowedHosts = [...new Set([...explicit, ...loopbackForms(port)])];
|
|
226
226
|
return { host, port, allowedHosts };
|
|
227
227
|
}
|
|
228
|
-
function parseAdminListen(env) {
|
|
229
|
-
const rawPort = read(env, "NYLORUN_ADMIN_LISTEN_PORT");
|
|
230
|
-
const rawHost = read(env, "NYLORUN_ADMIN_LISTEN_HOST");
|
|
231
|
-
const rawAllowed = read(env, "NYLORUN_ADMIN_ALLOWED_HOSTS");
|
|
232
|
-
if (rawPort === undefined) {
|
|
233
|
-
if (rawHost !== undefined || rawAllowed !== undefined)
|
|
234
|
-
throw new StackConfigError("NYLORUN_ADMIN_LISTEN_PORT is required with NYLORUN_ADMIN_LISTEN_HOST or NYLORUN_ADMIN_ALLOWED_HOSTS");
|
|
235
|
-
return undefined;
|
|
236
|
-
}
|
|
237
|
-
const host = rawHost ?? DEFAULT_CONTAINER_LISTEN_HOST;
|
|
238
|
-
if (/\s|\//.test(host))
|
|
239
|
-
throw new StackConfigError(`NYLORUN_ADMIN_LISTEN_HOST is not an address: ${host}`);
|
|
240
|
-
const port = parsePort("NYLORUN_ADMIN_LISTEN_PORT", rawPort);
|
|
241
|
-
const explicit = rawAllowed === undefined
|
|
242
|
-
? []
|
|
243
|
-
: rawAllowed
|
|
244
|
-
.split(",")
|
|
245
|
-
.map((entry) => entry.trim())
|
|
246
|
-
.filter((entry) => entry !== "")
|
|
247
|
-
.map((entry) => normalizeAllowedHost("NYLORUN_ADMIN_ALLOWED_HOSTS", entry));
|
|
248
|
-
if (explicit.length === 0 && !isLoopbackAddress(host))
|
|
249
|
-
throw new StackConfigError(`NYLORUN_ADMIN_ALLOWED_HOSTS is required when NYLORUN_ADMIN_LISTEN_HOST is ${host}: list the Host headers operators send, e.g. runtime:${port},localhost:<published port>`);
|
|
250
|
-
return { host, port, allowedHosts: [...new Set([...explicit, ...loopbackForms(port)])] };
|
|
251
|
-
}
|
|
252
228
|
const IDENTITY_KEY = /^publickeyv1_[1-9A-HJ-NP-Za-km-z]{32,64}$/;
|
|
253
229
|
function parseIdentityKeys(env) {
|
|
254
230
|
const raw = read(env, "NYLORUN_RESTATE_IDENTITY_KEY");
|
|
@@ -271,15 +247,12 @@ export function parseStackConfig(env, argv) {
|
|
|
271
247
|
// The image sets NYLORUN_LISTEN_*: only the API's processes read them.
|
|
272
248
|
const servesApi = services.has("core") || services.has("loop");
|
|
273
249
|
const listen = servesApi ? parseListen(env) : undefined;
|
|
274
|
-
const operator = servesApi ? parseAdminListen(env) : undefined;
|
|
275
250
|
const gates = services.has("gates") || services.has("keys") ? parseGates(env) : undefined;
|
|
276
251
|
const egress = services.has("egress") ? parseEgress(env) : undefined;
|
|
277
|
-
// The gates service's clients: the loop's model and tool calls
|
|
252
|
+
// The gates service's clients: the loop's model and tool calls.
|
|
278
253
|
const modelGate = servesApi ? parseModelGate(env) : undefined;
|
|
279
254
|
// The keys service is the gateway's unless NYLORUN_KEYS_URL names another listener.
|
|
280
255
|
const keys = servesApi ? (parseKeysEndpoint(env) ?? modelGate) : undefined;
|
|
281
|
-
if (operator && listen && operator.port === listen.port)
|
|
282
|
-
throw new StackConfigError("NYLORUN_ADMIN_LISTEN_PORT must differ from NYLORUN_LISTEN_PORT");
|
|
283
256
|
if (services.has("loop") && listen && !modelGate)
|
|
284
257
|
throw new StackConfigError("NYLORUN_GATES_URL is required for the loop service in a container: model calls go through the gates service (the gateway container), so the loop never holds a model credential. Set NYLORUN_GATES_URL and NYLORUN_GATES_TOKEN (`nylorun start` sets both)");
|
|
285
258
|
const rawPacking = read(env, "NYLORUN_PACKING");
|
|
@@ -326,9 +299,8 @@ export function parseStackConfig(env, argv) {
|
|
|
326
299
|
const harnessListener = harnessMode === "remote" || sandboxes
|
|
327
300
|
? parseHarnessListener(env, harnessMode === "remote")
|
|
328
301
|
: undefined;
|
|
329
|
-
if (harnessListener &&
|
|
330
|
-
|
|
331
|
-
throw new StackConfigError("NYLORUN_HARNESS_LISTEN_PORT must differ from NYLORUN_LISTEN_PORT and NYLORUN_ADMIN_LISTEN_PORT");
|
|
302
|
+
if (harnessListener && listen?.port === harnessListener.listen.port)
|
|
303
|
+
throw new StackConfigError("NYLORUN_HARNESS_LISTEN_PORT must differ from NYLORUN_LISTEN_PORT");
|
|
332
304
|
const delivery = parseDelivery(env);
|
|
333
305
|
const tenant = servesApi ? parseTenant(env) : undefined;
|
|
334
306
|
const objectStore = parseObjectStore(env);
|
|
@@ -346,7 +318,6 @@ export function parseStackConfig(env, argv) {
|
|
|
346
318
|
...(harnessMode ? { harnessMode } : {}),
|
|
347
319
|
...(harnessListener ? { harnessListener } : {}),
|
|
348
320
|
...(sandboxes ? { sandboxes } : {}),
|
|
349
|
-
...(operator ? { operator } : {}),
|
|
350
321
|
...(delivery ? { delivery } : {}),
|
|
351
322
|
...(tenant ? { tenant } : {}),
|
|
352
323
|
...(objectStore ? { objectStore } : {}),
|
|
@@ -379,10 +350,12 @@ function parseTenant(env) {
|
|
|
379
350
|
if (id !== undefined && !isTenantId(id))
|
|
380
351
|
throw new StackConfigError(`NYLORUN_TENANT_ID must be a Tenant id (tn_ and 26 Crockford characters), not ${id}`);
|
|
381
352
|
const identityFile = read(env, "NYLORUN_IDENTITY_FILE");
|
|
353
|
+
const managementKeyFile = read(env, "NYLORUN_MANAGEMENT_KEY_FILE");
|
|
382
354
|
return {
|
|
383
355
|
...(id ? { id } : {}),
|
|
384
356
|
name: read(env, "NYLORUN_TENANT_NAME") ?? "default",
|
|
385
357
|
...(identityFile ? { identityFile } : {}),
|
|
358
|
+
...(managementKeyFile ? { managementKeyFile } : {}),
|
|
386
359
|
};
|
|
387
360
|
}
|
|
388
361
|
const GATES_TOKEN = /^[0-9a-f]{64,}$/i;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export type { ResolverConfig } from "./vault/sources.js";
|
|
|
8
8
|
export type { RuntimeAgent } from "./contracts.js";
|
|
9
9
|
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
10
10
|
export type { RuntimeModelAdapter } from "./contracts.js";
|
|
11
|
-
export { baselineEnvironment, hostProcessEnvironment,
|
|
11
|
+
export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
|
|
12
12
|
export type { HostProcessPathInputs } from "./host/environment.js";
|
|
13
13
|
export type { HostConfigFile, HostCredentialsFile, } from "./host/config.js";
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { parseIdentityFile, IdentityFileError, } from "./tenant/identity-file.js
|
|
|
4
4
|
export { createTrustedIssuers, } from "./tenant/issuers.js";
|
|
5
5
|
export { scriptedModel, gatewayModel } from "./core/provider.js";
|
|
6
6
|
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
7
|
-
export { baselineEnvironment, hostProcessEnvironment,
|
|
7
|
+
export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
|
package/dist/infra/workers.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* | Services | Serves | `execution.start` |
|
|
5
5
|
* | --- | --- | --- |
|
|
6
|
-
* | `core` |
|
|
6
|
+
* | `core` | Runtime and Management APIs, SSE | **never**: wakes, timers and sweep arming go through Restate's ingress, which needs no Worker endpoint |
|
|
7
7
|
* | `loop` | the Restate endpoint (`NylorunSession.advance`, `NylorunTenant.sweep`, timers) | yes |
|
|
8
8
|
* | `core,loop` | both (the local stack's `runtime` container) | yes |
|
|
9
9
|
*
|
package/dist/keys/keys.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The keys seam (blueprint §14, F4.2): everything that needs the vault key. Vault writes that
|
|
3
3
|
* touch a secret (creating and rotating a credential, setting and selecting the host model;
|
|
4
|
-
* rotation and selection decrypt the old secret first) and all token signing (
|
|
5
|
-
*
|
|
4
|
+
* rotation and selection decrypt the old secret first) and all token signing (capability
|
|
5
|
+
* links, run and host tokens, signing-key rotation) run here, whole, so the process that calls them never
|
|
6
6
|
* holds the key or a private signing key.
|
|
7
7
|
*
|
|
8
8
|
* MCP OAuth connect (F9-D14) runs here too: discovery, registration, the code exchange and the
|