@nylorun/runtime 0.23.0-beta → 0.24.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 +148 -0
- package/README.md +79 -28
- package/dist/adapters/execution/restate.js +1 -1
- package/dist/adapters/replication/pgoutput.d.ts +2 -0
- package/dist/adapters/replication/pgoutput.js +6 -3
- package/dist/adapters/sandbox/local.d.ts +9 -1
- package/dist/adapters/sandbox/local.js +16 -4
- package/dist/adapters/sandbox/virtual.js +4 -1
- package/dist/api/a2a/endpoint.js +5 -1
- package/dist/api/a2a/routes.d.ts +2 -0
- package/dist/api/a2a/routes.js +2 -2
- package/dist/api/a2a/tasks.js +2 -1
- package/dist/api/ag-ui/endpoint.js +4 -2
- package/dist/api/ag-ui/history.js +4 -1
- package/dist/api/ag-ui/routes.d.ts +4 -1
- package/dist/api/ag-ui/routes.js +11 -10
- package/dist/api/ag-ui/translate.js +3 -2
- package/dist/api/components.d.ts +4953 -1204
- package/dist/api/components.js +25 -16
- package/dist/api/gate/keys.js +7 -1
- package/dist/api/gate/routes.d.ts +6 -0
- package/dist/api/gate/routes.js +39 -3
- package/dist/api/http/app.js +4 -4
- package/dist/api/http/define.d.ts +6 -0
- package/dist/api/http/define.js +13 -1
- package/dist/api/http/respond.js +1 -1
- package/dist/api/http/routes/artifacts.js +35 -8
- package/dist/api/http/routes/coverage.d.ts +9 -0
- package/dist/api/http/routes/coverage.js +34 -0
- package/dist/api/http/routes/environments.d.ts +11 -0
- package/dist/api/http/routes/environments.js +283 -0
- package/dist/api/http/routes/mcp.d.ts +1 -1
- package/dist/api/http/routes/mcp.js +2 -2
- package/dist/api/http/routes/me.js +1 -1
- package/dist/api/http/routes/reads.d.ts +45 -9
- package/dist/api/http/routes/reads.js +54 -5
- package/dist/api/http/routes/sessions.js +11 -6
- package/dist/api/http/routes/tenant.js +32 -10
- package/dist/api/openapi.js +31 -26
- package/dist/artifacts/disk.d.ts +59 -0
- package/dist/artifacts/disk.js +563 -0
- package/dist/artifacts/export.d.ts +1 -1
- package/dist/artifacts/export.js +2 -3
- package/dist/artifacts/folders.d.ts +6 -3
- package/dist/artifacts/folders.js +17 -12
- package/dist/artifacts/service.d.ts +2 -1
- package/dist/artifacts/service.js +6 -1
- package/dist/artifacts/workspace.d.ts +1 -1
- package/dist/artifacts/workspace.js +1 -1
- package/dist/core/flow-host.d.ts +2 -3
- package/dist/core/flow-host.js +3 -0
- package/dist/core/provider.d.ts +3 -2
- package/dist/core/provider.js +5 -37
- package/dist/core/sandbox-routes.d.ts +2 -21
- package/dist/core/sandbox-routes.js +10 -43
- package/dist/execution/types.d.ts +1 -1
- package/dist/gates/contract.d.ts +3 -24
- package/dist/gates/contract.js +1 -36
- package/dist/gates/egress-credentials.d.ts +9 -0
- package/dist/gates/egress-credentials.js +11 -4
- package/dist/gates/egress.d.ts +28 -1
- package/dist/gates/egress.js +115 -10
- package/dist/gates/fixture-model.d.ts +28 -0
- package/dist/gates/fixture-model.js +92 -0
- package/dist/gates/handler.js +4 -4
- package/dist/gates/http-tool.d.ts +6 -2
- package/dist/gates/http-tool.js +3 -5
- package/dist/gates/in-process.d.ts +5 -0
- package/dist/gates/in-process.js +12 -1
- package/dist/gates/journal.d.ts +30 -0
- package/dist/gates/journal.js +52 -0
- package/dist/gates/mcp-handler.d.ts +4 -0
- package/dist/gates/mcp-handler.js +5 -1
- package/dist/gates/meter.d.ts +10 -2
- package/dist/gates/meter.js +20 -5
- package/dist/gates/policy.d.ts +49 -0
- package/dist/gates/policy.js +134 -0
- package/dist/gates/tenant-vaults.d.ts +7 -7
- package/dist/gates/tenant-vaults.js +5 -5
- package/dist/gates/tool-calls.d.ts +10 -5
- package/dist/gates/tool-calls.js +28 -10
- package/dist/gates/tool-client.d.ts +8 -0
- package/dist/gates/tool-client.js +27 -11
- package/dist/gates/tool-contract.d.ts +10 -68
- package/dist/gates/tool-contract.js +1 -85
- package/dist/gates/tool-gate.d.ts +1 -1
- package/dist/harness/calls.d.ts +12 -5
- package/dist/harness/calls.js +21 -12
- package/dist/harness/executors.js +12 -9
- package/dist/harness/main.js +18 -5
- package/dist/harness/pod.d.ts +34 -0
- package/dist/harness/pod.js +41 -0
- package/dist/harness/service.d.ts +0 -1
- package/dist/harness/service.js +13 -5
- package/dist/harness/setup.d.ts +27 -0
- package/dist/harness/setup.js +150 -0
- package/dist/harness/ws-client.js +3 -2
- package/dist/harness/ws-port.d.ts +1 -5
- package/dist/harness/ws-port.js +1 -5
- package/dist/harness-api/in-process.js +3 -3
- package/dist/harness-api/record.d.ts +15 -1
- package/dist/harness-api/record.js +174 -44
- package/dist/harness-api/redact.d.ts +3 -0
- package/dist/harness-api/redact.js +45 -0
- package/dist/harness-api/server.d.ts +8 -0
- package/dist/harness-api/server.js +93 -13
- package/dist/harness-api/start.d.ts +10 -3
- package/dist/harness-api/start.js +40 -10
- package/dist/harness-api/tool-journal.d.ts +96 -0
- package/dist/harness-api/tool-journal.js +102 -0
- package/dist/harness-api/tool-status.d.ts +13 -0
- package/dist/harness-api/tool-status.js +26 -0
- package/dist/harness-api/workspace.d.ts +6 -3
- package/dist/harness-api/workspace.js +27 -11
- package/dist/harness-api/ws-server.d.ts +11 -3
- package/dist/harness-api/ws-server.js +94 -15
- package/dist/host/gates.d.ts +2 -1
- package/dist/host/gates.js +10 -2
- package/dist/host/main.js +54 -5
- package/dist/host/operate.js +67 -5
- package/dist/host/stack-config.d.ts +64 -0
- package/dist/host/stack-config.js +61 -1
- package/dist/keys/client.js +4 -1
- package/dist/keys/keys.d.ts +5 -5
- package/dist/keys/keys.js +5 -7
- package/dist/management-openapi.json +999 -2211
- package/dist/mcp/connect.d.ts +25 -20
- package/dist/mcp/connect.js +29 -30
- package/dist/mcp/pool.d.ts +2 -2
- package/dist/mcp/pool.js +1 -1
- package/dist/mcp/preview.d.ts +3 -3
- package/dist/mcp/preview.js +10 -20
- package/dist/mcp/snapshot.d.ts +5 -11
- package/dist/mcp/snapshot.js +6 -7
- package/dist/model/pi-model.js +8 -0
- package/dist/openapi.json +6955 -2526
- package/dist/reads/types.d.ts +16 -3
- package/dist/record/append.d.ts +3 -1
- package/dist/record/append.js +1 -0
- package/dist/record/envelope.d.ts +5 -1
- package/dist/record/envelope.js +7 -3
- package/dist/record/environment.d.ts +25 -0
- package/dist/record/environment.js +44 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/sandbox/disk-fs.d.ts +95 -0
- package/dist/sandbox/disk-fs.js +206 -0
- package/dist/sandbox/join.d.ts +17 -18
- package/dist/sandbox/join.js +42 -15
- package/dist/sandbox/manager.d.ts +16 -8
- package/dist/sandbox/manager.js +33 -17
- package/dist/sandbox/placement.d.ts +13 -12
- package/dist/sandbox/placement.js +29 -12
- package/dist/sandbox/pods/client.d.ts +6 -1
- package/dist/sandbox/pods/lifecycle.d.ts +28 -27
- package/dist/sandbox/pods/lifecycle.js +14 -26
- package/dist/sandbox/pods/reconcile.js +18 -12
- package/dist/sandbox/pods/spec.d.ts +15 -13
- package/dist/sandbox/pods/spec.js +16 -20
- package/dist/sandbox/records.d.ts +2 -2
- package/dist/sandbox/records.js +3 -3
- package/dist/sandbox/resolve.d.ts +35 -27
- package/dist/sandbox/resolve.js +66 -41
- package/dist/sandbox/session-sandbox.js +1 -1
- package/dist/sandbox/setup.d.ts +61 -0
- package/dist/sandbox/setup.js +123 -0
- package/dist/sandbox/share.d.ts +11 -21
- package/dist/sandbox/share.js +12 -41
- package/dist/sandbox/tenant-config.d.ts +12 -14
- package/dist/sandbox/tenant-config.js +12 -17
- package/dist/sandbox/types.d.ts +6 -0
- package/dist/store/postgres/drizzle/0020_environments.sql +63 -0
- package/dist/store/postgres/drizzle/0021_disk_versions.sql +7 -0
- package/dist/store/postgres/drizzle/0022_model_usage_timing.sql +5 -0
- package/dist/store/postgres/drizzle/0023_artifact_pages.sql +1 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/reads.js +117 -17
- package/dist/store/postgres/record-writer.d.ts +4 -4
- package/dist/store/postgres/record-writer.js +9 -9
- package/dist/store/postgres/record.d.ts +7 -0
- package/dist/store/postgres/record.js +23 -2
- package/dist/store/postgres/schema.d.ts +934 -761
- package/dist/store/postgres/schema.js +102 -73
- package/dist/store/postgres/store.js +119 -66
- package/dist/store/types.d.ts +109 -48
- package/dist/tenant/advance.d.ts +7 -6
- package/dist/tenant/advance.js +115 -42
- package/dist/tenant/artifact-tool.js +3 -4
- package/dist/tenant/commands.d.ts +15 -2
- package/dist/tenant/commands.js +93 -12
- package/dist/tenant/context.d.ts +48 -22
- package/dist/tenant/context.js +4 -15
- package/dist/tenant/effects.d.ts +1 -1
- package/dist/tenant/effects.js +37 -20
- package/dist/tenant/environments.d.ts +167 -0
- package/dist/tenant/environments.js +823 -0
- package/dist/tenant/harness-tokens.d.ts +21 -0
- package/dist/tenant/harness-tokens.js +61 -0
- package/dist/tenant/host-token.d.ts +1 -1
- package/dist/tenant/host-token.js +2 -4
- package/dist/tenant/identity-file.d.ts +4 -4
- package/dist/tenant/identity-file.js +17 -8
- package/dist/tenant/issuers.js +3 -3
- package/dist/tenant/principals.js +1 -1
- package/dist/tenant/reset.js +3 -3
- package/dist/tenant/runtime.d.ts +6 -1
- package/dist/tenant/runtime.js +7 -1
- package/dist/tenant/session.d.ts +3 -3
- package/dist/tenant/session.js +0 -14
- package/dist/tenant/sessions.d.ts +13 -13
- package/dist/tenant/sessions.js +61 -138
- package/dist/tenant/signing-keys.d.ts +5 -1
- package/dist/tenant/status.js +3 -3
- package/dist/tenant/sweep.js +12 -6
- package/dist/tenant/transcript.d.ts +210 -20
- package/dist/tenant/transcript.js +62 -11
- package/dist/tenant/turn-timing.d.ts +43 -0
- package/dist/tenant/turn-timing.js +39 -0
- package/dist/tenant/types.d.ts +2 -0
- package/dist/traces/ids.d.ts +42 -0
- package/dist/traces/ids.js +85 -0
- package/dist/traces/key.d.ts +6 -0
- package/dist/traces/key.js +37 -0
- package/dist/traces/map.d.ts +113 -0
- package/dist/traces/map.js +317 -0
- package/dist/traces/options.d.ts +7 -0
- package/dist/traces/options.js +1 -0
- package/dist/traces/otlp.d.ts +42 -0
- package/dist/traces/otlp.js +84 -0
- package/dist/traces/projector.d.ts +58 -0
- package/dist/traces/projector.js +200 -0
- package/dist/traces/read.d.ts +23 -0
- package/dist/traces/read.js +16 -0
- package/dist/vault/coverage.d.ts +26 -0
- package/dist/vault/coverage.js +64 -0
- package/dist/vault/error.d.ts +11 -1
- package/dist/vault/error.js +5 -1
- package/dist/vault/service.d.ts +47 -48
- package/dist/vault/service.js +321 -382
- package/dist/vault/sources.d.ts +5 -16
- package/dist/vault/sources.js +6 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/package.json +13 -13
- package/dist/api/http/routes/sandboxes.d.ts +0 -9
- package/dist/api/http/routes/sandboxes.js +0 -153
- package/dist/api/http/routes/vaults.d.ts +0 -10
- package/dist/api/http/routes/vaults.js +0 -139
- package/dist/record/sandbox.d.ts +0 -25
- package/dist/record/sandbox.js +0 -43
- package/dist/tenant/sandboxes.d.ts +0 -123
- package/dist/tenant/sandboxes.js +0 -566
|
@@ -69,14 +69,14 @@ export const sessions = nylorun.table("sessions", {
|
|
|
69
69
|
ownerExpiresAt: timestamp({ withTimezone: true, mode: "date" }),
|
|
70
70
|
/** The session owner (`ownerUserId`), not the lease column `owner`. */
|
|
71
71
|
ownerUserId: field("->>'ownerUserId'"),
|
|
72
|
-
/** The
|
|
73
|
-
|
|
72
|
+
/** The Environment the session names (`environment`, protocol 11). */
|
|
73
|
+
environmentId: field("->>'environmentId'"),
|
|
74
74
|
}, (t) => [
|
|
75
75
|
index("sessions_created").on(t.createdAt.desc().nullsLast(), t.id.desc().nullsFirst()),
|
|
76
76
|
index("sessions_status").on(t.status, t.ownerExpiresAt),
|
|
77
77
|
index("sessions_agent").on(t.agentId),
|
|
78
78
|
index("sessions_owner_user").on(t.ownerUserId),
|
|
79
|
-
index("
|
|
79
|
+
index("sessions_environment").on(t.environmentId),
|
|
80
80
|
]);
|
|
81
81
|
export const commands = documents("commands");
|
|
82
82
|
export const effects = nylorun.table("effects", {
|
|
@@ -107,22 +107,92 @@ export const principals = nylorun.table("principals", {
|
|
|
107
107
|
idempotencyKey: text(),
|
|
108
108
|
createdAt: text().notNull(),
|
|
109
109
|
});
|
|
110
|
+
/**
|
|
111
|
+
* An Environment (D55, protocol 11): its id, kind, labels, the recipe the app asked for and the
|
|
112
|
+
* recipe its compute runs (`applied`, copied from `recipe` when a stopped pod is asked to run),
|
|
113
|
+
* the owner guard, and when it was last used. Kind `pod` adds the pod's lifecycle columns.
|
|
114
|
+
* Sessions name it by id (the session body's `environmentId`). Not the `sandboxes` document
|
|
115
|
+
* table, which holds the virtual backend's compute records, keyed by workspace key.
|
|
116
|
+
*/
|
|
117
|
+
export const environments = nylorun.table("environments", {
|
|
118
|
+
id: textC().primaryKey(),
|
|
119
|
+
kind: text({ enum: ["virtual", "pod", "none"] }).notNull(),
|
|
120
|
+
/** `{ key: value }`, filtered through `nylorun.doc(labels)`. */
|
|
121
|
+
labels: jsonText().notNull(),
|
|
122
|
+
/** What the app asked for (`EnvironmentRecipe`), resolved against the Tenant's limits. */
|
|
123
|
+
recipe: jsonText().notNull(),
|
|
124
|
+
/** The recipe the current pod or workspace runs. */
|
|
125
|
+
applied: jsonText().notNull(),
|
|
126
|
+
version: integer().notNull().default(1),
|
|
127
|
+
appliedVersion: integer().notNull().default(1),
|
|
128
|
+
/** The owner guard: only this owner's sessions may name the Environment. */
|
|
129
|
+
ownerUserId: text(),
|
|
130
|
+
/** A turn's end, a recipe change, a secret write, a stop or a reset (`lifecycle.deleteAfter`). */
|
|
131
|
+
lastUsedAt: timestamp({ withTimezone: true, mode: "string" }).notNull().defaultNow(),
|
|
132
|
+
createdAt: textC().notNull(),
|
|
133
|
+
updatedAt: textC().notNull(),
|
|
134
|
+
// Kind `pod` (F7.2, D34): null otherwise. `tenant/environments.ts` and the `Sandbox`
|
|
135
|
+
// object's reconcile (`sandbox/pods/reconcile.ts`) are the only writers.
|
|
136
|
+
/** The agent-sandbox Sandbox's name, `sbx-<16 base32>-g<volume_gen>` (`podName`). */
|
|
137
|
+
k8sName: textC(),
|
|
138
|
+
/** Bumped by every reset: a new Sandbox on a new volume. */
|
|
139
|
+
volumeGen: integer().notNull().default(0),
|
|
140
|
+
/** `running`, `suspended` or `deleted`. */
|
|
141
|
+
desired: text({ enum: ["running", "suspended", "deleted"] }),
|
|
142
|
+
/** `creating`, `running`, `suspended`, `lost`, `failed` or `deleting`. */
|
|
143
|
+
observed: text({ enum: ["creating", "running", "suspended", "lost", "failed", "deleting"] }),
|
|
144
|
+
/** The UID of the pod that last joined. */
|
|
145
|
+
podUid: text(),
|
|
146
|
+
/**
|
|
147
|
+
* Bumped by every join (`host.join`), relaunch, loss and reset: host and egress tokens carry
|
|
148
|
+
* it (`epc`), and one of an older epoch is refused (egress-gate reads it too).
|
|
149
|
+
*/
|
|
150
|
+
hostEpoch: integer().notNull().default(0),
|
|
151
|
+
/** sha256 (hex) of the incarnation's join token; the token itself is never stored. */
|
|
152
|
+
joinTokenHash: text(),
|
|
153
|
+
/** Bumped by every change the Runtime asks of the pod: the sandboxes service's `opId`. */
|
|
154
|
+
rev: integer().notNull().default(0),
|
|
155
|
+
/** When a turn last ended on it, for the idle timer. */
|
|
156
|
+
lastActiveAt: timestamp({ withTimezone: true }),
|
|
157
|
+
/** When the current incarnation was asked to run, for the ready deadline. */
|
|
158
|
+
startedAt: timestamp({ withTimezone: true }),
|
|
159
|
+
/** Why it failed or was lost. */
|
|
160
|
+
reason: text(),
|
|
161
|
+
/** An earlier incarnation's Sandbox still to delete (after a reset or a loss). */
|
|
162
|
+
retiring: textC(),
|
|
163
|
+
/**
|
|
164
|
+
* The id the engine keeps on the volume (`/harness/volume-id`), recorded at the first join:
|
|
165
|
+
* a join with another one means the volume was replaced (its files are gone), so `lost`.
|
|
166
|
+
*/
|
|
167
|
+
volumeId: text(),
|
|
168
|
+
/**
|
|
169
|
+
* The disk lease (R2d step 3): `{ kind, version?, startedAt, until }` while a snapshot, a
|
|
170
|
+
* restore or a seed copies the disk; turns are refused until it ends or `until` passes.
|
|
171
|
+
*/
|
|
172
|
+
diskOp: jsonText(),
|
|
173
|
+
/** The disk version the next volume starts from: `{ artifactId, version }` (R2d step 3). */
|
|
174
|
+
diskSeed: jsonText(),
|
|
175
|
+
}, (t) => [check("environments_kind_check", sql `${t.kind} IN ('virtual', 'pod', 'none')`)]);
|
|
176
|
+
/**
|
|
177
|
+
* A vault (D56): the host model's (`id = 'host'`), or one Environment's, created and deleted with
|
|
178
|
+
* it (`environment_id`, unique, deleted with the Environment's row). No other vault exists.
|
|
179
|
+
*/
|
|
110
180
|
export const vaults = nylorun.table("vaults", {
|
|
111
181
|
id: textC().primaryKey(),
|
|
112
182
|
name: text().notNull(),
|
|
113
|
-
ownerUserId: text().notNull(),
|
|
114
183
|
/** JSON text, or null. */
|
|
115
184
|
metadataJson: text(),
|
|
116
185
|
createdAt: textC().notNull(),
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
* attachable to any session (owner `installation`); `host`: the model vault, never attached.
|
|
120
|
-
*/
|
|
121
|
-
scope: text({ enum: ["user", "installation", "host"] }).notNull().default("user"),
|
|
186
|
+
/** The Environment the vault belongs to; null only for the host vault. */
|
|
187
|
+
environmentId: textC(),
|
|
122
188
|
}, (t) => [
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
189
|
+
uniqueIndex("vaults_environment").on(t.environmentId),
|
|
190
|
+
foreignKey({
|
|
191
|
+
name: "vaults_environment_id_fkey",
|
|
192
|
+
columns: [t.environmentId],
|
|
193
|
+
foreignColumns: [environments.id],
|
|
194
|
+
}).onDelete("cascade"),
|
|
195
|
+
check("vaults_owner_check", sql `(${t.id} = 'host') = (${t.environmentId} IS NULL)`),
|
|
126
196
|
]);
|
|
127
197
|
/** A credential of a vault: its binding in the clear, its secret sealed. */
|
|
128
198
|
export const vaultCredentials = nylorun.table("vault_credentials", {
|
|
@@ -146,6 +216,7 @@ export const vaultCredentials = nylorun.table("vault_credentials", {
|
|
|
146
216
|
foreignColumns: [vaults.id],
|
|
147
217
|
}).onDelete("cascade"),
|
|
148
218
|
index("vault_credentials_vault").on(t.vaultId, t.createdAt, t.id),
|
|
219
|
+
uniqueIndex("vault_credentials_name").on(t.vaultId, t.name),
|
|
149
220
|
]);
|
|
150
221
|
export const vaultAudit = nylorun.table("vault_audit", {
|
|
151
222
|
ord: bigint({ mode: "number" }).primaryKey().generatedAlwaysAsIdentity(),
|
|
@@ -198,7 +269,9 @@ export const signingKeys = nylorun.table("signing_keys", {
|
|
|
198
269
|
* The model usage ledger (P1.3): one row per model call a gate served, written after the call.
|
|
199
270
|
* A second row for the same effect (a call the gate ran again after a gateway restart) is
|
|
200
271
|
* flagged `duplicate`; both were billed. Budgets read their spend from it. Token counts are 0
|
|
201
|
-
* when the provider sent none.
|
|
272
|
+
* when the provider sent none. `startedAt`, `endedAt` and `firstTokenMs` are the gate's own
|
|
273
|
+
* timing of the call (R7.1, migration 0022); null on rows written before, and `firstTokenMs`
|
|
274
|
+
* when the provider streamed nothing.
|
|
202
275
|
*/
|
|
203
276
|
export const modelUsage = nylorun.table("model_usage", {
|
|
204
277
|
id: textC().primaryKey(),
|
|
@@ -222,6 +295,12 @@ export const modelUsage = nylorun.table("model_usage", {
|
|
|
222
295
|
/** True when an earlier row has the same `effectKey`: the provider billed the call twice. */
|
|
223
296
|
duplicate: boolean().notNull(),
|
|
224
297
|
createdAt: text().notNull(),
|
|
298
|
+
/** When the gate sent the call to the provider. */
|
|
299
|
+
startedAt: text(),
|
|
300
|
+
/** When the provider's answer was complete. */
|
|
301
|
+
endedAt: text(),
|
|
302
|
+
/** From `startedAt` to the provider's first stream event. */
|
|
303
|
+
firstTokenMs: integer(),
|
|
225
304
|
}, (t) => [
|
|
226
305
|
index("model_usage_session").on(t.sessionId, t.createdAt, t.id),
|
|
227
306
|
index("model_usage_export").on(t.txid, t.id),
|
|
@@ -260,60 +339,6 @@ export const toolCrossings = nylorun.table("tool_crossings", {
|
|
|
260
339
|
/** The gate's answer (`McpAnswer`), once the call ended; null while it runs or once lost. */
|
|
261
340
|
answer: jsonText(),
|
|
262
341
|
}, (t) => [index("tool_crossings_settled").on(t.settledAt)]);
|
|
263
|
-
/**
|
|
264
|
-
* A sandbox resource (blueprint D39, F7.1): its id, kind, the spec resolved against the
|
|
265
|
-
* Tenant's limits when it was created, and the developer's labels. Sessions attach to it by id
|
|
266
|
-
* (the session body's `sandboxId`). Not the `sandboxes` document table, which holds the virtual
|
|
267
|
-
* backend's compute records, keyed by workspace key.
|
|
268
|
-
*/
|
|
269
|
-
export const sandboxResources = nylorun.table("sandbox_resources", {
|
|
270
|
-
id: textC().primaryKey(),
|
|
271
|
-
kind: text({ enum: ["virtual", "pod"] }).notNull(),
|
|
272
|
-
/** The resolved `SandboxManifest`. */
|
|
273
|
-
spec: jsonText().notNull(),
|
|
274
|
-
/** `{ key: value }`, filtered through `nylorun.doc(labels)`. */
|
|
275
|
-
labels: jsonText().notNull(),
|
|
276
|
-
createdAt: textC().notNull(),
|
|
277
|
-
updatedAt: textC().notNull(),
|
|
278
|
-
// Kind `pod` (F7.2, D34): null for a virtual sandbox. `tenant/sandboxes.ts` and the
|
|
279
|
-
// `Sandbox` object's reconcile (`sandbox/pods/reconcile.ts`) are the only writers.
|
|
280
|
-
/** The agent-sandbox Sandbox's name, `sbx-<16 base32>-g<volume_gen>` (`podName`). */
|
|
281
|
-
k8sName: textC(),
|
|
282
|
-
/** Bumped by every reset: a new Sandbox on a new volume. */
|
|
283
|
-
volumeGen: integer().notNull().default(0),
|
|
284
|
-
/** `running`, `suspended` or `deleted`. */
|
|
285
|
-
desired: text({ enum: ["running", "suspended", "deleted"] }),
|
|
286
|
-
/** `creating`, `running`, `suspended`, `expired`, `lost`, `failed` or `deleting`. */
|
|
287
|
-
observed: text({
|
|
288
|
-
enum: ["creating", "running", "suspended", "expired", "lost", "failed", "deleting"],
|
|
289
|
-
}),
|
|
290
|
-
/** The UID of the pod that last joined. */
|
|
291
|
-
podUid: text(),
|
|
292
|
-
/**
|
|
293
|
-
* Bumped by every join (`host.join`), relaunch, loss and reset: host and egress tokens carry
|
|
294
|
-
* it (`epc`), and one of an older epoch is refused (egress-gate reads it too).
|
|
295
|
-
*/
|
|
296
|
-
hostEpoch: integer().notNull().default(0),
|
|
297
|
-
/** sha256 (hex) of the incarnation's join token; the token itself is never stored. */
|
|
298
|
-
joinTokenHash: text(),
|
|
299
|
-
/** Bumped by every change the Runtime asks of the pod: the sandboxes service's `opId`. */
|
|
300
|
-
rev: integer().notNull().default(0),
|
|
301
|
-
/** When a turn last ended on it, for the idle timer. */
|
|
302
|
-
lastActiveAt: timestamp({ withTimezone: true }),
|
|
303
|
-
/** When its TTL passes (`createdAt + lifecycle.ttl`). */
|
|
304
|
-
expiresAt: timestamp({ withTimezone: true }),
|
|
305
|
-
/** When the current incarnation was asked to run, for the ready deadline. */
|
|
306
|
-
startedAt: timestamp({ withTimezone: true }),
|
|
307
|
-
/** Why it failed or was lost. */
|
|
308
|
-
reason: text(),
|
|
309
|
-
/** An earlier incarnation's Sandbox still to delete (after a reset or a loss). */
|
|
310
|
-
retiring: textC(),
|
|
311
|
-
/**
|
|
312
|
-
* The id the engine keeps on the volume (`/harness/volume-id`), recorded at the first join:
|
|
313
|
-
* a join with another one means the volume was replaced (its files are gone), so `lost`.
|
|
314
|
-
*/
|
|
315
|
-
volumeId: text(),
|
|
316
|
-
});
|
|
317
342
|
/**
|
|
318
343
|
* Artifacts (blueprint D35, F8.1 and F8.2): an id, a name and numbered versions. The bytes are in
|
|
319
344
|
* the Object store under each version's `blobKey` (a folder's: its manifest); a blob counts only
|
|
@@ -331,8 +356,12 @@ export const artifacts = nylorun.table("artifacts", {
|
|
|
331
356
|
labelsJson: text(),
|
|
332
357
|
createdAt: textC().notNull(),
|
|
333
358
|
updatedAt: text().notNull(),
|
|
359
|
+
/** The Environment whose disk versions this folder holds (R2d step 3); one per Environment. */
|
|
360
|
+
environmentId: textC(),
|
|
334
361
|
}, (t) => [
|
|
335
362
|
index("artifacts_session").on(t.sessionId, t.createdAt, t.id),
|
|
363
|
+
uniqueIndex("artifacts_environment_disk").on(t.environmentId).where(sql `environment_id IS NOT NULL`),
|
|
364
|
+
index("artifacts_creation_order").on(t.createdAt, t.id),
|
|
336
365
|
check("artifacts_kind_check", sql `kind IN ('file', 'folder')`),
|
|
337
366
|
]);
|
|
338
367
|
/** The versions of an artifact, immutable once written. */
|
|
@@ -344,7 +373,7 @@ export const artifactVersions = nylorun.table("artifact_versions", {
|
|
|
344
373
|
size: bigint({ mode: "number" }).notNull(),
|
|
345
374
|
sha256: text().notNull(),
|
|
346
375
|
contentType: text().notNull(),
|
|
347
|
-
source: text({ enum: ["upload", "engine", "export"] }).notNull(),
|
|
376
|
+
source: text({ enum: ["upload", "engine", "export", "snapshot"] }).notNull(),
|
|
348
377
|
createdAt: textC().notNull(),
|
|
349
378
|
}, (t) => [
|
|
350
379
|
primaryKey({ name: "artifact_versions_pkey", columns: [t.artifactId, t.version] }),
|
|
@@ -471,16 +500,16 @@ export const sandboxSignals = nylorun.table("sandbox_signals", {
|
|
|
471
500
|
// ---------------------------------------------------------------------------
|
|
472
501
|
// nylorun_streams: the record
|
|
473
502
|
/**
|
|
474
|
-
* Each
|
|
475
|
-
* Written only by the record module; not published to the stream relay.
|
|
503
|
+
* Each Environment's lifecycle stream (`nylorun.environment-event/1`), numbered from 0 per
|
|
504
|
+
* Environment id. Written only by the record module; not published to the stream relay.
|
|
476
505
|
*/
|
|
477
|
-
export const
|
|
478
|
-
|
|
506
|
+
export const environmentEvents = nylorunStreams.table("environment_events", {
|
|
507
|
+
environmentId: textC().notNull(),
|
|
479
508
|
seq: bigint({ mode: "number" }).notNull(),
|
|
480
509
|
type: text().notNull(),
|
|
481
510
|
body: jsonText().notNull(),
|
|
482
511
|
committedAt: timestamp({ withTimezone: true, mode: "string" }).notNull().defaultNow(),
|
|
483
|
-
}, (t) => [primaryKey({ name: "
|
|
512
|
+
}, (t) => [primaryKey({ name: "environment_events_pkey", columns: [t.environmentId, t.seq] })]);
|
|
484
513
|
/** Each session's log head: the next seq and the basin generation its events go to. */
|
|
485
514
|
export const sessionLogHeads = nylorunStreams.table("session_log_heads", {
|
|
486
515
|
sessionId: textC().primaryKey(),
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* head).
|
|
33
33
|
* 2. A transaction that touches several sessions locks them in ascending id
|
|
34
34
|
* order, with `lockSessions` (`./locking.ts`), before writing any of them.
|
|
35
|
-
* 3.
|
|
35
|
+
* 3. An Environment's row (`environment(id, { lock: true })`, which orders its
|
|
36
36
|
* lifecycle stream) is locked after every session row the transaction locks, never
|
|
37
37
|
* before one.
|
|
38
38
|
*
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
52
52
|
import { randomUUID } from "node:crypto";
|
|
53
53
|
import { and, count, eq, getTableColumns, gte, gt, inArray, isNotNull, isNull, lt, lte, ne, or, sql, } from "drizzle-orm";
|
|
54
|
-
import {
|
|
54
|
+
import { appendEnvironmentEvent, appendEvent } from "../../record/index.js";
|
|
55
55
|
import { OwnershipLostError, PrincipalRoleConflict } from "../ownership.js";
|
|
56
56
|
import { followControlSignals, pruneSignals, writeSignal } from "./control.js";
|
|
57
57
|
import { database, driverError } from "./db.js";
|
|
58
58
|
import { expectedSchemaVersion, readSchemaVersion } from "./migrate.js";
|
|
59
59
|
import { createPostgresRecordReader } from "./record.js";
|
|
60
|
-
import {
|
|
61
|
-
import { artifacts, artifactVersions, artifactContent, definitionFiles, definitionFileUses, commands, definitions, effects, links, modelBudgets, modelUsage, principals, sandboxes,
|
|
60
|
+
import { postgresEnvironmentRecordWriter, postgresRecordWriter } from "./record-writer.js";
|
|
61
|
+
import { artifacts, artifactVersions, artifactContent, definitionFiles, definitionFileUses, commands, definitions, effects, links, modelBudgets, modelUsage, principals, sandboxes, environmentEvents, environments, sessionEvents, sessionLogHeads, sessions, toJson, toolCrossings, signingKeys, tenant, tenantSettings, vaultAudit, vaultCredentials, vaultIdempotency, vaults, sandboxSignals, wakes, } from "./schema.js";
|
|
62
62
|
export function createPostgresSessionStore(options) {
|
|
63
63
|
return new PostgresSessionStore(options);
|
|
64
64
|
}
|
|
@@ -240,14 +240,21 @@ function ownership(row) {
|
|
|
240
240
|
ownerExpiresAt: row.ownerExpiresAt?.toISOString() ?? null,
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
|
-
function
|
|
243
|
+
function environmentOf(row) {
|
|
244
244
|
return {
|
|
245
245
|
id: row.id,
|
|
246
246
|
kind: row.kind,
|
|
247
|
-
spec: row.spec,
|
|
248
247
|
labels: row.labels,
|
|
248
|
+
recipe: row.recipe,
|
|
249
|
+
applied: row.applied,
|
|
250
|
+
version: row.version,
|
|
251
|
+
appliedVersion: row.appliedVersion,
|
|
252
|
+
...(row.ownerUserId !== null ? { ownerUserId: row.ownerUserId } : {}),
|
|
253
|
+
lastUsedAt: new Date(row.lastUsedAt).toISOString(),
|
|
249
254
|
createdAt: row.createdAt,
|
|
250
255
|
updatedAt: row.updatedAt,
|
|
256
|
+
...(row.diskOp ? { diskOp: row.diskOp } : {}),
|
|
257
|
+
...(row.diskSeed ? { diskSeed: row.diskSeed } : {}),
|
|
251
258
|
...(row.k8sName !== null && row.desired !== null && row.observed !== null
|
|
252
259
|
? {
|
|
253
260
|
pod: {
|
|
@@ -260,7 +267,6 @@ function sandboxResourceOf(row) {
|
|
|
260
267
|
...(row.joinTokenHash !== null ? { joinTokenHash: row.joinTokenHash } : {}),
|
|
261
268
|
rev: row.rev,
|
|
262
269
|
...(row.lastActiveAt ? { lastActiveAt: row.lastActiveAt.toISOString() } : {}),
|
|
263
|
-
...(row.expiresAt ? { expiresAt: row.expiresAt.toISOString() } : {}),
|
|
264
270
|
...(row.startedAt ? { startedAt: row.startedAt.toISOString() } : {}),
|
|
265
271
|
...(row.reason !== null ? { reason: row.reason } : {}),
|
|
266
272
|
...(row.retiring !== null ? { retiring: row.retiring } : {}),
|
|
@@ -283,7 +289,6 @@ function podColumns(patch) {
|
|
|
283
289
|
joinTokenHash: patch.joinTokenHash,
|
|
284
290
|
rev: patch.rev,
|
|
285
291
|
lastActiveAt: date(patch.lastActiveAt),
|
|
286
|
-
expiresAt: date(patch.expiresAt),
|
|
287
292
|
startedAt: date(patch.startedAt),
|
|
288
293
|
reason: patch.reason,
|
|
289
294
|
retiring: patch.retiring,
|
|
@@ -389,7 +394,7 @@ class PostgresTx {
|
|
|
389
394
|
const [row] = await this.sessions().where(eq(sessions.id, id)).for("update");
|
|
390
395
|
return row && storedSession(row);
|
|
391
396
|
}
|
|
392
|
-
async event(sessionId, turnId, type, payload) {
|
|
397
|
+
async event(sessionId, turnId, type, payload, writer) {
|
|
393
398
|
this.check();
|
|
394
399
|
const db = this.db;
|
|
395
400
|
// The session row lock orders the session's events; the log head allocates under it.
|
|
@@ -408,6 +413,7 @@ class PostgresTx {
|
|
|
408
413
|
time: this.now(),
|
|
409
414
|
type,
|
|
410
415
|
payload,
|
|
416
|
+
...(writer ? { writer } : {}),
|
|
411
417
|
});
|
|
412
418
|
this.events.push(event);
|
|
413
419
|
this.generations.push(generation);
|
|
@@ -612,88 +618,92 @@ class PostgresTx {
|
|
|
612
618
|
this.check();
|
|
613
619
|
return this.bodies(sandboxes);
|
|
614
620
|
}
|
|
615
|
-
// ---
|
|
616
|
-
async
|
|
621
|
+
// --- Environments ----------------------------------------------------------
|
|
622
|
+
async environment(id, options = {}) {
|
|
617
623
|
this.check();
|
|
618
|
-
const query = this.db.select().from(
|
|
624
|
+
const query = this.db.select().from(environments).where(eq(environments.id, id));
|
|
619
625
|
const [row] = options.lock ? await query.for("update") : await query;
|
|
620
|
-
return row &&
|
|
626
|
+
return row && environmentOf(row);
|
|
621
627
|
}
|
|
622
|
-
async
|
|
628
|
+
async createEnvironment(row, limit) {
|
|
623
629
|
this.check();
|
|
624
630
|
// Every create takes this lock, so two never both see room for one more.
|
|
625
|
-
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.
|
|
631
|
+
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.environments'))`);
|
|
626
632
|
const [found] = await this.db
|
|
627
|
-
.select({ id:
|
|
628
|
-
.from(
|
|
629
|
-
.where(eq(
|
|
633
|
+
.select({ id: environments.id })
|
|
634
|
+
.from(environments)
|
|
635
|
+
.where(eq(environments.id, row.id));
|
|
630
636
|
if (found)
|
|
631
637
|
return "exists";
|
|
632
|
-
const [counted] = await this.db.select({ n: count() }).from(
|
|
638
|
+
const [counted] = await this.db.select({ n: count() }).from(environments);
|
|
633
639
|
if ((counted?.n ?? 0) >= limit)
|
|
634
640
|
return "limit";
|
|
635
|
-
const { pod, ...
|
|
636
|
-
await this.db.insert(
|
|
641
|
+
const { pod, ownerUserId, diskOp, diskSeed, ...environment } = row;
|
|
642
|
+
await this.db.insert(environments).values({
|
|
643
|
+
...environment,
|
|
644
|
+
ownerUserId: ownerUserId ?? null,
|
|
645
|
+
diskOp: diskOp ?? null,
|
|
646
|
+
diskSeed: diskSeed ?? null,
|
|
647
|
+
...(pod ? podColumns(pod) : {}),
|
|
648
|
+
});
|
|
637
649
|
return "created";
|
|
638
650
|
}
|
|
639
|
-
async
|
|
640
|
-
this.check();
|
|
641
|
-
await this.db.update(sandboxResources).set({ spec, updatedAt }).where(eq(sandboxResources.id, id));
|
|
642
|
-
}
|
|
643
|
-
async updateSandboxPod(id, patch, updatedAt) {
|
|
651
|
+
async updateEnvironment(id, patch, updatedAt) {
|
|
644
652
|
this.check();
|
|
653
|
+
const columns = Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined));
|
|
645
654
|
await this.db
|
|
646
|
-
.update(
|
|
647
|
-
.set({ ...
|
|
648
|
-
.where(eq(
|
|
655
|
+
.update(environments)
|
|
656
|
+
.set({ ...columns, updatedAt })
|
|
657
|
+
.where(eq(environments.id, id));
|
|
649
658
|
}
|
|
650
|
-
async
|
|
659
|
+
async updateEnvironmentPod(id, patch, updatedAt) {
|
|
651
660
|
this.check();
|
|
652
661
|
await this.db
|
|
653
|
-
.update(
|
|
654
|
-
.set({
|
|
655
|
-
.where(eq(
|
|
662
|
+
.update(environments)
|
|
663
|
+
.set({ ...podColumns(patch), updatedAt })
|
|
664
|
+
.where(eq(environments.id, id));
|
|
656
665
|
}
|
|
657
|
-
async
|
|
666
|
+
async deleteEnvironment(id) {
|
|
658
667
|
this.check();
|
|
659
|
-
|
|
668
|
+
// Its vault and credentials go with it (ON DELETE CASCADE).
|
|
669
|
+
await this.db.delete(environments).where(eq(environments.id, id));
|
|
660
670
|
}
|
|
661
|
-
async
|
|
671
|
+
async listEnvironments(filter = {}) {
|
|
662
672
|
this.check();
|
|
663
673
|
const labels = filter.labels;
|
|
664
674
|
const rows = await this.db
|
|
665
675
|
.select()
|
|
666
|
-
.from(
|
|
676
|
+
.from(environments)
|
|
667
677
|
.where(labels === undefined || Object.keys(labels).length === 0
|
|
668
678
|
? undefined
|
|
669
|
-
: sql `nylorun.doc(${
|
|
670
|
-
.orderBy(
|
|
671
|
-
return rows.map(
|
|
679
|
+
: sql `nylorun.doc(${environments.labels}) @> ${toJson(labels)}::jsonb`)
|
|
680
|
+
.orderBy(environments.id);
|
|
681
|
+
return rows.map(environmentOf);
|
|
672
682
|
}
|
|
673
|
-
async
|
|
683
|
+
async sessionsOnEnvironment(environmentId) {
|
|
674
684
|
this.check();
|
|
675
685
|
const rows = await this.sessions()
|
|
676
|
-
.where(eq(sessions.
|
|
686
|
+
.where(eq(sessions.environmentId, environmentId))
|
|
677
687
|
.orderBy(sessions.id);
|
|
678
688
|
return rows.map((row) => storedSession(row));
|
|
679
689
|
}
|
|
680
|
-
async
|
|
690
|
+
async environmentEvent(environmentId, type, payload) {
|
|
681
691
|
this.check();
|
|
682
|
-
return
|
|
692
|
+
return appendEnvironmentEvent(postgresEnvironmentRecordWriter(this.db), {
|
|
683
693
|
tenantId: this.tenantId,
|
|
684
|
-
|
|
694
|
+
environmentId,
|
|
685
695
|
time: this.now(),
|
|
686
696
|
type,
|
|
687
697
|
payload,
|
|
688
698
|
});
|
|
689
699
|
}
|
|
690
|
-
async
|
|
700
|
+
async environmentEvents(environmentId, options = {}) {
|
|
691
701
|
this.check();
|
|
692
702
|
const rows = await this.db
|
|
693
|
-
.select({ body:
|
|
694
|
-
.from(
|
|
695
|
-
.where(and(eq(
|
|
696
|
-
.orderBy(
|
|
703
|
+
.select({ body: environmentEvents.body })
|
|
704
|
+
.from(environmentEvents)
|
|
705
|
+
.where(and(eq(environmentEvents.environmentId, environmentId), options.fromSeq === undefined ? undefined : gte(environmentEvents.seq, options.fromSeq)))
|
|
706
|
+
.orderBy(environmentEvents.seq)
|
|
697
707
|
.limit(options.limit ?? 1000);
|
|
698
708
|
return rows.map((row) => row.body);
|
|
699
709
|
}
|
|
@@ -831,21 +841,13 @@ class PostgresTx {
|
|
|
831
841
|
const [row] = await this.db.select().from(vaults).where(eq(vaults.id, id));
|
|
832
842
|
return row;
|
|
833
843
|
}
|
|
834
|
-
async
|
|
835
|
-
this.check();
|
|
836
|
-
return this.db
|
|
837
|
-
.select()
|
|
838
|
-
.from(vaults)
|
|
839
|
-
.where(and(eq(vaults.ownerUserId, ownerUserId), eq(vaults.scope, "user")))
|
|
840
|
-
.orderBy(vaults.createdAt, vaults.id);
|
|
841
|
-
}
|
|
842
|
-
async installationVaults() {
|
|
844
|
+
async vaultOfEnvironment(environmentId) {
|
|
843
845
|
this.check();
|
|
844
|
-
|
|
846
|
+
const [row] = await this.db
|
|
845
847
|
.select()
|
|
846
848
|
.from(vaults)
|
|
847
|
-
.where(eq(vaults.
|
|
848
|
-
|
|
849
|
+
.where(eq(vaults.environmentId, environmentId));
|
|
850
|
+
return row;
|
|
849
851
|
}
|
|
850
852
|
async updateVaultMetadata(id, metadataJson) {
|
|
851
853
|
this.check();
|
|
@@ -868,6 +870,14 @@ class PostgresTx {
|
|
|
868
870
|
.where(eq(vaultCredentials.id, id));
|
|
869
871
|
return row;
|
|
870
872
|
}
|
|
873
|
+
async credentialByName(vaultId, name) {
|
|
874
|
+
this.check();
|
|
875
|
+
const [row] = await this.db
|
|
876
|
+
.select()
|
|
877
|
+
.from(vaultCredentials)
|
|
878
|
+
.where(and(eq(vaultCredentials.vaultId, vaultId), eq(vaultCredentials.name, name)));
|
|
879
|
+
return row;
|
|
880
|
+
}
|
|
871
881
|
async credentialsForVault(vaultId, filter = {}) {
|
|
872
882
|
this.check();
|
|
873
883
|
return this.db
|
|
@@ -976,6 +986,16 @@ class PostgresTx {
|
|
|
976
986
|
.returning();
|
|
977
987
|
return inserted;
|
|
978
988
|
}
|
|
989
|
+
async modelUsageOf(effectKey) {
|
|
990
|
+
this.check();
|
|
991
|
+
const [row] = await this.db
|
|
992
|
+
.select()
|
|
993
|
+
.from(modelUsage)
|
|
994
|
+
.where(and(eq(modelUsage.effectKey, effectKey), eq(modelUsage.duplicate, false)))
|
|
995
|
+
.orderBy(modelUsage.createdAt, modelUsage.id)
|
|
996
|
+
.limit(1);
|
|
997
|
+
return row;
|
|
998
|
+
}
|
|
979
999
|
async modelUsageTotals(query) {
|
|
980
1000
|
this.check();
|
|
981
1001
|
const [row] = await this.db
|
|
@@ -1016,6 +1036,15 @@ class PostgresTx {
|
|
|
1016
1036
|
.returning({ key: toolCrossings.key });
|
|
1017
1037
|
return inserted.length === 1;
|
|
1018
1038
|
}
|
|
1039
|
+
async restartToolCrossing(row) {
|
|
1040
|
+
this.check();
|
|
1041
|
+
const restarted = await this.db
|
|
1042
|
+
.update(toolCrossings)
|
|
1043
|
+
.set({ startedAt: row.startedAt })
|
|
1044
|
+
.where(and(eq(toolCrossings.key, row.key), eq(toolCrossings.hash, row.hash), isNull(toolCrossings.settledAt)))
|
|
1045
|
+
.returning({ key: toolCrossings.key });
|
|
1046
|
+
return restarted.length === 1;
|
|
1047
|
+
}
|
|
1019
1048
|
async settleToolCrossing(key, answer, settledAt) {
|
|
1020
1049
|
this.check();
|
|
1021
1050
|
await this.db
|
|
@@ -1038,6 +1067,12 @@ class PostgresTx {
|
|
|
1038
1067
|
const [row] = options.lock ? await query.for("update") : await query;
|
|
1039
1068
|
return row;
|
|
1040
1069
|
}
|
|
1070
|
+
async diskArtifact(environmentId, options = {}) {
|
|
1071
|
+
this.check();
|
|
1072
|
+
const query = this.db.select().from(artifacts).where(eq(artifacts.environmentId, environmentId));
|
|
1073
|
+
const [row] = options.lock ? await query.for("update") : await query;
|
|
1074
|
+
return row;
|
|
1075
|
+
}
|
|
1041
1076
|
async listArtifacts(filter = {}) {
|
|
1042
1077
|
this.check();
|
|
1043
1078
|
return this.db
|
|
@@ -1088,6 +1123,24 @@ class PostgresTx {
|
|
|
1088
1123
|
await this.db.delete(artifacts).where(eq(artifacts.id, id));
|
|
1089
1124
|
return deleted.map((row) => row.blobKey);
|
|
1090
1125
|
}
|
|
1126
|
+
async deleteArtifactVersion(artifactId, version) {
|
|
1127
|
+
this.check();
|
|
1128
|
+
const [deleted] = await this.db
|
|
1129
|
+
.delete(artifactVersions)
|
|
1130
|
+
.where(and(eq(artifactVersions.artifactId, artifactId), eq(artifactVersions.version, version)))
|
|
1131
|
+
.returning({ blobKey: artifactVersions.blobKey });
|
|
1132
|
+
if (!deleted)
|
|
1133
|
+
return undefined;
|
|
1134
|
+
const [left] = await this.db
|
|
1135
|
+
.select({ latest: sql `max(${artifactVersions.version})` })
|
|
1136
|
+
.from(artifactVersions)
|
|
1137
|
+
.where(eq(artifactVersions.artifactId, artifactId));
|
|
1138
|
+
await this.db
|
|
1139
|
+
.update(artifacts)
|
|
1140
|
+
.set({ latestVersion: Number(left?.latest ?? 0), updatedAt: new Date().toISOString() })
|
|
1141
|
+
.where(eq(artifacts.id, artifactId));
|
|
1142
|
+
return deleted.blobKey;
|
|
1143
|
+
}
|
|
1091
1144
|
async lockArtifactQuota() {
|
|
1092
1145
|
this.check();
|
|
1093
1146
|
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.artifacts'))`);
|
|
@@ -1228,10 +1281,11 @@ class PostgresTx {
|
|
|
1228
1281
|
basinGeneration: sql `${tenant.basinGeneration} + 1`,
|
|
1229
1282
|
});
|
|
1230
1283
|
}
|
|
1231
|
-
if (scope === "
|
|
1284
|
+
if (scope === "environments" || scope === "all") {
|
|
1232
1285
|
await db.delete(sandboxes);
|
|
1233
|
-
|
|
1234
|
-
await db.delete(
|
|
1286
|
+
// Their vaults and credentials go with them (ON DELETE CASCADE).
|
|
1287
|
+
await db.delete(environments);
|
|
1288
|
+
await db.delete(environmentEvents);
|
|
1235
1289
|
await db.delete(sandboxSignals);
|
|
1236
1290
|
}
|
|
1237
1291
|
if (scope === "all") {
|
|
@@ -1241,7 +1295,6 @@ class PostgresTx {
|
|
|
1241
1295
|
await db.delete(modelUsage);
|
|
1242
1296
|
await db.delete(modelBudgets);
|
|
1243
1297
|
await db.delete(artifacts);
|
|
1244
|
-
await db.delete(vaults).where(ne(vaults.scope, "host"));
|
|
1245
1298
|
}
|
|
1246
1299
|
}
|
|
1247
1300
|
}
|