@nylorun/runtime 0.25.0-beta → 0.27.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 +169 -0
- package/README.md +26 -16
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.js +1 -1
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +12 -18
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +4 -4
- package/dist/api/components.d.ts +326 -45
- package/dist/api/components.js +5 -1
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.d.ts +2 -2
- package/dist/api/http/app.js +2 -2
- package/dist/api/http/body.d.ts +15 -3
- package/dist/api/http/body.js +15 -7
- package/dist/api/http/define.d.ts +1 -1
- package/dist/api/http/respond.js +7 -3
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/artifacts.js +19 -7
- package/dist/api/http/routes/coverage.js +1 -1
- package/dist/api/http/routes/environment-manifests.js +1 -1
- package/dist/api/http/routes/environments.js +6 -6
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +1 -1
- package/dist/api/http/routes/reads.d.ts +35 -0
- package/dist/api/http/routes/reads.js +50 -22
- package/dist/api/http/routes/sessions.js +12 -11
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +69 -16
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +6 -2
- package/dist/artifacts/disk.d.ts +0 -4
- package/dist/artifacts/disk.js +77 -73
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +70 -25
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +4 -3
- package/dist/gates/egress.d.ts +9 -9
- package/dist/gates/egress.js +19 -19
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +53 -17
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +28 -5
- package/dist/gates/policy.js +60 -17
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +0 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +4 -9
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +102 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +82 -73
- package/dist/harness-api/start.d.ts +20 -2
- package/dist/harness-api/start.js +57 -18
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +2 -45
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +0 -2
- package/dist/host/http.js +0 -27
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +1 -3
- package/dist/keys/contract.js +1 -3
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.js +6 -1
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +1961 -994
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +13 -6
- package/dist/mcp/preview.js +37 -20
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +866 -1218
- package/dist/reads/cursor.js +3 -1
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/environment.d.ts +2 -0
- package/dist/record/environment.js +2 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +3 -2
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +5 -8
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +19 -11
- package/dist/sandbox/pods/lifecycle.js +45 -23
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +4 -1
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +8 -3
- package/dist/sandbox/resolve.js +12 -2
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +34 -11
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/migrate.js +1 -1
- package/dist/store/postgres/reads.js +10 -2
- package/dist/store/postgres/schema.d.ts +19 -0
- package/dist/store/postgres/schema.js +7 -0
- package/dist/store/postgres/store.js +55 -26
- package/dist/store/types.d.ts +17 -7
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +21 -31
- package/dist/tenant/commands.js +9 -16
- package/dist/tenant/context.d.ts +14 -17
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +9 -36
- package/dist/tenant/environment-manifests.js +9 -1
- package/dist/tenant/environments.d.ts +0 -2
- package/dist/tenant/environments.js +54 -30
- package/dist/tenant/ephemeral.d.ts +1 -1
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +12 -11
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +24 -1
- package/dist/tenant/identity-file.js +35 -7
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +31 -23
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +90 -0
- package/dist/tenant/limits.js +175 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +17 -19
- package/dist/tenant/outbound.js +18 -21
- package/dist/tenant/run-grants.js +3 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.js +30 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.js +32 -29
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +4 -3
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +39 -48
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/types.d.ts +11 -16
- package/dist/tenant/worker.d.ts +2 -10
- package/dist/tenant/worker.js +5 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +9 -6
- package/dist/traces/map.js +9 -11
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +383 -0
- package/dist/tuning.js +658 -0
- package/dist/vault/headers.js +4 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
|
@@ -14,10 +14,6 @@ type Exists = (id: string) => boolean | Promise<boolean>;
|
|
|
14
14
|
export interface WorkspacePort {
|
|
15
15
|
/** Runs one sandbox tool in the workspace of `session`. */
|
|
16
16
|
run(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, toolName: SandboxToolName, input: unknown, signal: AbortSignal): Promise<SandboxToolOutcome>;
|
|
17
|
-
/** Lists the files under `dir` of the workspace of `session` (the turn-end export, F8.2). */
|
|
18
|
-
listFiles(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, dir: string, maxEntries: number, signal: AbortSignal): ReturnType<SandboxManager["listFiles"]>;
|
|
19
|
-
/** Reads a file of the workspace of `session` as bytes, at most `maxBytes` (`save_artifact`). */
|
|
20
|
-
readBytes(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, path: string, maxBytes: number, signal: AbortSignal): ReturnType<SandboxManager["readBytes"]>;
|
|
21
17
|
/** One operation on the disk of the workspace of `session` (R2d step 3). */
|
|
22
18
|
disk(session: SandboxSessionRef, sandbox: SandboxManifest | undefined, op: DiskOp, signal: AbortSignal): ReturnType<SandboxManager["disk"]>;
|
|
23
19
|
report(): Promise<WorkspaceReport>;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* removes theirs (`workspace.remove`); it also keeps the `sandboxes` table in step with the
|
|
14
14
|
* list.
|
|
15
15
|
* - **Pod sandboxes** (F7.2, `withPodWorkspaces`): a session attached to a pod sandbox has its
|
|
16
|
-
* workspace in the pod, served by the sandbox's engine (its host connection). Its tool calls
|
|
17
|
-
*
|
|
16
|
+
* workspace in the pod, served by the sandbox's engine (its host connection). Its tool calls
|
|
17
|
+
* and disk operations go there whatever serves the other workspaces; with the pod stopped or
|
|
18
18
|
* starting they are `409 environment_unavailable`.
|
|
19
19
|
*/
|
|
20
20
|
import { HarnessApiError } from "@nylorun/core/harness-api";
|
|
@@ -26,8 +26,6 @@ import { NoWorkspaceHarness } from "./server.js";
|
|
|
26
26
|
export function localWorkspace(manager) {
|
|
27
27
|
return {
|
|
28
28
|
run: (session, sandbox, toolName, input, signal) => manager.run(session, sandbox, toolName, input, signal),
|
|
29
|
-
listFiles: (session, sandbox, dir, maxEntries, signal) => manager.listFiles(session, sandbox, dir, maxEntries, signal),
|
|
30
|
-
readBytes: (session, sandbox, path, maxBytes, signal) => manager.readBytes(session, sandbox, path, maxBytes, signal),
|
|
31
29
|
disk: (session, sandbox, op, signal) => manager.disk(session, sandbox, op, signal),
|
|
32
30
|
report: () => manager.report(),
|
|
33
31
|
sweep: (options) => manager.sweep(options),
|
|
@@ -119,45 +117,6 @@ export function remoteWorkspace(options) {
|
|
|
119
117
|
throw error;
|
|
120
118
|
}
|
|
121
119
|
},
|
|
122
|
-
async listFiles(session, sandbox, dir, maxEntries, signal) {
|
|
123
|
-
try {
|
|
124
|
-
const answer = (await options.server.workspace("workspace.read", { session: sessionOf(session), spec: sandbox ?? {}, list: { dir, maxEntries } }, signal, target));
|
|
125
|
-
if (answer.kind === "listed")
|
|
126
|
-
return {
|
|
127
|
-
kind: "listed",
|
|
128
|
-
path: String(answer.path),
|
|
129
|
-
listing: answer.listing,
|
|
130
|
-
};
|
|
131
|
-
if (answer.kind === "missing")
|
|
132
|
-
return { kind: "missing", path: String(answer.path) };
|
|
133
|
-
return { kind: "failed", code: String(answer.code), message: String(answer.message) };
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
// No harness holds the workspaces: there is nothing to export.
|
|
137
|
-
if (error instanceof NoWorkspaceHarness)
|
|
138
|
-
return { kind: "missing", path: dir };
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
async readBytes(session, sandbox, path, maxBytes, signal) {
|
|
143
|
-
try {
|
|
144
|
-
const answer = (await options.server.workspace("workspace.read", {
|
|
145
|
-
session: sessionOf(session),
|
|
146
|
-
spec: sandbox ?? {},
|
|
147
|
-
bytes: { path, maxBytes },
|
|
148
|
-
}, signal, target));
|
|
149
|
-
if (answer.kind === "read")
|
|
150
|
-
return { kind: "read", path: String(answer.path), bytes: new Uint8Array(Buffer.from(String(answer.base64), "base64")) };
|
|
151
|
-
if (answer.kind === "missing")
|
|
152
|
-
return { kind: "missing", path: String(answer.path) };
|
|
153
|
-
return { kind: "failed", code: String(answer.code), message: String(answer.message) };
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
if (error instanceof NoWorkspaceHarness)
|
|
157
|
-
return { kind: "failed", code: "sandbox.unavailable", message: error.message };
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
120
|
async disk(session, sandbox, op, signal) {
|
|
162
121
|
try {
|
|
163
122
|
const answer = (await options.server.workspace("list" in op || "read" in op ? "workspace.read" : "workspace.write", { session: sessionOf(session), spec: sandbox ?? {}, disk: toWireOp(op) }, signal, target));
|
|
@@ -215,8 +174,6 @@ export function withPodWorkspaces(base, options) {
|
|
|
215
174
|
return {
|
|
216
175
|
...base,
|
|
217
176
|
run: async (session, sandbox, toolName, input, signal) => (await routed(session)).run(session, sandbox, toolName, input, signal),
|
|
218
|
-
listFiles: async (session, sandbox, dir, maxEntries, signal) => (await routed(session)).listFiles(session, sandbox, dir, maxEntries, signal),
|
|
219
|
-
readBytes: async (session, sandbox, path, maxBytes, signal) => (await routed(session)).readBytes(session, sandbox, path, maxBytes, signal),
|
|
220
177
|
disk: async (session, sandbox, op, signal) => (await routed(session)).disk(session, sandbox, op, signal),
|
|
221
178
|
};
|
|
222
179
|
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* An upgrade is accepted only on `/nylorun/harness/v1`, with a `Host` in the allowlist (421),
|
|
7
7
|
* `Authorization: Bearer <token>` (compared in constant time; 401) and `Nylorun-Harness-Api: 1`
|
|
8
8
|
* (426), while the Tenant is open (503). Frames are at most 64 MiB and every one is validated
|
|
9
|
-
* (`createChannel({ validate: true })`). Core pings every 30 s and
|
|
10
|
-
* 90 s; the connection then counts as lost (`connection.lost`).
|
|
9
|
+
* (`createChannel({ validate: true })`). Core pings every `harness.pingMs` (Host tuning, 30 s) and
|
|
10
|
+
* drops a harness silent for `harness.silenceMs` (90 s); the connection then counts as lost (`connection.lost`).
|
|
11
11
|
*
|
|
12
12
|
* Sandbox pods (F7.2, D42) reach this listener too, on the Docker host's published port, and hold
|
|
13
13
|
* no harness token. A pod's engine exchanges its join token for a host token with `POST
|
|
@@ -30,15 +30,12 @@ import { tokenType } from "../tenant/jwt.js";
|
|
|
30
30
|
import { HOST_TOKEN_TYP } from "../tenant/host-token.js";
|
|
31
31
|
import { HostAuthError } from "../sandbox/join.js";
|
|
32
32
|
import { parseSetupReport } from "../sandbox/setup.js";
|
|
33
|
+
import { hostTuning } from "../tuning.js";
|
|
33
34
|
export { HOST_JOIN_PATH, HOST_RENEW_PATH };
|
|
34
35
|
/** Where a pod's engine reads its setup and reports it (R2d step 2, `sandbox/setup.ts`). */
|
|
35
36
|
export const HOST_SETUP_PATH = `${HARNESS_API_PATH}/host/setup`;
|
|
36
37
|
/** Where it reads its input files: `/{index}`, and a folder's file `/{index}/{path}`. */
|
|
37
38
|
export const HOST_FILES_PATH = `${HARNESS_API_PATH}/host/files`;
|
|
38
|
-
/** A join's body is small. */
|
|
39
|
-
const MAX_JOIN_BODY = 8 * 1024;
|
|
40
|
-
/** A setup report carries the end of a command's output. */
|
|
41
|
-
const MAX_REPORT_BODY = 64 * 1024;
|
|
42
39
|
function refuse(socket, status, text, headers = {}) {
|
|
43
40
|
const lines = [
|
|
44
41
|
`HTTP/1.1 ${status} ${text}`,
|
|
@@ -85,7 +82,9 @@ export async function startHarnessListener(options) {
|
|
|
85
82
|
});
|
|
86
83
|
response.end(text);
|
|
87
84
|
};
|
|
88
|
-
|
|
85
|
+
// A join's body is small (`harnessApi.joinBodyBytes`); a setup report carries the end of a
|
|
86
|
+
// command's output (`harnessApi.reportBodyBytes`).
|
|
87
|
+
const readBody = (request, max = hostTuning().harnessApi.joinBodyBytes) => new Promise((resolve, reject) => {
|
|
89
88
|
let size = 0;
|
|
90
89
|
const chunks = [];
|
|
91
90
|
request.on("data", (chunk) => {
|
|
@@ -139,7 +138,7 @@ export async function startHarnessListener(options) {
|
|
|
139
138
|
return reply(response, 200, await authority.setup(token));
|
|
140
139
|
if (request.method !== "POST")
|
|
141
140
|
return reply(response, 405, undefined, { allow: "GET, POST" });
|
|
142
|
-
const report = parseSetupReport(JSON.parse(await readBody(request,
|
|
141
|
+
const report = parseSetupReport(JSON.parse(await readBody(request, hostTuning().harnessApi.reportBodyBytes)));
|
|
143
142
|
if (!report)
|
|
144
143
|
throw new HostAuthError(400, "The report is malformed");
|
|
145
144
|
await authority.reportSetup(token, report);
|
|
@@ -148,7 +147,8 @@ export async function startHarnessListener(options) {
|
|
|
148
147
|
if (request.method !== "GET")
|
|
149
148
|
return reply(response, 405, undefined, { allow: "GET" });
|
|
150
149
|
const [index, ...rest] = path.slice(HOST_FILES_PATH.length + 1).split("/");
|
|
151
|
-
|
|
150
|
+
// Any entry of the recipe: `setupFile` answers none for an index past its last.
|
|
151
|
+
if (!/^\d+$/.test(index ?? "") || !Number.isSafeInteger(Number(index)))
|
|
152
152
|
return reply(response, 404);
|
|
153
153
|
const file = await authority.setupFile(token, Number(index), rest.length === 0 ? undefined : rest.map((part) => decodeURIComponent(part)).join("/"));
|
|
154
154
|
if (!file)
|
|
@@ -281,7 +281,7 @@ export async function startHarnessListener(options) {
|
|
|
281
281
|
setTimeout(() => {
|
|
282
282
|
for (const client of wss.clients)
|
|
283
283
|
client.terminate();
|
|
284
|
-
},
|
|
284
|
+
}, hostTuning().harnessApi.closeGraceMs).unref();
|
|
285
285
|
await closed;
|
|
286
286
|
wss.close();
|
|
287
287
|
},
|
package/dist/host/create-host.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createServer, } from "node:http";
|
|
2
2
|
import { getRequestListener, RequestError } from "@hono/node-server";
|
|
3
3
|
import { createHostApp } from "./app.js";
|
|
4
|
+
import { hostTuning } from "../tuning.js";
|
|
4
5
|
import { isLoopbackHost } from "./stack-config.js";
|
|
5
6
|
import { bindListener as bind, EXIT_NON_LOOPBACK, headerValue, HostListenError, isAllowedRequestHost, pathnameIsLogged, redactRoutePath, rejectedResponse, sendRejected, } from "./http.js";
|
|
6
7
|
export function createHost(options) {
|
|
@@ -132,7 +133,7 @@ export function createHost(options) {
|
|
|
132
133
|
const graceful = await Promise.race([
|
|
133
134
|
stopped.then(() => true),
|
|
134
135
|
new Promise((resolve) => {
|
|
135
|
-
grace = setTimeout(() => resolve(false),
|
|
136
|
+
grace = setTimeout(() => resolve(false), hostTuning().host.shutdownGraceMs);
|
|
136
137
|
grace.unref();
|
|
137
138
|
}),
|
|
138
139
|
]);
|
|
@@ -172,8 +173,6 @@ const SENT = {
|
|
|
172
173
|
function alreadySent(response) {
|
|
173
174
|
return response.headers.has("x-hono-already-sent") ? SENT : response;
|
|
174
175
|
}
|
|
175
|
-
/** How long shutdown waits for requests in progress before closing their connections. */
|
|
176
|
-
const SHUTDOWN_GRACE_MS = 10_000;
|
|
177
176
|
/**
|
|
178
177
|
* A response that has started and not ended is a stream: JSON answers are written and ended
|
|
179
178
|
* at once. (`getHeader` cannot tell, since streams pass their headers to `writeHead`.)
|
package/dist/host/execution.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { startWorker, stopWorker } from "../infra/workers.js";
|
|
2
2
|
import { TenantWorkers, } from "../tenant/worker.js";
|
|
3
|
-
|
|
4
|
-
const ARM_CONCURRENCY = 16;
|
|
3
|
+
import { hostTuning } from "../tuning.js";
|
|
5
4
|
export function createHostExecution(options) {
|
|
6
5
|
const { execution, services, logger } = options;
|
|
7
6
|
const workers = new TenantWorkers({
|
|
@@ -58,7 +57,7 @@ export function createHostExecution(options) {
|
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
};
|
|
61
|
-
await Promise.all(Array.from({ length: Math.min(
|
|
60
|
+
await Promise.all(Array.from({ length: Math.min(hostTuning().sweep.armConcurrency, queue.length) }, next));
|
|
62
61
|
if (failures.length > 0)
|
|
63
62
|
throw new AggregateError(failures, `Arming ${failures.length} Tenant sweep(s) failed`);
|
|
64
63
|
},
|
package/dist/host/gates.d.ts
CHANGED
|
@@ -15,14 +15,11 @@ import { type InflightCallsOptions } from "../gates/inflight.js";
|
|
|
15
15
|
import type { openMcpServer } from "../mcp/connect.js";
|
|
16
16
|
import type { OutboundPolicy } from "../tenant/outbound.js";
|
|
17
17
|
import { type BlobStore } from "../blob/index.js";
|
|
18
|
-
import type { ModelCallSettings } from "../gates/model-gate.js";
|
|
19
18
|
import { type TenantVaults } from "../gates/tenant-vaults.js";
|
|
20
19
|
import type { PostgresClient } from "../store/postgres/connect.js";
|
|
21
20
|
import type { Logger } from "../tenant/types.js";
|
|
22
21
|
import type { EgressConfig, GatesConfig } from "./stack-config.js";
|
|
23
22
|
import { type EgressGate, type StartEgressGateOptions } from "../gates/egress.js";
|
|
24
|
-
/** Above the gate's 600 s provider request timeout and the loop's 630 s client timeout. */
|
|
25
|
-
export declare const GATES_REQUEST_TIMEOUT_MS = 660000;
|
|
26
23
|
export interface StartGatesOptions {
|
|
27
24
|
readonly gates: GatesConfig;
|
|
28
25
|
readonly logger: Logger;
|
|
@@ -32,11 +29,7 @@ export interface StartGatesOptions {
|
|
|
32
29
|
readonly database?: PostgresClient;
|
|
33
30
|
/** Replaces the Postgres-backed Tenant vault (tests). */
|
|
34
31
|
readonly vaults?: TenantVaults;
|
|
35
|
-
/**
|
|
36
|
-
readonly settings?: ModelCallSettings;
|
|
37
|
-
/** Largest model call body (tests). */
|
|
38
|
-
readonly maxBodyBytes?: number;
|
|
39
|
-
/** How long `close` lets running calls finish. Default 10 s. */
|
|
32
|
+
/** How long `close` lets running calls finish. Default `gates.drainMs` (Host tuning, 10 s). */
|
|
40
33
|
readonly drainMs?: number;
|
|
41
34
|
/** How long keyed outcomes are kept, and how many (tests). */
|
|
42
35
|
readonly inflight?: InflightCallsOptions;
|
|
@@ -45,7 +38,7 @@ export interface StartGatesOptions {
|
|
|
45
38
|
* Default: no limits.
|
|
46
39
|
*/
|
|
47
40
|
readonly delivery?: OutboundPolicy;
|
|
48
|
-
/** How long an MCP connection may sit unused. Default `
|
|
41
|
+
/** How long an MCP connection may sit unused. Default `gates.mcpIdleMs` (Host tuning, 15 min). */
|
|
49
42
|
readonly mcpIdleMs?: number;
|
|
50
43
|
/** Tests replace how a remote MCP server is opened. */
|
|
51
44
|
readonly openMcp?: typeof openMcpServer;
|
package/dist/host/gates.js
CHANGED
|
@@ -15,7 +15,7 @@ import { getRequestListener } from "@hono/node-server";
|
|
|
15
15
|
import { createGatesApp } from "../api/gate/routes.js";
|
|
16
16
|
import { createModelCallHandler } from "../gates/handler.js";
|
|
17
17
|
import { createInflightCalls } from "../gates/inflight.js";
|
|
18
|
-
import { createMcpHandler
|
|
18
|
+
import { createMcpHandler } from "../gates/mcp-handler.js";
|
|
19
19
|
import { createToolCalls } from "../gates/tool-calls.js";
|
|
20
20
|
import { gateHttpTools } from "../gates/http-tool.js";
|
|
21
21
|
import { existsSync } from "node:fs";
|
|
@@ -24,13 +24,13 @@ import { staleRun, verifyRunToken } from "../tenant/run-token.js";
|
|
|
24
24
|
import { createFsBlobStore } from "../blob/index.js";
|
|
25
25
|
import { createTenantVaults } from "../gates/tenant-vaults.js";
|
|
26
26
|
import { journalCheck } from "../gates/journal.js";
|
|
27
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
27
28
|
import { probeDatabase } from "../infra/database.js";
|
|
28
29
|
import { bindListener, headerValue, isAllowedRequestHost, sendRejected } from "./http.js";
|
|
29
30
|
import { startEgressGate, storeEgressRecord, storeEgressSandboxes, } from "../gates/egress.js";
|
|
30
31
|
import { verifyEgressToken } from "../sandbox/egress-token.js";
|
|
31
32
|
import { leafKeyPair } from "../keys/x509.js";
|
|
32
|
-
|
|
33
|
-
export const GATES_REQUEST_TIMEOUT_MS = 660_000;
|
|
33
|
+
import { hostTuning } from "../tuning.js";
|
|
34
34
|
export async function startGates(options) {
|
|
35
35
|
const { gates, logger, database } = options;
|
|
36
36
|
let vaults = options.vaults;
|
|
@@ -47,8 +47,9 @@ export async function startGates(options) {
|
|
|
47
47
|
(options.hostRoot !== undefined
|
|
48
48
|
? createFsBlobStore({ root: tenantPaths(options.hostRoot).blobs })
|
|
49
49
|
: undefined);
|
|
50
|
+
const tuning = hostTuning().gates;
|
|
50
51
|
const inflight = createInflightCalls(options.inflight);
|
|
51
|
-
const mcpIdleMs = options.mcpIdleMs ??
|
|
52
|
+
const mcpIdleMs = options.mcpIdleMs ?? tuning.mcpIdleMs;
|
|
52
53
|
const mcp = createMcpHandler({
|
|
53
54
|
vaults,
|
|
54
55
|
logger,
|
|
@@ -61,9 +62,9 @@ export async function startGates(options) {
|
|
|
61
62
|
logger,
|
|
62
63
|
...(options.inflight ? { inflight: options.inflight } : {}),
|
|
63
64
|
});
|
|
64
|
-
const sweep = setInterval(() => void mcp.sweep(), Math.max(
|
|
65
|
+
const sweep = setInterval(() => void mcp.sweep(), Math.max(tuning.mcpSweepMinMs, Math.min(mcpIdleMs, tuning.mcpSweepMaxMs)));
|
|
65
66
|
sweep.unref();
|
|
66
|
-
const prune = setInterval(() => void toolCalls.prune(),
|
|
67
|
+
const prune = setInterval(() => void toolCalls.prune(), tuning.toolCrossingPruneMs);
|
|
67
68
|
prune.unref();
|
|
68
69
|
// Run tokens verify against the Tenant's public keys and session rows (F5): the same
|
|
69
70
|
// database the ledger and `tool_crossings` use.
|
|
@@ -81,7 +82,6 @@ export async function startGates(options) {
|
|
|
81
82
|
modelGate: createModelCallHandler({
|
|
82
83
|
vaults,
|
|
83
84
|
logger,
|
|
84
|
-
...(options.settings ? { settings: options.settings } : {}),
|
|
85
85
|
...(blobs ? { blobs } : {}),
|
|
86
86
|
}),
|
|
87
87
|
ready: async () => {
|
|
@@ -91,14 +91,15 @@ export async function startGates(options) {
|
|
|
91
91
|
if (!database)
|
|
92
92
|
return true;
|
|
93
93
|
try {
|
|
94
|
-
await probeDatabase(database, AbortSignal.timeout(
|
|
94
|
+
await probeDatabase(database, AbortSignal.timeout(tuning.probeTimeoutMs));
|
|
95
95
|
return true;
|
|
96
96
|
}
|
|
97
97
|
catch {
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
|
-
|
|
101
|
+
// The Tenant's Runtime limits, read from its database for each call.
|
|
102
|
+
limits: async () => (await vaults.open()).store.tx((t) => readRuntimeLimits(t)),
|
|
102
103
|
mcp,
|
|
103
104
|
http: gateHttpTools(vaults, options.delivery ?? {}, logger),
|
|
104
105
|
toolCalls,
|
|
@@ -122,7 +123,9 @@ export async function startGates(options) {
|
|
|
122
123
|
res.once("close", () => inFlight.delete(res));
|
|
123
124
|
void listener(req, res);
|
|
124
125
|
});
|
|
125
|
-
|
|
126
|
+
// Above the gate's 600 s provider request timeout and the loop's client timeout
|
|
127
|
+
// (`gates.clientTimeoutMs`): the tuning refuses one at or below the other.
|
|
128
|
+
server.requestTimeout = tuning.requestTimeoutMs;
|
|
126
129
|
await bindListener(server, gates.listen.port, gates.listen.host, (error) => logger.error("listener_error", { error: error.message }));
|
|
127
130
|
const port = server.address().port;
|
|
128
131
|
allowedHosts = [
|
|
@@ -137,9 +140,9 @@ export async function startGates(options) {
|
|
|
137
140
|
closing ??= (async () => {
|
|
138
141
|
const closed = new Promise((resolve) => server.close(() => resolve()));
|
|
139
142
|
server.closeIdleConnections();
|
|
140
|
-
const deadline = Date.now() + (options.drainMs ??
|
|
143
|
+
const deadline = Date.now() + (options.drainMs ?? tuning.drainMs);
|
|
141
144
|
while (inFlight.size > 0 && Date.now() < deadline)
|
|
142
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
145
|
+
await new Promise((resolve) => setTimeout(resolve, tuning.drainPollMs));
|
|
143
146
|
// Closing a running call's connection aborts an unkeyed one; keyed calls stop here.
|
|
144
147
|
server.closeAllConnections();
|
|
145
148
|
inflight.close();
|
package/dist/host/http.d.ts
CHANGED
|
@@ -52,5 +52,3 @@ export declare function redactRoutePath(pathname: string): string;
|
|
|
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
|
-
/** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
|
|
56
|
-
export declare function readJsonBody(request: Request, limit?: number): Promise<unknown>;
|
package/dist/host/http.js
CHANGED
|
@@ -164,30 +164,3 @@ export function headerValue(incoming, name) {
|
|
|
164
164
|
return raw[0];
|
|
165
165
|
return raw;
|
|
166
166
|
}
|
|
167
|
-
/** A JSON request body, capped at `limit` bytes (413); `undefined` when there is none. */
|
|
168
|
-
export async function readJsonBody(request, limit = 1024 * 1024) {
|
|
169
|
-
if (request.body === null)
|
|
170
|
-
return undefined;
|
|
171
|
-
const chunks = [];
|
|
172
|
-
let size = 0;
|
|
173
|
-
const reader = request.body.getReader();
|
|
174
|
-
for (;;) {
|
|
175
|
-
const { done, value } = await reader.read();
|
|
176
|
-
if (done)
|
|
177
|
-
break;
|
|
178
|
-
size += value.byteLength;
|
|
179
|
-
if (size > limit) {
|
|
180
|
-
await reader.cancel();
|
|
181
|
-
throw Object.assign(new Error("Request too large"), { status: 413 });
|
|
182
|
-
}
|
|
183
|
-
chunks.push(value);
|
|
184
|
-
}
|
|
185
|
-
if (size === 0)
|
|
186
|
-
return undefined;
|
|
187
|
-
try {
|
|
188
|
-
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
throw Object.assign(new Error("Invalid JSON"), { status: 400 });
|
|
192
|
-
}
|
|
193
|
-
}
|
package/dist/host/main.js
CHANGED
|
@@ -44,6 +44,7 @@ import { createPostgresSessionStore } from "../store/postgres/store.js";
|
|
|
44
44
|
import { createTraceProjector, TRACE_SLOT } from "../traces/projector.js";
|
|
45
45
|
import { createOtlpPoster } from "../traces/otlp.js";
|
|
46
46
|
import { traceKey } from "../traces/key.js";
|
|
47
|
+
import { readRuntimeLimits } from "../tenant/runtime-limits.js";
|
|
47
48
|
import { configForFactory } from "./config-for.js";
|
|
48
49
|
import { createHost } from "./create-host.js";
|
|
49
50
|
import { createHostExecution } from "./execution.js";
|
|
@@ -63,6 +64,7 @@ import { runHarness } from "../harness/main.js";
|
|
|
63
64
|
import { startHarnessListener } from "../harness-api/ws-server.js";
|
|
64
65
|
import { httpSandboxesClient } from "../sandbox/pods/client.js";
|
|
65
66
|
import { retry } from "../retry.js";
|
|
67
|
+
import { hostTuning, useHostTuning } from "../tuning.js";
|
|
66
68
|
const nodeRequire = createRequire(import.meta.url);
|
|
67
69
|
function coreVersion() {
|
|
68
70
|
try {
|
|
@@ -129,7 +131,8 @@ function objectStore(config) {
|
|
|
129
131
|
*/
|
|
130
132
|
async function ensureBucket(store, bucket, logger) {
|
|
131
133
|
try {
|
|
132
|
-
|
|
134
|
+
const { bucketRetry, bucketAttempts } = hostTuning().objectStore;
|
|
135
|
+
await retry(() => store.ensureBucket(), { ...bucketRetry, attempts: bucketAttempts });
|
|
133
136
|
logger.info("object_store_ready", { bucket });
|
|
134
137
|
}
|
|
135
138
|
catch (error) {
|
|
@@ -191,7 +194,7 @@ async function runGates(stack) {
|
|
|
191
194
|
}
|
|
192
195
|
catch (error) {
|
|
193
196
|
await Promise.all(servers.map((server) => server.close()));
|
|
194
|
-
await database.end({ timeout:
|
|
197
|
+
await database.end({ timeout: hostTuning().postgres.closeTimeoutSeconds });
|
|
195
198
|
if (error instanceof HostListenError) {
|
|
196
199
|
logger.error("listen_failed", { message: error.message, exitCode: error.exitCode });
|
|
197
200
|
process.exitCode = error.exitCode;
|
|
@@ -206,12 +209,15 @@ async function runGates(stack) {
|
|
|
206
209
|
return;
|
|
207
210
|
stopping = true;
|
|
208
211
|
void Promise.all(servers.map((server) => server.close()))
|
|
209
|
-
.then(() => database.end({ timeout:
|
|
212
|
+
.then(() => database.end({ timeout: hostTuning().postgres.closeTimeoutSeconds }))
|
|
210
213
|
.finally(() => process.exit(0));
|
|
211
214
|
});
|
|
212
215
|
}
|
|
213
216
|
export async function main() {
|
|
214
217
|
const stack = parseStackConfig(process.env, process.argv.slice(2));
|
|
218
|
+
// Read once, before any service starts: each reads the parts it uses where it uses them.
|
|
219
|
+
if (stack.tuning)
|
|
220
|
+
useHostTuning(stack.tuning.values, stack.tuning.overrides);
|
|
215
221
|
if (stack.services.has("gates") || stack.services.has("keys") || stack.services.has("egress"))
|
|
216
222
|
return runGates(stack);
|
|
217
223
|
if (stack.services.has("harness"))
|
|
@@ -332,6 +338,7 @@ export async function main() {
|
|
|
332
338
|
log: (message, fields) => logger.warn(message, fields),
|
|
333
339
|
}),
|
|
334
340
|
key: () => traceKey(store),
|
|
341
|
+
contentBytes: async () => (await store.tx((t) => readRuntimeLimits(t))).traceContentBytes,
|
|
335
342
|
turnMessage: createPostgresTurnMessages(database),
|
|
336
343
|
content: new Set(stack.traces.content),
|
|
337
344
|
log,
|
package/dist/host/operate.js
CHANGED
|
@@ -33,6 +33,7 @@ import { operatorKeys } from "../tenant/operator-keys.js";
|
|
|
33
33
|
import { deleteHarnessToken, listHarnessTokens, putHarnessToken } from "../tenant/harness-tokens.js";
|
|
34
34
|
import { RUNTIME_VERSION } from "../version.js";
|
|
35
35
|
import { DEFAULT_CONTAINER_LISTEN_PORT } from "./stack-config.js";
|
|
36
|
+
import { parseHostTuning } from "../tuning.js";
|
|
36
37
|
export const EXIT_REFUSED = 1;
|
|
37
38
|
export const EXIT_TENANT = 2;
|
|
38
39
|
export const EXIT_USAGE = 64;
|
|
@@ -51,6 +52,14 @@ class Exit extends Error {
|
|
|
51
52
|
this.code = code;
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* The Host tuning this container runs with (`NYLORUN_TUNING`, as the Host reads it): how long
|
|
57
|
+
* the command waits for the Host's `/ready` (`host.operateReadyTimeoutMs`) and for its pool to
|
|
58
|
+
* close (`postgres.closeTimeoutSeconds`).
|
|
59
|
+
*/
|
|
60
|
+
function tuningOf(io) {
|
|
61
|
+
return parseHostTuning(io.env.NYLORUN_TUNING).tuning;
|
|
62
|
+
}
|
|
54
63
|
/** Runs one command; resolves to the exit code. */
|
|
55
64
|
export async function runOperate(argv, io) {
|
|
56
65
|
try {
|
|
@@ -70,7 +79,7 @@ export async function runOperate(argv, io) {
|
|
|
70
79
|
return await command(operatorKeys(store), io, store);
|
|
71
80
|
}
|
|
72
81
|
finally {
|
|
73
|
-
await sql.end({ timeout:
|
|
82
|
+
await sql.end({ timeout: tuningOf(io).postgres.closeTimeoutSeconds });
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
85
|
catch (error) {
|
|
@@ -242,13 +251,13 @@ async function tenantStatus(io) {
|
|
|
242
251
|
return unavailable(envelope ?? null, `The database cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
243
252
|
}
|
|
244
253
|
finally {
|
|
245
|
-
await sql.end({ timeout:
|
|
254
|
+
await sql.end({ timeout: tuningOf(io).postgres.closeTimeoutSeconds });
|
|
246
255
|
}
|
|
247
256
|
const port = io.env.NYLORUN_LISTEN_PORT ?? String(DEFAULT_CONTAINER_LISTEN_PORT);
|
|
248
257
|
const ready = `http://127.0.0.1:${port}/ready`;
|
|
249
258
|
let answer;
|
|
250
259
|
try {
|
|
251
|
-
const response = await (io.fetch ?? fetch)(ready, { signal: AbortSignal.timeout(
|
|
260
|
+
const response = await (io.fetch ?? fetch)(ready, { signal: AbortSignal.timeout(tuningOf(io).host.operateReadyTimeoutMs) });
|
|
252
261
|
answer = (await response.json());
|
|
253
262
|
}
|
|
254
263
|
catch {
|
|
@@ -41,10 +41,13 @@
|
|
|
41
41
|
* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
|
|
42
42
|
* their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
|
|
43
43
|
* (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
|
|
44
|
+
* So is the Host tuning (`NYLORUN_TUNING`, `tuning.ts`), for every service: its timings,
|
|
45
|
+
* retries and internal sizes.
|
|
44
46
|
*/
|
|
45
47
|
import { type TraceResource } from "../traces/otlp.js";
|
|
46
48
|
import { type TraceContentKind } from "../traces/map.js";
|
|
47
49
|
import { type BlockedAddress } from "../sandbox/pods/network-gate.js";
|
|
50
|
+
import { type HostTuning } from "../tuning.js";
|
|
48
51
|
/** A Runtime service this release has. */
|
|
49
52
|
export type RuntimeService = "core" | "loop" | "gates" | "keys" | "egress" | "harness";
|
|
50
53
|
export type RuntimeServices = ReadonlySet<RuntimeService>;
|
|
@@ -249,6 +252,14 @@ export interface StackConfig {
|
|
|
249
252
|
* present only with an endpoint (Q5): without one there is no replication slot and no cost.
|
|
250
253
|
*/
|
|
251
254
|
traces?: TraceConfig;
|
|
255
|
+
/**
|
|
256
|
+
* Present when `NYLORUN_TUNING` is set: the Host tuning (`tuning.ts`) over its defaults,
|
|
257
|
+
* and the overrides as written, which core hands a pod sandbox's engine. Every service reads it.
|
|
258
|
+
*/
|
|
259
|
+
tuning?: {
|
|
260
|
+
values: HostTuning;
|
|
261
|
+
overrides: Record<string, unknown>;
|
|
262
|
+
};
|
|
252
263
|
}
|
|
253
264
|
/** Traces (R7.2), from the standard `OTEL_*` variables and `NYLORUN_TRACE_CONTENT`. */
|
|
254
265
|
export interface TraceConfig {
|
|
@@ -41,11 +41,14 @@
|
|
|
41
41
|
* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, with `OTEL_EXPORTER_OTLP_HEADERS`), what they say of
|
|
42
42
|
* their source (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`) and which content they carry
|
|
43
43
|
* (`NYLORUN_TRACE_CONTENT`, or all of it with `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`).
|
|
44
|
+
* So is the Host tuning (`NYLORUN_TUNING`, `tuning.ts`), for every service: its timings,
|
|
45
|
+
* retries and internal sizes.
|
|
44
46
|
*/
|
|
45
47
|
import { DEFAULT_SERVICE_NAME } from "../traces/otlp.js";
|
|
46
48
|
import { TRACE_CONTENT_KINDS } from "../traces/map.js";
|
|
47
49
|
import { isTenantId } from "@nylorun/core/compatibility";
|
|
48
50
|
import { blockedAddresses } from "../sandbox/pods/network-gate.js";
|
|
51
|
+
import { HostTuningError, parseHostTuning } from "../tuning.js";
|
|
49
52
|
export const RUNTIME_SERVICES = ["core", "loop", "gates", "keys", "egress", "harness"];
|
|
50
53
|
/**
|
|
51
54
|
* Services that may share a process (D12): they hold the same secrets and parse the same
|
|
@@ -254,8 +257,9 @@ function parseIdentityKeys(env) {
|
|
|
254
257
|
/** Parse the stack configuration; throws `StackConfigError` naming the variable. */
|
|
255
258
|
export function parseStackConfig(env, argv) {
|
|
256
259
|
const { services, deprecatedRole } = parseServices(argv);
|
|
260
|
+
const tuning = parseTuning(env);
|
|
257
261
|
if (services.has("harness"))
|
|
258
|
-
return { services, harness: parseHarnessService(env), endpoints: {} };
|
|
262
|
+
return { services, harness: parseHarnessService(env), endpoints: {}, ...(tuning ? { tuning } : {}) };
|
|
259
263
|
// The image sets NYLORUN_LISTEN_*: only the API's processes read them.
|
|
260
264
|
const servesApi = services.has("core") || services.has("loop");
|
|
261
265
|
const listen = servesApi ? parseListen(env) : undefined;
|
|
@@ -345,8 +349,24 @@ export function parseStackConfig(env, argv) {
|
|
|
345
349
|
...(tenant ? { tenant } : {}),
|
|
346
350
|
...(objectStore ? { objectStore } : {}),
|
|
347
351
|
...(traces ? { traces } : {}),
|
|
352
|
+
...(tuning ? { tuning } : {}),
|
|
348
353
|
};
|
|
349
354
|
}
|
|
355
|
+
/** `StackConfig.tuning` from `NYLORUN_TUNING`, or undefined when it is unset. */
|
|
356
|
+
function parseTuning(env) {
|
|
357
|
+
const raw = read(env, "NYLORUN_TUNING");
|
|
358
|
+
if (raw === undefined)
|
|
359
|
+
return undefined;
|
|
360
|
+
try {
|
|
361
|
+
const { tuning, overrides } = parseHostTuning(raw);
|
|
362
|
+
return { values: tuning, overrides: overrides ?? {} };
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (error instanceof HostTuningError)
|
|
366
|
+
throw new StackConfigError(error.message);
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
350
370
|
/**
|
|
351
371
|
* `StackConfig.traces` (R7.2). The OTLP endpoint is the collector's base URL, to which
|
|
352
372
|
* `/v1/traces` is added; `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is the full URL and wins. Headers
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ export { createTrustedIssuers, type TrustedIssuers, type TrustedIssuersOptions,
|
|
|
5
5
|
export type { TenantConfig, TenantHandle, TenantPaths, Logger, OpenTenantRuntime, } from "./tenant/types.js";
|
|
6
6
|
export { scriptedModel, gatewayModel, type ModelProvider } from "./core/provider.js";
|
|
7
7
|
export type { RuntimeAgent } from "./contracts.js";
|
|
8
|
-
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
9
8
|
export type { RuntimeModelAdapter } from "./contracts.js";
|
|
10
9
|
export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
|
|
11
10
|
export type { HostProcessPathInputs } from "./host/environment.js";
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,4 @@ export { startEphemeralRuntime, } from "./tenant/ephemeral.js";
|
|
|
3
3
|
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
|
-
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes, } from "./media.js";
|
|
7
6
|
export { baselineEnvironment, hostProcessEnvironment, } from "./host/environment.js";
|
package/dist/infra/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createDatabase } from "./database.js";
|
|
|
2
2
|
import { createExecution, executionKind } from "./execution.js";
|
|
3
3
|
import { createReadiness, infraProbes } from "./readiness.js";
|
|
4
4
|
import { createStreams, streamsKind } from "./streams.js";
|
|
5
|
+
import { hostTuning } from "../tuning.js";
|
|
5
6
|
export { createDatabase, probeDatabase } from "./database.js";
|
|
6
7
|
export { createExecution, probeExecution, WORKER_LISTEN } from "./execution.js";
|
|
7
8
|
export { createReadiness, infraProbes } from "./readiness.js";
|
|
@@ -30,7 +31,7 @@ export function createInfra(config, options = {}) {
|
|
|
30
31
|
...(streams ? { streams } : {}),
|
|
31
32
|
...(readiness ? { readiness } : {}),
|
|
32
33
|
async close() {
|
|
33
|
-
await Promise.allSettled([database?.end({ timeout:
|
|
34
|
+
await Promise.allSettled([database?.end({ timeout: hostTuning().postgres.closeTimeoutSeconds }), streams?.close()]);
|
|
34
35
|
},
|
|
35
36
|
};
|
|
36
37
|
}
|
|
@@ -21,8 +21,6 @@ export interface ReadinessReport {
|
|
|
21
21
|
errors: Record<string, string>;
|
|
22
22
|
}
|
|
23
23
|
export type Readiness = () => Promise<ReadinessReport>;
|
|
24
|
-
/** Default bound on one readiness pass. Compose probes `/ready` every 2 s with a 5 s timeout. */
|
|
25
|
-
export declare const READINESS_TIMEOUT_MS = 2000;
|
|
26
24
|
export declare function createReadiness(probes: Readonly<Record<string, Probe>>, options?: {
|
|
27
25
|
timeoutMs?: number;
|
|
28
26
|
}): Readiness;
|
package/dist/infra/readiness.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { probeDatabase } from "./database.js";
|
|
2
2
|
import { probeExecution } from "./execution.js";
|
|
3
|
-
|
|
4
|
-
export const READINESS_TIMEOUT_MS = 2000;
|
|
3
|
+
import { hostTuning } from "../tuning.js";
|
|
5
4
|
export function createReadiness(probes, options = {}) {
|
|
6
|
-
|
|
5
|
+
// The bound on one pass: `host.readinessTimeoutMs` (Host tuning). Compose probes `/ready` every
|
|
6
|
+
// 2 s with a 5 s timeout.
|
|
7
|
+
const timeoutMs = options.timeoutMs ?? hostTuning().host.readinessTimeoutMs;
|
|
7
8
|
return async () => {
|
|
8
9
|
const entries = Object.entries(probes);
|
|
9
10
|
const results = await Promise.all(entries.map(async ([name, probe]) => [name, await runProbe(probe, timeoutMs)]));
|
package/dist/keys/client.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface HttpKeysOptions {
|
|
|
4
4
|
readonly url: string;
|
|
5
5
|
/** `NYLORUN_GATES_TOKEN`. */
|
|
6
6
|
readonly token: string;
|
|
7
|
+
/** Default `keys.timeoutMs` (Host tuning): signing and vault writes are short. */
|
|
7
8
|
readonly timeoutMs?: number;
|
|
8
9
|
}
|
|
9
10
|
export declare function httpKeys(options: HttpKeysOptions): Keys;
|
package/dist/keys/client.js
CHANGED
|
@@ -10,12 +10,11 @@ import { HttpError } from "../tenant/http.js";
|
|
|
10
10
|
import { VaultError } from "../vault/error.js";
|
|
11
11
|
import { KEYS_PATH } from "./contract.js";
|
|
12
12
|
import { KEYS_OPERATIONS } from "./keys.js";
|
|
13
|
-
|
|
14
|
-
const KEYS_TIMEOUT_MS = 30_000;
|
|
13
|
+
import { hostTuning } from "../tuning.js";
|
|
15
14
|
export function httpKeys(options) {
|
|
16
15
|
const where = new URL(options.url).origin;
|
|
17
16
|
function call(operation, args) {
|
|
18
|
-
const timeoutMs = options.timeoutMs ??
|
|
17
|
+
const timeoutMs = options.timeoutMs ?? hostTuning().keys.timeoutMs;
|
|
19
18
|
const url = new URL(`${KEYS_PATH}/${operation}`, options.url);
|
|
20
19
|
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
21
20
|
const payload = Buffer.from(JSON.stringify({ args }), "utf8");
|