@nylorun/runtime 0.13.0-beta → 0.15.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 +434 -0
- package/README.md +137 -41
- package/dist/adapters/execution/restate.d.ts +7 -2
- package/dist/adapters/execution/restate.js +40 -10
- package/dist/adapters/replication/pgoutput.d.ts +21 -0
- package/dist/adapters/replication/pgoutput.js +170 -0
- package/dist/adapters/streams/s2.js +37 -20
- package/dist/admin-openapi.json +570 -0
- package/dist/api/a2a/card.d.ts +15 -0
- package/dist/api/a2a/card.js +24 -0
- package/dist/api/a2a/endpoint.d.ts +8 -0
- package/dist/api/a2a/endpoint.js +81 -0
- package/dist/api/a2a/protocol.d.ts +144 -0
- package/dist/api/a2a/protocol.js +188 -0
- package/dist/api/a2a/routes.d.ts +19 -0
- package/dist/api/a2a/routes.js +289 -0
- package/dist/api/a2a/tasks.d.ts +50 -0
- package/dist/api/a2a/tasks.js +186 -0
- package/dist/api/ag-ui/endpoint.d.ts +8 -0
- package/dist/api/ag-ui/endpoint.js +126 -0
- package/dist/api/ag-ui/history.d.ts +7 -0
- package/dist/api/ag-ui/history.js +60 -0
- package/dist/api/ag-ui/routes.d.ts +32 -0
- package/dist/api/ag-ui/routes.js +305 -0
- package/dist/api/ag-ui/session-id.d.ts +1 -0
- package/dist/api/ag-ui/session-id.js +12 -0
- package/dist/api/ag-ui/sse.d.ts +9 -0
- package/dist/api/ag-ui/sse.js +7 -0
- package/dist/api/ag-ui/translate.d.ts +38 -0
- package/dist/api/ag-ui/translate.js +222 -0
- package/dist/api/components.d.ts +8065 -0
- package/dist/api/components.js +82 -0
- package/dist/api/gate/keys.d.ts +14 -0
- package/dist/api/gate/keys.js +59 -0
- package/dist/api/gate/routes.d.ts +34 -0
- package/dist/api/gate/routes.js +235 -0
- package/dist/api/http/app.d.ts +18 -0
- package/dist/api/http/app.js +58 -0
- package/dist/api/http/body.d.ts +4 -0
- package/dist/api/http/body.js +37 -0
- package/dist/api/http/define.d.ts +39 -0
- package/dist/api/http/define.js +122 -0
- package/dist/api/http/respond.d.ts +9 -0
- package/dist/api/http/respond.js +44 -0
- package/dist/api/http/routes/access.d.ts +24 -0
- package/dist/api/http/routes/access.js +266 -0
- package/dist/api/http/routes/actions.d.ts +8 -0
- package/dist/api/http/routes/actions.js +76 -0
- package/dist/api/http/routes/endpoints.d.ts +7 -0
- package/dist/api/http/routes/endpoints.js +100 -0
- package/dist/api/http/routes/sessions.d.ts +3 -0
- package/dist/api/http/routes/sessions.js +205 -0
- package/dist/api/http/routes/tenant.d.ts +11 -0
- package/dist/api/http/routes/tenant.js +244 -0
- package/dist/api/http/routes/vaults.d.ts +8 -0
- package/dist/api/http/routes/vaults.js +167 -0
- package/dist/api/openapi.d.ts +19 -0
- package/dist/api/openapi.js +137 -0
- package/dist/api/route.d.ts +12 -0
- package/dist/api/route.js +4 -0
- package/dist/core/bearer.d.ts +8 -0
- package/dist/core/bearer.js +11 -0
- package/dist/core/flow-host.d.ts +7 -15
- package/dist/core/flow-host.js +16 -41
- package/dist/core/sandbox-routes.d.ts +12 -7
- package/dist/core/sandbox-routes.js +24 -25
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +33 -13
- package/dist/execution/types.d.ts +38 -6
- package/dist/execution/types.js +12 -3
- package/dist/gates/contract.d.ts +51 -0
- package/dist/gates/contract.js +37 -0
- package/dist/gates/handler.d.ts +21 -0
- package/dist/gates/handler.js +55 -0
- package/dist/gates/http-client.d.ts +12 -0
- package/dist/gates/http-client.js +160 -0
- package/dist/gates/in-process.d.ts +32 -0
- package/dist/gates/in-process.js +42 -0
- package/dist/gates/inflight.d.ts +39 -0
- package/dist/gates/inflight.js +70 -0
- package/dist/gates/mcp-handler.d.ts +35 -0
- package/dist/gates/mcp-handler.js +142 -0
- package/dist/gates/meter.d.ts +24 -0
- package/dist/gates/meter.js +165 -0
- package/dist/gates/model-gate.d.ts +45 -0
- package/dist/gates/model-gate.js +1 -0
- package/dist/gates/tenant-vaults.d.ts +64 -0
- package/dist/gates/tenant-vaults.js +94 -0
- package/dist/gates/tool-calls.d.ts +51 -0
- package/dist/gates/tool-calls.js +64 -0
- package/dist/gates/tool-client.d.ts +12 -0
- package/dist/gates/tool-client.js +201 -0
- package/dist/gates/tool-contract.d.ts +116 -0
- package/dist/gates/tool-contract.js +64 -0
- package/dist/gates/tool-gate.d.ts +45 -0
- package/dist/gates/tool-gate.js +10 -0
- package/dist/host/admin-api.d.ts +23 -0
- package/dist/host/admin-api.js +74 -0
- package/dist/host/app.d.ts +34 -0
- package/dist/host/app.js +181 -0
- package/dist/host/config-for.d.ts +3 -1
- package/dist/host/config-for.js +3 -2
- package/dist/host/config.d.ts +11 -0
- package/dist/host/cors.d.ts +27 -0
- package/dist/host/cors.js +64 -0
- package/dist/host/create-host.d.ts +36 -8
- package/dist/host/create-host.js +161 -330
- package/dist/host/execution.d.ts +20 -18
- package/dist/host/execution.js +2 -2
- package/dist/host/gates.d.ts +58 -0
- package/dist/host/gates.js +127 -0
- package/dist/host/http.d.ts +20 -4
- package/dist/host/http.js +81 -23
- package/dist/host/index.d.ts +1 -1
- package/dist/host/main.js +189 -58
- package/dist/host/stack-config.d.ts +91 -19
- package/dist/host/stack-config.js +246 -17
- package/dist/infra/database.d.ts +2 -2
- package/dist/infra/execution.d.ts +5 -5
- package/dist/infra/execution.js +8 -6
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/workers.d.ts +17 -17
- package/dist/infra/workers.js +9 -9
- package/dist/keys/client.d.ts +9 -0
- package/dist/keys/client.js +70 -0
- package/dist/keys/contract.d.ts +24 -0
- package/dist/keys/contract.js +12 -0
- package/dist/keys/keys.d.ts +41 -0
- package/dist/keys/keys.js +50 -0
- package/dist/mcp/connect.d.ts +43 -3
- package/dist/mcp/connect.js +12 -5
- package/dist/mcp/pool.d.ts +39 -3
- package/dist/mcp/pool.js +74 -11
- package/dist/model/catalog.d.ts +2 -10
- package/dist/model/classify.d.ts +13 -0
- package/dist/model/classify.js +61 -0
- package/dist/model/models.d.ts +10 -1
- package/dist/model/models.js +14 -6
- package/dist/model/pi-model.d.ts +17 -2
- package/dist/model/pi-model.js +366 -222
- package/dist/openapi.json +25889 -0
- package/dist/record/append.d.ts +51 -0
- package/dist/record/append.js +20 -0
- package/dist/record/envelope.d.ts +19 -0
- package/dist/record/envelope.js +49 -0
- package/dist/record/index.d.ts +11 -0
- package/dist/record/index.js +11 -0
- package/dist/sandbox/manager.d.ts +13 -1
- package/dist/sandbox/manager.js +8 -11
- package/dist/sandbox/resolve.d.ts +33 -0
- package/dist/sandbox/resolve.js +81 -0
- package/dist/sandbox/session-sandbox.d.ts +11 -0
- package/dist/sandbox/session-sandbox.js +51 -0
- package/dist/sandbox/share.d.ts +14 -2
- package/dist/sandbox/share.js +33 -3
- package/dist/sandbox/tenant-config.d.ts +37 -0
- package/dist/sandbox/tenant-config.js +51 -0
- package/dist/sandbox/tools.d.ts +7 -9
- package/dist/sandbox/tools.js +11 -3
- package/dist/sandbox/types.d.ts +8 -2
- package/dist/store/postgres/connect.d.ts +25 -9
- package/dist/store/postgres/connect.js +27 -10
- package/dist/store/postgres/db.d.ts +16 -0
- package/dist/store/postgres/db.js +28 -0
- package/dist/store/postgres/drizzle/0000_foundation.sql +22 -0
- package/dist/store/postgres/drizzle/0001_baseline.sql +255 -0
- package/dist/store/postgres/drizzle/0002_stream_relay.sql +4 -0
- package/dist/store/postgres/drizzle/0003_tool_crossings.sql +9 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +34 -0
- package/dist/store/postgres/migrate.d.ts +68 -0
- package/dist/store/postgres/migrate.js +183 -0
- package/dist/store/postgres/record-writer.d.ts +4 -0
- package/dist/store/postgres/record-writer.js +34 -0
- package/dist/store/postgres/record.d.ts +5 -0
- package/dist/store/postgres/record.js +60 -0
- package/dist/store/postgres/schema.d.ts +3096 -0
- package/dist/store/postgres/schema.js +363 -0
- package/dist/store/postgres/store.d.ts +2 -4
- package/dist/store/postgres/store.js +590 -434
- package/dist/store/postgres/tenant.d.ts +56 -0
- package/dist/store/postgres/tenant.js +154 -0
- package/dist/store/types.d.ts +183 -132
- package/dist/store/types.js +0 -1
- package/dist/streams/basin.d.ts +18 -1
- package/dist/streams/basin.js +34 -2
- package/dist/streams/control.d.ts +18 -0
- package/dist/streams/control.js +33 -0
- package/dist/streams/memory.d.ts +3 -1
- package/dist/streams/memory.js +14 -8
- package/dist/streams/relay/core.d.ts +35 -0
- package/dist/streams/relay/core.js +366 -0
- package/dist/streams/relay/types.d.ts +78 -0
- package/dist/streams/relay/types.js +8 -0
- package/dist/streams/types.d.ts +71 -36
- package/dist/streams/types.js +18 -53
- package/dist/tenant/access-policy.d.ts +30 -0
- package/dist/tenant/access-policy.js +52 -0
- package/dist/tenant/actions.d.ts +10 -149
- package/dist/tenant/actions.js +5 -167
- package/dist/tenant/advance.d.ts +7 -2
- package/dist/tenant/advance.js +125 -14
- package/dist/tenant/auth.d.ts +19 -17
- package/dist/tenant/auth.js +90 -93
- package/dist/tenant/browser.d.ts +15 -0
- package/dist/tenant/browser.js +39 -0
- package/dist/tenant/cause.d.ts +25 -0
- package/dist/tenant/cause.js +45 -0
- package/dist/tenant/commands.d.ts +18 -2
- package/dist/tenant/commands.js +111 -115
- package/dist/tenant/context.d.ts +101 -21
- package/dist/tenant/context.js +14 -10
- package/dist/tenant/delivery-token.d.ts +41 -0
- package/dist/tenant/delivery-token.js +104 -0
- package/dist/tenant/delivery.d.ts +62 -0
- package/dist/tenant/delivery.js +481 -0
- package/dist/tenant/effects.d.ts +20 -1
- package/dist/tenant/effects.js +159 -42
- package/dist/tenant/endpoints.d.ts +67 -0
- package/dist/tenant/endpoints.js +90 -0
- package/dist/tenant/ephemeral.d.ts +22 -7
- package/dist/tenant/ephemeral.js +83 -125
- package/dist/tenant/history.d.ts +64 -0
- package/dist/tenant/history.js +120 -0
- package/dist/tenant/http.d.ts +8 -9
- package/dist/tenant/http.js +13 -27
- package/dist/tenant/jwt.d.ts +16 -0
- package/dist/tenant/jwt.js +50 -0
- package/dist/tenant/model-setting.d.ts +2 -2
- package/dist/tenant/module.d.ts +17 -28
- package/dist/tenant/module.js +133 -279
- package/dist/tenant/outbound.d.ts +44 -0
- package/dist/tenant/outbound.js +127 -0
- package/dist/tenant/paths.d.ts +11 -16
- package/dist/tenant/paths.js +24 -68
- package/dist/tenant/pool.d.ts +1 -3
- package/dist/tenant/pool.js +1 -19
- package/dist/tenant/principals.d.ts +23 -11
- package/dist/tenant/principals.js +61 -49
- package/dist/tenant/reset.d.ts +0 -4
- package/dist/tenant/reset.js +0 -5
- package/dist/tenant/runtime.d.ts +28 -20
- package/dist/tenant/runtime.js +95 -64
- package/dist/tenant/scheduler.d.ts +10 -3
- package/dist/tenant/scheduler.js +9 -5
- package/dist/tenant/session-streams.d.ts +127 -0
- package/dist/tenant/session-streams.js +404 -0
- package/dist/tenant/sessions.d.ts +28 -4
- package/dist/tenant/sessions.js +118 -24
- package/dist/tenant/signing-keys.d.ts +55 -0
- package/dist/tenant/signing-keys.js +172 -0
- package/dist/tenant/slim.d.ts +8 -0
- package/dist/tenant/slim.js +21 -0
- package/dist/tenant/status.d.ts +3 -3
- package/dist/tenant/status.js +20 -13
- package/dist/tenant/store-pg.d.ts +9 -6
- package/dist/tenant/store-pg.js +60 -125
- package/dist/tenant/streams.d.ts +62 -85
- package/dist/tenant/streams.js +143 -226
- package/dist/tenant/subject-limits.d.ts +16 -0
- package/dist/tenant/subject-limits.js +45 -0
- package/dist/tenant/sweep.d.ts +1 -10
- package/dist/tenant/sweep.js +29 -69
- package/dist/tenant/tokens.d.ts +14 -0
- package/dist/tenant/tokens.js +180 -0
- package/dist/tenant/transcript.d.ts +48 -5
- package/dist/tenant/transcript.js +58 -1
- package/dist/tenant/types.d.ts +69 -98
- package/dist/tenant/types.js +4 -15
- package/dist/tenant/worker.d.ts +3 -1
- package/dist/tenant/worker.js +13 -7
- package/dist/vault/host-model.d.ts +31 -0
- package/dist/vault/host-model.js +57 -0
- package/dist/vault/service.d.ts +44 -17
- package/dist/vault/service.js +126 -121
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +25 -9
- package/dist/core/executors.d.ts +0 -44
- package/dist/core/executors.js +0 -95
- package/dist/store/memory.d.ts +0 -57
- package/dist/store/memory.js +0 -645
- package/dist/store/postgres/migrations/001_initial.d.ts +0 -27
- package/dist/store/postgres/migrations/001_initial.js +0 -196
- package/dist/store/postgres/migrations/002_session_owner.d.ts +0 -6
- package/dist/store/postgres/migrations/002_session_owner.js +0 -13
- package/dist/store/postgres/migrations/index.d.ts +0 -49
- package/dist/store/postgres/migrations/index.js +0 -76
- package/dist/store/postgres/names.d.ts +0 -11
- package/dist/store/postgres/names.js +0 -42
- package/dist/store/postgres/tenants.d.ts +0 -90
- package/dist/store/postgres/tenants.js +0 -214
- package/dist/streams/relay.d.ts +0 -37
- package/dist/streams/relay.js +0 -254
- package/dist/tenant/envelope.d.ts +0 -7
- package/dist/tenant/envelope.js +0 -22
- package/dist/tenant/live.d.ts +0 -92
- package/dist/tenant/live.js +0 -290
- package/dist/tenant/quarantine-error.d.ts +0 -6
- package/dist/tenant/quarantine-error.js +0 -9
- package/dist/tenant/quarantine.d.ts +0 -27
- package/dist/tenant/quarantine.js +0 -72
- package/dist/tenant/routes-tenant.d.ts +0 -12
- package/dist/tenant/routes-tenant.js +0 -137
- package/dist/tenant/routes.d.ts +0 -9
- package/dist/tenant/routes.js +0 -151
- package/dist/tenant/store-memory.d.ts +0 -16
- package/dist/tenant/store-memory.js +0 -111
- /package/dist/{store → record}/cursor.d.ts +0 -0
- /package/dist/{store → record}/cursor.js +0 -0
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
* snapshot and argv. Pure: `host/main.ts` passes the process environment and
|
|
4
4
|
* arguments; nothing here reads ambient state.
|
|
5
5
|
*
|
|
6
|
+
* `--service core,loop` names the Runtime services the process runs (blueprint
|
|
7
|
+
* §19, D12): one image runs every service, and a container may pack several.
|
|
8
|
+
* Without the flag a process runs core and loop. `--role api|worker|all` is
|
|
9
|
+
* the deprecated name of the same choice (core, loop, or both). `gates` (the
|
|
10
|
+
* Model Gate) never shares a process with core or loop: it holds the
|
|
11
|
+
* credentials they must not. Only a process that runs core or loop parses the
|
|
12
|
+
* API listener (`NYLORUN_LISTEN_*`); only one that runs gates parses its
|
|
13
|
+
* listener (`NYLORUN_GATES_LISTEN_*`), and only one that runs loop parses
|
|
14
|
+
* where to reach the gate (`NYLORUN_GATES_URL`). Both read
|
|
15
|
+
* `NYLORUN_GATES_TOKEN`. In a container, loop requires the gate: the loop
|
|
16
|
+
* process must never hold a model credential.
|
|
17
|
+
*
|
|
6
18
|
* Two listen modes:
|
|
7
19
|
* - **local** (no `NYLORUN_LISTEN_*` / `NYLORUN_ALLOWED_HOSTS`): the Host binds
|
|
8
20
|
* what `host.json` names, loopback only unless `allowNonLoopback`.
|
|
@@ -12,9 +24,41 @@
|
|
|
12
24
|
* probes from inside the container.
|
|
13
25
|
*
|
|
14
26
|
* The Postgres, Restate and S2 endpoints are parsed and validated here;
|
|
15
|
-
* `infra/*` builds the clients from them.
|
|
27
|
+
* `infra/*` builds the clients from them. So is who the Host's Tenant is when its database
|
|
28
|
+
* holds none yet (`NYLORUN_TENANT_ID`, `NYLORUN_TENANT_NAME`, `NYLORUN_DERIVED_PRINCIPALS`).
|
|
29
|
+
*/
|
|
30
|
+
import { DERIVED_PRINCIPAL_ID_PATTERN, isTenantId } from "@nylorun/core/compatibility";
|
|
31
|
+
export const RUNTIME_SERVICES = ["core", "loop", "gates", "keys"];
|
|
32
|
+
/**
|
|
33
|
+
* Services that may share a process (D12): they hold the same secrets and parse the same
|
|
34
|
+
* trust class of input. keys joins gates (F4.2); egress joins them in a later release.
|
|
16
35
|
*/
|
|
17
|
-
|
|
36
|
+
const SERVICE_GROUPS = [
|
|
37
|
+
["core", "loop"],
|
|
38
|
+
["gates", "keys"],
|
|
39
|
+
];
|
|
40
|
+
/** Where the gates service listens by default. */
|
|
41
|
+
export const DEFAULT_GATES_LISTEN_PORT = 4100;
|
|
42
|
+
/** What a process runs without `--service`: core and loop, as `--role all` did. */
|
|
43
|
+
export const DEFAULT_SERVICES = new Set([
|
|
44
|
+
"core",
|
|
45
|
+
"loop",
|
|
46
|
+
]);
|
|
47
|
+
/** Services of the blueprint this release doesn't have yet. */
|
|
48
|
+
const LATER_SERVICES = [
|
|
49
|
+
"egress",
|
|
50
|
+
"harness",
|
|
51
|
+
"sandboxd",
|
|
52
|
+
];
|
|
53
|
+
const ROLE_SERVICES = {
|
|
54
|
+
api: ["core"],
|
|
55
|
+
worker: ["loop"],
|
|
56
|
+
all: ["core", "loop"],
|
|
57
|
+
};
|
|
58
|
+
/** `services` as the `--service` value that selects them, e.g. `core,loop`. */
|
|
59
|
+
export function describeServices(services) {
|
|
60
|
+
return RUNTIME_SERVICES.filter((service) => services.has(service)).join(",");
|
|
61
|
+
}
|
|
18
62
|
export const DEFAULT_CONTAINER_LISTEN_HOST = "0.0.0.0";
|
|
19
63
|
export const DEFAULT_CONTAINER_LISTEN_PORT = 4000;
|
|
20
64
|
export class StackConfigError extends Error {
|
|
@@ -30,30 +74,69 @@ function read(env, name) {
|
|
|
30
74
|
const trimmed = value.trim();
|
|
31
75
|
return trimmed === "" ? undefined : trimmed;
|
|
32
76
|
}
|
|
33
|
-
|
|
77
|
+
const USAGE = "Usage: main.js [--service core,loop|gates]";
|
|
78
|
+
/** Parses `--service a,b` (or the deprecated `--role`); throws `StackConfigError`. */
|
|
79
|
+
export function parseServices(argv) {
|
|
80
|
+
let service;
|
|
34
81
|
let role;
|
|
35
82
|
for (let index = 0; index < argv.length; index += 1) {
|
|
36
83
|
const arg = argv[index];
|
|
84
|
+
const flag = arg.startsWith("--service")
|
|
85
|
+
? "--service"
|
|
86
|
+
: arg.startsWith("--role")
|
|
87
|
+
? "--role"
|
|
88
|
+
: undefined;
|
|
37
89
|
let value;
|
|
38
|
-
if (arg ===
|
|
90
|
+
if (flag && arg === flag) {
|
|
39
91
|
value = argv[index + 1];
|
|
40
92
|
index += 1;
|
|
41
93
|
if (value === undefined || value.startsWith("-"))
|
|
42
|
-
throw new StackConfigError(
|
|
94
|
+
throw new StackConfigError(flag === "--service"
|
|
95
|
+
? "--service requires a value, e.g. core,loop"
|
|
96
|
+
: "--role requires a value: api, worker or all");
|
|
43
97
|
}
|
|
44
|
-
else if (arg.startsWith(
|
|
45
|
-
value = arg.slice(
|
|
98
|
+
else if (flag && arg.startsWith(`${flag}=`)) {
|
|
99
|
+
value = arg.slice(flag.length + 1);
|
|
46
100
|
}
|
|
47
101
|
else {
|
|
48
|
-
throw new StackConfigError(`Unknown argument ${arg}.
|
|
102
|
+
throw new StackConfigError(`Unknown argument ${arg}. ${USAGE}`);
|
|
49
103
|
}
|
|
50
|
-
if (role !== undefined)
|
|
51
|
-
throw new StackConfigError(
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
104
|
+
if ((flag === "--service" ? service : role) !== undefined)
|
|
105
|
+
throw new StackConfigError(`${flag} may only be supplied once`);
|
|
106
|
+
if (flag === "--service")
|
|
107
|
+
service = value;
|
|
108
|
+
else
|
|
109
|
+
role = value;
|
|
110
|
+
}
|
|
111
|
+
if (service !== undefined && role !== undefined)
|
|
112
|
+
throw new StackConfigError("--role is the deprecated name of --service; supply only --service");
|
|
113
|
+
if (role !== undefined) {
|
|
114
|
+
if (!Object.hasOwn(ROLE_SERVICES, role))
|
|
115
|
+
throw new StackConfigError(`Invalid --role ${role}; expected api, worker or all (deprecated: use --service core, loop or core,loop)`);
|
|
116
|
+
const deprecatedRole = role;
|
|
117
|
+
return { services: new Set(ROLE_SERVICES[deprecatedRole]), deprecatedRole };
|
|
118
|
+
}
|
|
119
|
+
if (service === undefined)
|
|
120
|
+
return { services: DEFAULT_SERVICES };
|
|
121
|
+
const names = service.split(",").map((name) => name.trim());
|
|
122
|
+
const services = new Set();
|
|
123
|
+
for (const name of names) {
|
|
124
|
+
if (name === "")
|
|
125
|
+
throw new StackConfigError(`--service ${service} has an empty entry. ${USAGE}`);
|
|
126
|
+
if (name === "all")
|
|
127
|
+
throw new StackConfigError("--service all is not a service; use --service core,loop");
|
|
128
|
+
if (LATER_SERVICES.includes(name))
|
|
129
|
+
throw new StackConfigError(`The ${name} service is not in this release of the Runtime`);
|
|
130
|
+
if (!RUNTIME_SERVICES.includes(name))
|
|
131
|
+
throw new StackConfigError(`Unknown service ${name}; expected ${RUNTIME_SERVICES.join(" or ")}`);
|
|
132
|
+
if (services.has(name))
|
|
133
|
+
throw new StackConfigError(`--service names ${name} twice`);
|
|
134
|
+
services.add(name);
|
|
55
135
|
}
|
|
56
|
-
|
|
136
|
+
const groups = SERVICE_GROUPS.filter((group) => group.some((name) => services.has(name)));
|
|
137
|
+
if (groups.length > 1)
|
|
138
|
+
throw new StackConfigError(`--service ${service}: ${groups.map((group) => group.join(" and ")).join(" may not share a process with ")}; run them as separate processes`);
|
|
139
|
+
return { services };
|
|
57
140
|
}
|
|
58
141
|
function parsePort(name, raw) {
|
|
59
142
|
if (!/^\d+$/.test(raw))
|
|
@@ -124,6 +207,30 @@ function parseListen(env) {
|
|
|
124
207
|
const allowedHosts = [...new Set([...explicit, ...loopbackForms(port)])];
|
|
125
208
|
return { host, port, allowedHosts };
|
|
126
209
|
}
|
|
210
|
+
function parseAdminListen(env) {
|
|
211
|
+
const rawPort = read(env, "NYLORUN_ADMIN_LISTEN_PORT");
|
|
212
|
+
const rawHost = read(env, "NYLORUN_ADMIN_LISTEN_HOST");
|
|
213
|
+
const rawAllowed = read(env, "NYLORUN_ADMIN_ALLOWED_HOSTS");
|
|
214
|
+
if (rawPort === undefined) {
|
|
215
|
+
if (rawHost !== undefined || rawAllowed !== undefined)
|
|
216
|
+
throw new StackConfigError("NYLORUN_ADMIN_LISTEN_PORT is required with NYLORUN_ADMIN_LISTEN_HOST or NYLORUN_ADMIN_ALLOWED_HOSTS");
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
const host = rawHost ?? DEFAULT_CONTAINER_LISTEN_HOST;
|
|
220
|
+
if (/\s|\//.test(host))
|
|
221
|
+
throw new StackConfigError(`NYLORUN_ADMIN_LISTEN_HOST is not an address: ${host}`);
|
|
222
|
+
const port = parsePort("NYLORUN_ADMIN_LISTEN_PORT", rawPort);
|
|
223
|
+
const explicit = rawAllowed === undefined
|
|
224
|
+
? []
|
|
225
|
+
: rawAllowed
|
|
226
|
+
.split(",")
|
|
227
|
+
.map((entry) => entry.trim())
|
|
228
|
+
.filter((entry) => entry !== "")
|
|
229
|
+
.map((entry) => normalizeAllowedHost("NYLORUN_ADMIN_ALLOWED_HOSTS", entry));
|
|
230
|
+
if (explicit.length === 0 && !isLoopbackAddress(host))
|
|
231
|
+
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>`);
|
|
232
|
+
return { host, port, allowedHosts: [...new Set([...explicit, ...loopbackForms(port)])] };
|
|
233
|
+
}
|
|
127
234
|
const IDENTITY_KEY = /^publickeyv1_[1-9A-HJ-NP-Za-km-z]{32,64}$/;
|
|
128
235
|
function parseIdentityKeys(env) {
|
|
129
236
|
const raw = read(env, "NYLORUN_RESTATE_IDENTITY_KEY");
|
|
@@ -140,8 +247,23 @@ function parseIdentityKeys(env) {
|
|
|
140
247
|
}
|
|
141
248
|
/** Parse the stack configuration; throws `StackConfigError` naming the variable. */
|
|
142
249
|
export function parseStackConfig(env, argv) {
|
|
143
|
-
const
|
|
144
|
-
|
|
250
|
+
const { services, deprecatedRole } = parseServices(argv);
|
|
251
|
+
// The image sets NYLORUN_LISTEN_*: only the API's processes read them.
|
|
252
|
+
const servesApi = services.has("core") || services.has("loop");
|
|
253
|
+
const listen = servesApi ? parseListen(env) : undefined;
|
|
254
|
+
const operator = servesApi ? parseAdminListen(env) : undefined;
|
|
255
|
+
const gates = services.has("gates") || services.has("keys") ? parseGates(env) : undefined;
|
|
256
|
+
// The gates service's clients: the loop's model and tool calls, and core's endpoint pings.
|
|
257
|
+
const modelGate = servesApi ? parseModelGate(env) : undefined;
|
|
258
|
+
// The keys service is the gateway's unless NYLORUN_KEYS_URL names another listener.
|
|
259
|
+
const keys = servesApi ? (parseKeysEndpoint(env) ?? modelGate) : undefined;
|
|
260
|
+
if (operator && listen && operator.port === listen.port)
|
|
261
|
+
throw new StackConfigError("NYLORUN_ADMIN_LISTEN_PORT must differ from NYLORUN_LISTEN_PORT");
|
|
262
|
+
if (services.has("loop") && listen && !modelGate)
|
|
263
|
+
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)");
|
|
264
|
+
const rawPacking = read(env, "NYLORUN_PACKING");
|
|
265
|
+
if (rawPacking !== undefined && rawPacking !== "combined" && rawPacking !== "split")
|
|
266
|
+
throw new StackConfigError(`NYLORUN_PACKING must be combined or split, not ${rawPacking}`);
|
|
145
267
|
const http = ["http:", "https:"];
|
|
146
268
|
const endpoints = {};
|
|
147
269
|
const databaseUrl = parseUrl(env, "NYLORUN_DATABASE_URL", [
|
|
@@ -177,11 +299,118 @@ export function parseStackConfig(env, argv) {
|
|
|
177
299
|
if (restateIdentityKeys)
|
|
178
300
|
endpoints.restateIdentityKeys = restateIdentityKeys;
|
|
179
301
|
const publicUrl = parseUrl(env, "NYLORUN_PUBLIC_URL", http)?.replace(/\/+$/, "");
|
|
302
|
+
const rawBrowser = read(env, "NYLORUN_BROWSER_ACCESS");
|
|
303
|
+
if (rawBrowser !== undefined && rawBrowser !== "on" && rawBrowser !== "off")
|
|
304
|
+
throw new StackConfigError(`NYLORUN_BROWSER_ACCESS must be on or off, not ${rawBrowser}`);
|
|
305
|
+
const delivery = parseDelivery(env);
|
|
306
|
+
const tenant = servesApi ? parseTenant(env) : undefined;
|
|
180
307
|
return {
|
|
181
|
-
|
|
308
|
+
services,
|
|
309
|
+
...(deprecatedRole ? { deprecatedRole } : {}),
|
|
310
|
+
...(gates ? { gates } : {}),
|
|
311
|
+
...(modelGate ? { modelGate } : {}),
|
|
312
|
+
...(keys ? { keys } : {}),
|
|
313
|
+
...(rawPacking ? { packing: rawPacking } : {}),
|
|
182
314
|
...(listen ? { listen } : {}),
|
|
183
315
|
endpoints,
|
|
184
316
|
...(publicUrl ? { publicUrl } : {}),
|
|
317
|
+
...(rawBrowser === undefined ? {} : { browserAccess: rawBrowser === "on" }),
|
|
318
|
+
...(operator ? { operator } : {}),
|
|
319
|
+
...(delivery ? { delivery } : {}),
|
|
320
|
+
...(tenant ? { tenant } : {}),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function parseTenant(env) {
|
|
324
|
+
const id = read(env, "NYLORUN_TENANT_ID");
|
|
325
|
+
if (id !== undefined && !isTenantId(id))
|
|
326
|
+
throw new StackConfigError(`NYLORUN_TENANT_ID must be a Tenant id (tn_ and 26 Crockford characters), not ${id}`);
|
|
327
|
+
const raw = read(env, "NYLORUN_DERIVED_PRINCIPALS");
|
|
328
|
+
const derivedPrincipals = raw === undefined ? ["project"] : raw.split(",").map((entry) => entry.trim());
|
|
329
|
+
for (const principal of derivedPrincipals)
|
|
330
|
+
if (!DERIVED_PRINCIPAL_ID_PATTERN.test(principal) || principal === "studio")
|
|
331
|
+
throw new StackConfigError(`NYLORUN_DERIVED_PRINCIPALS has '${principal}': each entry must match ${DERIVED_PRINCIPAL_ID_PATTERN} and not be studio`);
|
|
332
|
+
return {
|
|
333
|
+
...(id ? { id } : {}),
|
|
334
|
+
name: read(env, "NYLORUN_TENANT_NAME") ?? "default",
|
|
335
|
+
derivedPrincipals: [...new Set(derivedPrincipals)],
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
const GATES_TOKEN = /^[0-9a-f]{64,}$/i;
|
|
339
|
+
/** `StackConfig.gates` from `NYLORUN_GATES_*`. */
|
|
340
|
+
function parseGates(env) {
|
|
341
|
+
const host = read(env, "NYLORUN_GATES_LISTEN_HOST") ?? DEFAULT_CONTAINER_LISTEN_HOST;
|
|
342
|
+
if (/\s|\//.test(host))
|
|
343
|
+
throw new StackConfigError(`NYLORUN_GATES_LISTEN_HOST is not an address: ${host}`);
|
|
344
|
+
const rawPort = read(env, "NYLORUN_GATES_LISTEN_PORT");
|
|
345
|
+
const port = rawPort === undefined
|
|
346
|
+
? DEFAULT_GATES_LISTEN_PORT
|
|
347
|
+
: parsePort("NYLORUN_GATES_LISTEN_PORT", rawPort);
|
|
348
|
+
const rawAllowed = read(env, "NYLORUN_GATES_ALLOWED_HOSTS");
|
|
349
|
+
const explicit = rawAllowed === undefined
|
|
350
|
+
? []
|
|
351
|
+
: rawAllowed
|
|
352
|
+
.split(",")
|
|
353
|
+
.map((entry) => entry.trim())
|
|
354
|
+
.filter((entry) => entry !== "")
|
|
355
|
+
.map((entry) => normalizeAllowedHost("NYLORUN_GATES_ALLOWED_HOSTS", entry));
|
|
356
|
+
if (explicit.length === 0 && !isLoopbackAddress(host))
|
|
357
|
+
throw new StackConfigError(`NYLORUN_GATES_ALLOWED_HOSTS is required when NYLORUN_GATES_LISTEN_HOST is ${host}: list the Host headers the loop sends, e.g. gateway:${port}`);
|
|
358
|
+
const token = read(env, "NYLORUN_GATES_TOKEN");
|
|
359
|
+
if (token === undefined)
|
|
360
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN is required for --service gates: the token the loop presents (`nylorun start` sets it)");
|
|
361
|
+
if (!GATES_TOKEN.test(token))
|
|
362
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN must be at least 32 bytes as hex");
|
|
363
|
+
return {
|
|
364
|
+
listen: { host, port, allowedHosts: [...new Set([...explicit, ...loopbackForms(port)])] },
|
|
365
|
+
token,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/** `StackConfig.modelGate` from `NYLORUN_GATES_URL` and `NYLORUN_GATES_TOKEN`. */
|
|
369
|
+
function parseModelGate(env) {
|
|
370
|
+
const url = parseUrl(env, "NYLORUN_GATES_URL", ["http:", "https:"]);
|
|
371
|
+
const token = read(env, "NYLORUN_GATES_TOKEN");
|
|
372
|
+
if (url === undefined) {
|
|
373
|
+
if (token !== undefined)
|
|
374
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN is set without NYLORUN_GATES_URL: set the URL of the gates service, e.g. http://gateway:4100");
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
if (token === undefined)
|
|
378
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN is required with NYLORUN_GATES_URL: the gates service refuses calls without it");
|
|
379
|
+
if (!GATES_TOKEN.test(token))
|
|
380
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN must be at least 32 bytes as hex");
|
|
381
|
+
return { url: url.replace(/\/+$/, ""), token };
|
|
382
|
+
}
|
|
383
|
+
/** `StackConfig.keys` from `NYLORUN_KEYS_URL` and `NYLORUN_GATES_TOKEN`. */
|
|
384
|
+
function parseKeysEndpoint(env) {
|
|
385
|
+
const url = parseUrl(env, "NYLORUN_KEYS_URL", ["http:", "https:"]);
|
|
386
|
+
if (url === undefined)
|
|
387
|
+
return undefined;
|
|
388
|
+
const token = read(env, "NYLORUN_GATES_TOKEN");
|
|
389
|
+
if (token === undefined)
|
|
390
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN is required with NYLORUN_KEYS_URL: the keys service refuses calls without it");
|
|
391
|
+
if (!GATES_TOKEN.test(token))
|
|
392
|
+
throw new StackConfigError("NYLORUN_GATES_TOKEN must be at least 32 bytes as hex");
|
|
393
|
+
return { url: url.replace(/\/+$/, ""), token };
|
|
394
|
+
}
|
|
395
|
+
/** `StackConfig.delivery` from `NYLORUN_ENDPOINT_*`, or `undefined` when none is set. */
|
|
396
|
+
function parseDelivery(env) {
|
|
397
|
+
const loopback = read(env, "NYLORUN_ENDPOINT_LOOPBACK");
|
|
398
|
+
if (loopback !== undefined && loopback !== "docker-host")
|
|
399
|
+
throw new StackConfigError(`NYLORUN_ENDPOINT_LOOPBACK must be docker-host, not ${loopback}`);
|
|
400
|
+
const choice = (name) => {
|
|
401
|
+
const value = read(env, name);
|
|
402
|
+
if (value !== undefined && value !== "allow" && value !== "refuse")
|
|
403
|
+
throw new StackConfigError(`${name} must be allow or refuse, not ${value}`);
|
|
404
|
+
return value;
|
|
405
|
+
};
|
|
406
|
+
const privateAddresses = choice("NYLORUN_ENDPOINT_PRIVATE");
|
|
407
|
+
const http = choice("NYLORUN_ENDPOINT_HTTP");
|
|
408
|
+
if (!loopback && !privateAddresses && !http)
|
|
409
|
+
return undefined;
|
|
410
|
+
return {
|
|
411
|
+
...(loopback ? { loopback } : {}),
|
|
412
|
+
...(privateAddresses ? { privateAddresses } : {}),
|
|
413
|
+
...(http ? { allowHttp: http === "allow" } : {}),
|
|
185
414
|
};
|
|
186
415
|
}
|
|
187
416
|
/** Endpoint summary for logs: which endpoints are set, never their values. */
|
package/dist/infra/database.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Builds the Host's Postgres connection pool from the stack configuration
|
|
3
|
-
* (architecture §12.2, §14.5)
|
|
4
|
-
*
|
|
3
|
+
* (architecture §12.2, §14.5): the pool on the Tenant's database, for its bootstrap and its
|
|
4
|
+
* Session Store; the Host ends it at shutdown. The driver stays behind
|
|
5
5
|
* `store/postgres/connect.ts`.
|
|
6
6
|
*/
|
|
7
7
|
import type { StackConfig } from "../host/stack-config.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DurableExecution } from "../execution/types.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type RuntimeServices, type StackConfig } from "../host/stack-config.js";
|
|
3
3
|
/** Where a Worker serves the Restate endpoint (architecture §14.3). */
|
|
4
4
|
export declare const WORKER_LISTEN: {
|
|
5
5
|
readonly host: "0.0.0.0";
|
|
@@ -23,14 +23,14 @@ export interface CreateExecutionOptions {
|
|
|
23
23
|
export declare function executionKind(config: Pick<StackConfig, "endpoints">): "restate" | "memory";
|
|
24
24
|
/**
|
|
25
25
|
* Throws naming the missing variable when the Restate endpoints are
|
|
26
|
-
* incomplete for `
|
|
27
|
-
* needs no `NYLORUN_WORKER_URL`.
|
|
26
|
+
* incomplete for `services`. Without the loop service a process never serves
|
|
27
|
+
* the Worker endpoint, so it needs no `NYLORUN_WORKER_URL`.
|
|
28
28
|
*/
|
|
29
29
|
export declare function validateExecutionConfig(config: Pick<StackConfig, "endpoints"> & {
|
|
30
|
-
|
|
30
|
+
services?: RuntimeServices;
|
|
31
31
|
}): void;
|
|
32
32
|
export declare function createExecution(config: Pick<StackConfig, "endpoints"> & {
|
|
33
|
-
|
|
33
|
+
services?: RuntimeServices;
|
|
34
34
|
}, options?: CreateExecutionOptions): DurableExecution;
|
|
35
35
|
/**
|
|
36
36
|
* Readiness probe: resolves when Restate's admin API and ingress answer
|
package/dist/infra/execution.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* With `NYLORUN_RESTATE_INGRESS_URL` and `NYLORUN_RESTATE_ADMIN_URL` the Host
|
|
6
6
|
* uses Restate:
|
|
7
7
|
*
|
|
8
|
-
* - wakes, timers and sweep arming go through the ingress, so every
|
|
8
|
+
* - wakes, timers and sweep arming go through the ingress, so every process can
|
|
9
9
|
* call them;
|
|
10
|
-
* - a Worker (
|
|
10
|
+
* - a Worker (a process running the loop service) serves the Restate endpoint on
|
|
11
11
|
* `0.0.0.0:9080` and registers `NYLORUN_WORKER_URL` with the admin API when
|
|
12
12
|
* `infra/workers.ts` starts it;
|
|
13
13
|
* - with `NYLORUN_RESTATE_IDENTITY_KEY` the endpoint accepts only requests
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { createRestateExecution } from "../adapters/execution/restate.js";
|
|
20
20
|
import { MemoryExecution } from "../execution/memory.js";
|
|
21
|
+
import { DEFAULT_SERVICES, describeServices, } from "../host/stack-config.js";
|
|
21
22
|
/** Where a Worker serves the Restate endpoint (architecture §14.3). */
|
|
22
23
|
export const WORKER_LISTEN = { host: "0.0.0.0", port: 9080 };
|
|
23
24
|
/** Which implementation `createExecution` chooses for a configuration. */
|
|
@@ -27,8 +28,8 @@ export function executionKind(config) {
|
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
30
|
* Throws naming the missing variable when the Restate endpoints are
|
|
30
|
-
* incomplete for `
|
|
31
|
-
* needs no `NYLORUN_WORKER_URL`.
|
|
31
|
+
* incomplete for `services`. Without the loop service a process never serves
|
|
32
|
+
* the Worker endpoint, so it needs no `NYLORUN_WORKER_URL`.
|
|
32
33
|
*/
|
|
33
34
|
export function validateExecutionConfig(config) {
|
|
34
35
|
const { restateIngressUrl, restateAdminUrl, workerUrl, restateIdentityKeys } = config.endpoints;
|
|
@@ -41,8 +42,9 @@ export function validateExecutionConfig(config) {
|
|
|
41
42
|
throw new Error("NYLORUN_RESTATE_INGRESS_URL is required with NYLORUN_RESTATE_ADMIN_URL");
|
|
42
43
|
if (!restateAdminUrl)
|
|
43
44
|
throw new Error("NYLORUN_RESTATE_ADMIN_URL is required with NYLORUN_RESTATE_INGRESS_URL");
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const services = config.services ?? DEFAULT_SERVICES;
|
|
46
|
+
if (services.has("loop") && !workerUrl)
|
|
47
|
+
throw new Error(`NYLORUN_WORKER_URL is required for --service ${describeServices(services)}: the Worker endpoint URL Restate calls`);
|
|
46
48
|
}
|
|
47
49
|
export function createExecution(config, options = {}) {
|
|
48
50
|
validateExecutionConfig(config);
|
package/dist/infra/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export interface Infra {
|
|
|
35
35
|
* Builds the configured clients. Throws naming the variable when endpoints are
|
|
36
36
|
* incomplete (e.g. a Restate ingress without an admin URL).
|
|
37
37
|
*/
|
|
38
|
-
export declare function createInfra(config: Pick<StackConfig, "
|
|
38
|
+
export declare function createInfra(config: Pick<StackConfig, "services" | "endpoints">, options?: {
|
|
39
39
|
logger?: Logger;
|
|
40
40
|
execution?: CreateExecutionOptions;
|
|
41
41
|
streams?: CreateStreamsOptions;
|
package/dist/infra/workers.d.ts
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Worker lifecycle by
|
|
2
|
+
* Worker lifecycle by service (architecture §6, blueprint §19).
|
|
3
3
|
*
|
|
4
|
-
* |
|
|
4
|
+
* | Services | Serves | `execution.start` |
|
|
5
5
|
* | --- | --- | --- |
|
|
6
|
-
* | `
|
|
7
|
-
* | `
|
|
8
|
-
* | `
|
|
6
|
+
* | `core` | Tenant API, Admin API, SSE | **never**: wakes, timers and sweep arming go through Restate's ingress, which needs no Worker endpoint |
|
|
7
|
+
* | `loop` | the Restate endpoint (`NylorunSession.advance`, `NylorunTenant.sweep`, timers) | yes |
|
|
8
|
+
* | `core,loop` | both (the local stack's `runtime` container) | yes |
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* A core-only process that called `start` would register its own endpoint with
|
|
11
11
|
* Restate, and Restate would send new invocations to it (it routes to the
|
|
12
12
|
* latest registered deployment), so the rule is enforced here rather than left
|
|
13
|
-
* to callers: `startWorker`
|
|
14
|
-
* started anything.
|
|
13
|
+
* to callers: `startWorker` without the loop service returns a handle that
|
|
14
|
+
* never started anything.
|
|
15
15
|
*/
|
|
16
16
|
import type { DurableExecution, WorkerHandlers } from "../execution/types.js";
|
|
17
|
-
import type {
|
|
17
|
+
import type { RuntimeServices } from "../host/stack-config.js";
|
|
18
18
|
export interface WorkerHandle {
|
|
19
|
-
readonly
|
|
19
|
+
readonly services: RuntimeServices;
|
|
20
20
|
/** True when this process serves the Worker endpoint. */
|
|
21
21
|
readonly serving: boolean;
|
|
22
22
|
}
|
|
23
|
-
/** Whether a process
|
|
24
|
-
export declare function servesWorker(
|
|
23
|
+
/** Whether a process running `services` serves the Worker endpoint: when it runs loop. */
|
|
24
|
+
export declare function servesWorker(services: RuntimeServices): boolean;
|
|
25
25
|
/**
|
|
26
|
-
* Starts delivering Restate invocations to `handlers` when
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* Starts delivering Restate invocations to `handlers` when the process runs
|
|
27
|
+
* loop (serving and registering the Worker endpoint, for Restate). Without
|
|
28
|
+
* loop it does nothing. Rejects if the endpoint cannot listen or register.
|
|
29
29
|
*/
|
|
30
30
|
export declare function startWorker(input: {
|
|
31
|
-
|
|
31
|
+
services: RuntimeServices;
|
|
32
32
|
execution: DurableExecution;
|
|
33
33
|
handlers: WorkerHandlers;
|
|
34
34
|
}): Promise<WorkerHandle>;
|
|
35
35
|
/**
|
|
36
36
|
* Stops a Worker started by `startWorker`: aborts running advances' signals
|
|
37
|
-
* and waits for them. Idempotent; a no-op
|
|
37
|
+
* and waits for them. Idempotent; a no-op without the loop service.
|
|
38
38
|
*/
|
|
39
39
|
export declare function stopWorker(handle: WorkerHandle): Promise<void>;
|
package/dist/infra/workers.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/** Whether a process
|
|
2
|
-
export function servesWorker(
|
|
3
|
-
return
|
|
1
|
+
/** Whether a process running `services` serves the Worker endpoint: when it runs loop. */
|
|
2
|
+
export function servesWorker(services) {
|
|
3
|
+
return services.has("loop");
|
|
4
4
|
}
|
|
5
5
|
/** What `stopWorker` needs for a handle that serves the endpoint. */
|
|
6
6
|
const started = new WeakMap();
|
|
7
7
|
/**
|
|
8
|
-
* Starts delivering Restate invocations to `handlers` when
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Starts delivering Restate invocations to `handlers` when the process runs
|
|
9
|
+
* loop (serving and registering the Worker endpoint, for Restate). Without
|
|
10
|
+
* loop it does nothing. Rejects if the endpoint cannot listen or register.
|
|
11
11
|
*/
|
|
12
12
|
export async function startWorker(input) {
|
|
13
|
-
const {
|
|
14
|
-
const handle = Object.freeze({
|
|
13
|
+
const { services, execution, handlers } = input;
|
|
14
|
+
const handle = Object.freeze({ services, serving: servesWorker(services) });
|
|
15
15
|
if (!handle.serving)
|
|
16
16
|
return handle;
|
|
17
17
|
await execution.start(handlers);
|
|
@@ -20,7 +20,7 @@ export async function startWorker(input) {
|
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Stops a Worker started by `startWorker`: aborts running advances' signals
|
|
23
|
-
* and waits for them. Idempotent; a no-op
|
|
23
|
+
* and waits for them. Idempotent; a no-op without the loop service.
|
|
24
24
|
*/
|
|
25
25
|
export async function stopWorker(handle) {
|
|
26
26
|
const worker = started.get(handle);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Keys } from "./keys.js";
|
|
2
|
+
export interface HttpKeysOptions {
|
|
3
|
+
/** The gateway, e.g. `http://gateway:4100` (`NYLORUN_KEYS_URL`). */
|
|
4
|
+
readonly url: string;
|
|
5
|
+
/** `NYLORUN_GATES_TOKEN`. */
|
|
6
|
+
readonly token: string;
|
|
7
|
+
readonly timeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function httpKeys(options: HttpKeysOptions): Keys;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime container's client of the `keys` service in the gateway (F4.2, `contract.ts`).
|
|
3
|
+
* Over `node:http` rather than `fetch`, which tests and embedders may replace. A refusal comes
|
|
4
|
+
* back as the error the operation threw in the gateway (`VaultError` or `HttpError`, same status,
|
|
5
|
+
* code and details); a hop failure is a 503, so the API route answers that the gateway is down.
|
|
6
|
+
*/
|
|
7
|
+
import { request as httpRequest } from "node:http";
|
|
8
|
+
import { request as httpsRequest } from "node:https";
|
|
9
|
+
import { HttpError } from "../tenant/http.js";
|
|
10
|
+
import { VaultError } from "../vault/error.js";
|
|
11
|
+
import { KEYS_PATH } from "./contract.js";
|
|
12
|
+
import { KEYS_OPERATIONS } from "./keys.js";
|
|
13
|
+
/** Signing and vault writes are short; anything slower is a gateway in trouble. */
|
|
14
|
+
const KEYS_TIMEOUT_MS = 30_000;
|
|
15
|
+
export function httpKeys(options) {
|
|
16
|
+
const where = new URL(options.url).origin;
|
|
17
|
+
const timeoutMs = options.timeoutMs ?? KEYS_TIMEOUT_MS;
|
|
18
|
+
function call(operation, args) {
|
|
19
|
+
const url = new URL(`${KEYS_PATH}/${operation}`, options.url);
|
|
20
|
+
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
21
|
+
const payload = Buffer.from(JSON.stringify({ args }), "utf8");
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const unavailable = (detail) => reject(new HttpError(503, `The keys service at ${where} is unavailable (${detail}); check the gateway container`, {
|
|
24
|
+
code: "keys_unavailable",
|
|
25
|
+
}));
|
|
26
|
+
const outgoing = send(url, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
authorization: `Bearer ${options.token}`,
|
|
30
|
+
"content-type": "application/json",
|
|
31
|
+
"content-length": payload.byteLength,
|
|
32
|
+
},
|
|
33
|
+
timeout: timeoutMs,
|
|
34
|
+
}, (response) => {
|
|
35
|
+
const chunks = [];
|
|
36
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
37
|
+
response.on("error", (error) => unavailable(error.message));
|
|
38
|
+
response.on("end", () => {
|
|
39
|
+
let answer;
|
|
40
|
+
try {
|
|
41
|
+
answer = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
answer = undefined;
|
|
45
|
+
}
|
|
46
|
+
if (response.statusCode !== 200 || !answer)
|
|
47
|
+
return unavailable(response.statusCode === 401
|
|
48
|
+
? "it refused this Runtime's token: the runtime and gateway containers must share NYLORUN_GATES_TOKEN"
|
|
49
|
+
: `it answered ${response.statusCode}`);
|
|
50
|
+
if ("result" in answer)
|
|
51
|
+
return resolve(answer.result);
|
|
52
|
+
const error = answer.error;
|
|
53
|
+
reject(error.kind === "vault"
|
|
54
|
+
? new VaultError(error.status, error.message)
|
|
55
|
+
: new HttpError(error.status, error.message, {
|
|
56
|
+
...(error.code === undefined ? {} : { code: error.code }),
|
|
57
|
+
...(error.details === undefined ? {} : { details: error.details }),
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
outgoing.on("timeout", () => outgoing.destroy(new Error(`no answer in ${timeoutMs / 1000} s`)));
|
|
62
|
+
outgoing.on("error", (error) => unavailable(error.message));
|
|
63
|
+
outgoing.end(payload);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const keys = {};
|
|
67
|
+
for (const operation of KEYS_OPERATIONS)
|
|
68
|
+
keys[operation] = (...args) => call(operation, args);
|
|
69
|
+
return keys;
|
|
70
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `keys` service's wire format (F4.2): `POST /nylorun/v1/keys/{operation}` with
|
|
3
|
+
* `{args}`, the operation's arguments in order, on the gateway's listener with the stack's
|
|
4
|
+
* gates token. Internal: only core calls it, so it is not in the published OpenAPI documents.
|
|
5
|
+
*
|
|
6
|
+
* `200 {result}` when the operation succeeded, `200 {error}` when it refused: a vault error or
|
|
7
|
+
* a Tenant API error with its status, code and details, so the route that called it answers
|
|
8
|
+
* exactly what it answered when the operation ran in its own process.
|
|
9
|
+
*/
|
|
10
|
+
export declare const KEYS_PATH = "/nylorun/v1/keys";
|
|
11
|
+
/** Largest request body: a credential with its secret, or a token's claims. */
|
|
12
|
+
export declare const MAX_KEYS_BODY_BYTES: number;
|
|
13
|
+
export interface KeysError {
|
|
14
|
+
readonly kind: "vault" | "http";
|
|
15
|
+
readonly status: number;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
readonly code?: string;
|
|
18
|
+
readonly details?: unknown;
|
|
19
|
+
}
|
|
20
|
+
export type KeysAnswer = {
|
|
21
|
+
readonly result: unknown;
|
|
22
|
+
} | {
|
|
23
|
+
readonly error: KeysError;
|
|
24
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `keys` service's wire format (F4.2): `POST /nylorun/v1/keys/{operation}` with
|
|
3
|
+
* `{args}`, the operation's arguments in order, on the gateway's listener with the stack's
|
|
4
|
+
* gates token. Internal: only core calls it, so it is not in the published OpenAPI documents.
|
|
5
|
+
*
|
|
6
|
+
* `200 {result}` when the operation succeeded, `200 {error}` when it refused: a vault error or
|
|
7
|
+
* a Tenant API error with its status, code and details, so the route that called it answers
|
|
8
|
+
* exactly what it answered when the operation ran in its own process.
|
|
9
|
+
*/
|
|
10
|
+
export const KEYS_PATH = "/nylorun/v1/keys";
|
|
11
|
+
/** Largest request body: a credential with its secret, or a token's claims. */
|
|
12
|
+
export const MAX_KEYS_BODY_BYTES = 1024 * 1024;
|