@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
package/dist/host/config-for.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { tenantPaths } from "../tenant/paths.js";
|
|
2
|
-
import { tenantChildEnvironment } from "./environment.js";
|
|
3
2
|
/**
|
|
4
3
|
* Builds the TenantConfig of the Host's Tenant for `openTenantRuntime`.
|
|
5
4
|
* The sandbox backend here is the Host default; a Tenant's seeded `sandbox.backend`
|
|
6
5
|
* setting overrides it when the Tenant opens (I1, A18).
|
|
7
6
|
*/
|
|
8
7
|
export function configForFactory(options) {
|
|
9
|
-
const { baseline } = options;
|
|
10
8
|
return (id) => {
|
|
11
9
|
const tenant = tenantPaths(options.hostRoot);
|
|
12
10
|
const sandboxBackend = "auto";
|
|
@@ -19,7 +17,6 @@ export function configForFactory(options) {
|
|
|
19
17
|
...(options.delivery ? { delivery: options.delivery } : {}),
|
|
20
18
|
...(options.issuers ? { issuers: options.issuers } : {}),
|
|
21
19
|
...(options.publicUrl ? { publicUrl: options.publicUrl } : {}),
|
|
22
|
-
childEnv: tenantChildEnvironment(baseline, options.hostConfig, tenant),
|
|
23
20
|
logger: {
|
|
24
21
|
info: (message, fields) => options.logger.info(message, { tenantId: id, ...fields }),
|
|
25
22
|
warn: (message, fields) => options.logger.warn(message, { tenantId: id, ...fields }),
|
package/dist/host/config.d.ts
CHANGED
|
@@ -4,11 +4,6 @@ export interface HostConfigFile {
|
|
|
4
4
|
host: string;
|
|
5
5
|
port: number;
|
|
6
6
|
allowNonLoopback?: boolean;
|
|
7
|
-
/**
|
|
8
|
-
* The operator listener's port: the Admin API moves there (on `host`, loopback only), and
|
|
9
|
-
* `port` serves the Tenant API alone. Absent: one port serves both.
|
|
10
|
-
*/
|
|
11
|
-
adminPort?: number;
|
|
12
7
|
proxy?: {
|
|
13
8
|
httpsProxy?: string;
|
|
14
9
|
noProxy?: string;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { Logger, TenantModule } from "../tenant/types.js";
|
|
2
|
-
import type { HostConfigFile
|
|
2
|
+
import type { HostConfigFile } from "./config.js";
|
|
3
3
|
import type { ContainerListen } from "./stack-config.js";
|
|
4
4
|
export interface CreateHostOptions {
|
|
5
5
|
hostRoot: string;
|
|
6
6
|
module: TenantModule;
|
|
7
7
|
config: HostConfigFile;
|
|
8
|
-
credentials: HostCredentialsFile;
|
|
9
8
|
logger: Logger;
|
|
10
9
|
/** Diagnostic package version of `@nylorun/core` for `/health`. */
|
|
11
10
|
coreVersion: string;
|
|
12
|
-
/** Process id reported by `/health
|
|
11
|
+
/** Process id reported by `/health`. Defaults to `process.pid`. */
|
|
13
12
|
pid?: number;
|
|
14
13
|
/**
|
|
15
14
|
* Container mode: bind this address and port instead of host.json's, and
|
|
@@ -17,19 +16,6 @@ export interface CreateHostOptions {
|
|
|
17
16
|
* loopback-only rule). host.json then describes the client-facing address.
|
|
18
17
|
*/
|
|
19
18
|
listen?: ContainerListen;
|
|
20
|
-
/**
|
|
21
|
-
* The client-facing URL `/v1/admin/status` reports as `host.url`
|
|
22
|
-
* (`NYLORUN_PUBLIC_URL`). Defaults to the bound address, which in container
|
|
23
|
-
* mode is `http://0.0.0.0:4000`.
|
|
24
|
-
*/
|
|
25
|
-
publicUrl?: string;
|
|
26
|
-
/**
|
|
27
|
-
* The operator listener: the Admin API (and the Tenant API, never to browsers) on
|
|
28
|
-
* its own address, kept off the network that reaches the Tenant API. When set, the main
|
|
29
|
-
* listener is public and answers admin routes with the opaque 404. Absent: one listener
|
|
30
|
-
* serves everything, as before.
|
|
31
|
-
*/
|
|
32
|
-
operator?: OperatorListen;
|
|
33
19
|
/**
|
|
34
20
|
* Infrastructure readiness (`infra/readiness.ts`). `/ready` adds its checks
|
|
35
21
|
* and answers 503 while it reports not ok. Default: the listener and the
|
|
@@ -41,7 +27,7 @@ export interface CreateHostOptions {
|
|
|
41
27
|
}>;
|
|
42
28
|
/**
|
|
43
29
|
* Shutdown steps around closing the Tenant. `close()` runs them whatever asked for it
|
|
44
|
-
* (SIGTERM in `host/main.ts
|
|
30
|
+
* (SIGTERM in `host/main.ts`): the listener stops, then
|
|
45
31
|
* `beforeTenants` (stop the Worker so no advance starts on the closing Tenant), the Tenant
|
|
46
32
|
* closes, then `afterTenants` (end the infrastructure clients). A failing step is logged
|
|
47
33
|
* and shutdown goes on.
|
|
@@ -51,27 +37,11 @@ export interface CreateHostOptions {
|
|
|
51
37
|
afterTenants?(): Promise<void>;
|
|
52
38
|
};
|
|
53
39
|
}
|
|
54
|
-
/** Where the operator listener binds, and the `Host` values it accepts. */
|
|
55
|
-
export interface OperatorListen {
|
|
56
|
-
host: string;
|
|
57
|
-
port: number;
|
|
58
|
-
/** Exact `Host` values (lowercase `name:port`); absent means the loopback forms of the port. */
|
|
59
|
-
allowedHosts?: readonly string[];
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* What a listener serves. `combined`: everything (one port). `public`: the Tenant API, to
|
|
63
|
-
* servers and to browsers with a trusted issuer's token; admin routes are the opaque 404. `operator`: the Admin API and
|
|
64
|
-
* the Tenant API, never to browsers.
|
|
65
|
-
*/
|
|
66
|
-
export type ListenerRole = "combined" | "public" | "operator";
|
|
67
40
|
export interface HostServer {
|
|
68
41
|
listen(): Promise<void>;
|
|
69
42
|
close(): Promise<void>;
|
|
70
43
|
/** Settles once `close()` has finished, whatever called it. */
|
|
71
44
|
readonly closed: Promise<void>;
|
|
72
45
|
readonly url: string;
|
|
73
|
-
/** Where the Admin API answers: the operator listener, or `url` when there is one listener. */
|
|
74
|
-
readonly adminUrl: string;
|
|
75
46
|
}
|
|
76
|
-
export { adminKeyMatches } from "./http.js";
|
|
77
47
|
export declare function createHost(options: CreateHostOptions): HostServer;
|
package/dist/host/create-host.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { createServer, } from "node:http";
|
|
2
2
|
import { getRequestListener, RequestError } from "@hono/node-server";
|
|
3
|
-
import { HOST_PROTOCOL } from "@nylorun/core/compatibility";
|
|
4
|
-
import { AdminStatusSchema } from "@nylorun/core/contracts";
|
|
5
|
-
import { adminDocument } from "../api/openapi.js";
|
|
6
|
-
import { createAdminApi } from "./admin-api.js";
|
|
7
3
|
import { createHostApp } from "./app.js";
|
|
8
4
|
import { bindListener as bind, EXIT_NON_LOOPBACK, headerValue, HostListenError, isAllowedRequestHost, isLoopbackHost, pathnameIsLogged, redactRoutePath, rejectedResponse, sendRejected, } from "./http.js";
|
|
9
|
-
import { RUNTIME_VERSION } from "../version.js";
|
|
10
|
-
export { adminKeyMatches } from "./http.js";
|
|
11
5
|
export function createHost(options) {
|
|
12
|
-
const {
|
|
6
|
+
const { module, config, logger, coreVersion } = options;
|
|
13
7
|
const pid = options.pid ?? process.pid;
|
|
14
8
|
const containerListen = options.listen;
|
|
15
9
|
const bindHost = containerListen?.host ?? config.host;
|
|
@@ -17,55 +11,20 @@ export function createHost(options) {
|
|
|
17
11
|
let server;
|
|
18
12
|
let url = "";
|
|
19
13
|
let listenPort = bindPort;
|
|
20
|
-
const operator = options.operator;
|
|
21
|
-
let operatorServer;
|
|
22
|
-
let operatorPort = operator?.port ?? 0;
|
|
23
|
-
let adminUrl = "";
|
|
24
|
-
const mainRole = operator ? "public" : "combined";
|
|
25
14
|
let closing = false;
|
|
26
15
|
let closePromise;
|
|
27
|
-
const adminStatusBody = async () => {
|
|
28
|
-
const aggregate = await module.summarize();
|
|
29
|
-
return AdminStatusSchema.parse({
|
|
30
|
-
service: "nylorun-runtime",
|
|
31
|
-
version: RUNTIME_VERSION,
|
|
32
|
-
protocol: {
|
|
33
|
-
min: HOST_PROTOCOL.min,
|
|
34
|
-
max: HOST_PROTOCOL.max,
|
|
35
|
-
features: [...HOST_PROTOCOL.features],
|
|
36
|
-
},
|
|
37
|
-
tenant: module.tenant(),
|
|
38
|
-
aggregate,
|
|
39
|
-
host: {
|
|
40
|
-
hostId: config.hostId,
|
|
41
|
-
url: options.publicUrl ?? url,
|
|
42
|
-
pid,
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
const adminApi = createAdminApi({
|
|
47
|
-
status: adminStatusBody,
|
|
48
|
-
shutdown: () => void close(),
|
|
49
|
-
document: adminDocument,
|
|
50
|
-
operatorKeys: async () => {
|
|
51
|
-
const resolved = await module.resolve();
|
|
52
|
-
return resolved.kind === "open" ? resolved.handle.operatorKeys?.() : undefined;
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
16
|
const app = createHostApp({
|
|
56
17
|
module,
|
|
57
18
|
logger,
|
|
58
19
|
hostId: config.hostId,
|
|
59
|
-
adminKey: credentials.adminKey,
|
|
60
20
|
coreVersion,
|
|
61
21
|
pid,
|
|
62
22
|
...(options.readiness ? { readiness: options.readiness } : {}),
|
|
63
|
-
listening: () => Boolean(server?.listening)
|
|
23
|
+
listening: () => Boolean(server?.listening),
|
|
64
24
|
closing: () => closing,
|
|
65
|
-
admin: async (request, node) => await adminApi.fetch(request, node),
|
|
66
25
|
});
|
|
67
|
-
/** Where
|
|
68
|
-
const pipeline = (
|
|
26
|
+
/** Where requests go once their `Host` header checks out. */
|
|
27
|
+
const pipeline = () => getRequestListener(async (request, node) => alreadySent(await app.fetch(request, node)), {
|
|
69
28
|
// The Runtime runs inside other processes (`startEphemeralRuntime`): leave their
|
|
70
29
|
// `Request` and `Response` alone.
|
|
71
30
|
overrideGlobalObjects: false,
|
|
@@ -79,30 +38,23 @@ export function createHost(options) {
|
|
|
79
38
|
return rejectedResponse(500, "internal_error", "Internal error");
|
|
80
39
|
},
|
|
81
40
|
});
|
|
82
|
-
/** The `Host` values
|
|
83
|
-
const hostAllowed = (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
})
|
|
90
|
-
: isAllowedRequestHost(hostHeader, {
|
|
91
|
-
port: listenPort,
|
|
92
|
-
host: config.host,
|
|
93
|
-
allowNonLoopback: config.allowNonLoopback,
|
|
94
|
-
...(containerListen ? { allowedHosts: containerListen.allowedHosts } : {}),
|
|
95
|
-
});
|
|
41
|
+
/** The `Host` values the listener accepts. */
|
|
42
|
+
const hostAllowed = (hostHeader) => isAllowedRequestHost(hostHeader, {
|
|
43
|
+
port: listenPort,
|
|
44
|
+
host: config.host,
|
|
45
|
+
allowNonLoopback: config.allowNonLoopback,
|
|
46
|
+
...(containerListen ? { allowedHosts: containerListen.allowedHosts } : {}),
|
|
47
|
+
});
|
|
96
48
|
/** Responses not yet finished; shutdown ends the streams among them. */
|
|
97
49
|
const inFlight = new Set();
|
|
98
|
-
const serve = (
|
|
99
|
-
const next = pipeline(
|
|
50
|
+
const serve = () => {
|
|
51
|
+
const next = pipeline();
|
|
100
52
|
return (req, res) => {
|
|
101
53
|
const started = Date.now();
|
|
102
54
|
inFlight.add(res);
|
|
103
55
|
res.once("close", () => inFlight.delete(res));
|
|
104
56
|
// D§11: `Host` first, before anything reads the request.
|
|
105
|
-
if (hostAllowed(
|
|
57
|
+
if (hostAllowed(headerValue(req, "host")))
|
|
106
58
|
return void next(req, res);
|
|
107
59
|
sendRejected(res, 421, "host_rejected", "Host header is not an allowed loopback or configured address");
|
|
108
60
|
if (pathnameIsLogged(req.url))
|
|
@@ -126,39 +78,15 @@ export function createHost(options) {
|
|
|
126
78
|
!isLoopbackHost(config.host)) {
|
|
127
79
|
throw new HostListenError(`Refusing to bind non-loopback host ${config.host} without allowNonLoopback`, EXIT_NON_LOOPBACK);
|
|
128
80
|
}
|
|
129
|
-
|
|
130
|
-
throw new HostListenError(`Refusing to bind the operator listener on non-loopback host ${operator.host}`, EXIT_NON_LOOPBACK);
|
|
131
|
-
server = createServer(serve(mainRole));
|
|
81
|
+
server = createServer(serve());
|
|
132
82
|
await bindListener(server, bindPort, bindHost);
|
|
133
83
|
const address = server.address();
|
|
134
84
|
listenPort =
|
|
135
85
|
typeof address === "object" && address ? address.port : bindPort;
|
|
136
86
|
url = `http://${bindHost}:${listenPort}`;
|
|
137
|
-
|
|
138
|
-
if (operator) {
|
|
139
|
-
operatorServer = createServer(serve("operator"));
|
|
140
|
-
await bindListener(operatorServer, operator.port, operator.host).catch(async (error) => {
|
|
141
|
-
// Leave nothing half-open: the main listener closes too.
|
|
142
|
-
await new Promise((resolve) => server.close(() => resolve()));
|
|
143
|
-
server = undefined;
|
|
144
|
-
throw error;
|
|
145
|
-
});
|
|
146
|
-
const operatorAddress = operatorServer.address();
|
|
147
|
-
operatorPort =
|
|
148
|
-
typeof operatorAddress === "object" && operatorAddress
|
|
149
|
-
? operatorAddress.port
|
|
150
|
-
: operator.port;
|
|
151
|
-
adminUrl = `http://${operator.host}:${operatorPort}`;
|
|
152
|
-
}
|
|
153
|
-
// The Tenant opens once the listener is bound: `/ready` and `/v1/admin/status` answer
|
|
154
|
-
// while it opens, and say why when it cannot.
|
|
87
|
+
// The Tenant opens once the listener is bound: `/ready` answers while it opens.
|
|
155
88
|
await module.start();
|
|
156
|
-
logger.info("host_listening", {
|
|
157
|
-
hostId: config.hostId,
|
|
158
|
-
url,
|
|
159
|
-
...(operator ? { adminUrl } : {}),
|
|
160
|
-
pid,
|
|
161
|
-
});
|
|
89
|
+
logger.info("host_listening", { hostId: config.hostId, url, pid });
|
|
162
90
|
}
|
|
163
91
|
const step = async (name, run) => {
|
|
164
92
|
try {
|
|
@@ -181,7 +109,7 @@ export function createHost(options) {
|
|
|
181
109
|
closePromise = (async () => {
|
|
182
110
|
closing = true;
|
|
183
111
|
logger.info("host_shutdown", { hostId: config.hostId });
|
|
184
|
-
const listeners = [server
|
|
112
|
+
const listeners = server ? [server] : [];
|
|
185
113
|
const stopped = Promise.all(listeners.map((listening) => new Promise((resolve) => {
|
|
186
114
|
listening.close(() => resolve());
|
|
187
115
|
listening.closeIdleConnections();
|
|
@@ -214,7 +142,6 @@ export function createHost(options) {
|
|
|
214
142
|
await stopped;
|
|
215
143
|
}
|
|
216
144
|
server = undefined;
|
|
217
|
-
operatorServer = undefined;
|
|
218
145
|
await step("beforeTenants", options.shutdown?.beforeTenants);
|
|
219
146
|
await step("tenants", () => module.close());
|
|
220
147
|
await step("afterTenants", options.shutdown?.afterTenants);
|
|
@@ -228,9 +155,6 @@ export function createHost(options) {
|
|
|
228
155
|
get url() {
|
|
229
156
|
return url;
|
|
230
157
|
},
|
|
231
|
-
get adminUrl() {
|
|
232
|
-
return adminUrl;
|
|
233
|
-
},
|
|
234
158
|
};
|
|
235
159
|
}
|
|
236
160
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { HostConfigFile } from "./config.js";
|
|
2
2
|
import type { HostPaths } from "../tenant/paths.js";
|
|
3
|
-
import type { TenantPaths } from "../tenant/types.js";
|
|
4
3
|
/**
|
|
5
4
|
* Allowlisted ambient keys for Host and Tenant child processes.
|
|
6
5
|
* `NODE_OPTIONS` is never copied.
|
|
@@ -9,5 +8,3 @@ export declare function baselineEnvironment(env: NodeJS.ProcessEnv | Readonly<Re
|
|
|
9
8
|
export type HostProcessPathInputs = Pick<HostPaths, "root" | "home" | "tmp">;
|
|
10
9
|
/** Environment for the Runtime Host process itself (CLI spawn / `runtime run`). */
|
|
11
10
|
export declare function hostProcessEnvironment(baseline: Readonly<Record<string, string>>, hostConfig: HostConfigFile, paths: HostProcessPathInputs): Record<string, string>;
|
|
12
|
-
/** Environment for Tenant-owned children (MCP stdio, sandboxes). */
|
|
13
|
-
export declare function tenantChildEnvironment(baseline: Readonly<Record<string, string>>, hostConfig: HostConfigFile, tenantPaths: TenantPaths): Record<string, string>;
|
package/dist/host/environment.js
CHANGED
|
@@ -42,12 +42,3 @@ export function hostProcessEnvironment(baseline, hostConfig, paths) {
|
|
|
42
42
|
NYLORUN_HOME: paths.root,
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
/** Environment for Tenant-owned children (MCP stdio, sandboxes). */
|
|
46
|
-
export function tenantChildEnvironment(baseline, hostConfig, tenantPaths) {
|
|
47
|
-
return {
|
|
48
|
-
...baseline,
|
|
49
|
-
...proxyEnv(hostConfig),
|
|
50
|
-
HOME: tenantPaths.home,
|
|
51
|
-
TMPDIR: tenantPaths.tmp,
|
|
52
|
-
};
|
|
53
|
-
}
|
package/dist/host/gates.d.ts
CHANGED
|
@@ -40,7 +40,10 @@ export interface StartGatesOptions {
|
|
|
40
40
|
readonly drainMs?: number;
|
|
41
41
|
/** How long keyed outcomes are kept, and how many (tests). */
|
|
42
42
|
readonly inflight?: InflightCallsOptions;
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* How the gate may call MCP servers and HTTP tools (`NYLORUN_ENDPOINT_*`).
|
|
45
|
+
* Default: no limits.
|
|
46
|
+
*/
|
|
44
47
|
readonly delivery?: OutboundPolicy;
|
|
45
48
|
/** How long an MCP connection may sit unused. Default `GATE_MCP_IDLE_MS`. */
|
|
46
49
|
readonly mcpIdleMs?: number;
|
package/dist/host/gates.js
CHANGED
|
@@ -17,6 +17,7 @@ import { createModelCallHandler } from "../gates/handler.js";
|
|
|
17
17
|
import { createInflightCalls } from "../gates/inflight.js";
|
|
18
18
|
import { createMcpHandler, GATE_MCP_IDLE_MS } from "../gates/mcp-handler.js";
|
|
19
19
|
import { createToolCalls } from "../gates/tool-calls.js";
|
|
20
|
+
import { gateHttpTools } from "../gates/http-tool.js";
|
|
20
21
|
import { existsSync } from "node:fs";
|
|
21
22
|
import { tenantPaths } from "../tenant/paths.js";
|
|
22
23
|
import { staleRun, verifyRunToken } from "../tenant/run-token.js";
|
|
@@ -52,10 +53,10 @@ export async function startGates(options) {
|
|
|
52
53
|
logger,
|
|
53
54
|
idleMs: mcpIdleMs,
|
|
54
55
|
...(options.openMcp ? { open: options.openMcp } : {}),
|
|
56
|
+
...(options.delivery ? { delivery: options.delivery } : {}),
|
|
55
57
|
});
|
|
56
58
|
const toolCalls = createToolCalls({
|
|
57
59
|
vaults,
|
|
58
|
-
mcp,
|
|
59
60
|
logger,
|
|
60
61
|
...(options.inflight ? { inflight: options.inflight } : {}),
|
|
61
62
|
});
|
|
@@ -98,8 +99,8 @@ export async function startGates(options) {
|
|
|
98
99
|
},
|
|
99
100
|
...(options.maxBodyBytes !== undefined ? { maxBodyBytes: options.maxBodyBytes } : {}),
|
|
100
101
|
mcp,
|
|
102
|
+
http: gateHttpTools(vaults, options.delivery ?? {}, logger),
|
|
101
103
|
toolCalls,
|
|
102
|
-
delivery: options.delivery ?? {},
|
|
103
104
|
logger,
|
|
104
105
|
...(options.keys ? { keys: async () => (await vaults.open()).keys() } : {}),
|
|
105
106
|
});
|
package/dist/host/http.d.ts
CHANGED
|
@@ -48,11 +48,10 @@ export declare function pathnameIsLogged(rawUrl: string | undefined): boolean;
|
|
|
48
48
|
* (a credential) after `/v1/artifact-links/`.
|
|
49
49
|
*/
|
|
50
50
|
export declare function redactRoutePath(pathname: string): string;
|
|
51
|
-
/** Constant-time comparison of SHA-256 digests of
|
|
51
|
+
/** Constant-time comparison of SHA-256 digests of a presented and a stored secret. */
|
|
52
52
|
export declare function adminKeyMatches(presented: string | undefined, adminKey: string): boolean;
|
|
53
53
|
/** A request header's first value. */
|
|
54
54
|
export declare function headerValue(incoming: IncomingMessage, name: string): string | undefined;
|
|
55
|
-
export declare function readBearer(authorization: string | undefined): string | undefined;
|
|
56
55
|
/** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
|
|
57
56
|
export declare function readJsonBody(request: Request, limit?: number): Promise<unknown>;
|
|
58
57
|
export declare function isLoopbackHost(host: string): boolean;
|
package/dist/host/http.js
CHANGED
|
@@ -149,7 +149,7 @@ export function redactRoutePath(pathname) {
|
|
|
149
149
|
return "/v1/artifact-links/:token";
|
|
150
150
|
return pathname;
|
|
151
151
|
}
|
|
152
|
-
/** Constant-time comparison of SHA-256 digests of
|
|
152
|
+
/** Constant-time comparison of SHA-256 digests of a presented and a stored secret. */
|
|
153
153
|
export function adminKeyMatches(presented, adminKey) {
|
|
154
154
|
if (presented === undefined)
|
|
155
155
|
return false;
|
|
@@ -164,12 +164,6 @@ export function headerValue(incoming, name) {
|
|
|
164
164
|
return raw[0];
|
|
165
165
|
return raw;
|
|
166
166
|
}
|
|
167
|
-
export function readBearer(authorization) {
|
|
168
|
-
if (!authorization)
|
|
169
|
-
return undefined;
|
|
170
|
-
const match = /^Bearer\s+(\S+)/i.exec(authorization.trim());
|
|
171
|
-
return match?.[1];
|
|
172
|
-
}
|
|
173
167
|
/** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
|
|
174
168
|
export async function readJsonBody(request, limit = 1024 * 1024) {
|
|
175
169
|
if (request.body === null)
|
package/dist/host/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { HostConfigFile, HostCredentialsFile, } from "./config.js";
|
|
2
|
-
export { baselineEnvironment, hostProcessEnvironment,
|
|
3
|
-
export { createHost,
|
|
2
|
+
export { baselineEnvironment, hostProcessEnvironment, } from "./environment.js";
|
|
3
|
+
export { createHost, type CreateHostOptions, type HostServer } from "./create-host.js";
|
|
4
4
|
export { createHostLogger } from "./logger.js";
|
|
5
5
|
export { configForFactory } from "./config-for.js";
|
|
6
6
|
export { createHostExecution, type CreateHostExecutionOptions, type HostExecution, } from "./execution.js";
|
package/dist/host/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { baselineEnvironment, hostProcessEnvironment,
|
|
2
|
-
export { createHost
|
|
1
|
+
export { baselineEnvironment, hostProcessEnvironment, } from "./environment.js";
|
|
2
|
+
export { createHost } from "./create-host.js";
|
|
3
3
|
export { createHostLogger } from "./logger.js";
|
|
4
4
|
export { configForFactory } from "./config-for.js";
|
|
5
5
|
export { createHostExecution, } from "./execution.js";
|
package/dist/host/main.js
CHANGED
|
@@ -83,6 +83,20 @@ function loadIssuers(path) {
|
|
|
83
83
|
}
|
|
84
84
|
return createTrustedIssuers(parseIdentityFile(text, path));
|
|
85
85
|
}
|
|
86
|
+
/** The bootstrap management key: 64 hex characters, surrounding whitespace ignored. */
|
|
87
|
+
function loadManagementKey(path) {
|
|
88
|
+
let text;
|
|
89
|
+
try {
|
|
90
|
+
text = readFileSync(path, "utf8");
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new Error(`NYLORUN_MANAGEMENT_KEY_FILE names ${path}, which cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
+
}
|
|
95
|
+
const key = text.trim();
|
|
96
|
+
if (!/^[0-9a-f]{64}$/.test(key))
|
|
97
|
+
throw new Error(`NYLORUN_MANAGEMENT_KEY_FILE (${path}) must hold one key of 64 lowercase hex characters, such as \`openssl rand -hex 32\``);
|
|
98
|
+
return key;
|
|
99
|
+
}
|
|
86
100
|
function resolveHostRoot() {
|
|
87
101
|
const fromEnv = process.env.NYLORUN_HOME;
|
|
88
102
|
const root = fromEnv && fromEnv.length > 0 ? fromEnv : resolve(homedir(), ".nylorun");
|
|
@@ -122,11 +136,6 @@ async function ensureBucket(store, bucket, logger) {
|
|
|
122
136
|
}
|
|
123
137
|
}
|
|
124
138
|
}
|
|
125
|
-
/** The slot's lag in WAL bytes, when the source can tell. */
|
|
126
|
-
async function lagOf(source) {
|
|
127
|
-
const lagBytes = await source.lag?.().catch(() => undefined);
|
|
128
|
-
return lagBytes === undefined ? {} : { lagBytes };
|
|
129
|
-
}
|
|
130
139
|
/**
|
|
131
140
|
* The gateway's services: the Model Gate's listener (gates, keys) and egress-gate's (egress), over
|
|
132
141
|
* the Postgres pool and the Host's tenant directory. Writes nothing to the Host root (the local
|
|
@@ -242,14 +251,11 @@ export async function main() {
|
|
|
242
251
|
const database = infra.database;
|
|
243
252
|
if (!database)
|
|
244
253
|
throw new Error("NYLORUN_DATABASE_URL did not yield a Postgres pool");
|
|
245
|
-
const baseline = baselineEnvironment(process.env);
|
|
246
254
|
// A Tenant that should answer with the fixture model carries the Tenant
|
|
247
255
|
// setting (`tenant/model-setting.ts`); the Host has no fixture mode.
|
|
248
256
|
const configFor = configForFactory({
|
|
249
257
|
hostRoot,
|
|
250
|
-
hostConfig: config,
|
|
251
258
|
logger,
|
|
252
|
-
baseline,
|
|
253
259
|
...(stack.delivery ? { delivery: stack.delivery } : {}),
|
|
254
260
|
...(issuers ? { issuers } : {}),
|
|
255
261
|
...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
|
|
@@ -270,7 +276,6 @@ export async function main() {
|
|
|
270
276
|
// running core runs one, once the Tenant is open (the relay reads its id); the replication
|
|
271
277
|
// slot lets exactly one be active. Without S2, the Tenant relays its own commits.
|
|
272
278
|
let relay;
|
|
273
|
-
let relayLag;
|
|
274
279
|
if (streams && stack.services.has("core"))
|
|
275
280
|
await assertLogicalReplication(database);
|
|
276
281
|
const startRelay = (tenantId) => {
|
|
@@ -287,14 +292,12 @@ export async function main() {
|
|
|
287
292
|
streams,
|
|
288
293
|
log: (message, fields) => logger.info(message, fields),
|
|
289
294
|
});
|
|
290
|
-
const status = relay.status;
|
|
291
|
-
relayLag = async () => ({ ...status(), ...(await lagOf(source)) });
|
|
292
295
|
relay.start();
|
|
293
296
|
};
|
|
294
|
-
// With the gates service the Tenant's vault-backed model calls, remote MCP calls and
|
|
295
|
-
//
|
|
296
|
-
// Their credentials (F5): each advance's run token for its session's model and
|
|
297
|
-
// core's credential (NYLORUN_GATES_TOKEN) for
|
|
297
|
+
// With the gates service the Tenant's vault-backed model calls, remote MCP calls and HTTP
|
|
298
|
+
// tool calls cross it, and this process never reads a model or MCP credential.
|
|
299
|
+
// Their credentials (F5): each advance's run token for its session's model and tool calls,
|
|
300
|
+
// core's credential (NYLORUN_GATES_TOKEN) for MCP requests outside a run.
|
|
298
301
|
const runGrants = stack.modelGate ? createRunGrants() : undefined;
|
|
299
302
|
const modelGate = stack.modelGate && runGrants
|
|
300
303
|
? httpModelGate({ url: stack.modelGate.url, runTokens: runGrants })
|
|
@@ -326,7 +329,12 @@ export async function main() {
|
|
|
326
329
|
create: {
|
|
327
330
|
...(tenantSettings.id ? { tenantId: tenantSettings.id } : {}),
|
|
328
331
|
name: tenantSettings.name,
|
|
329
|
-
principals: hostPrincipals({
|
|
332
|
+
principals: hostPrincipals({
|
|
333
|
+
adminKey: credentials.adminKey,
|
|
334
|
+
...(stack.tenant?.managementKeyFile
|
|
335
|
+
? { bootstrapKey: loadManagementKey(stack.tenant.managementKeyFile) }
|
|
336
|
+
: {}),
|
|
337
|
+
}),
|
|
330
338
|
},
|
|
331
339
|
configFor,
|
|
332
340
|
logger,
|
|
@@ -345,31 +353,16 @@ export async function main() {
|
|
|
345
353
|
}),
|
|
346
354
|
logger,
|
|
347
355
|
onOpen: (handle) => startRelay(handle.envelope.id),
|
|
348
|
-
relayStatus: async () => {
|
|
349
|
-
if (!relayLag)
|
|
350
|
-
throw new Error("no relay");
|
|
351
|
-
return relayLag();
|
|
352
|
-
},
|
|
353
356
|
});
|
|
354
357
|
const options = {
|
|
355
358
|
hostRoot,
|
|
356
359
|
module,
|
|
357
360
|
config,
|
|
358
|
-
credentials,
|
|
359
361
|
logger,
|
|
360
362
|
coreVersion: coreVersion(),
|
|
361
363
|
...(stack.listen ? { listen: stack.listen } : {}),
|
|
362
|
-
...(stack.publicUrl ? { publicUrl: stack.publicUrl } : {}),
|
|
363
|
-
// The Admin API on its own listener: from the container environment, or from host.json
|
|
364
|
-
// (loopback, on the same host) when the Host runs outside a container.
|
|
365
|
-
...(stack.operator
|
|
366
|
-
? { operator: stack.operator }
|
|
367
|
-
: !stack.listen && typeof config.adminPort === "number"
|
|
368
|
-
? { operator: { host: config.host, port: config.adminPort } }
|
|
369
|
-
: {}),
|
|
370
364
|
...(infra.readiness ? { readiness: infra.readiness } : {}),
|
|
371
|
-
// SIGTERM and
|
|
372
|
-
// stop the Worker and the relay, close the Tenant, then end the infrastructure clients.
|
|
365
|
+
// SIGTERM closes the Host this way: stop the Worker and the relay, close the Tenant, then end the infrastructure clients.
|
|
373
366
|
shutdown: {
|
|
374
367
|
beforeTenants: async () => {
|
|
375
368
|
await hostExecution.stop();
|
|
@@ -392,8 +385,8 @@ export async function main() {
|
|
|
392
385
|
// listener starts and opens the Tenant (migrating its database, creating it on
|
|
393
386
|
// first start); opening arms its sweep, which recovers wakes lost with
|
|
394
387
|
// Restate's state (§14.8), and starts the stream relay. A Tenant that cannot
|
|
395
|
-
// be opened leaves the Host listening but not ready
|
|
396
|
-
//
|
|
388
|
+
// be opened leaves the Host listening but not ready; `nylorun-operate status` names the
|
|
389
|
+
// cause.
|
|
397
390
|
// The Harness API listener (NYLORUN_HARNESS=remote): only harnesses connect here, with the
|
|
398
391
|
// harness credential, and only while the Tenant is open.
|
|
399
392
|
let harnessListener;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TenantCause } from "@nylorun/core/contracts";
|
|
2
|
+
export declare const EXIT_REFUSED = 1;
|
|
3
|
+
export declare const EXIT_TENANT = 2;
|
|
4
|
+
export declare const EXIT_USAGE = 64;
|
|
5
|
+
export interface OperateIo {
|
|
6
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
7
|
+
out(line: string): void;
|
|
8
|
+
err(line: string): void;
|
|
9
|
+
/** For the Host's `/ready`. Default: the global `fetch`. */
|
|
10
|
+
fetch?: typeof fetch;
|
|
11
|
+
}
|
|
12
|
+
/** `nylorun-operate status --json`. */
|
|
13
|
+
export interface OperateStatus {
|
|
14
|
+
version: string;
|
|
15
|
+
protocol: {
|
|
16
|
+
min: number;
|
|
17
|
+
max: number;
|
|
18
|
+
features: string[];
|
|
19
|
+
};
|
|
20
|
+
tenant: {
|
|
21
|
+
id: string | null;
|
|
22
|
+
name: string | null;
|
|
23
|
+
state: "open" | "unavailable";
|
|
24
|
+
cause?: TenantCause["code"];
|
|
25
|
+
message?: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Runs one command; resolves to the exit code. */
|
|
29
|
+
export declare function runOperate(argv: readonly string[], io: OperateIo): Promise<number>;
|