@opengeni/core 2.6.4 → 2.7.5-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.
@@ -454,31 +454,18 @@ export async function listFleet(
454
454
  };
455
455
  }
456
456
 
457
- /** Resolve a swap target id the value `setActiveSandbox` writes. The session's
458
- * own group id maps to NULL (the default pointer); a first-class sandbox id is
459
- * validated (workspace ownership + liveness) and written verbatim. */
460
- async function resolveTarget(
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: FleetContext,
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`:
@@ -29,7 +29,6 @@ import {
29
29
  type SandboxRetainedProcess,
30
30
  type SandboxWorkspaceMutationAdmission,
31
31
  } from "@opengeni/db";
32
- import { settleSessionBackgroundCommandForRetainedProcess } from "@opengeni/db/session-background-commands";
33
32
  import { appendAndPublishEvents, type EventBus } from "@opengeni/events";
34
33
  import {
35
34
  isProviderSandboxGoneDuringRoutedOperation,
@@ -449,14 +448,11 @@ export function wrapChannelABoxWithRouting(
449
448
  reason: proof.reason,
450
449
  idleGraceMs: settings.sandboxIdleGraceMs,
451
450
  });
452
- const backgroundSettlement = retainedProcessBackgroundSettlement(settlement.process, proof);
453
- await settleSessionBackgroundCommandForRetainedProcess(db, {
454
- accountId: ids.accountId,
455
- workspaceId: ids.workspaceId,
456
- sessionId: ids.sessionId,
457
- retainedProcessId: process.id,
458
- ...backgroundSettlement,
459
- });
451
+ if (settlement.backgroundCommandEvents.length > 0 && bus) {
452
+ await bus
453
+ .publish(ids.workspaceId, ids.sessionId, settlement.backgroundCommandEvents)
454
+ .catch(() => undefined);
455
+ }
460
456
  }
461
457
  : undefined;
462
458
  const resolver = makeActiveBackendResolver({
@@ -520,6 +516,7 @@ export function wrapChannelABoxWithRouting(
520
516
  });
521
517
 
522
518
  const proxy = new RoutingSandboxSession({
519
+ bindActiveRouteOnFirstResolve: true,
523
520
  defaultResolved: {
524
521
  session: established.session as RoutableBackendSession,
525
522
  sandboxId: null,