@opengeni/core 2.6.4-canary.0 → 2.7.2-canary.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/dependencies.d.ts +2 -0
- package/dist/domain/host-mcp-authority-source-admission.d.ts +10 -0
- package/dist/domain/packs.d.ts +1 -0
- package/dist/domain/product-integration-pack.d.ts +50 -0
- package/dist/domain/sessions.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1194 -395
- package/dist/index.js.map +1 -1
- package/dist/model-catalog.d.ts +16 -0
- package/dist/sandbox/fleet.d.ts +19 -0
- package/package.json +10 -10
- package/src/dependencies.ts +2 -0
- package/src/domain/capabilities.ts +19 -3
- package/src/domain/host-mcp-authority-source-admission.ts +25 -0
- package/src/domain/insights.ts +32 -0
- package/src/domain/packs.ts +48 -18
- package/src/domain/personal-connection-delegations.ts +14 -6
- package/src/domain/product-integration-pack.ts +494 -0
- package/src/domain/scheduled-tasks.ts +6 -15
- package/src/domain/sessions.ts +197 -65
- package/src/index.ts +2 -0
- package/src/model-catalog.ts +183 -6
- package/src/sandbox/fleet.ts +85 -23
package/src/model-catalog.ts
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
applyModelCatalogDocument,
|
|
3
3
|
configuredGatewayWorkspaceProductModelIds,
|
|
4
|
+
configuredGatewayOrganizationProductModelIds,
|
|
4
5
|
configuredModels,
|
|
5
6
|
configuredModelNotes,
|
|
6
7
|
configuredOpenRouterWorkspaceProductModelIds,
|
|
8
|
+
configuredOpenRouterOrganizationProductModelIds,
|
|
7
9
|
configuredProviders,
|
|
8
10
|
validateModelCatalogSettings,
|
|
9
11
|
withCodexCatalogProvider,
|
|
12
|
+
withOrganizationGatewayCatalogProvider,
|
|
13
|
+
withOrganizationOpenRouterCatalogProvider,
|
|
10
14
|
withWorkspaceGatewayCatalogProvider,
|
|
11
15
|
withWorkspaceOpenRouterCatalogProvider,
|
|
12
16
|
withXaiSubscriptionCatalogProvider,
|
|
13
17
|
WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
|
|
14
18
|
WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
|
|
15
19
|
WORKSPACE_OPENROUTER_PROVIDER_ID,
|
|
20
|
+
ORGANIZATION_OPENROUTER_PROVIDER_ID,
|
|
21
|
+
ORGANIZATION_GATEWAY_MODEL_ID_PREFIX,
|
|
22
|
+
ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX,
|
|
16
23
|
type ConfiguredModel,
|
|
17
24
|
type Settings,
|
|
18
25
|
} from "@opengeni/config";
|
|
@@ -26,8 +33,13 @@ import {
|
|
|
26
33
|
getDeploymentModelCatalog,
|
|
27
34
|
getWorkspaceGatewayCustomModelForExecution,
|
|
28
35
|
getWorkspaceOpenRouterCustomModelForExecution,
|
|
36
|
+
getOrganizationModelProviderCustomModelForExecution,
|
|
29
37
|
listWorkspaceGatewayCustomModels,
|
|
30
38
|
listWorkspaceOpenRouterCustomModels,
|
|
39
|
+
listOrganizationModelProviderCustomModelsForWorkspace,
|
|
40
|
+
lockActiveOrganizationModelProviderCustomModelForAdmission,
|
|
41
|
+
lockActiveWorkspaceGatewayCustomModelForAdmission,
|
|
42
|
+
lockActiveWorkspaceOpenRouterCustomModelForAdmission,
|
|
31
43
|
type Database,
|
|
32
44
|
} from "@opengeni/db";
|
|
33
45
|
|
|
@@ -58,6 +70,7 @@ export function isWorkspaceOpenRouterCustomModelId(settings: Settings, modelId:
|
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
export type WorkspaceCustomModelReference = {
|
|
73
|
+
scope: "workspace" | "organization";
|
|
61
74
|
providerKind: "vercel_gateway" | "openrouter";
|
|
62
75
|
upstreamModelId: string;
|
|
63
76
|
};
|
|
@@ -68,16 +81,38 @@ export function workspaceCustomModelReference(
|
|
|
68
81
|
): WorkspaceCustomModelReference | null {
|
|
69
82
|
if (isWorkspaceGatewayCustomModelId(settings, modelId)) {
|
|
70
83
|
return {
|
|
84
|
+
scope: "workspace",
|
|
71
85
|
providerKind: "vercel_gateway",
|
|
72
86
|
upstreamModelId: modelId.slice(WORKSPACE_GATEWAY_MODEL_ID_PREFIX.length),
|
|
73
87
|
};
|
|
74
88
|
}
|
|
75
89
|
if (isWorkspaceOpenRouterCustomModelId(settings, modelId)) {
|
|
76
90
|
return {
|
|
91
|
+
scope: "workspace",
|
|
77
92
|
providerKind: "openrouter",
|
|
78
93
|
upstreamModelId: modelId.slice(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX.length),
|
|
79
94
|
};
|
|
80
95
|
}
|
|
96
|
+
if (
|
|
97
|
+
modelId.startsWith(ORGANIZATION_GATEWAY_MODEL_ID_PREFIX) &&
|
|
98
|
+
!configuredGatewayOrganizationProductModelIds(settings).includes(modelId)
|
|
99
|
+
) {
|
|
100
|
+
return {
|
|
101
|
+
scope: "organization",
|
|
102
|
+
providerKind: "vercel_gateway",
|
|
103
|
+
upstreamModelId: modelId.slice(ORGANIZATION_GATEWAY_MODEL_ID_PREFIX.length),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (
|
|
107
|
+
modelId.startsWith(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX) &&
|
|
108
|
+
!configuredOpenRouterOrganizationProductModelIds(settings).includes(modelId)
|
|
109
|
+
) {
|
|
110
|
+
return {
|
|
111
|
+
scope: "organization",
|
|
112
|
+
providerKind: "openrouter",
|
|
113
|
+
upstreamModelId: modelId.slice(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX.length),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
81
116
|
return null;
|
|
82
117
|
}
|
|
83
118
|
|
|
@@ -85,6 +120,39 @@ export function isWorkspaceCustomModelId(settings: Settings, modelId: string): b
|
|
|
85
120
|
return workspaceCustomModelReference(settings, modelId) !== null;
|
|
86
121
|
}
|
|
87
122
|
|
|
123
|
+
export async function lockActiveCustomModelForAdmission(
|
|
124
|
+
db: Database,
|
|
125
|
+
input: {
|
|
126
|
+
accountId: string;
|
|
127
|
+
workspaceId: string;
|
|
128
|
+
reference: WorkspaceCustomModelReference;
|
|
129
|
+
},
|
|
130
|
+
): Promise<boolean> {
|
|
131
|
+
if (input.reference.scope === "organization") {
|
|
132
|
+
return Boolean(
|
|
133
|
+
await lockActiveOrganizationModelProviderCustomModelForAdmission(db, {
|
|
134
|
+
accountId: input.accountId,
|
|
135
|
+
workspaceId: input.workspaceId,
|
|
136
|
+
providerKind: input.reference.providerKind,
|
|
137
|
+
upstreamModelId: input.reference.upstreamModelId,
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return Boolean(
|
|
142
|
+
input.reference.providerKind === "openrouter"
|
|
143
|
+
? await lockActiveWorkspaceOpenRouterCustomModelForAdmission(db, {
|
|
144
|
+
accountId: input.accountId,
|
|
145
|
+
workspaceId: input.workspaceId,
|
|
146
|
+
upstreamModelId: input.reference.upstreamModelId,
|
|
147
|
+
})
|
|
148
|
+
: await lockActiveWorkspaceGatewayCustomModelForAdmission(db, {
|
|
149
|
+
accountId: input.accountId,
|
|
150
|
+
workspaceId: input.workspaceId,
|
|
151
|
+
upstreamModelId: input.reference.upstreamModelId,
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
88
156
|
/**
|
|
89
157
|
* Resolve the deployment catalog without making synchronous env settings read
|
|
90
158
|
* Postgres. Database mode fails closed when the singleton is absent or invalid;
|
|
@@ -133,6 +201,11 @@ export async function resolveWorkspaceCatalogSettings(
|
|
|
133
201
|
retainedProductModelIds?: readonly (string | null | undefined)[];
|
|
134
202
|
},
|
|
135
203
|
): Promise<ResolvedCatalogSettings> {
|
|
204
|
+
// A few pure catalog tests inject the historical minimal DB port and mock the
|
|
205
|
+
// workspace model helpers directly. Real/injected runtime databases always
|
|
206
|
+
// expose transactions; keep that narrow test port compatible.
|
|
207
|
+
const supportsOrganizationProviderReads =
|
|
208
|
+
typeof (db as Database & { transaction?: unknown }).transaction === "function";
|
|
136
209
|
const retainedProductModelIds = [
|
|
137
210
|
...(input.retainedProductModelIds ?? []),
|
|
138
211
|
input.retainedProductModelId,
|
|
@@ -147,12 +220,28 @@ export async function resolveWorkspaceCatalogSettings(
|
|
|
147
220
|
? [productModelId.slice(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX.length)]
|
|
148
221
|
: [],
|
|
149
222
|
);
|
|
223
|
+
const retainedOrganizationGatewayUpstreamModelIds = retainedProductModelIds.flatMap(
|
|
224
|
+
(productModelId) =>
|
|
225
|
+
productModelId?.startsWith(ORGANIZATION_GATEWAY_MODEL_ID_PREFIX)
|
|
226
|
+
? [productModelId.slice(ORGANIZATION_GATEWAY_MODEL_ID_PREFIX.length)]
|
|
227
|
+
: [],
|
|
228
|
+
);
|
|
229
|
+
const retainedOrganizationOpenRouterUpstreamModelIds = retainedProductModelIds.flatMap(
|
|
230
|
+
(productModelId) =>
|
|
231
|
+
productModelId?.startsWith(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX)
|
|
232
|
+
? [productModelId.slice(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX.length)]
|
|
233
|
+
: [],
|
|
234
|
+
);
|
|
150
235
|
const [
|
|
151
236
|
resolved,
|
|
152
237
|
activeGatewayCustomModels,
|
|
153
238
|
activeOpenRouterCustomModels,
|
|
154
239
|
retainedGatewayCustomModels,
|
|
155
240
|
retainedOpenRouterCustomModels,
|
|
241
|
+
organizationGatewayCustomModels,
|
|
242
|
+
organizationOpenRouterCustomModels,
|
|
243
|
+
retainedOrganizationGatewayCustomModels,
|
|
244
|
+
retainedOrganizationOpenRouterCustomModels,
|
|
156
245
|
] = await Promise.all([
|
|
157
246
|
resolveCatalogSettings(db, envSettings),
|
|
158
247
|
listWorkspaceGatewayCustomModels(db, {
|
|
@@ -183,6 +272,46 @@ export async function resolveWorkspaceCatalogSettings(
|
|
|
183
272
|
}),
|
|
184
273
|
),
|
|
185
274
|
),
|
|
275
|
+
supportsOrganizationProviderReads
|
|
276
|
+
? listOrganizationModelProviderCustomModelsForWorkspace(db, {
|
|
277
|
+
accountId: input.accountId,
|
|
278
|
+
workspaceId: input.workspaceId,
|
|
279
|
+
providerKind: "vercel_gateway",
|
|
280
|
+
})
|
|
281
|
+
: Promise.resolve([]),
|
|
282
|
+
supportsOrganizationProviderReads
|
|
283
|
+
? listOrganizationModelProviderCustomModelsForWorkspace(db, {
|
|
284
|
+
accountId: input.accountId,
|
|
285
|
+
workspaceId: input.workspaceId,
|
|
286
|
+
providerKind: "openrouter",
|
|
287
|
+
})
|
|
288
|
+
: Promise.resolve([]),
|
|
289
|
+
supportsOrganizationProviderReads
|
|
290
|
+
? Promise.all(
|
|
291
|
+
[...new Set(retainedOrganizationGatewayUpstreamModelIds)].map(
|
|
292
|
+
async (upstreamModelId) =>
|
|
293
|
+
await getOrganizationModelProviderCustomModelForExecution(db, {
|
|
294
|
+
accountId: input.accountId,
|
|
295
|
+
workspaceId: input.workspaceId,
|
|
296
|
+
providerKind: "vercel_gateway",
|
|
297
|
+
upstreamModelId,
|
|
298
|
+
}),
|
|
299
|
+
),
|
|
300
|
+
)
|
|
301
|
+
: Promise.resolve([]),
|
|
302
|
+
supportsOrganizationProviderReads
|
|
303
|
+
? Promise.all(
|
|
304
|
+
[...new Set(retainedOrganizationOpenRouterUpstreamModelIds)].map(
|
|
305
|
+
async (upstreamModelId) =>
|
|
306
|
+
await getOrganizationModelProviderCustomModelForExecution(db, {
|
|
307
|
+
accountId: input.accountId,
|
|
308
|
+
workspaceId: input.workspaceId,
|
|
309
|
+
providerKind: "openrouter",
|
|
310
|
+
upstreamModelId,
|
|
311
|
+
}),
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
: Promise.resolve([]),
|
|
186
315
|
]);
|
|
187
316
|
const includeRetainedModels = <T extends { upstreamModelId: string }>(
|
|
188
317
|
activeModels: readonly T[],
|
|
@@ -209,11 +338,25 @@ export async function resolveWorkspaceCatalogSettings(
|
|
|
209
338
|
activeOpenRouterCustomModels,
|
|
210
339
|
retainedOpenRouterCustomModels,
|
|
211
340
|
);
|
|
341
|
+
const organizationGatewayModels = includeRetainedModels(
|
|
342
|
+
organizationGatewayCustomModels,
|
|
343
|
+
retainedOrganizationGatewayCustomModels,
|
|
344
|
+
);
|
|
345
|
+
const organizationOpenRouterModels = includeRetainedModels(
|
|
346
|
+
organizationOpenRouterCustomModels,
|
|
347
|
+
retainedOrganizationOpenRouterCustomModels,
|
|
348
|
+
);
|
|
212
349
|
return {
|
|
213
350
|
...resolved,
|
|
214
|
-
settings:
|
|
215
|
-
|
|
216
|
-
|
|
351
|
+
settings: withOrganizationOpenRouterCatalogProvider(
|
|
352
|
+
withOrganizationGatewayCatalogProvider(
|
|
353
|
+
withWorkspaceOpenRouterCatalogProvider(
|
|
354
|
+
withWorkspaceGatewayCatalogProvider(resolved.settings, gatewayCustomModels),
|
|
355
|
+
openRouterCustomModels,
|
|
356
|
+
),
|
|
357
|
+
organizationGatewayModels,
|
|
358
|
+
),
|
|
359
|
+
organizationOpenRouterModels,
|
|
217
360
|
),
|
|
218
361
|
};
|
|
219
362
|
}
|
|
@@ -242,6 +385,8 @@ export type WorkspaceModelSelectionInput = {
|
|
|
242
385
|
xaiSubscriptionActive?: boolean;
|
|
243
386
|
workspaceGatewayConnectionActive?: boolean;
|
|
244
387
|
workspaceOpenRouterConnectionActive?: boolean;
|
|
388
|
+
organizationGatewayConnectionActive?: boolean;
|
|
389
|
+
organizationOpenRouterConnectionActive?: boolean;
|
|
245
390
|
workspaceGatewayCustomModels?: readonly {
|
|
246
391
|
upstreamModelId: string;
|
|
247
392
|
label?: string | null;
|
|
@@ -250,6 +395,14 @@ export type WorkspaceModelSelectionInput = {
|
|
|
250
395
|
upstreamModelId: string;
|
|
251
396
|
label?: string | null;
|
|
252
397
|
}[];
|
|
398
|
+
organizationGatewayCustomModels?: readonly {
|
|
399
|
+
upstreamModelId: string;
|
|
400
|
+
label?: string | null;
|
|
401
|
+
}[];
|
|
402
|
+
organizationOpenRouterCustomModels?: readonly {
|
|
403
|
+
upstreamModelId: string;
|
|
404
|
+
label?: string | null;
|
|
405
|
+
}[];
|
|
253
406
|
credentialReadinessObservations?:
|
|
254
407
|
| Readonly<Record<string, ModelCredentialReadinessObservation>>
|
|
255
408
|
| undefined;
|
|
@@ -332,6 +485,8 @@ function credentialReadinessFor(input: {
|
|
|
332
485
|
xaiSubscriptionActive: boolean;
|
|
333
486
|
workspaceGatewayConnectionActive: boolean;
|
|
334
487
|
workspaceOpenRouterConnectionActive: boolean;
|
|
488
|
+
organizationGatewayConnectionActive: boolean;
|
|
489
|
+
organizationOpenRouterConnectionActive: boolean;
|
|
335
490
|
observation: ModelCredentialReadinessObservation | undefined;
|
|
336
491
|
nowMs: number;
|
|
337
492
|
maxAgeMs: number;
|
|
@@ -363,6 +518,20 @@ function credentialReadinessFor(input: {
|
|
|
363
518
|
checkedAt: null,
|
|
364
519
|
};
|
|
365
520
|
}
|
|
521
|
+
if (source.kind === "organization_connection") {
|
|
522
|
+
const connectionActive =
|
|
523
|
+
input.model.providerId === ORGANIZATION_OPENROUTER_PROVIDER_ID
|
|
524
|
+
? input.organizationOpenRouterConnectionActive
|
|
525
|
+
: input.organizationGatewayConnectionActive;
|
|
526
|
+
return connectionActive
|
|
527
|
+
? { status: "ready", reason: null, basis: "connection", checkedAt: null }
|
|
528
|
+
: {
|
|
529
|
+
status: "not_ready",
|
|
530
|
+
reason: "needs_reauth",
|
|
531
|
+
basis: "connection",
|
|
532
|
+
checkedAt: null,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
366
535
|
if (source.kind === "deployment" && source.mechanism === "none") {
|
|
367
536
|
return { status: "ready", reason: null, basis: "configuration", checkedAt: null };
|
|
368
537
|
}
|
|
@@ -512,9 +681,15 @@ export function resolveWorkspaceModelSelection(
|
|
|
512
681
|
const xaiSettings = input.settings.supergrokSubscriptionEnabled
|
|
513
682
|
? withXaiSubscriptionCatalogProvider(codexSettings)
|
|
514
683
|
: codexSettings;
|
|
515
|
-
const catalogSettings =
|
|
516
|
-
|
|
517
|
-
|
|
684
|
+
const catalogSettings = withOrganizationOpenRouterCatalogProvider(
|
|
685
|
+
withOrganizationGatewayCatalogProvider(
|
|
686
|
+
withWorkspaceOpenRouterCatalogProvider(
|
|
687
|
+
withWorkspaceGatewayCatalogProvider(xaiSettings, input.workspaceGatewayCustomModels ?? []),
|
|
688
|
+
input.workspaceOpenRouterCustomModels ?? [],
|
|
689
|
+
),
|
|
690
|
+
input.organizationGatewayCustomModels ?? [],
|
|
691
|
+
),
|
|
692
|
+
input.organizationOpenRouterCustomModels ?? [],
|
|
518
693
|
);
|
|
519
694
|
const providers = new Map(
|
|
520
695
|
configuredProviders(catalogSettings).map((provider) => [provider.id, provider]),
|
|
@@ -544,6 +719,8 @@ export function resolveWorkspaceModelSelection(
|
|
|
544
719
|
xaiSubscriptionActive: input.xaiSubscriptionActive === true,
|
|
545
720
|
workspaceGatewayConnectionActive: input.workspaceGatewayConnectionActive === true,
|
|
546
721
|
workspaceOpenRouterConnectionActive: input.workspaceOpenRouterConnectionActive === true,
|
|
722
|
+
organizationGatewayConnectionActive: input.organizationGatewayConnectionActive === true,
|
|
723
|
+
organizationOpenRouterConnectionActive: input.organizationOpenRouterConnectionActive === true,
|
|
547
724
|
observation: input.credentialReadinessObservations?.[model.definitionVersion],
|
|
548
725
|
nowMs,
|
|
549
726
|
maxAgeMs,
|
package/src/sandbox/fleet.ts
CHANGED
|
@@ -454,31 +454,18 @@ export async function listFleet(
|
|
|
454
454
|
};
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
457
|
+
type FleetResourceContext = Pick<FleetContext, "accountId" | "workspaceId" | "subjectId">;
|
|
458
|
+
|
|
459
|
+
type ResolvedNamedSandboxTarget =
|
|
460
|
+
| { ok: true; targetSandboxId: string; workspaceRoot?: string }
|
|
461
|
+
| { ok: false; reason: string; code: BackendUnresolvableCode };
|
|
462
|
+
|
|
463
|
+
/** Resolve one named sandbox, independent of any existing session pointer. */
|
|
464
|
+
async function resolveNamedSandboxTarget(
|
|
461
465
|
services: FleetServices,
|
|
462
|
-
ctx:
|
|
466
|
+
ctx: FleetResourceContext,
|
|
463
467
|
target: string,
|
|
464
|
-
): Promise<
|
|
465
|
-
| { ok: true; targetSandboxId: string | null; workspaceRoot?: string }
|
|
466
|
-
| { ok: false; reason: string; code: BackendUnresolvableCode }
|
|
467
|
-
> {
|
|
468
|
-
// The session's own group box → the default pointer (null).
|
|
469
|
-
if (target === ctx.sessionGroupId || target === "session" || target === "default") {
|
|
470
|
-
if (!managedSessionGroupBackend(services.settings.sandboxBackend, ctx.sessionBackend)) {
|
|
471
|
-
return {
|
|
472
|
-
ok: false,
|
|
473
|
-
reason:
|
|
474
|
-
ctx.sessionBackend === "none"
|
|
475
|
-
? "this session has no home sandbox; attach a Connected Machine"
|
|
476
|
-
: "this deployment has no managed session sandbox; select an enrolled machine",
|
|
477
|
-
code: "unsupported_backend_context",
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
return { ok: true, targetSandboxId: null };
|
|
481
|
-
}
|
|
468
|
+
): Promise<ResolvedNamedSandboxTarget> {
|
|
482
469
|
const sandbox = await getSandbox(
|
|
483
470
|
services.db,
|
|
484
471
|
ctx.subjectId
|
|
@@ -566,6 +553,81 @@ async function resolveTarget(
|
|
|
566
553
|
};
|
|
567
554
|
}
|
|
568
555
|
|
|
556
|
+
export type CreateTimeSandboxTargetPreflight =
|
|
557
|
+
| { ok: true; targetSandboxId: string; workingDir: string | null }
|
|
558
|
+
| {
|
|
559
|
+
ok: false;
|
|
560
|
+
reason: string;
|
|
561
|
+
code: BackendUnresolvableCode | "invalid_working_directory";
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Validate a named create-time machine target before any session row exists.
|
|
566
|
+
* The caller still commits the pointer with `setActiveSandbox` inside the
|
|
567
|
+
* session-create transaction, which rechecks durable ownership/enrollment
|
|
568
|
+
* authority and closes the remove/revoke race without holding database locks
|
|
569
|
+
* across the liveness probe.
|
|
570
|
+
*/
|
|
571
|
+
export async function preflightCreateTimeSandboxTarget(
|
|
572
|
+
services: FleetServices,
|
|
573
|
+
ctx: FleetResourceContext,
|
|
574
|
+
target: string,
|
|
575
|
+
workingDir: string | null,
|
|
576
|
+
): Promise<CreateTimeSandboxTargetPreflight> {
|
|
577
|
+
const resolved = await resolveNamedSandboxTarget(services, ctx, target);
|
|
578
|
+
if (!resolved.ok) return resolved;
|
|
579
|
+
if (!resolved.workspaceRoot) {
|
|
580
|
+
return {
|
|
581
|
+
ok: true,
|
|
582
|
+
targetSandboxId: resolved.targetSandboxId,
|
|
583
|
+
workingDir,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
return {
|
|
588
|
+
ok: true,
|
|
589
|
+
targetSandboxId: resolved.targetSandboxId,
|
|
590
|
+
workingDir:
|
|
591
|
+
resolveConnectedMachineWorkspaceRoot(resolved.workspaceRoot, workingDir) ??
|
|
592
|
+
resolved.workspaceRoot,
|
|
593
|
+
};
|
|
594
|
+
} catch (error) {
|
|
595
|
+
return {
|
|
596
|
+
ok: false,
|
|
597
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
598
|
+
code: "invalid_working_directory",
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Resolve a swap target id → the value `setActiveSandbox` writes. The session's
|
|
604
|
+
* own group id maps to NULL (the default pointer); a first-class sandbox id is
|
|
605
|
+
* validated (workspace ownership + liveness) and written verbatim. */
|
|
606
|
+
async function resolveTarget(
|
|
607
|
+
services: FleetServices,
|
|
608
|
+
ctx: FleetContext,
|
|
609
|
+
target: string,
|
|
610
|
+
): Promise<
|
|
611
|
+
| { ok: true; targetSandboxId: string | null; workspaceRoot?: string }
|
|
612
|
+
| { ok: false; reason: string; code: BackendUnresolvableCode }
|
|
613
|
+
> {
|
|
614
|
+
// The session's own group box → the default pointer (null).
|
|
615
|
+
if (target === ctx.sessionGroupId || target === "session" || target === "default") {
|
|
616
|
+
if (!managedSessionGroupBackend(services.settings.sandboxBackend, ctx.sessionBackend)) {
|
|
617
|
+
return {
|
|
618
|
+
ok: false,
|
|
619
|
+
reason:
|
|
620
|
+
ctx.sessionBackend === "none"
|
|
621
|
+
? "this session has no home sandbox; attach a Connected Machine"
|
|
622
|
+
: "this deployment has no managed session sandbox; select an enrolled machine",
|
|
623
|
+
code: "unsupported_backend_context",
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
return { ok: true, targetSandboxId: null };
|
|
627
|
+
}
|
|
628
|
+
return await resolveNamedSandboxTarget(services, ctx, target);
|
|
629
|
+
}
|
|
630
|
+
|
|
569
631
|
/**
|
|
570
632
|
* THE SWAP (and attach — identical mechanic). Validate the target's ownership +
|
|
571
633
|
* liveness, then repoint the session via the epoch-fenced CAS `setActiveSandbox`:
|