@nylorun/runtime 0.15.2-beta → 0.17.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 +134 -0
- package/README.md +68 -29
- package/dist/adapters/execution/restate.d.ts +9 -1
- package/dist/adapters/execution/restate.js +64 -5
- package/dist/adapters/sandbox/local.d.ts +10 -0
- package/dist/adapters/sandbox/local.js +195 -0
- package/dist/adapters/sandbox/virtual.js +36 -1
- package/dist/admin-openapi.json +341 -7
- package/dist/api/a2a/tasks.js +6 -1
- package/dist/api/ag-ui/endpoint.js +1 -2
- package/dist/api/ag-ui/history.js +6 -1
- package/dist/api/ag-ui/routes.d.ts +1 -1
- package/dist/api/ag-ui/routes.js +15 -13
- package/dist/api/components.d.ts +2667 -711
- package/dist/api/components.js +24 -11
- package/dist/api/gate/keys.d.ts +4 -3
- package/dist/api/gate/routes.d.ts +19 -3
- package/dist/api/gate/routes.js +162 -53
- package/dist/api/http/app.js +6 -0
- package/dist/api/http/define.d.ts +19 -12
- package/dist/api/http/define.js +13 -27
- package/dist/api/http/routes/access.d.ts +3 -19
- package/dist/api/http/routes/access.js +12 -172
- package/dist/api/http/routes/actions.js +0 -1
- package/dist/api/http/routes/artifacts.d.ts +11 -0
- package/dist/api/http/routes/artifacts.js +516 -0
- package/dist/api/http/routes/me.d.ts +13 -0
- package/dist/api/http/routes/me.js +45 -0
- package/dist/api/http/routes/sandboxes.d.ts +9 -0
- package/dist/api/http/routes/sandboxes.js +150 -0
- package/dist/api/http/routes/sessions.js +9 -13
- package/dist/api/http/routes/tenant.js +36 -2
- package/dist/api/http/routes/vaults.d.ts +12 -3
- package/dist/api/http/routes/vaults.js +122 -54
- package/dist/api/openapi.js +4 -10
- package/dist/artifacts/config.d.ts +23 -0
- package/dist/artifacts/config.js +27 -0
- package/dist/artifacts/export.d.ts +8 -0
- package/dist/artifacts/export.js +205 -0
- package/dist/artifacts/files.d.ts +36 -0
- package/dist/artifacts/files.js +45 -0
- package/dist/artifacts/folders.d.ts +60 -0
- package/dist/artifacts/folders.js +209 -0
- package/dist/artifacts/links.d.ts +28 -0
- package/dist/artifacts/links.js +92 -0
- package/dist/artifacts/media-types.d.ts +13 -0
- package/dist/artifacts/media-types.js +80 -0
- package/dist/artifacts/parts.d.ts +34 -0
- package/dist/artifacts/parts.js +38 -0
- package/dist/artifacts/service.d.ts +74 -0
- package/dist/artifacts/service.js +301 -0
- package/dist/artifacts/workspace.d.ts +60 -0
- package/dist/artifacts/workspace.js +57 -0
- package/dist/artifacts/zip.d.ts +10 -0
- package/dist/artifacts/zip.js +126 -0
- package/dist/blob/fs.d.ts +13 -0
- package/dist/blob/fs.js +194 -0
- package/dist/blob/index.d.ts +9 -0
- package/dist/blob/index.js +9 -0
- package/dist/blob/s3.d.ts +42 -0
- package/dist/blob/s3.js +366 -0
- package/dist/blob/types.d.ts +108 -0
- package/dist/blob/types.js +85 -0
- package/dist/core/sandbox-routes.d.ts +2 -2
- package/dist/core/sandbox-routes.js +4 -3
- package/dist/execution/memory.d.ts +11 -1
- package/dist/execution/memory.js +77 -3
- package/dist/execution/types.d.ts +54 -1
- package/dist/execution/types.js +31 -0
- package/dist/gates/contract.d.ts +14 -13
- package/dist/gates/contract.js +13 -12
- package/dist/gates/egress.d.ts +72 -0
- package/dist/gates/egress.js +319 -0
- package/dist/gates/handler.d.ts +3 -0
- package/dist/gates/handler.js +3 -2
- package/dist/gates/http-client.d.ts +3 -2
- package/dist/gates/http-client.js +17 -6
- package/dist/gates/in-process.d.ts +13 -1
- package/dist/gates/in-process.js +11 -0
- package/dist/gates/inflight.d.ts +23 -4
- package/dist/gates/inflight.js +18 -4
- package/dist/gates/mcp-handler.js +5 -1
- package/dist/gates/model-gate.d.ts +6 -2
- package/dist/gates/tenant-vaults.d.ts +18 -9
- package/dist/gates/tenant-vaults.js +17 -12
- package/dist/gates/tool-calls.d.ts +9 -5
- package/dist/gates/tool-calls.js +6 -6
- package/dist/gates/tool-client.d.ts +9 -2
- package/dist/gates/tool-client.js +56 -22
- package/dist/gates/tool-contract.d.ts +12 -7
- package/dist/gates/tool-contract.js +10 -4
- package/dist/gates/tool-gate.d.ts +1 -0
- package/dist/harness/calls.d.ts +52 -0
- package/dist/harness/calls.js +113 -0
- package/dist/harness/executors.d.ts +22 -0
- package/dist/harness/executors.js +96 -0
- package/dist/harness/main.d.ts +6 -0
- package/dist/harness/main.js +103 -0
- package/dist/harness/pod.d.ts +21 -0
- package/dist/harness/pod.js +119 -0
- package/dist/harness/run-tokens.d.ts +18 -0
- package/dist/harness/run-tokens.js +15 -0
- package/dist/harness/service.d.ts +46 -0
- package/dist/harness/service.js +179 -0
- package/dist/harness/ws-client.d.ts +43 -0
- package/dist/harness/ws-client.js +122 -0
- package/dist/harness/ws-port.d.ts +22 -0
- package/dist/harness/ws-port.js +78 -0
- package/dist/harness-api/in-process.d.ts +24 -0
- package/dist/harness-api/in-process.js +119 -0
- package/dist/harness-api/record.d.ts +44 -0
- package/dist/harness-api/record.js +250 -0
- package/dist/harness-api/renew.d.ts +37 -0
- package/dist/harness-api/renew.js +77 -0
- package/dist/harness-api/server.d.ts +104 -0
- package/dist/harness-api/server.js +408 -0
- package/dist/harness-api/start.d.ts +36 -0
- package/dist/harness-api/start.js +118 -0
- package/dist/harness-api/workspace.d.ts +53 -0
- package/dist/harness-api/workspace.js +201 -0
- package/dist/harness-api/ws-server.d.ts +34 -0
- package/dist/harness-api/ws-server.js +227 -0
- package/dist/host/admin-api.d.ts +12 -5
- package/dist/host/admin-api.js +72 -6
- package/dist/host/app.d.ts +0 -1
- package/dist/host/app.js +35 -24
- package/dist/host/config-for.d.ts +4 -0
- package/dist/host/config-for.js +2 -0
- package/dist/host/config.d.ts +0 -6
- package/dist/host/create-host.d.ts +3 -10
- package/dist/host/create-host.js +4 -1
- package/dist/host/gates.d.ts +27 -2
- package/dist/host/gates.js +52 -2
- package/dist/host/http.d.ts +4 -1
- package/dist/host/http.js +6 -1
- package/dist/host/main.js +167 -37
- package/dist/host/stack-config.d.ts +112 -9
- package/dist/host/stack-config.js +222 -20
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/keys/client.js +9 -1
- package/dist/keys/keys.d.ts +12 -2
- package/dist/keys/keys.js +8 -0
- package/dist/mcp/connect.js +2 -1
- package/dist/mcp/pool.d.ts +1 -0
- package/dist/mcp/pool.js +1 -0
- package/dist/mcp/snapshot.d.ts +8 -1
- package/dist/mcp/snapshot.js +28 -0
- package/dist/media.d.ts +5 -24
- package/dist/media.js +5 -0
- package/dist/model/pi-model.d.ts +6 -2
- package/dist/model/pi-model.js +39 -22
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +1 -1
- package/dist/openapi.json +14084 -9426
- package/dist/record/index.d.ts +1 -0
- package/dist/record/index.js +1 -0
- package/dist/record/sandbox.d.ts +25 -0
- package/dist/record/sandbox.js +43 -0
- package/dist/sandbox/capability.d.ts +3 -0
- package/dist/sandbox/capability.js +10 -0
- package/dist/sandbox/egress-token.d.ts +26 -0
- package/dist/sandbox/egress-token.js +8 -0
- package/dist/sandbox/join.d.ts +37 -0
- package/dist/sandbox/join.js +165 -0
- package/dist/sandbox/manager.d.ts +99 -34
- package/dist/sandbox/manager.js +216 -84
- package/dist/sandbox/placement.d.ts +27 -0
- package/dist/sandbox/placement.js +44 -0
- package/dist/sandbox/pods/client.d.ts +90 -0
- package/dist/sandbox/pods/client.js +79 -0
- package/dist/sandbox/pods/lifecycle.d.ts +91 -0
- package/dist/sandbox/pods/lifecycle.js +178 -0
- package/dist/sandbox/pods/name.d.ts +1 -0
- package/dist/sandbox/pods/name.js +28 -0
- package/dist/sandbox/pods/network-gate.d.ts +25 -0
- package/dist/sandbox/pods/network-gate.js +71 -0
- package/dist/sandbox/pods/reconcile.d.ts +11 -0
- package/dist/sandbox/pods/reconcile.js +152 -0
- package/dist/sandbox/pods/spec.d.ts +45 -0
- package/dist/sandbox/pods/spec.js +62 -0
- package/dist/sandbox/records.d.ts +29 -0
- package/dist/sandbox/records.js +79 -0
- package/dist/sandbox/resolve.d.ts +5 -0
- package/dist/sandbox/resolve.js +3 -2
- package/dist/sandbox/session-sandbox.js +7 -5
- package/dist/sandbox/share.d.ts +11 -0
- package/dist/sandbox/share.js +10 -0
- package/dist/sandbox/tenant-config.d.ts +16 -1
- package/dist/sandbox/tenant-config.js +9 -0
- package/dist/sandbox/types.d.ts +29 -1
- package/dist/sandbox/types.js +13 -1
- package/dist/store/postgres/drizzle/0004_sandbox_resources.sql +20 -0
- package/dist/store/postgres/drizzle/0005_artifacts.sql +27 -0
- package/dist/store/postgres/drizzle/0006_folder_artifacts.sql +12 -0
- package/dist/store/postgres/drizzle/0007_sandbox_pods.sql +14 -0
- package/dist/store/postgres/drizzle/0008_installation_vaults.sql +2 -0
- package/dist/store/postgres/drizzle/0009_oauth_pending.sql +19 -0
- package/dist/store/postgres/drizzle/0010_oss_auth_removals.sql +3 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +49 -0
- package/dist/store/postgres/record-writer.d.ts +6 -1
- package/dist/store/postgres/record-writer.js +20 -2
- package/dist/store/postgres/schema.d.ts +1168 -250
- package/dist/store/postgres/schema.js +174 -28
- package/dist/store/postgres/store.js +339 -103
- package/dist/store/postgres/tenant.d.ts +1 -1
- package/dist/store/postgres/tenant.js +2 -2
- package/dist/store/types.d.ts +163 -32
- package/dist/streams/control.d.ts +2 -5
- package/dist/streams/control.js +5 -8
- package/dist/streams/types.d.ts +8 -8
- package/dist/streams/types.js +1 -1
- package/dist/tenant/advance.js +218 -160
- package/dist/tenant/artifact-tool.d.ts +5 -0
- package/dist/tenant/artifact-tool.js +119 -0
- package/dist/tenant/auth.d.ts +13 -9
- package/dist/tenant/auth.js +25 -32
- package/dist/tenant/commands.js +31 -10
- package/dist/tenant/context.d.ts +83 -26
- package/dist/tenant/delivery-token.js +4 -4
- package/dist/tenant/effects.d.ts +24 -14
- package/dist/tenant/effects.js +10 -398
- package/dist/tenant/ephemeral.d.ts +14 -2
- package/dist/tenant/ephemeral.js +8 -1
- package/dist/tenant/history.d.ts +13 -23
- package/dist/tenant/history.js +14 -62
- package/dist/tenant/host-token.d.ts +92 -0
- package/dist/tenant/host-token.js +179 -0
- package/dist/tenant/identity-file.d.ts +75 -0
- package/dist/tenant/identity-file.js +202 -0
- package/dist/tenant/issuers.d.ts +48 -0
- package/dist/tenant/issuers.js +311 -0
- package/dist/tenant/jwt.js +3 -3
- package/dist/tenant/module.js +1 -0
- package/dist/tenant/operator-keys.d.ts +18 -0
- package/dist/tenant/operator-keys.js +55 -0
- package/dist/tenant/outbound.d.ts +30 -0
- package/dist/tenant/outbound.js +142 -10
- package/dist/tenant/paths.d.ts +2 -1
- package/dist/tenant/paths.js +3 -1
- package/dist/tenant/principals.d.ts +2 -11
- package/dist/tenant/principals.js +10 -30
- package/dist/tenant/reset.d.ts +5 -2
- package/dist/tenant/reset.js +22 -2
- package/dist/tenant/run-grants.d.ts +49 -0
- package/dist/tenant/run-grants.js +62 -0
- package/dist/tenant/run-token.d.ts +70 -0
- package/dist/tenant/run-token.js +138 -0
- package/dist/tenant/runtime.d.ts +42 -0
- package/dist/tenant/runtime.js +134 -33
- package/dist/tenant/sandboxes.d.ts +119 -0
- package/dist/tenant/sandboxes.js +551 -0
- package/dist/tenant/session-streams.d.ts +6 -10
- package/dist/tenant/session-streams.js +9 -27
- package/dist/tenant/session.d.ts +2 -13
- package/dist/tenant/session.js +4 -28
- package/dist/tenant/sessions.d.ts +6 -1
- package/dist/tenant/sessions.js +29 -2
- package/dist/tenant/signing-keys.d.ts +2 -1
- package/dist/tenant/signing-keys.js +2 -2
- package/dist/tenant/status.d.ts +8 -3
- package/dist/tenant/status.js +5 -0
- package/dist/tenant/streams.d.ts +8 -2
- package/dist/tenant/streams.js +25 -6
- package/dist/tenant/sweep.js +37 -4
- package/dist/tenant/types.d.ts +39 -1
- package/dist/tenant/worker.d.ts +11 -5
- package/dist/tenant/worker.js +12 -6
- package/dist/vault/crypto.d.ts +7 -0
- package/dist/vault/crypto.js +16 -0
- package/dist/vault/oauth.d.ts +55 -0
- package/dist/vault/oauth.js +186 -0
- package/dist/vault/service.d.ts +54 -7
- package/dist/vault/service.js +261 -17
- package/dist/vault/sources.d.ts +54 -0
- package/dist/vault/sources.js +185 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -10
- package/dist/adapters/media.d.ts +0 -24
- package/dist/adapters/media.js +0 -111
- package/dist/host/cors.d.ts +0 -27
- package/dist/host/cors.js +0 -64
- package/dist/tenant/access-policy.d.ts +0 -30
- package/dist/tenant/access-policy.js +0 -52
- package/dist/tenant/browser.d.ts +0 -15
- package/dist/tenant/browser.js +0 -39
- package/dist/tenant/subject-limits.d.ts +0 -16
- package/dist/tenant/subject-limits.js +0 -45
- package/dist/tenant/tokens.d.ts +0 -14
- package/dist/tenant/tokens.js +0 -180
|
@@ -32,6 +32,9 @@
|
|
|
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. A sandbox resource's row (`sandboxResource(id, { lock: true })`, which orders its
|
|
36
|
+
* lifecycle stream) is locked after every session row the transaction locks, never
|
|
37
|
+
* before one.
|
|
35
38
|
*
|
|
36
39
|
* Serialization failures and deadlocks are not retried here: with the order
|
|
37
40
|
* above, READ COMMITTED transactions do not raise them. A `40P01` means a
|
|
@@ -47,17 +50,19 @@
|
|
|
47
50
|
*/
|
|
48
51
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
49
52
|
import { and, count, eq, getTableColumns, gte, inArray, isNotNull, isNull, lt, lte, ne, or, sql, } from "drizzle-orm";
|
|
50
|
-
import { appendEvent } from "../../record/index.js";
|
|
53
|
+
import { appendEvent, appendSandboxEvent } from "../../record/index.js";
|
|
51
54
|
import { OwnershipLostError } from "../ownership.js";
|
|
52
55
|
import { database, driverError } from "./db.js";
|
|
53
56
|
import { expectedSchemaVersion, readSchemaVersion } from "./migrate.js";
|
|
54
57
|
import { createPostgresRecordReader } from "./record.js";
|
|
55
|
-
import { postgresRecordWriter } from "./record-writer.js";
|
|
56
|
-
import { actions, commands, definitions, effects, endpoints, links, modelBudgets, modelUsage, principals,
|
|
58
|
+
import { postgresRecordWriter, postgresSandboxRecordWriter } from "./record-writer.js";
|
|
59
|
+
import { actions, artifacts, artifactVersions, artifactContent, commands, definitions, effects, endpoints, links, modelBudgets, modelUsage, principals, sandboxes, sandboxEvents, sandboxResources, sessionEvents, sessionLogHeads, sessions, toJson, toolCrossings, signingKeys, tenant, tenantSettings, vaultAudit, vaultCredentials, vaultIdempotency, vaults, oauthPending, } from "./schema.js";
|
|
57
60
|
export function createPostgresSessionStore(options) {
|
|
58
61
|
return new PostgresSessionStore(options);
|
|
59
62
|
}
|
|
60
63
|
const OPEN_SESSION = ["running", "runnable"];
|
|
64
|
+
/** Rows per statement when a folder version names many files. */
|
|
65
|
+
const CONTENT_BATCH = 1000;
|
|
61
66
|
const DOCUMENTS = {
|
|
62
67
|
definitions,
|
|
63
68
|
sessions: sessions,
|
|
@@ -187,6 +192,57 @@ function ownership(row) {
|
|
|
187
192
|
ownerExpiresAt: row.ownerExpiresAt?.toISOString() ?? null,
|
|
188
193
|
};
|
|
189
194
|
}
|
|
195
|
+
function sandboxResourceOf(row) {
|
|
196
|
+
return {
|
|
197
|
+
id: row.id,
|
|
198
|
+
kind: row.kind,
|
|
199
|
+
spec: row.spec,
|
|
200
|
+
labels: row.labels,
|
|
201
|
+
createdAt: row.createdAt,
|
|
202
|
+
updatedAt: row.updatedAt,
|
|
203
|
+
...(row.k8sName !== null && row.desired !== null && row.observed !== null
|
|
204
|
+
? {
|
|
205
|
+
pod: {
|
|
206
|
+
k8sName: row.k8sName,
|
|
207
|
+
volumeGen: row.volumeGen,
|
|
208
|
+
desired: row.desired,
|
|
209
|
+
observed: row.observed,
|
|
210
|
+
...(row.podUid !== null ? { podUid: row.podUid } : {}),
|
|
211
|
+
hostEpoch: row.hostEpoch,
|
|
212
|
+
...(row.joinTokenHash !== null ? { joinTokenHash: row.joinTokenHash } : {}),
|
|
213
|
+
rev: row.rev,
|
|
214
|
+
...(row.lastActiveAt ? { lastActiveAt: row.lastActiveAt.toISOString() } : {}),
|
|
215
|
+
...(row.expiresAt ? { expiresAt: row.expiresAt.toISOString() } : {}),
|
|
216
|
+
...(row.startedAt ? { startedAt: row.startedAt.toISOString() } : {}),
|
|
217
|
+
...(row.reason !== null ? { reason: row.reason } : {}),
|
|
218
|
+
...(row.retiring !== null ? { retiring: row.retiring } : {}),
|
|
219
|
+
...(row.volumeId !== null ? { volumeId: row.volumeId } : {}),
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
: {}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/** A pod patch as column values: ISO times become dates, `null` clears. */
|
|
226
|
+
function podColumns(patch) {
|
|
227
|
+
const date = (value) => value === undefined ? undefined : value === null ? null : new Date(value);
|
|
228
|
+
const columns = {
|
|
229
|
+
k8sName: patch.k8sName,
|
|
230
|
+
volumeGen: patch.volumeGen,
|
|
231
|
+
desired: patch.desired,
|
|
232
|
+
observed: patch.observed,
|
|
233
|
+
podUid: patch.podUid,
|
|
234
|
+
hostEpoch: patch.hostEpoch,
|
|
235
|
+
joinTokenHash: patch.joinTokenHash,
|
|
236
|
+
rev: patch.rev,
|
|
237
|
+
lastActiveAt: date(patch.lastActiveAt),
|
|
238
|
+
expiresAt: date(patch.expiresAt),
|
|
239
|
+
startedAt: date(patch.startedAt),
|
|
240
|
+
reason: patch.reason,
|
|
241
|
+
retiring: patch.retiring,
|
|
242
|
+
volumeId: patch.volumeId,
|
|
243
|
+
};
|
|
244
|
+
return Object.fromEntries(Object.entries(columns).filter(([, value]) => value !== undefined));
|
|
245
|
+
}
|
|
190
246
|
/** An endpoint row with its unset (null) columns left out, as `EndpointRow` has them. */
|
|
191
247
|
function endpointRow(row) {
|
|
192
248
|
return Object.fromEntries(Object.entries(row).filter(([, value]) => value !== null));
|
|
@@ -345,6 +401,18 @@ class PostgresTx {
|
|
|
345
401
|
throw new OwnershipLostError(sessionId, epoch, session.epoch);
|
|
346
402
|
return session;
|
|
347
403
|
}
|
|
404
|
+
async runState(sessionId) {
|
|
405
|
+
this.check();
|
|
406
|
+
const [row] = await this.db
|
|
407
|
+
.select({
|
|
408
|
+
epoch: sessions.epoch,
|
|
409
|
+
status: sessions.status,
|
|
410
|
+
activeTurnId: sql `nylorun.doc(${sessions.body})->>'activeTurnId'`,
|
|
411
|
+
})
|
|
412
|
+
.from(sessions)
|
|
413
|
+
.where(eq(sessions.id, sessionId));
|
|
414
|
+
return row && { epoch: row.epoch, status: row.status ?? "", activeTurnId: row.activeTurnId ?? null };
|
|
415
|
+
}
|
|
348
416
|
// --- typed queries -------------------------------------------------------
|
|
349
417
|
async sessionsWithStatus(statuses) {
|
|
350
418
|
this.check();
|
|
@@ -383,6 +451,91 @@ class PostgresTx {
|
|
|
383
451
|
this.check();
|
|
384
452
|
return this.bodies(sandboxes);
|
|
385
453
|
}
|
|
454
|
+
// --- sandbox resources ---------------------------------------------------
|
|
455
|
+
async sandboxResource(id, options = {}) {
|
|
456
|
+
this.check();
|
|
457
|
+
const query = this.db.select().from(sandboxResources).where(eq(sandboxResources.id, id));
|
|
458
|
+
const [row] = options.lock ? await query.for("update") : await query;
|
|
459
|
+
return row && sandboxResourceOf(row);
|
|
460
|
+
}
|
|
461
|
+
async createSandboxResource(row, limit) {
|
|
462
|
+
this.check();
|
|
463
|
+
// Every create takes this lock, so two never both see room for one more.
|
|
464
|
+
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.sandbox_resources'))`);
|
|
465
|
+
const [found] = await this.db
|
|
466
|
+
.select({ id: sandboxResources.id })
|
|
467
|
+
.from(sandboxResources)
|
|
468
|
+
.where(eq(sandboxResources.id, row.id));
|
|
469
|
+
if (found)
|
|
470
|
+
return "exists";
|
|
471
|
+
const [counted] = await this.db.select({ n: count() }).from(sandboxResources);
|
|
472
|
+
if ((counted?.n ?? 0) >= limit)
|
|
473
|
+
return "limit";
|
|
474
|
+
const { pod, ...resource } = row;
|
|
475
|
+
await this.db.insert(sandboxResources).values({ ...resource, ...(pod ? podColumns(pod) : {}) });
|
|
476
|
+
return "created";
|
|
477
|
+
}
|
|
478
|
+
async updateSandboxSpec(id, spec, updatedAt) {
|
|
479
|
+
this.check();
|
|
480
|
+
await this.db.update(sandboxResources).set({ spec, updatedAt }).where(eq(sandboxResources.id, id));
|
|
481
|
+
}
|
|
482
|
+
async updateSandboxPod(id, patch, updatedAt) {
|
|
483
|
+
this.check();
|
|
484
|
+
await this.db
|
|
485
|
+
.update(sandboxResources)
|
|
486
|
+
.set({ ...podColumns(patch), updatedAt })
|
|
487
|
+
.where(eq(sandboxResources.id, id));
|
|
488
|
+
}
|
|
489
|
+
async updateSandboxLabels(id, labels, updatedAt) {
|
|
490
|
+
this.check();
|
|
491
|
+
await this.db
|
|
492
|
+
.update(sandboxResources)
|
|
493
|
+
.set({ labels, updatedAt })
|
|
494
|
+
.where(eq(sandboxResources.id, id));
|
|
495
|
+
}
|
|
496
|
+
async deleteSandboxResource(id) {
|
|
497
|
+
this.check();
|
|
498
|
+
await this.db.delete(sandboxResources).where(eq(sandboxResources.id, id));
|
|
499
|
+
}
|
|
500
|
+
async listSandboxResources(filter = {}) {
|
|
501
|
+
this.check();
|
|
502
|
+
const labels = filter.labels;
|
|
503
|
+
const rows = await this.db
|
|
504
|
+
.select()
|
|
505
|
+
.from(sandboxResources)
|
|
506
|
+
.where(labels === undefined || Object.keys(labels).length === 0
|
|
507
|
+
? undefined
|
|
508
|
+
: sql `nylorun.doc(${sandboxResources.labels}) @> ${toJson(labels)}::jsonb`)
|
|
509
|
+
.orderBy(sandboxResources.id);
|
|
510
|
+
return rows.map(sandboxResourceOf);
|
|
511
|
+
}
|
|
512
|
+
async sessionsOnSandbox(sandboxId) {
|
|
513
|
+
this.check();
|
|
514
|
+
const rows = await this.sessions()
|
|
515
|
+
.where(eq(sessions.sandboxId, sandboxId))
|
|
516
|
+
.orderBy(sessions.id);
|
|
517
|
+
return rows.map((row) => storedSession(row));
|
|
518
|
+
}
|
|
519
|
+
async sandboxEvent(sandboxId, type, payload) {
|
|
520
|
+
this.check();
|
|
521
|
+
return appendSandboxEvent(postgresSandboxRecordWriter(this.db), {
|
|
522
|
+
tenantId: this.tenantId,
|
|
523
|
+
sandboxId,
|
|
524
|
+
time: this.now(),
|
|
525
|
+
type,
|
|
526
|
+
payload,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
async sandboxEvents(sandboxId, options = {}) {
|
|
530
|
+
this.check();
|
|
531
|
+
const rows = await this.db
|
|
532
|
+
.select({ body: sandboxEvents.body })
|
|
533
|
+
.from(sandboxEvents)
|
|
534
|
+
.where(and(eq(sandboxEvents.sandboxId, sandboxId), options.fromSeq === undefined ? undefined : gte(sandboxEvents.seq, options.fromSeq)))
|
|
535
|
+
.orderBy(sandboxEvents.seq)
|
|
536
|
+
.limit(options.limit ?? 1000);
|
|
537
|
+
return rows.map((row) => row.body);
|
|
538
|
+
}
|
|
386
539
|
async actionBodies(where) {
|
|
387
540
|
const rows = await this.db
|
|
388
541
|
.select({ body: actions.body })
|
|
@@ -607,6 +760,30 @@ class PostgresTx {
|
|
|
607
760
|
.orderBy(principals.id);
|
|
608
761
|
return rows.map((row) => row.tokenHash);
|
|
609
762
|
}
|
|
763
|
+
async listPrincipals() {
|
|
764
|
+
this.check();
|
|
765
|
+
return await this.db.select().from(principals).orderBy(principals.id);
|
|
766
|
+
}
|
|
767
|
+
async putPrincipal(id, tokenHash, createdAt) {
|
|
768
|
+
this.check();
|
|
769
|
+
const [row] = await this.db
|
|
770
|
+
.insert(principals)
|
|
771
|
+
.values({ id, role: "application", tokenHash, idempotencyKey: null, createdAt })
|
|
772
|
+
.onConflictDoUpdate({
|
|
773
|
+
target: principals.id,
|
|
774
|
+
set: { tokenHash, createdAt, idempotencyKey: null },
|
|
775
|
+
})
|
|
776
|
+
.returning();
|
|
777
|
+
return row;
|
|
778
|
+
}
|
|
779
|
+
async deletePrincipal(id) {
|
|
780
|
+
this.check();
|
|
781
|
+
const rows = await this.db
|
|
782
|
+
.delete(principals)
|
|
783
|
+
.where(eq(principals.id, id))
|
|
784
|
+
.returning({ id: principals.id });
|
|
785
|
+
return rows.length > 0;
|
|
786
|
+
}
|
|
610
787
|
// --- vault ---------------------------------------------------------------
|
|
611
788
|
async insertVault(row) {
|
|
612
789
|
this.check();
|
|
@@ -625,6 +802,14 @@ class PostgresTx {
|
|
|
625
802
|
.where(and(eq(vaults.ownerUserId, ownerUserId), eq(vaults.scope, "user")))
|
|
626
803
|
.orderBy(vaults.createdAt, vaults.id);
|
|
627
804
|
}
|
|
805
|
+
async installationVaults() {
|
|
806
|
+
this.check();
|
|
807
|
+
return this.db
|
|
808
|
+
.select()
|
|
809
|
+
.from(vaults)
|
|
810
|
+
.where(eq(vaults.scope, "installation"))
|
|
811
|
+
.orderBy(vaults.createdAt, vaults.id);
|
|
812
|
+
}
|
|
628
813
|
async updateVaultMetadata(id, metadataJson) {
|
|
629
814
|
this.check();
|
|
630
815
|
await this.db.update(vaults).set({ metadataJson }).where(eq(vaults.id, id));
|
|
@@ -705,7 +890,24 @@ class PostgresTx {
|
|
|
705
890
|
this.check();
|
|
706
891
|
await this.db.insert(vaultIdempotency).values(row);
|
|
707
892
|
}
|
|
708
|
-
|
|
893
|
+
async insertOAuthPending(row) {
|
|
894
|
+
this.check();
|
|
895
|
+
await this.db.insert(oauthPending).values(row);
|
|
896
|
+
}
|
|
897
|
+
async takeOAuthPending(stateHash) {
|
|
898
|
+
this.check();
|
|
899
|
+
const [row] = await this.db
|
|
900
|
+
.delete(oauthPending)
|
|
901
|
+
.where(eq(oauthPending.stateHash, stateHash))
|
|
902
|
+
.returning();
|
|
903
|
+
return row;
|
|
904
|
+
}
|
|
905
|
+
async deleteExpiredOAuthPending(now) {
|
|
906
|
+
this.check();
|
|
907
|
+
const result = await this.db.delete(oauthPending).where(lt(oauthPending.expiresAt, now));
|
|
908
|
+
return result.count;
|
|
909
|
+
}
|
|
910
|
+
// --- signing keys --------------------------------------------------------
|
|
709
911
|
async lockSigningKeys() {
|
|
710
912
|
this.check();
|
|
711
913
|
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.signing_keys'))`);
|
|
@@ -742,103 +944,6 @@ class PostgresTx {
|
|
|
742
944
|
const [row] = await this.db.select({ n: count() }).from(signingKeys);
|
|
743
945
|
return row.n;
|
|
744
946
|
}
|
|
745
|
-
async subjectEpoch(subject) {
|
|
746
|
-
this.check();
|
|
747
|
-
const [row] = await this.db
|
|
748
|
-
.select({ epoch: subjectEpochs.epoch })
|
|
749
|
-
.from(subjectEpochs)
|
|
750
|
-
.where(eq(subjectEpochs.subject, subject));
|
|
751
|
-
return row?.epoch ?? 0;
|
|
752
|
-
}
|
|
753
|
-
async subjectEpochs(subjects) {
|
|
754
|
-
this.check();
|
|
755
|
-
if (subjects.length === 0)
|
|
756
|
-
return new Map();
|
|
757
|
-
const rows = await this.db
|
|
758
|
-
.select({ subject: subjectEpochs.subject, epoch: subjectEpochs.epoch })
|
|
759
|
-
.from(subjectEpochs)
|
|
760
|
-
.where(inArray(subjectEpochs.subject, subjects));
|
|
761
|
-
return new Map(rows.map((row) => [row.subject, row.epoch]));
|
|
762
|
-
}
|
|
763
|
-
async bumpSubjectEpoch(subject, at) {
|
|
764
|
-
this.check();
|
|
765
|
-
const [row] = await this.db
|
|
766
|
-
.insert(subjectEpochs)
|
|
767
|
-
.values({ subject, epoch: 1, revokedAt: at })
|
|
768
|
-
.onConflictDoUpdate({
|
|
769
|
-
target: subjectEpochs.subject,
|
|
770
|
-
set: { epoch: sql `${subjectEpochs.epoch} + 1`, revokedAt: excluded("revoked_at") },
|
|
771
|
-
})
|
|
772
|
-
.returning({ epoch: subjectEpochs.epoch });
|
|
773
|
-
return row.epoch;
|
|
774
|
-
}
|
|
775
|
-
async lockSubjectUsage(initial) {
|
|
776
|
-
this.check();
|
|
777
|
-
await this.db.insert(subjectUsage).values(initial).onConflictDoNothing();
|
|
778
|
-
const [row] = await this.db
|
|
779
|
-
.select()
|
|
780
|
-
.from(subjectUsage)
|
|
781
|
-
.where(eq(subjectUsage.subject, initial.subject))
|
|
782
|
-
.for("update");
|
|
783
|
-
return row;
|
|
784
|
-
}
|
|
785
|
-
async putSubjectUsage(row) {
|
|
786
|
-
this.check();
|
|
787
|
-
await this.db
|
|
788
|
-
.insert(subjectUsage)
|
|
789
|
-
.values(row)
|
|
790
|
-
.onConflictDoUpdate({
|
|
791
|
-
target: subjectUsage.subject,
|
|
792
|
-
set: { turnTokens: excluded("turn_tokens"), refilledAt: excluded("refilled_at") },
|
|
793
|
-
});
|
|
794
|
-
}
|
|
795
|
-
async countOwnerSessions(ownerUserId, statuses) {
|
|
796
|
-
this.check();
|
|
797
|
-
const [row] = await this.db
|
|
798
|
-
.select({ n: count() })
|
|
799
|
-
.from(sessions)
|
|
800
|
-
.where(and(eq(sessions.ownerUserId, ownerUserId), inArray(sessions.status, statuses)));
|
|
801
|
-
return row.n;
|
|
802
|
-
}
|
|
803
|
-
// --- publishable keys ----------------------------------------------------
|
|
804
|
-
async insertPublishableKey(row) {
|
|
805
|
-
this.check();
|
|
806
|
-
await this.db.insert(publishableKeys).values(row);
|
|
807
|
-
}
|
|
808
|
-
async publishableKeyByKey(key) {
|
|
809
|
-
this.check();
|
|
810
|
-
const [row] = await this.db
|
|
811
|
-
.select()
|
|
812
|
-
.from(publishableKeys)
|
|
813
|
-
.where(eq(publishableKeys.key, key));
|
|
814
|
-
return row;
|
|
815
|
-
}
|
|
816
|
-
async publishableKey(id) {
|
|
817
|
-
this.check();
|
|
818
|
-
const [row] = await this.db
|
|
819
|
-
.select()
|
|
820
|
-
.from(publishableKeys)
|
|
821
|
-
.where(eq(publishableKeys.id, id));
|
|
822
|
-
return row;
|
|
823
|
-
}
|
|
824
|
-
async publishableKeys() {
|
|
825
|
-
this.check();
|
|
826
|
-
return this.db
|
|
827
|
-
.select()
|
|
828
|
-
.from(publishableKeys)
|
|
829
|
-
.orderBy(publishableKeys.createdAt, publishableKeys.id);
|
|
830
|
-
}
|
|
831
|
-
async updatePublishableKey(id, patch) {
|
|
832
|
-
this.check();
|
|
833
|
-
if (patch.originsJson === undefined && patch.revokedAt === undefined)
|
|
834
|
-
return (await this.publishableKey(id)) !== undefined;
|
|
835
|
-
const rows = await this.db
|
|
836
|
-
.update(publishableKeys)
|
|
837
|
-
.set(patch)
|
|
838
|
-
.where(eq(publishableKeys.id, id))
|
|
839
|
-
.returning({ id: publishableKeys.id });
|
|
840
|
-
return rows.length === 1;
|
|
841
|
-
}
|
|
842
947
|
// --- model usage ---------------------------------------------------------
|
|
843
948
|
async recordModelUsage(row) {
|
|
844
949
|
this.check();
|
|
@@ -906,6 +1011,132 @@ class PostgresTx {
|
|
|
906
1011
|
.returning({ key: toolCrossings.key });
|
|
907
1012
|
return deleted.length;
|
|
908
1013
|
}
|
|
1014
|
+
// --- file artifacts --------------------------------------------------------
|
|
1015
|
+
async artifact(id, options = {}) {
|
|
1016
|
+
this.check();
|
|
1017
|
+
const query = this.db.select().from(artifacts).where(eq(artifacts.id, id));
|
|
1018
|
+
const [row] = options.lock ? await query.for("update") : await query;
|
|
1019
|
+
return row;
|
|
1020
|
+
}
|
|
1021
|
+
async listArtifacts(filter = {}) {
|
|
1022
|
+
this.check();
|
|
1023
|
+
return this.db
|
|
1024
|
+
.select()
|
|
1025
|
+
.from(artifacts)
|
|
1026
|
+
.where(filter.sessionId === undefined ? undefined : eq(artifacts.sessionId, filter.sessionId))
|
|
1027
|
+
.orderBy(artifacts.createdAt, artifacts.id);
|
|
1028
|
+
}
|
|
1029
|
+
async insertArtifact(row, version) {
|
|
1030
|
+
this.check();
|
|
1031
|
+
await this.db.insert(artifacts).values(row);
|
|
1032
|
+
await this.db.insert(artifactVersions).values(version);
|
|
1033
|
+
}
|
|
1034
|
+
async insertArtifactVersion(version) {
|
|
1035
|
+
this.check();
|
|
1036
|
+
await this.db.insert(artifactVersions).values(version);
|
|
1037
|
+
await this.db
|
|
1038
|
+
.update(artifacts)
|
|
1039
|
+
.set({
|
|
1040
|
+
latestVersion: version.version,
|
|
1041
|
+
contentType: version.contentType,
|
|
1042
|
+
updatedAt: version.createdAt,
|
|
1043
|
+
})
|
|
1044
|
+
.where(eq(artifacts.id, version.artifactId));
|
|
1045
|
+
}
|
|
1046
|
+
async artifactVersion(artifactId, version) {
|
|
1047
|
+
this.check();
|
|
1048
|
+
const [row] = await this.db
|
|
1049
|
+
.select()
|
|
1050
|
+
.from(artifactVersions)
|
|
1051
|
+
.where(and(eq(artifactVersions.artifactId, artifactId), eq(artifactVersions.version, version)));
|
|
1052
|
+
return row;
|
|
1053
|
+
}
|
|
1054
|
+
async artifactVersions(artifactId) {
|
|
1055
|
+
this.check();
|
|
1056
|
+
return this.db
|
|
1057
|
+
.select()
|
|
1058
|
+
.from(artifactVersions)
|
|
1059
|
+
.where(eq(artifactVersions.artifactId, artifactId))
|
|
1060
|
+
.orderBy(artifactVersions.version);
|
|
1061
|
+
}
|
|
1062
|
+
async deleteArtifact(id) {
|
|
1063
|
+
this.check();
|
|
1064
|
+
const deleted = await this.db
|
|
1065
|
+
.delete(artifactVersions)
|
|
1066
|
+
.where(eq(artifactVersions.artifactId, id))
|
|
1067
|
+
.returning({ blobKey: artifactVersions.blobKey });
|
|
1068
|
+
await this.db.delete(artifacts).where(eq(artifacts.id, id));
|
|
1069
|
+
return deleted.map((row) => row.blobKey);
|
|
1070
|
+
}
|
|
1071
|
+
async lockArtifactQuota() {
|
|
1072
|
+
this.check();
|
|
1073
|
+
await this.db.execute(sql `SELECT pg_advisory_xact_lock(hashtext('nylorun.artifacts'))`);
|
|
1074
|
+
}
|
|
1075
|
+
async artifactBytes() {
|
|
1076
|
+
this.check();
|
|
1077
|
+
const [files] = await this.db
|
|
1078
|
+
.select({
|
|
1079
|
+
bytes: sql `coalesce(sum(${artifactVersions.size}), 0)::bigint`.mapWith(Number),
|
|
1080
|
+
})
|
|
1081
|
+
.from(artifactVersions)
|
|
1082
|
+
.innerJoin(artifacts, eq(artifacts.id, artifactVersions.artifactId))
|
|
1083
|
+
.where(eq(artifacts.kind, "file"));
|
|
1084
|
+
// A folder's files are stored once per distinct hash, however many versions name them.
|
|
1085
|
+
const distinct = this.db
|
|
1086
|
+
.select({ size: sql `max(${artifactContent.size})`.as("size") })
|
|
1087
|
+
.from(artifactContent)
|
|
1088
|
+
.groupBy(artifactContent.sha256)
|
|
1089
|
+
.as("distinct_content");
|
|
1090
|
+
const [content] = await this.db
|
|
1091
|
+
.select({ bytes: sql `coalesce(sum(${distinct.size}), 0)::bigint`.mapWith(Number) })
|
|
1092
|
+
.from(distinct);
|
|
1093
|
+
return files.bytes + content.bytes;
|
|
1094
|
+
}
|
|
1095
|
+
async insertArtifactContent(artifactId, version, files) {
|
|
1096
|
+
this.check();
|
|
1097
|
+
for (let at = 0; at < files.length; at += CONTENT_BATCH)
|
|
1098
|
+
await this.db
|
|
1099
|
+
.insert(artifactContent)
|
|
1100
|
+
.values(files
|
|
1101
|
+
.slice(at, at + CONTENT_BATCH)
|
|
1102
|
+
.map((file) => ({ artifactId, version, sha256: file.sha256, size: file.size })))
|
|
1103
|
+
.onConflictDoNothing();
|
|
1104
|
+
}
|
|
1105
|
+
async referencedArtifactContent(shas) {
|
|
1106
|
+
this.check();
|
|
1107
|
+
const found = new Set();
|
|
1108
|
+
for (let at = 0; at < shas.length; at += CONTENT_BATCH) {
|
|
1109
|
+
const rows = await this.db
|
|
1110
|
+
.selectDistinct({ sha256: artifactContent.sha256 })
|
|
1111
|
+
.from(artifactContent)
|
|
1112
|
+
.where(inArray(artifactContent.sha256, shas.slice(at, at + CONTENT_BATCH)));
|
|
1113
|
+
for (const row of rows)
|
|
1114
|
+
found.add(row.sha256);
|
|
1115
|
+
}
|
|
1116
|
+
return found;
|
|
1117
|
+
}
|
|
1118
|
+
async artifactContentShas(scope) {
|
|
1119
|
+
this.check();
|
|
1120
|
+
const rows = await this.db
|
|
1121
|
+
.selectDistinct({ sha256: artifactContent.sha256 })
|
|
1122
|
+
.from(artifactContent)
|
|
1123
|
+
.innerJoin(artifacts, eq(artifacts.id, artifactContent.artifactId))
|
|
1124
|
+
.where(scope === "all"
|
|
1125
|
+
? undefined
|
|
1126
|
+
: scope === "sessions"
|
|
1127
|
+
? isNotNull(artifacts.sessionId)
|
|
1128
|
+
: eq(artifacts.id, scope.artifactId));
|
|
1129
|
+
return rows.map((row) => row.sha256);
|
|
1130
|
+
}
|
|
1131
|
+
async artifactBlobKeys(scope) {
|
|
1132
|
+
this.check();
|
|
1133
|
+
const rows = await this.db
|
|
1134
|
+
.select({ blobKey: artifactVersions.blobKey })
|
|
1135
|
+
.from(artifactVersions)
|
|
1136
|
+
.innerJoin(artifacts, eq(artifacts.id, artifactVersions.artifactId))
|
|
1137
|
+
.where(scope === "sessions" ? isNotNull(artifacts.sessionId) : undefined);
|
|
1138
|
+
return rows.map((row) => row.blobKey);
|
|
1139
|
+
}
|
|
909
1140
|
// --- settings ------------------------------------------------------------
|
|
910
1141
|
async getSetting(key) {
|
|
911
1142
|
this.check();
|
|
@@ -929,8 +1160,9 @@ class PostgresTx {
|
|
|
929
1160
|
if (scope === "sessions" || scope === "all") {
|
|
930
1161
|
for (const table of SESSION_TABLES)
|
|
931
1162
|
await db.delete(table);
|
|
932
|
-
await db.delete(subjectUsage);
|
|
933
1163
|
await db.delete(toolCrossings);
|
|
1164
|
+
// A session's artifacts go with it (their versions by cascade).
|
|
1165
|
+
await db.delete(artifacts).where(isNotNull(artifacts.sessionId));
|
|
934
1166
|
// The record goes with the sessions, and the Tenant moves to a new basin: the ids it
|
|
935
1167
|
// frees start again in an empty one (Durable Streams §8.1).
|
|
936
1168
|
await db.delete(sessionEvents);
|
|
@@ -940,13 +1172,17 @@ class PostgresTx {
|
|
|
940
1172
|
basinGeneration: sql `${tenant.basinGeneration} + 1`,
|
|
941
1173
|
});
|
|
942
1174
|
}
|
|
943
|
-
if (scope === "sandboxes" || scope === "all")
|
|
1175
|
+
if (scope === "sandboxes" || scope === "all") {
|
|
944
1176
|
await db.delete(sandboxes);
|
|
1177
|
+
await db.delete(sandboxResources);
|
|
1178
|
+
await db.delete(sandboxEvents);
|
|
1179
|
+
}
|
|
945
1180
|
if (scope === "all") {
|
|
946
1181
|
await db.delete(definitions);
|
|
947
1182
|
await db.delete(endpoints);
|
|
948
1183
|
await db.delete(modelUsage);
|
|
949
1184
|
await db.delete(modelBudgets);
|
|
1185
|
+
await db.delete(artifacts);
|
|
950
1186
|
await db.delete(vaults).where(ne(vaults.scope, "host"));
|
|
951
1187
|
}
|
|
952
1188
|
}
|
|
@@ -16,7 +16,7 @@ export interface TenantCreation {
|
|
|
16
16
|
/** Ignored when the Tenant exists. */
|
|
17
17
|
name: string;
|
|
18
18
|
/**
|
|
19
|
-
* The principals the Tenant must have, given its id (derived
|
|
19
|
+
* The principals the Tenant must have, given its id (Studio's derived key depends on it). Created
|
|
20
20
|
* with the Tenant; on later opens, the missing ones are added.
|
|
21
21
|
*/
|
|
22
22
|
principals?(tenantId: string): readonly InitialPrincipal[];
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* 3. When `nylorun.tenant` is empty, create the Tenant: its row (id, name, created time) and
|
|
15
15
|
* its first principals. A database whose row is gone but that holds a Tenant's data
|
|
16
16
|
* (principals, sessions, agents, vaults, keys) is `envelope-invalid`, never given a new
|
|
17
|
-
* Tenant. Principals given later that the Tenant does not have yet are added (
|
|
18
|
-
*
|
|
17
|
+
* Tenant. Principals given later that the Tenant does not have yet are added (the
|
|
18
|
+
* ephemeral Runtime's application key); existing ones are kept, as ordinary keys.
|
|
19
19
|
* 4. Read the envelope back (`envelope-invalid` when it does not parse), and open the store.
|
|
20
20
|
*
|
|
21
21
|
* A lost connection or an unavailable server says nothing about the Tenant: it is thrown as
|