@opengeni/core 0.21.10 → 0.28.0
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/dist/access/index.d.ts +1 -1
- package/dist/application/new-session-drafts.d.ts +1 -1
- package/dist/application/session-commands.d.ts +2 -2
- package/dist/billing/limits.d.ts +1 -1
- package/dist/canonical-human-identities.d.ts +12 -0
- package/dist/canonical-human-identities.js +23 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/chunk-6WKPWE5S.js +2380 -0
- package/dist/chunk-6WKPWE5S.js.map +1 -0
- package/dist/chunk-IBOEYG6N.js +34 -0
- package/dist/chunk-IBOEYG6N.js.map +1 -0
- package/dist/chunk-JJU6I5XD.js +2713 -0
- package/dist/chunk-JJU6I5XD.js.map +1 -0
- package/dist/chunk-NYPMQ7NO.js +2270 -0
- package/dist/chunk-NYPMQ7NO.js.map +1 -0
- package/dist/dependencies.d.ts +30 -6
- package/dist/domain/capabilities.d.ts +28 -3
- package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -0
- package/dist/domain/conversation-integrations.d.ts +225 -0
- package/dist/domain/durable-learning-slack-publication.d.ts +72 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +192 -0
- package/dist/domain/editable-artifacts/durable-export-postgres.d.ts +4 -0
- package/dist/domain/editable-artifacts/durable-export.d.ts +212 -0
- package/dist/domain/editable-artifacts/errors.d.ts +47 -0
- package/dist/domain/editable-artifacts/hash.d.ts +11 -0
- package/dist/domain/editable-artifacts/in-memory.d.ts +99 -0
- package/dist/domain/editable-artifacts/index.d.ts +14 -0
- package/dist/domain/editable-artifacts/ports.d.ts +341 -0
- package/dist/domain/editable-artifacts/postgres-adapters.d.ts +13 -0
- package/dist/domain/editable-artifacts/service.d.ts +105 -0
- package/dist/domain/editable-artifacts/snapshot-compaction.d.ts +44 -0
- package/dist/domain/editable-artifacts/snapshot-verifier-genesis.d.ts +50 -0
- package/dist/domain/editable-artifacts/snapshot-verifier-materialization.d.ts +21 -0
- package/dist/domain/editable-artifacts/snapshot-verifier.d.ts +118 -0
- package/dist/domain/editable-artifacts/types.d.ts +410 -0
- package/dist/domain/fiken.d.ts +35 -0
- package/dist/domain/memory-slack-delivery.d.ts +19 -0
- package/dist/domain/packs.d.ts +29 -1
- package/dist/domain/personal-connection-delegations.d.ts +9 -1
- package/dist/domain/scheduled-tasks.d.ts +2 -2
- package/dist/domain/session-tool-policy.d.ts +2 -2
- package/dist/domain/sessions.d.ts +24 -8
- package/dist/domain/skill-imports.d.ts +32 -0
- package/dist/domain/slack-publication-secret-safety.d.ts +8 -0
- package/dist/domain/timeline-annotations.d.ts +5 -0
- package/dist/domain/video-generation-capabilities.d.ts +10 -0
- package/dist/domain/video-generation.d.ts +47 -0
- package/dist/editable-artifact-live/application.d.ts +69 -0
- package/dist/editable-artifact-live/errors.d.ts +1 -0
- package/dist/editable-artifact-live/index.d.ts +8 -0
- package/dist/editable-artifact-live/outbox-dispatcher.d.ts +162 -0
- package/dist/editable-artifact-live/ports.d.ts +100 -0
- package/dist/editable-artifact-live/server.d.ts +71 -0
- package/dist/editable-artifact-live/ticket.d.ts +40 -0
- package/dist/editable-artifact-live/types.d.ts +213 -0
- package/dist/editable-artifact-live/wire.d.ts +34 -0
- package/dist/editable-artifact-live.d.ts +2 -0
- package/dist/editable-artifact-live.js +58 -0
- package/dist/editable-artifact-live.js.map +1 -0
- package/dist/editable-artifacts.d.ts +2 -0
- package/dist/editable-artifacts.js +179 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/index.d.ts +37 -26
- package/dist/index.js +4145 -959
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +6 -2
- package/dist/rigs/index.d.ts +1 -1
- package/dist/rigs/provider-images.d.ts +31 -0
- package/dist/sandbox/fleet.d.ts +8 -5
- package/dist/sandbox/routing.d.ts +1 -0
- package/dist/session-authorization.d.ts +4 -2
- package/dist/transcription.d.ts +5 -0
- package/dist/workflow-wake-contract.d.ts +7 -0
- package/package.json +22 -10
- package/src/access/index.ts +14 -2
- package/src/application/new-session-drafts.ts +2 -0
- package/src/application/session-commands.ts +188 -107
- package/src/canonical-human-identities.ts +27 -0
- package/src/dependencies.ts +34 -2
- package/src/domain/capabilities.ts +729 -408
- package/src/domain/company-profile-durable-learning-adapter.ts +221 -0
- package/src/domain/conversation-integrations.ts +1238 -0
- package/src/domain/durable-learning-slack-publication.ts +202 -0
- package/src/domain/editable-artifacts/agent-application.ts +687 -0
- package/src/domain/editable-artifacts/durable-export-postgres.ts +139 -0
- package/src/domain/editable-artifacts/durable-export.ts +734 -0
- package/src/domain/editable-artifacts/errors.ts +115 -0
- package/src/domain/editable-artifacts/hash.ts +131 -0
- package/src/domain/editable-artifacts/in-memory.ts +1662 -0
- package/src/domain/editable-artifacts/index.ts +14 -0
- package/src/domain/editable-artifacts/ports.ts +462 -0
- package/src/domain/editable-artifacts/postgres-adapters.ts +49 -0
- package/src/domain/editable-artifacts/service.ts +2402 -0
- package/src/domain/editable-artifacts/snapshot-compaction.ts +246 -0
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +235 -0
- package/src/domain/editable-artifacts/snapshot-verifier-materialization.ts +74 -0
- package/src/domain/editable-artifacts/snapshot-verifier.ts +687 -0
- package/src/domain/editable-artifacts/types.ts +835 -0
- package/src/domain/fiken.ts +88 -0
- package/src/domain/insights.ts +163 -50
- package/src/domain/memory-slack-delivery.ts +204 -0
- package/src/domain/memory-slack-publication.ts +5 -2
- package/src/domain/packs.ts +266 -5
- package/src/domain/personal-connection-delegations.ts +97 -6
- package/src/domain/scheduled-tasks.ts +171 -29
- package/src/domain/session-tool-policy.ts +8 -2
- package/src/domain/sessions.ts +395 -148
- package/src/domain/skill-imports.ts +350 -0
- package/src/domain/slack-publication-secret-safety.ts +42 -0
- package/src/domain/timeline-annotations.ts +204 -0
- package/src/domain/video-generation-capabilities.ts +104 -0
- package/src/domain/video-generation.ts +249 -0
- package/src/editable-artifact-live/application.ts +182 -0
- package/src/editable-artifact-live/errors.ts +4 -0
- package/src/editable-artifact-live/index.ts +8 -0
- package/src/editable-artifact-live/outbox-dispatcher.ts +916 -0
- package/src/editable-artifact-live/ports.ts +139 -0
- package/src/editable-artifact-live/server.ts +1707 -0
- package/src/editable-artifact-live/ticket.ts +217 -0
- package/src/editable-artifact-live/types.ts +286 -0
- package/src/editable-artifact-live/wire.ts +95 -0
- package/src/editable-artifact-live.ts +2 -0
- package/src/editable-artifacts.ts +2 -0
- package/src/index.ts +15 -0
- package/src/managed-session.ts +19 -2
- package/src/rigs/index.ts +26 -19
- package/src/rigs/provider-images.ts +110 -0
- package/src/sandbox/fleet.ts +72 -66
- package/src/sandbox/routing.ts +6 -3
- package/src/session-authorization.ts +187 -52
- package/src/transcription.ts +5 -0
- package/src/workflow-wake-contract.ts +8 -0
package/src/rigs/index.ts
CHANGED
|
@@ -36,6 +36,9 @@ import {
|
|
|
36
36
|
type Database,
|
|
37
37
|
} from "@opengeni/db";
|
|
38
38
|
import { HTTPException } from "hono/http-exception";
|
|
39
|
+
import { rigProviderImagesFromVerification } from "./provider-images";
|
|
40
|
+
|
|
41
|
+
export * from "./provider-images";
|
|
39
42
|
|
|
40
43
|
export const MAX_RIGS_PER_WORKSPACE = 50;
|
|
41
44
|
export const MAX_CHECKS_PER_RIG = 100;
|
|
@@ -352,6 +355,13 @@ export async function promoteSetupAppendChange(
|
|
|
352
355
|
if (typeof payload.command !== "string" || !payload.command.trim()) {
|
|
353
356
|
throw new HTTPException(422, { message: "setup_append change is missing command" });
|
|
354
357
|
}
|
|
358
|
+
const nextDefinition = {
|
|
359
|
+
image: base.image,
|
|
360
|
+
setupScript: appendRigSetupCommand(base.setupScript, payload.command),
|
|
361
|
+
checks: base.checks,
|
|
362
|
+
credentialHooks: base.credentialHooks,
|
|
363
|
+
defaultVariableSetIds: base.defaultVariableSetIds,
|
|
364
|
+
};
|
|
355
365
|
const { version, change: updated } = await promoteChangeWithActiveCas(
|
|
356
366
|
deps,
|
|
357
367
|
grant.workspaceId,
|
|
@@ -359,16 +369,13 @@ export async function promoteSetupAppendChange(
|
|
|
359
369
|
change.id,
|
|
360
370
|
{
|
|
361
371
|
expectedActiveVersionId: change.baseVersionId,
|
|
362
|
-
|
|
363
|
-
setupScript: appendRigSetupCommand(base.setupScript, payload.command),
|
|
364
|
-
checks: base.checks,
|
|
365
|
-
credentialHooks: base.credentialHooks,
|
|
366
|
-
defaultVariableSetIds: base.defaultVariableSetIds,
|
|
372
|
+
...nextDefinition,
|
|
367
373
|
changelog:
|
|
368
374
|
typeof payload.note === "string" && payload.note.trim()
|
|
369
375
|
? payload.note
|
|
370
376
|
: "Verified setup append",
|
|
371
377
|
createdBy: change.proposedBy ?? rigActorForGrant(grant),
|
|
378
|
+
providerImages: rigProviderImagesFromVerification(change.verification, nextDefinition),
|
|
372
379
|
},
|
|
373
380
|
);
|
|
374
381
|
await recordRigAuditEvent(deps.db, {
|
|
@@ -418,6 +425,18 @@ export async function promoteVerifiedDefinitionEditChangeForApi(
|
|
|
418
425
|
defaultVariableSetIds?: unknown;
|
|
419
426
|
changelog?: unknown;
|
|
420
427
|
};
|
|
428
|
+
const nextDefinition = {
|
|
429
|
+
image: payload.image === undefined ? base.image : (payload.image as string | null),
|
|
430
|
+
setupScript:
|
|
431
|
+
payload.setupScript === undefined ? base.setupScript : (payload.setupScript as string | null),
|
|
432
|
+
checks: Array.isArray(payload.checks) ? (payload.checks as RigVersion["checks"]) : base.checks,
|
|
433
|
+
credentialHooks: Array.isArray(payload.credentialHooks)
|
|
434
|
+
? (payload.credentialHooks as string[])
|
|
435
|
+
: base.credentialHooks,
|
|
436
|
+
defaultVariableSetIds: Array.isArray(payload.defaultVariableSetIds)
|
|
437
|
+
? (payload.defaultVariableSetIds as string[])
|
|
438
|
+
: base.defaultVariableSetIds,
|
|
439
|
+
};
|
|
421
440
|
const { version, change: updated } = await promoteChangeWithActiveCas(
|
|
422
441
|
deps,
|
|
423
442
|
grant.workspaceId,
|
|
@@ -425,25 +444,13 @@ export async function promoteVerifiedDefinitionEditChangeForApi(
|
|
|
425
444
|
change.id,
|
|
426
445
|
{
|
|
427
446
|
expectedActiveVersionId: change.baseVersionId,
|
|
428
|
-
|
|
429
|
-
setupScript:
|
|
430
|
-
payload.setupScript === undefined
|
|
431
|
-
? base.setupScript
|
|
432
|
-
: (payload.setupScript as string | null),
|
|
433
|
-
checks: Array.isArray(payload.checks)
|
|
434
|
-
? (payload.checks as RigVersion["checks"])
|
|
435
|
-
: base.checks,
|
|
436
|
-
credentialHooks: Array.isArray(payload.credentialHooks)
|
|
437
|
-
? (payload.credentialHooks as string[])
|
|
438
|
-
: base.credentialHooks,
|
|
439
|
-
defaultVariableSetIds: Array.isArray(payload.defaultVariableSetIds)
|
|
440
|
-
? (payload.defaultVariableSetIds as string[])
|
|
441
|
-
: base.defaultVariableSetIds,
|
|
447
|
+
...nextDefinition,
|
|
442
448
|
changelog:
|
|
443
449
|
typeof payload.changelog === "string" && payload.changelog.trim()
|
|
444
450
|
? payload.changelog
|
|
445
451
|
: "Verified definition edit",
|
|
446
452
|
createdBy: rigActorForGrant(grant),
|
|
453
|
+
providerImages: rigProviderImagesFromVerification(change.verification, nextDefinition),
|
|
447
454
|
},
|
|
448
455
|
);
|
|
449
456
|
await recordRigAuditEvent(deps.db, {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
RigProviderImage as RigProviderImageContract,
|
|
4
|
+
stableJson,
|
|
5
|
+
type RigChangeVerification,
|
|
6
|
+
type RigProviderImage,
|
|
7
|
+
type RigProviderImages,
|
|
8
|
+
type RigVersion,
|
|
9
|
+
type SandboxBackend,
|
|
10
|
+
} from "@opengeni/contracts";
|
|
11
|
+
|
|
12
|
+
export type RigProviderImageDefinition = Pick<
|
|
13
|
+
RigVersion,
|
|
14
|
+
"image" | "setupScript" | "checks" | "credentialHooks" | "defaultVariableSetIds"
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
function sha256(value: string): string {
|
|
18
|
+
return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function rigProviderImageProviderBindingKeyHash(bindingKey: string): string {
|
|
22
|
+
return sha256(bindingKey);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function rigProviderImageSetupHash(
|
|
26
|
+
definition: Pick<RigProviderImageDefinition, "setupScript">,
|
|
27
|
+
): string {
|
|
28
|
+
return sha256(definition.setupScript ?? "");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Hash every exact rig-definition input that can affect whether a provider
|
|
33
|
+
* image may be reused. Credential values and variable-set contents are
|
|
34
|
+
* intentionally absent: verification never injects them into the build box.
|
|
35
|
+
* Their declared hook/set identities still participate so a new rig version
|
|
36
|
+
* cannot silently inherit an older build record.
|
|
37
|
+
*/
|
|
38
|
+
export function rigProviderImageContentHash(input: {
|
|
39
|
+
backend: SandboxBackend;
|
|
40
|
+
sourceImage: string | null;
|
|
41
|
+
definition: RigProviderImageDefinition;
|
|
42
|
+
}): string {
|
|
43
|
+
return sha256(
|
|
44
|
+
stableJson({
|
|
45
|
+
version: 1,
|
|
46
|
+
backend: input.backend,
|
|
47
|
+
sourceImage: input.sourceImage,
|
|
48
|
+
image: input.definition.image,
|
|
49
|
+
setupScript: input.definition.setupScript,
|
|
50
|
+
checks: input.definition.checks,
|
|
51
|
+
credentialHooks: input.definition.credentialHooks,
|
|
52
|
+
defaultVariableSetIds: input.definition.defaultVariableSetIds,
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Modal's patched snapshot API accepts a caller-owned UUID idempotency key.
|
|
58
|
+
* v2 denotes images proven by a separate cold boot before publication. */
|
|
59
|
+
export function rigProviderImageBuildRequestId(input: {
|
|
60
|
+
targetId: string;
|
|
61
|
+
backend: SandboxBackend;
|
|
62
|
+
contentHash: string;
|
|
63
|
+
}): string {
|
|
64
|
+
const bytes = createHash("sha256")
|
|
65
|
+
.update("opengeni-rig-provider-image-build-v2\0", "utf8")
|
|
66
|
+
.update(input.targetId, "utf8")
|
|
67
|
+
.update("\0", "utf8")
|
|
68
|
+
.update(input.backend, "utf8")
|
|
69
|
+
.update("\0", "utf8")
|
|
70
|
+
.update(input.contentHash, "utf8")
|
|
71
|
+
.digest()
|
|
72
|
+
.subarray(0, 16);
|
|
73
|
+
// RFC 4122 variant + deterministic version-5-shaped UUID. The digest, not a
|
|
74
|
+
// provider response, is the idempotency identity.
|
|
75
|
+
bytes[6] = (bytes[6]! & 0x0f) | 0x50;
|
|
76
|
+
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
|
|
77
|
+
const hex = bytes.toString("hex");
|
|
78
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function rigProviderImageMatchesDefinition(
|
|
82
|
+
image: RigProviderImage,
|
|
83
|
+
definition: RigProviderImageDefinition,
|
|
84
|
+
): boolean {
|
|
85
|
+
return (
|
|
86
|
+
image.setupHash === rigProviderImageSetupHash(definition) &&
|
|
87
|
+
image.contentHash ===
|
|
88
|
+
rigProviderImageContentHash({
|
|
89
|
+
backend: image.backend,
|
|
90
|
+
sourceImage: image.sourceImage,
|
|
91
|
+
definition,
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Promotion copies only a finalized, structurally valid build record whose
|
|
98
|
+
* hashes match the exact version definition being inserted. A malformed or
|
|
99
|
+
* stale optimization record is discarded; the version remains correct and
|
|
100
|
+
* falls back to runtime setup.
|
|
101
|
+
*/
|
|
102
|
+
export function rigProviderImagesFromVerification(
|
|
103
|
+
verification: RigChangeVerification | null,
|
|
104
|
+
definition: RigProviderImageDefinition,
|
|
105
|
+
): RigProviderImages {
|
|
106
|
+
const parsed = RigProviderImageContract.safeParse(verification?.providerImage);
|
|
107
|
+
if (!parsed.success || parsed.data.status === "building") return {};
|
|
108
|
+
if (!rigProviderImageMatchesDefinition(parsed.data, definition)) return {};
|
|
109
|
+
return { [parsed.data.backend]: parsed.data };
|
|
110
|
+
}
|
package/src/sandbox/fleet.ts
CHANGED
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
type SelfhostedRelayConfig,
|
|
38
38
|
type SelfhostedOpStreamDeps,
|
|
39
39
|
} from "@opengeni/runtime/sandbox";
|
|
40
|
-
import { HTTPException } from "hono/http-exception";
|
|
41
40
|
import { relayConfigFromSettings } from "./routing";
|
|
42
41
|
|
|
43
42
|
export type FleetServices = {
|
|
@@ -69,8 +68,9 @@ export type FleetContext = {
|
|
|
69
68
|
|
|
70
69
|
/**
|
|
71
70
|
* Build a session-scoped {@link FleetContext}: load the session (workspace-
|
|
72
|
-
* scoped)
|
|
73
|
-
*
|
|
71
|
+
* scoped) and project its group backend/id. A backend:none session has no home
|
|
72
|
+
* box, but it may still discover, run on, and attach an owned Connected Machine.
|
|
73
|
+
* Shared
|
|
74
74
|
* by the worker-signed MCP fleet tools and the user-authenticated swap REST
|
|
75
75
|
* route so both resolve the SAME context (no drift). The `accountId`/`workspaceId`/
|
|
76
76
|
* `sessionId` come from the trusted grant/route; the backend + group id come from
|
|
@@ -81,11 +81,6 @@ export async function buildFleetContextForSession(
|
|
|
81
81
|
ctx: { accountId: string; workspaceId: string; sessionId: string },
|
|
82
82
|
): Promise<FleetContext> {
|
|
83
83
|
const session = await requireSession(deps.db, ctx.workspaceId, ctx.sessionId);
|
|
84
|
-
if (session.sandboxBackend === "none") {
|
|
85
|
-
throw new HTTPException(422, {
|
|
86
|
-
message: "this session has no sandbox (backend: none); the fleet is unavailable",
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
84
|
return {
|
|
90
85
|
accountId: ctx.accountId,
|
|
91
86
|
workspaceId: ctx.workspaceId,
|
|
@@ -229,9 +224,11 @@ async function probeEnrollment(
|
|
|
229
224
|
}
|
|
230
225
|
|
|
231
226
|
/**
|
|
232
|
-
* List the fleet: the session's own
|
|
233
|
-
* workspace's first-class selfhosted sandboxes (each probed for
|
|
234
|
-
* with an `active` marker derived from the session's active
|
|
227
|
+
* List the fleet: the session's own group box when it has one (a synthetic
|
|
228
|
+
* entry) + the workspace's first-class selfhosted sandboxes (each probed for
|
|
229
|
+
* liveness), each with an `active` marker derived from the session's active
|
|
230
|
+
* pointer. A backend:none session has no synthetic home entry; a null pointer
|
|
231
|
+
* then means no compute is attached.
|
|
235
232
|
*/
|
|
236
233
|
export async function listFleet(
|
|
237
234
|
services: FleetServices,
|
|
@@ -245,61 +242,63 @@ export async function listFleet(
|
|
|
245
242
|
|
|
246
243
|
const entries: FleetSandboxEntry[] = [];
|
|
247
244
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
groupLease.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
groupLease.recovery.restore.status === "
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
245
|
+
if (ctx.sessionBackend !== "none") {
|
|
246
|
+
// The session's own group box (the default/home sandbox; null active pointer ==
|
|
247
|
+
// this box). A session/group row is not provider existence. Online requires a
|
|
248
|
+
// warm lease, observed provider existence, and verified workspace readiness.
|
|
249
|
+
const groupActive = pointer.activeSandboxId === null;
|
|
250
|
+
const groupLease = await readLease(db, ctx.workspaceId, ctx.sessionGroupId);
|
|
251
|
+
const groupOnline = Boolean(
|
|
252
|
+
groupLease?.liveness === "warm" &&
|
|
253
|
+
groupLease.recovery.provider.status === "exists" &&
|
|
254
|
+
groupLease.recovery.workspace.status === "ready",
|
|
255
|
+
);
|
|
256
|
+
const groupRecovering = Boolean(
|
|
257
|
+
groupLease &&
|
|
258
|
+
(groupLease.liveness === "warming" ||
|
|
259
|
+
groupLease.recovery.restore.status === "pending" ||
|
|
260
|
+
groupLease.recovery.restore.status === "restoring" ||
|
|
261
|
+
groupLease.recovery.restore.status === "verifying"),
|
|
262
|
+
);
|
|
263
|
+
const groupRecoveryUnavailable = Boolean(
|
|
264
|
+
groupLease &&
|
|
265
|
+
(groupLease.recovery.restore.status === "degraded" ||
|
|
266
|
+
groupLease.recovery.restore.status === "unrecoverable" ||
|
|
267
|
+
groupLease.recovery.workspace.status === "degraded" ||
|
|
268
|
+
groupLease.recovery.workspace.status === "unrecoverable"),
|
|
269
|
+
);
|
|
270
|
+
const groupOperationAvailability: FleetOperationAvailability = groupOnline
|
|
271
|
+
? "ready"
|
|
272
|
+
: groupRecoveryUnavailable
|
|
273
|
+
? "unavailable"
|
|
274
|
+
: groupRecovering
|
|
275
|
+
? "recovering"
|
|
276
|
+
: ctx.sessionBackend === "selfhosted"
|
|
277
|
+
? "unavailable"
|
|
278
|
+
: "wakeable";
|
|
279
|
+
entries.push({
|
|
280
|
+
id: ctx.sessionGroupId,
|
|
281
|
+
kind: ctx.sessionBackend === "selfhosted" ? "selfhosted" : "modal",
|
|
282
|
+
name: "session sandbox",
|
|
283
|
+
liveness: groupOnline ? "online" : groupRecovering ? "reconnecting" : "offline",
|
|
284
|
+
active: groupActive,
|
|
285
|
+
isSessionGroup: true,
|
|
286
|
+
enrollmentId: null,
|
|
287
|
+
attachable: groupOnline,
|
|
288
|
+
operationAvailability: groupOperationAvailability,
|
|
289
|
+
providerStatus: groupLease?.recovery.provider.status ?? "not_created",
|
|
290
|
+
leaseLiveness: groupLease?.liveness ?? null,
|
|
291
|
+
routeStatus: groupActive ? "attached" : "detached",
|
|
292
|
+
archiveStatus: groupLease?.recovery.archive.status ?? "none",
|
|
293
|
+
restoreStatus: groupLease?.recovery.restore.status ?? "not_required",
|
|
294
|
+
workspaceStatus: groupLease?.recovery.workspace.status ?? "unknown",
|
|
295
|
+
leaseEpoch: groupLease?.leaseEpoch ?? null,
|
|
296
|
+
routeEpoch: pointer.activeEpoch,
|
|
297
|
+
workspaceGeneration: groupLease?.workspaceGeneration ?? null,
|
|
298
|
+
archiveGeneration: groupLease?.archiveGeneration ?? null,
|
|
299
|
+
archiveComplete: groupLease?.archiveComplete ?? false,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
303
302
|
|
|
304
303
|
// The workspace's first-class selfhosted sandboxes (enrolled machines). Probe
|
|
305
304
|
// each for liveness; a missing enrollment is offline.
|
|
@@ -374,6 +373,13 @@ async function resolveTarget(
|
|
|
374
373
|
> {
|
|
375
374
|
// The session's own group box → the default pointer (null).
|
|
376
375
|
if (target === ctx.sessionGroupId || target === "session" || target === "default") {
|
|
376
|
+
if (ctx.sessionBackend === "none") {
|
|
377
|
+
return {
|
|
378
|
+
ok: false,
|
|
379
|
+
reason: "this session has no home sandbox; attach a Connected Machine",
|
|
380
|
+
code: "unsupported_backend_context",
|
|
381
|
+
};
|
|
382
|
+
}
|
|
377
383
|
return { ok: true, targetSandboxId: null };
|
|
378
384
|
}
|
|
379
385
|
const sandbox = await getSandbox(services.db, ctx.workspaceId, target);
|
package/src/sandbox/routing.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// The DB-coupled glue (readActiveSandbox / getSandbox / the selfhosted ControlRpc
|
|
12
12
|
// over the events bus) lives here, not in the leaf (which stays db-free).
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { sandboxLifecycleTransitionWaitMs, type Settings } from "@opengeni/config";
|
|
15
15
|
import {
|
|
16
16
|
advanceWorkspaceGenerationForDirectRequest,
|
|
17
17
|
advanceWorkspaceGenerationForRetainedProcess,
|
|
@@ -61,6 +61,7 @@ export type ChannelARoutingServices = {
|
|
|
61
61
|
settings: Settings;
|
|
62
62
|
bus?: EventBus;
|
|
63
63
|
onSandboxOperation?: RoutingSandboxOperationObserver;
|
|
64
|
+
waitSignal?: AbortSignal;
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
/** Map the deployment relay URL to the leaf's `SelfhostedRelayConfig` shape. The
|
|
@@ -214,7 +215,8 @@ export function wrapChannelABoxWithRouting(
|
|
|
214
215
|
routeTargetId: backend.sandboxId,
|
|
215
216
|
routeEpoch: backend.activeEpoch,
|
|
216
217
|
operation: op,
|
|
217
|
-
captureWaitMs:
|
|
218
|
+
captureWaitMs: sandboxLifecycleTransitionWaitMs(settings),
|
|
219
|
+
...(services.waitSignal ? { waitSignal: services.waitSignal } : {}),
|
|
218
220
|
});
|
|
219
221
|
return { admission, providerBinding };
|
|
220
222
|
}
|
|
@@ -310,7 +312,8 @@ export function wrapChannelABoxWithRouting(
|
|
|
310
312
|
sessionId: ids.sessionId,
|
|
311
313
|
processId: process.id,
|
|
312
314
|
operation: op,
|
|
313
|
-
captureWaitMs:
|
|
315
|
+
captureWaitMs: sandboxLifecycleTransitionWaitMs(settings),
|
|
316
|
+
...(services.waitSignal ? { waitSignal: services.waitSignal } : {}),
|
|
314
317
|
})
|
|
315
318
|
: undefined;
|
|
316
319
|
const afterProcessMutation = homeLease
|