@nylorun/runtime 0.26.0-beta → 0.28.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 +171 -0
- package/README.md +18 -14
- 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.d.ts +4 -3
- package/dist/api/a2a/card.js +4 -4
- 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 +30 -25
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +8 -11
- package/dist/api/components.d.ts +6789 -3405
- package/dist/api/components.js +43 -30
- 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.js +9 -4
- package/dist/api/http/body.d.ts +17 -3
- package/dist/api/http/body.js +35 -7
- package/dist/api/http/respond.js +6 -2
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/agent-manifests.d.ts +10 -0
- package/dist/api/http/routes/agent-manifests.js +135 -0
- package/dist/api/http/routes/agents.d.ts +11 -0
- package/dist/api/http/routes/agents.js +418 -0
- package/dist/api/http/routes/artifacts.js +21 -9
- package/dist/api/http/routes/coverage.d.ts +4 -4
- package/dist/api/http/routes/coverage.js +13 -7
- 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 +3 -3
- package/dist/api/http/routes/me.d.ts +1 -1
- package/dist/api/http/routes/me.js +1 -2
- package/dist/api/http/routes/memories.d.ts +9 -0
- package/dist/api/http/routes/memories.js +152 -0
- package/dist/api/http/routes/reads.d.ts +10 -111
- package/dist/api/http/routes/reads.js +51 -29
- package/dist/api/http/routes/sessions.js +26 -54
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +77 -24
- package/dist/api/http/routes/triggers.d.ts +10 -0
- package/dist/api/http/routes/triggers.js +180 -0
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +73 -47
- package/dist/artifacts/disk.d.ts +10 -14
- package/dist/artifacts/disk.js +99 -95
- 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 +72 -27
- 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/flow-host.d.ts +0 -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 +5 -4
- package/dist/gates/egress.d.ts +13 -13
- package/dist/gates/egress.js +19 -21
- 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 +41 -13
- 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/meter.js +6 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +34 -6
- package/dist/gates/policy.js +66 -18
- 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 +2 -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 +21 -15
- 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 +113 -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 +86 -73
- package/dist/harness-api/start.d.ts +27 -5
- package/dist/harness-api/start.js +102 -19
- 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 +4 -47
- 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 +2 -4
- package/dist/host/http.js +5 -29
- 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 +0 -2
- package/dist/keys/contract.js +0 -2
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.d.ts +15 -4
- package/dist/keys/keys.js +22 -1
- package/dist/keys/webhooks.d.ts +25 -0
- package/dist/keys/webhooks.js +62 -0
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +3043 -1954
- 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 +12 -5
- package/dist/mcp/preview.js +43 -26
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/memory/service.d.ts +97 -0
- package/dist/memory/service.js +335 -0
- package/dist/memory/tool.d.ts +36 -0
- package/dist/memory/tool.js +84 -0
- 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 +13653 -9986
- package/dist/reads/cursor.js +3 -1
- package/dist/reads/types.d.ts +3 -3
- package/dist/record/agent.d.ts +27 -0
- package/dist/record/agent.js +46 -0
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/index.d.ts +2 -2
- package/dist/record/index.js +2 -2
- 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 +8 -7
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +2 -2
- package/dist/sandbox/placement.js +7 -10
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +25 -17
- package/dist/sandbox/pods/lifecycle.js +51 -29
- 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 +14 -3
- 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 +11 -6
- package/dist/sandbox/resolve.js +19 -9
- package/dist/sandbox/setup.d.ts +6 -2
- package/dist/sandbox/setup.js +47 -14
- 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/0032_agents.sql +54 -0
- package/dist/store/postgres/drizzle/0033_agent_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0034_protocol_17_session_queue.sql +13 -0
- package/dist/store/postgres/drizzle/0035_t4_triggers.sql +41 -0
- package/dist/store/postgres/drizzle/0036_t5_memories.sql +30 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +63 -0
- package/dist/store/postgres/reads.js +17 -82
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/schema.d.ts +1382 -151
- package/dist/store/postgres/schema.js +191 -37
- package/dist/store/postgres/store.js +384 -105
- package/dist/store/postgres/tenant.js +1 -2
- package/dist/store/types.d.ts +172 -57
- package/dist/store/types.js +0 -1
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +41 -39
- package/dist/tenant/agent-manifests.d.ts +72 -0
- package/dist/tenant/agent-manifests.js +359 -0
- package/dist/tenant/agent-versions.d.ts +126 -0
- package/dist/tenant/agent-versions.js +389 -0
- package/dist/tenant/agents.d.ts +230 -0
- package/dist/tenant/{environments.js → agents.js} +493 -328
- package/dist/tenant/auth.d.ts +1 -1
- package/dist/tenant/auth.js +3 -3
- package/dist/tenant/commands.d.ts +63 -2
- package/dist/tenant/commands.js +240 -90
- package/dist/tenant/context.d.ts +57 -33
- package/dist/tenant/context.js +6 -2
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +15 -51
- 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 +11 -10
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +29 -6
- package/dist/tenant/identity-file.js +45 -15
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +34 -26
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +87 -0
- package/dist/tenant/limits.js +172 -0
- package/dist/tenant/memories.d.ts +38 -0
- package/dist/tenant/memories.js +119 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +11 -14
- package/dist/tenant/outbound.js +10 -15
- package/dist/tenant/reset.js +2 -2
- package/dist/tenant/run-grants.d.ts +6 -0
- package/dist/tenant/run-grants.js +7 -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.d.ts +1 -1
- package/dist/tenant/runtime.js +38 -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.d.ts +28 -45
- package/dist/tenant/sessions.js +89 -209
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +10 -10
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +51 -51
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/triggers.d.ts +143 -0
- package/dist/tenant/triggers.js +715 -0
- package/dist/tenant/turn-timing.d.ts +11 -1
- package/dist/tenant/turn-timing.js +3 -1
- package/dist/tenant/types.d.ts +9 -14
- package/dist/tenant/worker.d.ts +4 -10
- package/dist/tenant/worker.js +10 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +10 -7
- package/dist/traces/map.js +16 -12
- 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 +390 -0
- package/dist/tuning.js +677 -0
- package/dist/vault/headers.js +4 -2
- package/dist/vault/service.d.ts +13 -8
- package/dist/vault/service.js +61 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -12
- package/dist/api/http/routes/environment-manifests.d.ts +0 -9
- package/dist/api/http/routes/environment-manifests.js +0 -118
- package/dist/api/http/routes/environments.d.ts +0 -11
- package/dist/api/http/routes/environments.js +0 -332
- 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/record/environment.d.ts +0 -25
- package/dist/record/environment.js +0 -44
- 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/environment-manifests.d.ts +0 -57
- package/dist/tenant/environment-manifests.js +0 -200
- package/dist/tenant/environments.d.ts +0 -174
- 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
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Our engine's built-in artifact tools, which the Runtime pins in a session agent's `runtime`
|
|
3
|
-
* block (`sandbox/session-sandbox.ts`). The Runtime runs them itself, like the sandbox tools.
|
|
4
|
-
*
|
|
5
|
-
* `save_artifact` (F8.1), which comes with a session's sandbox, saves a file as an artifact of
|
|
6
|
-
* its session:
|
|
7
|
-
* - `path`: a file in the session's sandbox, read as bytes in the sandbox's queue;
|
|
8
|
-
* - `content`: text the model passes.
|
|
9
|
-
* The version commits with `artifact.created` (or `artifact.version.created`) on the session's
|
|
10
|
-
* stream, carrying the turn and the tool call.
|
|
11
|
-
*
|
|
12
|
-
* `read_artifact` (R2b C11, Q24; every agent since protocol 12) reads a text artifact in pages of
|
|
13
|
-
* at most 32 KiB, from a byte `offset`: how the model reads a tool result the Runtime stored
|
|
14
|
-
* because it was too large to show (`tool-results.ts`), and a file of an earlier turn again, by
|
|
15
|
-
* its reference line. Another file is shown as a file of the result when the Tenant's model reads
|
|
16
|
-
* its type, and refused (`artifact.unsupported`) when it does not. It reaches the session's own
|
|
17
|
-
* artifacts and the Tenant-wide ones its messages named (`Session.fileArtifacts`), never another
|
|
18
|
-
* session's. `version` picks a version; the latest by default.
|
|
19
|
-
*
|
|
20
|
-
* Expected problems (no such file, too large, the Tenant's total reached, not text) are failed
|
|
21
|
-
* outcomes the model sees; only infrastructure errors throw.
|
|
22
|
-
*/
|
|
23
|
-
import { basename } from "node:path";
|
|
24
|
-
import { READ_ARTIFACT_MAX_BYTES } from "@nylorun/core/define";
|
|
25
|
-
import { isArtifactId } from "@nylorun/core/compatibility";
|
|
26
|
-
import { readArtifactLimits } from "../artifacts/config.js";
|
|
27
|
-
import { contentTypeFor, essence, isText, normalizeContentType, unreadableReason, } from "../artifacts/media-types.js";
|
|
28
|
-
import { currentModelInputs } from "../model/catalog.js";
|
|
29
|
-
import { uploadArtifact } from "../artifacts/service.js";
|
|
30
|
-
import { DEFAULT_CONTENT_TYPE } from "../blob/index.js";
|
|
31
|
-
import { sandboxToolSpec } from "../sandbox/manager.js";
|
|
32
|
-
import { sandboxWorkspaceOf } from "../sandbox/share.js";
|
|
33
|
-
import { sessionOf } from "./context.js";
|
|
34
|
-
import { HttpError } from "./http.js";
|
|
35
|
-
import { manifestFor } from "./session.js";
|
|
36
|
-
import { jsonBytes, utf8Boundary } from "./tool-results.js";
|
|
37
|
-
import { toolIds } from "./transcript.js";
|
|
38
|
-
export { isReadArtifactCall, isSaveArtifactCall } from "../harness/calls.js";
|
|
39
|
-
const failed = (code, message) => ({
|
|
40
|
-
kind: "failed",
|
|
41
|
-
code,
|
|
42
|
-
message,
|
|
43
|
-
});
|
|
44
|
-
export async function callSaveArtifact(ctx, request, signal) {
|
|
45
|
-
const input = (request.input ?? {});
|
|
46
|
-
const path = typeof input.path === "string" && input.path !== "" ? input.path : undefined;
|
|
47
|
-
const content = typeof input.content === "string" ? input.content : undefined;
|
|
48
|
-
if ((path === undefined) === (content === undefined))
|
|
49
|
-
return failed("artifact.invalid_input", "Give path or content, not both.");
|
|
50
|
-
const artifactId = typeof input.artifactId === "string" ? input.artifactId : undefined;
|
|
51
|
-
const { s, workspace, limits, ours } = await ctx.store.tx(async (t) => {
|
|
52
|
-
const s = await sessionOf(t, request.sessionId);
|
|
53
|
-
// A new version only of an artifact of this session.
|
|
54
|
-
const ours = artifactId === undefined ||
|
|
55
|
-
(isArtifactId(artifactId) && (await t.artifact(artifactId))?.sessionId === s.id);
|
|
56
|
-
return {
|
|
57
|
-
s,
|
|
58
|
-
workspace: sandboxWorkspaceOf(s),
|
|
59
|
-
limits: await readArtifactLimits(t),
|
|
60
|
-
ours,
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
if (!ours)
|
|
64
|
-
return failed("artifact.not_found", `Artifact ${artifactId} is not an artifact of this session.`);
|
|
65
|
-
let bytes;
|
|
66
|
-
let fileName;
|
|
67
|
-
if (path !== undefined) {
|
|
68
|
-
const spec = sandboxToolSpec(manifestFor(s.manifest, request.agent), "read");
|
|
69
|
-
if (!spec)
|
|
70
|
-
return failed("artifact.no_sandbox", "This session has no sandbox to read a path from.");
|
|
71
|
-
const read = await ctx.sandbox.readBytes({
|
|
72
|
-
id: workspace.ownerId,
|
|
73
|
-
activeTurnId: s.activeTurnId,
|
|
74
|
-
manifest: s.manifest,
|
|
75
|
-
...(workspace.environmentId === undefined ? {} : { environmentId: workspace.environmentId }),
|
|
76
|
-
}, spec, path, limits.fileBytes, signal);
|
|
77
|
-
if (read.kind === "failed")
|
|
78
|
-
return read;
|
|
79
|
-
if (read.kind === "missing")
|
|
80
|
-
return failed("artifact.not_found", `${read.path} does not exist in the sandbox.`);
|
|
81
|
-
bytes = read.bytes;
|
|
82
|
-
fileName = basename(read.path);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
bytes = new TextEncoder().encode(content);
|
|
86
|
-
fileName = "artifact.txt";
|
|
87
|
-
}
|
|
88
|
-
const name = typeof input.name === "string" && input.name !== "" ? input.name : fileName;
|
|
89
|
-
let contentType;
|
|
90
|
-
if (typeof input.contentType === "string") {
|
|
91
|
-
const declared = normalizeContentType(input.contentType);
|
|
92
|
-
if (declared === undefined)
|
|
93
|
-
return failed("artifact.invalid_input", `${input.contentType} is not a media type.`);
|
|
94
|
-
contentType = declared;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
contentType = contentTypeFor(name);
|
|
98
|
-
if (content !== undefined && contentType === DEFAULT_CONTENT_TYPE)
|
|
99
|
-
contentType = "text/plain; charset=utf-8";
|
|
100
|
-
}
|
|
101
|
-
const ids = toolIds(request.context);
|
|
102
|
-
try {
|
|
103
|
-
const saved = await uploadArtifact(ctx, {
|
|
104
|
-
...(artifactId !== undefined ? { artifactId } : { name }),
|
|
105
|
-
contentType,
|
|
106
|
-
sessionId: s.id,
|
|
107
|
-
source: "engine",
|
|
108
|
-
turnId: request.turnId,
|
|
109
|
-
...("callId" in ids ? { callId: ids.callId } : {}),
|
|
110
|
-
}, bytes,
|
|
111
|
-
// Our engine saves into its own session only.
|
|
112
|
-
{ owner: s.ownerUserId }, signal);
|
|
113
|
-
return {
|
|
114
|
-
kind: "completed",
|
|
115
|
-
output: {
|
|
116
|
-
artifactId: saved.artifact.artifactId,
|
|
117
|
-
version: saved.version.version,
|
|
118
|
-
name: saved.artifact.name,
|
|
119
|
-
contentType: saved.version.contentType,
|
|
120
|
-
size: saved.version.size,
|
|
121
|
-
sha256: saved.version.sha256,
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
if (error instanceof HttpError)
|
|
127
|
-
return failed(error.rejection.code === "limit_exceeded" ? "artifact.too_large" : "artifact.rejected", error.message);
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
export async function callReadArtifact(ctx, request, signal) {
|
|
132
|
-
const input = (request.input ?? {});
|
|
133
|
-
const artifactId = typeof input.artifactId === "string" ? input.artifactId : "";
|
|
134
|
-
const wanted = input.version;
|
|
135
|
-
const offset = input.offset ?? 0;
|
|
136
|
-
const length = input.length ?? READ_ARTIFACT_MAX_BYTES;
|
|
137
|
-
if (artifactId === "" ||
|
|
138
|
-
(wanted !== undefined &&
|
|
139
|
-
(typeof wanted !== "number" || !Number.isSafeInteger(wanted) || wanted < 1)) ||
|
|
140
|
-
typeof offset !== "number" ||
|
|
141
|
-
!Number.isSafeInteger(offset) ||
|
|
142
|
-
offset < 0 ||
|
|
143
|
-
typeof length !== "number" ||
|
|
144
|
-
!Number.isSafeInteger(length) ||
|
|
145
|
-
length < 1 ||
|
|
146
|
-
length > READ_ARTIFACT_MAX_BYTES)
|
|
147
|
-
return failed("artifact.invalid_input", `Give artifactId, a version of 1 or more if any, an offset of 0 or more, and a length of 1 to ${READ_ARTIFACT_MAX_BYTES}.`);
|
|
148
|
-
const found = await ctx.store.tx(async (t) => {
|
|
149
|
-
const s = await sessionOf(t, request.sessionId);
|
|
150
|
-
if (!isArtifactId(artifactId))
|
|
151
|
-
return undefined;
|
|
152
|
-
// An artifact of this session, or a Tenant-wide one its messages named; never another
|
|
153
|
-
// session's.
|
|
154
|
-
const artifact = await t.artifact(artifactId);
|
|
155
|
-
if (!artifact ||
|
|
156
|
-
(artifact.sessionId !== s.id &&
|
|
157
|
-
!(artifact.sessionId === null && s.fileArtifacts?.includes(artifact.id))))
|
|
158
|
-
return undefined;
|
|
159
|
-
const version = await t.artifactVersion(artifact.id, typeof wanted === "number" ? wanted : artifact.latestVersion);
|
|
160
|
-
return { artifact, version };
|
|
161
|
-
});
|
|
162
|
-
if (!found)
|
|
163
|
-
return failed("artifact.not_found", `Artifact ${artifactId} is not an artifact of this session.`);
|
|
164
|
-
if (!found.version)
|
|
165
|
-
return failed("artifact.not_found", `Artifact ${artifactId} has no version ${String(wanted)}.`);
|
|
166
|
-
const { artifact, version } = found;
|
|
167
|
-
const about = {
|
|
168
|
-
artifactId: artifact.id,
|
|
169
|
-
version: version.version,
|
|
170
|
-
name: artifact.name,
|
|
171
|
-
contentType: version.contentType,
|
|
172
|
-
size: version.size,
|
|
173
|
-
};
|
|
174
|
-
if (artifact.kind !== "file")
|
|
175
|
-
return failed("artifact.not_text", `${artifact.name} is a folder; read_artifact reads a file.`);
|
|
176
|
-
if (!isText(version.contentType)) {
|
|
177
|
-
// A file the Tenant's model reads goes to it beside the result (protocol 12). Without a
|
|
178
|
-
// model in the vault (a test's fake provider), text and images.
|
|
179
|
-
const inputs = (await currentModelInputs(ctx)) ?? ["text", "image"];
|
|
180
|
-
const reason = unreadableReason(version.contentType, inputs);
|
|
181
|
-
if (reason)
|
|
182
|
-
return failed("artifact.unsupported", `${artifact.name} is ${essence(version.contentType)}: ${reason}.`);
|
|
183
|
-
return {
|
|
184
|
-
kind: "completed",
|
|
185
|
-
output: about,
|
|
186
|
-
files: [
|
|
187
|
-
{
|
|
188
|
-
mediaType: version.contentType,
|
|
189
|
-
reference: {
|
|
190
|
-
artifactId: artifact.id,
|
|
191
|
-
version: version.version,
|
|
192
|
-
name: artifact.name,
|
|
193
|
-
size: version.size,
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
if (offset > version.size || (offset === version.size && version.size > 0))
|
|
200
|
-
return failed("artifact.invalid_input", `Offset ${offset} is past the end of ${artifact.name}, which is ${version.size} bytes.`);
|
|
201
|
-
const end = Math.min(offset + length, version.size);
|
|
202
|
-
const bytes = end === offset ? new Uint8Array() : await readRange(ctx, version.blobKey, offset, end - 1, signal);
|
|
203
|
-
const page = textPage(bytes, end === version.size);
|
|
204
|
-
const next = offset + page.bytes;
|
|
205
|
-
return {
|
|
206
|
-
kind: "completed",
|
|
207
|
-
output: {
|
|
208
|
-
...about,
|
|
209
|
-
offset,
|
|
210
|
-
content: page.text,
|
|
211
|
-
...(next < version.size ? { nextOffset: next } : {}),
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
/** Bytes `start` to `end` (both inclusive) of a version's blob. */
|
|
216
|
-
async function readRange(ctx, blobKey, start, end, signal) {
|
|
217
|
-
const got = await ctx.blobs.get(blobKey, { range: { start, end }, signal });
|
|
218
|
-
if (!got)
|
|
219
|
-
throw new Error("The artifact's bytes are missing from the Object store");
|
|
220
|
-
return new Uint8Array(await new Response(got.body).arrayBuffer());
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* The text of a page of UTF-8 `bytes` in whole characters, at most `READ_ARTIFACT_MAX_BYTES`
|
|
224
|
-
* once escaped as JSON, and how many bytes it used: the next page starts there. A page that
|
|
225
|
-
* starts inside a character skips its rest, and one that ends inside one (not `last`) stops
|
|
226
|
-
* before it.
|
|
227
|
-
*/
|
|
228
|
-
function textPage(bytes, last) {
|
|
229
|
-
const from = utf8Boundary(bytes, 0, "up");
|
|
230
|
-
let to = last ? bytes.byteLength : utf8Boundary(bytes, bytes.byteLength, "down");
|
|
231
|
-
// A page shorter than one character: take what there is.
|
|
232
|
-
if (to <= from)
|
|
233
|
-
to = bytes.byteLength;
|
|
234
|
-
const decode = () => Buffer.from(bytes.subarray(from, to)).toString("utf8");
|
|
235
|
-
let text = decode();
|
|
236
|
-
// Escaped (quotes, newlines, control characters), text takes more room than its bytes: each
|
|
237
|
-
// byte left out frees at least one.
|
|
238
|
-
for (let size = jsonBytes(text); to > from && size > READ_ARTIFACT_MAX_BYTES; size = jsonBytes(text)) {
|
|
239
|
-
to = utf8Boundary(bytes, Math.max(from, to - (size - READ_ARTIFACT_MAX_BYTES)), "down");
|
|
240
|
-
text = decode();
|
|
241
|
-
}
|
|
242
|
-
return { text, bytes: to };
|
|
243
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment manifests (protocol 14, D63): `/v1/environment-manifests/{name}`, named and
|
|
3
|
-
* versioned definitions of a kind of Environment, saved like agents.
|
|
4
|
-
*
|
|
5
|
-
* - **Versions.** A save whose manifest hashes the same as the latest answers it unchanged;
|
|
6
|
-
* any other writes the next version. A version is never changed or deleted alone.
|
|
7
|
-
* - **No Environment changes.** Saving a version reaches no Environment: an Environment is made
|
|
8
|
-
* from one version and keeps it for life (`putEnvironment`). The plan counts those behind.
|
|
9
|
-
* - **Plan and drift.** `?dryRun=true` answers what a save would do and saves nothing: the
|
|
10
|
-
* changes from the latest, every reason the Runtime would refuse it (the Tenant's limits,
|
|
11
|
-
* missing artifacts), warnings, and how many Environments stay on older versions.
|
|
12
|
-
* `If-Match: <version>` refuses (`412 manifest_changed`) a save whose latest version moved
|
|
13
|
-
* since the client read it; `0` asks for a manifest that does not exist yet.
|
|
14
|
-
* - **Every client is equal** (Q12): a version records who saved it (`savedBy`), and nothing is
|
|
15
|
-
* read-only by where it was saved from.
|
|
16
|
-
*/
|
|
17
|
-
import { type EnvironmentManifest, type EnvironmentManifestPlan, type EnvironmentManifestVersions, type EnvironmentManifestView, type PutEnvironmentManifestRequest, type SaveEnvironmentManifestResponse } from "@nylorun/core/contracts";
|
|
18
|
-
import { type RecipeRequest } from "../sandbox/resolve.js";
|
|
19
|
-
import type { AuthScope, TenantContext } from "./context.js";
|
|
20
|
-
/** What an environment manifest asks of an Environment's recipe. */
|
|
21
|
-
export declare function manifestRecipeRequest(manifest: EnvironmentManifest): RecipeRequest;
|
|
22
|
-
/** The hash a manifest's version records: equal manifests hash the same. */
|
|
23
|
-
export declare function environmentManifestHash(manifest: EnvironmentManifest): string;
|
|
24
|
-
/** Each field that differs between `before` and `after`, by path; arrays compare whole. */
|
|
25
|
-
export declare function manifestChanges(before: unknown, after: unknown, path?: string): {
|
|
26
|
-
path: string;
|
|
27
|
-
from?: unknown;
|
|
28
|
-
to?: unknown;
|
|
29
|
-
}[];
|
|
30
|
-
/**
|
|
31
|
-
* `PUT /v1/environment-manifests/{name}`: saves `body` as manifest `name`'s next version, or
|
|
32
|
-
* answers the latest unchanged; with `dryRun`, the plan of that save and nothing saved.
|
|
33
|
-
*/
|
|
34
|
-
export declare function saveEnvironmentManifest(ctx: TenantContext, name: string, body: PutEnvironmentManifestRequest, scope: AuthScope, options: {
|
|
35
|
-
dryRun: true;
|
|
36
|
-
ifMatch?: number;
|
|
37
|
-
}): Promise<EnvironmentManifestPlan>;
|
|
38
|
-
export declare function saveEnvironmentManifest(ctx: TenantContext, name: string, body: PutEnvironmentManifestRequest, scope: AuthScope, options?: {
|
|
39
|
-
dryRun?: false;
|
|
40
|
-
ifMatch?: number;
|
|
41
|
-
}): Promise<SaveEnvironmentManifestResponse>;
|
|
42
|
-
/** `GET /v1/environment-manifests/{name}` (the latest) and `.../versions/{n}`. */
|
|
43
|
-
export declare function getEnvironmentManifest(ctx: TenantContext, name: string, version?: number): Promise<EnvironmentManifestView>;
|
|
44
|
-
/** `GET /v1/environment-manifests`: each manifest's latest version. */
|
|
45
|
-
export declare function listEnvironmentManifests(ctx: TenantContext): Promise<{
|
|
46
|
-
manifests: EnvironmentManifestView[];
|
|
47
|
-
}>;
|
|
48
|
-
/** `GET /v1/environment-manifests/{name}/versions`: every version, newest first. */
|
|
49
|
-
export declare function environmentManifestVersions(ctx: TenantContext, name: string): Promise<EnvironmentManifestVersions>;
|
|
50
|
-
/**
|
|
51
|
-
* `DELETE /v1/environment-manifests/{name}`: deletes every version, refused while any Environment
|
|
52
|
-
* was made from one (`409 manifest_in_use`): the Environment names the version it runs.
|
|
53
|
-
*/
|
|
54
|
-
export declare function deleteEnvironmentManifest(ctx: TenantContext, name: string): Promise<{
|
|
55
|
-
name: string;
|
|
56
|
-
deleted: boolean;
|
|
57
|
-
}>;
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Environment manifests (protocol 14, D63): `/v1/environment-manifests/{name}`, named and
|
|
3
|
-
* versioned definitions of a kind of Environment, saved like agents.
|
|
4
|
-
*
|
|
5
|
-
* - **Versions.** A save whose manifest hashes the same as the latest answers it unchanged;
|
|
6
|
-
* any other writes the next version. A version is never changed or deleted alone.
|
|
7
|
-
* - **No Environment changes.** Saving a version reaches no Environment: an Environment is made
|
|
8
|
-
* from one version and keeps it for life (`putEnvironment`). The plan counts those behind.
|
|
9
|
-
* - **Plan and drift.** `?dryRun=true` answers what a save would do and saves nothing: the
|
|
10
|
-
* changes from the latest, every reason the Runtime would refuse it (the Tenant's limits,
|
|
11
|
-
* missing artifacts), warnings, and how many Environments stay on older versions.
|
|
12
|
-
* `If-Match: <version>` refuses (`412 manifest_changed`) a save whose latest version moved
|
|
13
|
-
* since the client read it; `0` asks for a manifest that does not exist yet.
|
|
14
|
-
* - **Every client is equal** (Q12): a version records who saved it (`savedBy`), and nothing is
|
|
15
|
-
* read-only by where it was saved from.
|
|
16
|
-
*/
|
|
17
|
-
import {} from "@nylorun/core/contracts";
|
|
18
|
-
import { hashManifest } from "@nylorun/core/compatibility";
|
|
19
|
-
import { canonical } from "../store/canonical.js";
|
|
20
|
-
import { resolveRecipe } from "../sandbox/resolve.js";
|
|
21
|
-
import { effectiveSandboxConfig, readSandboxConfig } from "../sandbox/tenant-config.js";
|
|
22
|
-
import { actorOf, fileErrors } from "./environments.js";
|
|
23
|
-
import { fail } from "./http.js";
|
|
24
|
-
/** What an environment manifest asks of an Environment's recipe. */
|
|
25
|
-
export function manifestRecipeRequest(manifest) {
|
|
26
|
-
return {
|
|
27
|
-
...(manifest.image === undefined ? {} : { image: manifest.image }),
|
|
28
|
-
...(manifest.resources === undefined ? {} : { resources: manifest.resources }),
|
|
29
|
-
...(manifest.storage === undefined ? {} : { storage: manifest.storage }),
|
|
30
|
-
...(manifest.network === undefined ? {} : { network: manifest.network }),
|
|
31
|
-
...(manifest.harness === undefined ? {} : { harness: manifest.harness }),
|
|
32
|
-
...(manifest.lifecycle === undefined ? {} : { lifecycle: manifest.lifecycle }),
|
|
33
|
-
...(manifest.setup === undefined ? {} : { setup: manifest.setup }),
|
|
34
|
-
...(manifest.files === undefined ? {} : { files: manifest.files }),
|
|
35
|
-
...(manifest.secrets === undefined ? {} : { secrets: manifest.secrets }),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
/** The hash a manifest's version records: equal manifests hash the same. */
|
|
39
|
-
export function environmentManifestHash(manifest) {
|
|
40
|
-
return hashManifest(manifest);
|
|
41
|
-
}
|
|
42
|
-
const notFound = (name, version) => fail(404, version === undefined ? `No environment manifest is named ${name}.` : `Environment manifest ${name} has no version ${version}.`, {
|
|
43
|
-
code: "manifest_not_found",
|
|
44
|
-
});
|
|
45
|
-
function viewOf(row, counts) {
|
|
46
|
-
return {
|
|
47
|
-
name: row.name,
|
|
48
|
-
version: row.version,
|
|
49
|
-
hash: row.hash,
|
|
50
|
-
manifest: row.body,
|
|
51
|
-
savedBy: row.savedBy,
|
|
52
|
-
createdAt: row.createdAt,
|
|
53
|
-
environments: counts.get(row.version) ?? 0,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
/** Each field that differs between `before` and `after`, by path; arrays compare whole. */
|
|
57
|
-
export function manifestChanges(before, after, path = "") {
|
|
58
|
-
const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
59
|
-
if (object(before) && object(after)) {
|
|
60
|
-
const keys = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();
|
|
61
|
-
return keys.flatMap((key) => manifestChanges(before[key], after[key], path ? `${path}.${key}` : key));
|
|
62
|
-
}
|
|
63
|
-
if (canonical(before) === canonical(after))
|
|
64
|
-
return [];
|
|
65
|
-
return [
|
|
66
|
-
{
|
|
67
|
-
path,
|
|
68
|
-
...(before === undefined ? {} : { from: before }),
|
|
69
|
-
...(after === undefined ? {} : { to: after }),
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
}
|
|
73
|
-
/** What a save of `manifest` should tell its saver, though it would be saved. */
|
|
74
|
-
function warningsOf(ctx, manifest) {
|
|
75
|
-
const warnings = [];
|
|
76
|
-
if (manifest.image !== undefined && !manifest.image.includes("@sha256:"))
|
|
77
|
-
warnings.push({
|
|
78
|
-
code: "image_tag",
|
|
79
|
-
message: `image ${manifest.image} is a tag, not a digest: each Environment pins what its first pod pulls, so two Tenants may run different bytes.`,
|
|
80
|
-
});
|
|
81
|
-
if (manifest.kind === "pod" && !ctx.pods)
|
|
82
|
-
warnings.push({
|
|
83
|
-
code: "pods_unavailable",
|
|
84
|
-
message: "kind pod needs sandbox pods, which this Tenant does not have: an Environment made from it is refused until `nylorun local sandbox enable`.",
|
|
85
|
-
});
|
|
86
|
-
return warnings;
|
|
87
|
-
}
|
|
88
|
-
/** Every reason the Runtime would refuse `manifest` now: the Tenant's limits and its files. */
|
|
89
|
-
async function errorsOf(t, manifest) {
|
|
90
|
-
const config = effectiveSandboxConfig(await readSandboxConfig(t));
|
|
91
|
-
const resolved = resolveRecipe(manifest.kind ?? "virtual", manifestRecipeRequest(manifest), config);
|
|
92
|
-
return [...(resolved.kind === "error" ? resolved.errors : []), ...(await fileErrors(t, manifest.files))];
|
|
93
|
-
}
|
|
94
|
-
export async function saveEnvironmentManifest(ctx, name, body, scope, options = {}) {
|
|
95
|
-
const { requestId: _, ...manifest } = body;
|
|
96
|
-
if (manifest.name !== name)
|
|
97
|
-
fail(400, `The manifest is named ${manifest.name}, and the path names ${name}: they must match.`);
|
|
98
|
-
const hash = environmentManifestHash(manifest);
|
|
99
|
-
const warnings = warningsOf(ctx, manifest);
|
|
100
|
-
return ctx.store.tx(async (t) => {
|
|
101
|
-
await t.lockEnvironmentManifest(name);
|
|
102
|
-
const latest = await t.environmentManifest(name);
|
|
103
|
-
if (options.ifMatch !== undefined && (latest?.version ?? 0) !== options.ifMatch)
|
|
104
|
-
fail(412, latest === undefined
|
|
105
|
-
? `Environment manifest ${name} does not exist, and If-Match named version ${options.ifMatch}.`
|
|
106
|
-
: `Environment manifest ${name} is at version ${latest.version}, not ${options.ifMatch}: someone saved it since you read it. Read it again, or save without If-Match to replace it.`, { code: "manifest_changed", details: { latest: latest?.version ?? 0 } });
|
|
107
|
-
const unchanged = latest?.hash === hash;
|
|
108
|
-
const to = unchanged ? latest.version : (latest?.version ?? 0) + 1;
|
|
109
|
-
const counts = await t.environmentsOfManifest(name);
|
|
110
|
-
const errors = await errorsOf(t, manifest);
|
|
111
|
-
if (options.dryRun) {
|
|
112
|
-
let behind = 0;
|
|
113
|
-
for (const [version, n] of counts)
|
|
114
|
-
if (version < to)
|
|
115
|
-
behind += n;
|
|
116
|
-
return {
|
|
117
|
-
name,
|
|
118
|
-
from: latest?.version ?? null,
|
|
119
|
-
to,
|
|
120
|
-
unchanged,
|
|
121
|
-
changes: unchanged ? [] : manifestChanges(latest?.body ?? {}, manifest),
|
|
122
|
-
warnings,
|
|
123
|
-
errors,
|
|
124
|
-
behind,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
if (errors.length > 0)
|
|
128
|
-
fail(400, errors.join(" "));
|
|
129
|
-
if (unchanged)
|
|
130
|
-
return { manifest: viewOf(latest, counts), unchanged: true, warnings };
|
|
131
|
-
const row = {
|
|
132
|
-
name,
|
|
133
|
-
version: to,
|
|
134
|
-
body: manifest,
|
|
135
|
-
hash,
|
|
136
|
-
savedBy: { actor: actorOf(scope) },
|
|
137
|
-
createdAt: new Date().toISOString(),
|
|
138
|
-
};
|
|
139
|
-
await t.insertEnvironmentManifest(row);
|
|
140
|
-
// Its files by hash (Q6), recorded as a definition's are, for the sweep that deletes unused ones.
|
|
141
|
-
const shas = [...new Set((manifest.files ?? []).flatMap((file) => ("files" in file ? Object.values(file.files) : [])))];
|
|
142
|
-
if (shas.length > 0)
|
|
143
|
-
await t.insertDefinitionFileUses(`environment-manifest:${name}`, hash, shas);
|
|
144
|
-
return { manifest: viewOf(row, counts), unchanged: false, warnings };
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
/** `GET /v1/environment-manifests/{name}` (the latest) and `.../versions/{n}`. */
|
|
148
|
-
export async function getEnvironmentManifest(ctx, name, version) {
|
|
149
|
-
return ctx.store.tx(async (t) => {
|
|
150
|
-
const row = (await t.environmentManifest(name, version)) ?? notFound(name, version);
|
|
151
|
-
return viewOf(row, await t.environmentsOfManifest(name));
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
/** `GET /v1/environment-manifests`: each manifest's latest version. */
|
|
155
|
-
export async function listEnvironmentManifests(ctx) {
|
|
156
|
-
return ctx.store.tx(async (t) => {
|
|
157
|
-
const manifests = [];
|
|
158
|
-
for (const row of await t.listEnvironmentManifests())
|
|
159
|
-
manifests.push(viewOf(row, await t.environmentsOfManifest(row.name)));
|
|
160
|
-
return { manifests };
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
/** `GET /v1/environment-manifests/{name}/versions`: every version, newest first. */
|
|
164
|
-
export async function environmentManifestVersions(ctx, name) {
|
|
165
|
-
return ctx.store.tx(async (t) => {
|
|
166
|
-
const rows = await t.environmentManifestVersions(name);
|
|
167
|
-
if (rows.length === 0)
|
|
168
|
-
notFound(name);
|
|
169
|
-
const counts = await t.environmentsOfManifest(name);
|
|
170
|
-
return {
|
|
171
|
-
name,
|
|
172
|
-
latest: rows[0].version,
|
|
173
|
-
versions: rows.map((row) => ({
|
|
174
|
-
version: row.version,
|
|
175
|
-
hash: row.hash,
|
|
176
|
-
savedBy: row.savedBy,
|
|
177
|
-
createdAt: row.createdAt,
|
|
178
|
-
environments: counts.get(row.version) ?? 0,
|
|
179
|
-
})),
|
|
180
|
-
};
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* `DELETE /v1/environment-manifests/{name}`: deletes every version, refused while any Environment
|
|
185
|
-
* was made from one (`409 manifest_in_use`): the Environment names the version it runs.
|
|
186
|
-
*/
|
|
187
|
-
export async function deleteEnvironmentManifest(ctx, name) {
|
|
188
|
-
return ctx.store.tx(async (t) => {
|
|
189
|
-
await t.lockEnvironmentManifest(name);
|
|
190
|
-
let made = 0;
|
|
191
|
-
for (const n of (await t.environmentsOfManifest(name)).values())
|
|
192
|
-
made += n;
|
|
193
|
-
if (made > 0)
|
|
194
|
-
fail(409, `${made} Environment(s) were made from environment manifest ${name}: delete them first, or keep the manifest.`, {
|
|
195
|
-
code: "manifest_in_use",
|
|
196
|
-
details: { environments: made },
|
|
197
|
-
});
|
|
198
|
-
return { name, deleted: (await t.deleteEnvironmentManifest(name)) > 0 };
|
|
199
|
-
});
|
|
200
|
-
}
|