@loucompanion/forge-bridge 0.1.3-dev.c24058d57ab3 → 0.1.3-dev.c28767e94230

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.
@@ -76,5 +76,9 @@ export declare class SignalRBridgeConnection implements BridgeConnection {
76
76
  }
77
77
  export declare function defaultBridgeServiceConfig(overrides?: Partial<BridgeServiceConfig>): BridgeServiceConfig;
78
78
  export declare function resolveBridgeRunnerHubUrl(serverUrl: string): URL;
79
+ export declare function canReapManagedSandboxes(preflight: {
80
+ readonly healthy: boolean;
81
+ readonly unavailableCode?: string | null;
82
+ }): boolean;
79
83
  export type SandboxResourceVector = DispatchSandboxResourceRequest;
80
84
  export declare function canReserveSandboxResources(used: SandboxResourceVector, requested: SandboxResourceVector, capacity: SandboxResourceVector): boolean;
@@ -9,7 +9,7 @@ import { redactSecrets, redactText } from "../shared/redaction/redaction.js";
9
9
  import { BridgeSessionService } from "../session/session.service.js";
10
10
  import { failureResult, parseRunOnceDispatchPacket } from "../session/session.types.js";
11
11
  import { forgeBridgeVersion } from "../version.js";
12
- import { DockerPreflight } from "../sandbox/preflight.js";
12
+ import { canRunDockerMaintenance, DockerPreflight } from "../sandbox/preflight.js";
13
13
  import { DockerSandboxRuntime } from "../sandbox/runtime.js";
14
14
  import { codexSandboxProviderAuthAdapter, sandboxProviderAuthAdapterFor, sandboxProviderAuthUnavailableMessage } from "../sandbox/provider-auth/codex.js";
15
15
  export class ForgeBridgeService {
@@ -1001,8 +1001,8 @@ function abortPromise(signal) {
1001
1001
  }
1002
1002
  return new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
1003
1003
  }
1004
- function canReapManagedSandboxes(preflight) {
1005
- return preflight.healthy || preflight.unavailableCode === "docker_disk_low";
1004
+ export function canReapManagedSandboxes(preflight) {
1005
+ return canRunDockerMaintenance(preflight);
1006
1006
  }
1007
1007
  function sandboxNetworkPolicyFailure(dispatch) {
1008
1008
  const sandbox = dispatch.sandbox;
@@ -27,6 +27,10 @@ export declare class DockerPreflight {
27
27
  run(force?: boolean): Promise<SandboxPreflightResult>;
28
28
  private inspect;
29
29
  }
30
+ export declare function canRunDockerMaintenance(preflight: {
31
+ readonly healthy: boolean;
32
+ readonly unavailableCode?: string | null;
33
+ }): boolean;
30
34
  export declare function evaluateDiskPressure(availableBytes: number, totalBytes: number, minimumAvailableBytes: number, highWatermarkPercent: number, lowWatermarkPercent: number, pressureLatched: boolean): boolean;
31
35
  export declare function storagePolicyAvailable(storageQuota: boolean, environment?: NodeJS.ProcessEnv): boolean;
32
36
  export declare class DockerContextOwnershipLock {
@@ -26,7 +26,7 @@ export class DockerPreflight {
26
26
  async refresh() {
27
27
  await fs.mkdir(this.options.stateRoot ?? path.join(os.homedir(), ".forge", "bridge", "sandboxes"), { recursive: true, mode: 0o700 });
28
28
  const result = await this.run(true);
29
- if (result.healthy || result.unavailableCode === "docker_disk_low") {
29
+ if (canRunDockerMaintenance(result)) {
30
30
  await this.ownership.acquire(this.options.dockerContext);
31
31
  }
32
32
  return result;
@@ -108,14 +108,27 @@ export class DockerPreflight {
108
108
  const availableMemoryBytes = os.freemem();
109
109
  const cpuCount = Math.max(1, os.availableParallelism?.() ?? os.cpus().length);
110
110
  const availableCpuMillis = Math.max(0, (cpuCount - 1) * 1000);
111
- const memorySlots = Math.max(0, Math.floor((availableMemoryBytes - 2 * 1024 ** 3) / (2 * 1024 ** 3)));
112
- const cpuSlots = Math.max(0, Math.floor((cpuCount - 1) / 2));
113
- const configuredMax = this.options.maxConcurrentSandboxes ?? positiveInteger(process.env.FORGE_MAX_CONCURRENT_SANDBOXES, 3);
114
- const maxConcurrentSandboxes = diskLow ? 0 : Math.max(1, Math.min(3, configuredMax, Math.max(1, cpuSlots), Math.max(1, memorySlots)));
111
+ const memoryReserveBytes = 2 * 1024 ** 3;
112
+ const sandboxMemoryBytes = 2 * 1024 ** 3;
113
+ const sandboxCpuMillis = 2_000;
114
+ const memorySlots = Math.max(0, Math.floor((availableMemoryBytes - memoryReserveBytes) / sandboxMemoryBytes));
115
+ const cpuSlots = Math.max(0, Math.floor(availableCpuMillis / sandboxCpuMillis));
116
+ const configuredMax = positiveInteger(this.options.maxConcurrentSandboxes, positiveInteger(process.env.FORGE_MAX_CONCURRENT_SANDBOXES, 3));
117
+ const capacityLowCode = !diskLow && memorySlots <= 0 ? "docker_memory_low" : !diskLow && cpuSlots <= 0 ? "docker_cpu_low" : null;
118
+ const maxConcurrentSandboxes = diskLow || capacityLowCode ? 0 : Math.min(3, configuredMax, cpuSlots, memorySlots);
119
+ const unavailableCode = diskLow ? "docker_disk_low" : capacityLowCode;
120
+ const unavailableReason = diskLow
121
+ ? `Docker workspace has only ${disk.available} bytes available.`
122
+ : unavailableCode === "docker_memory_low"
123
+ ? `Docker host has ${availableMemoryBytes} bytes free; at least ${memoryReserveBytes + sandboxMemoryBytes} bytes are needed for one sandbox plus reserve.`
124
+ : unavailableCode === "docker_cpu_low"
125
+ ? `Docker host has ${availableCpuMillis} available CPU millis; at least ${sandboxCpuMillis} are needed for one sandbox.`
126
+ : null;
115
127
  const features = { rootless, userNamespaces, cgroupV2, seccomp, lsm, storageQuota, storagePolicy, compose, internalNetwork: true, buildkit, imagePolicy, securityProfile, egressProxy };
128
+ const capacityAvailable = unavailableCode === null;
116
129
  return {
117
- available: !diskLow,
118
- healthy: !diskLow,
130
+ available: capacityAvailable,
131
+ healthy: capacityAvailable,
119
132
  generation: this.generation,
120
133
  runtimeVersion: `docker/${String(info.ServerVersion ?? "unknown")}${rootless ? "-rootless" : "-desktop"}`,
121
134
  maxConcurrentSandboxes,
@@ -123,12 +136,16 @@ export class DockerPreflight {
123
136
  availableCpuMillis,
124
137
  availableWorkspaceBytes: disk.available,
125
138
  diskLow,
126
- unavailableCode: diskLow ? "docker_disk_low" : null,
127
- unavailableReason: diskLow ? `Docker workspace has only ${disk.available} bytes available.` : null,
139
+ unavailableCode,
140
+ unavailableReason,
128
141
  features,
129
142
  };
130
143
  }
131
144
  }
145
+ const dockerMaintenanceUnavailableCodes = new Set(["docker_disk_low", "docker_memory_low", "docker_cpu_low"]);
146
+ export function canRunDockerMaintenance(preflight) {
147
+ return preflight.healthy || dockerMaintenanceUnavailableCodes.has(preflight.unavailableCode ?? "");
148
+ }
132
149
  export function evaluateDiskPressure(availableBytes, totalBytes, minimumAvailableBytes, highWatermarkPercent, lowWatermarkPercent, pressureLatched) {
133
150
  if (availableBytes < minimumAvailableBytes)
134
151
  return true;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const forgeBridgeVersion = "0.1.3-dev.c24058d57ab3";
1
+ export declare const forgeBridgeVersion = "0.1.3-dev.c28767e94230";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const forgeBridgeVersion = "0.1.3-dev.c24058d57ab3";
1
+ export const forgeBridgeVersion = "0.1.3-dev.c28767e94230";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loucompanion/forge-bridge",
3
- "version": "0.1.3-dev.c24058d57ab3",
3
+ "version": "0.1.3-dev.c28767e94230",
4
4
  "description": "Forge runner bridge CLI for connecting local developer machines to Forge dispatch.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",