@loucompanion/forge-bridge 0.1.3-dev.09aeeb7ccadf → 0.1.3-dev.1fe428dd78ab
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/README.md +44 -0
- package/dist/bridge/bridge.base.d.ts +5 -1
- package/dist/bridge/bridge.service.d.ts +36 -2
- package/dist/bridge/bridge.service.js +564 -14
- package/dist/bridge/bridge.types.d.ts +110 -1
- package/dist/cli/cli.help.js +12 -0
- package/dist/cli/cli.service.d.ts +12 -0
- package/dist/cli/cli.service.js +168 -1
- package/dist/sandbox/command-runner.d.ts +4 -0
- package/dist/sandbox/command-runner.js +47 -0
- package/dist/sandbox/contract.d.ts +5 -0
- package/dist/sandbox/contract.js +276 -0
- package/dist/sandbox/event-budget.d.ts +11 -0
- package/dist/sandbox/event-budget.js +27 -0
- package/dist/sandbox/image-policy.d.ts +33 -0
- package/dist/sandbox/image-policy.js +245 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/lease-watchdog.d.ts +18 -0
- package/dist/sandbox/lease-watchdog.js +67 -0
- package/dist/sandbox/preflight.d.ts +36 -0
- package/dist/sandbox/preflight.js +239 -0
- package/dist/sandbox/process.d.ts +20 -0
- package/dist/sandbox/process.js +124 -0
- package/dist/sandbox/provider-auth/codex.d.ts +26 -0
- package/dist/sandbox/provider-auth/codex.js +212 -0
- package/dist/sandbox/repository-lock.d.ts +5 -0
- package/dist/sandbox/repository-lock.js +83 -0
- package/dist/sandbox/runtime.d.ts +218 -0
- package/dist/sandbox/runtime.js +1672 -0
- package/dist/sandbox/sandbox.types.d.ts +83 -0
- package/dist/sandbox/sandbox.types.js +1 -0
- package/dist/sandbox/worker-supervisor.d.ts +13 -0
- package/dist/sandbox/worker-supervisor.js +36 -0
- package/dist/sandbox/worker.d.ts +1 -0
- package/dist/sandbox/worker.js +228 -0
- package/dist/sandbox/workspace.service.d.ts +12 -0
- package/dist/sandbox/workspace.service.js +49 -0
- package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
- package/dist/session/provider-cli/provider-cli.deterministic.d.ts +2 -0
- package/dist/session/provider-cli/provider-cli.deterministic.js +20 -9
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +2 -0
- package/dist/session/provider-cli/provider-cli.prompts.js +31 -0
- package/dist/session/provider-cli/provider-cli.service.d.ts +3 -0
- package/dist/session/provider-cli/provider-cli.service.js +31 -13
- package/dist/session/session.service.js +14 -7
- package/dist/session/session.types.d.ts +41 -1
- package/dist/session/session.types.js +119 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
|
@@ -5,7 +5,7 @@ import { buildSpecBody, selectLaneCommand } from "../../worktree/artifact-inject
|
|
|
5
5
|
import { completionPhaseFromDispatch, prepareCompletionToolRuntime, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion } from "../phase-completion.js";
|
|
6
6
|
import { cleanupCompletionTools, commitWorktreeChanges, completionPhaseFromArtifact, emitArchitectStructuredEvents, emitCompletionArtifactRecorded, emitDecomposeGateArtifacts, emitDraftGateArtifacts, emitPhaseCompletionEvent, emitPhaseGateEvents, emitQaArtifactRecorded, emitQaStructuredEvents, enforceQaBrowserLaunchTargetGate, LiveArchitectArtifactEmitter, LiveCompletionArtifactEmitter, LiveQaArtifactEmitter, readCompletionArtifactsForRun, readQaCompletionArtifactBundle, summarizeStderr, waitForCompletionArtifact } from "./provider-cli.artifacts.js";
|
|
7
7
|
import { DeterministicProviderCliService } from "./provider-cli.deterministic.js";
|
|
8
|
-
import { buildArchitectPrompt, buildConflictResolvePrompt, buildDebugCyclePrompt, buildDevelopCyclePrompt, buildDraftPrompt, buildDraftSpec, buildImplementationPrompt, buildProviderCliCommand, durableDraftInput, buildPullRequestCommentsPrompt, buildQaPrompt, formatProviderCliCommandForCheckpoint, isDecomposeDraftDispatch, isPullRequestCommentsDispatch } from "./provider-cli.prompts.js";
|
|
8
|
+
import { buildArchitectPrompt, buildConflictResolvePrompt, buildDebugCyclePrompt, buildDevelopCyclePrompt, buildDraftPrompt, buildDraftSpec, buildImplementationPrompt, buildProviderCliCommand, durableDraftInput, buildPullRequestCheckRepairPrompt, buildPullRequestCommentsPrompt, buildQaPrompt, formatProviderCliCommandForCheckpoint, isDecomposeDraftDispatch, isPullRequestCheckRepairDispatch, isPullRequestCommentsDispatch } from "./provider-cli.prompts.js";
|
|
9
9
|
import { StreamingProviderProcessRunner } from "./provider-cli.process.js";
|
|
10
10
|
import { emitProviderUsageEvent, providerProcessEnv } from "./provider-cli.runtime.js";
|
|
11
11
|
import { clip, createProviderOutputState, parseJsonLine } from "./provider-output-state.js";
|
|
@@ -70,6 +70,11 @@ export class RoutingProviderCliService {
|
|
|
70
70
|
? await this.deterministicProvider.runArchitect(context)
|
|
71
71
|
: await this.realProvider.runArchitect(context);
|
|
72
72
|
}
|
|
73
|
+
async runCiRepair(context) {
|
|
74
|
+
return isFakeWorkerModel(context.dispatch.execution.model)
|
|
75
|
+
? await this.deterministicProvider.runCiRepair(context)
|
|
76
|
+
: await this.realProvider.runCiRepair(context);
|
|
77
|
+
}
|
|
73
78
|
async runConflictResolve(context) {
|
|
74
79
|
return isFakeWorkerModel(context.dispatch.execution.model)
|
|
75
80
|
? await this.deterministicProvider.runConflictResolve(context)
|
|
@@ -566,19 +571,28 @@ export class ProcessBackedProviderCliService {
|
|
|
566
571
|
artifacts: [qaArtifact]
|
|
567
572
|
};
|
|
568
573
|
}
|
|
574
|
+
async runCiRepair(context) {
|
|
575
|
+
return await this.runPullRequestRepair(context, "ci_repair");
|
|
576
|
+
}
|
|
569
577
|
async runConflictResolve(context) {
|
|
578
|
+
return await this.runPullRequestRepair(context, "conflict_resolve");
|
|
579
|
+
}
|
|
580
|
+
async runPullRequestRepair(context, route) {
|
|
570
581
|
const { dispatch, worktree } = context;
|
|
571
|
-
const
|
|
582
|
+
const repairChecks = route === "ci_repair" || isPullRequestCheckRepairDispatch(dispatch);
|
|
583
|
+
const addressComments = !repairChecks && isPullRequestCommentsDispatch(dispatch);
|
|
572
584
|
const command = buildProviderCliCommand(dispatch, {
|
|
573
585
|
worktreePath: worktree.worktreePath,
|
|
574
|
-
prompt:
|
|
575
|
-
?
|
|
576
|
-
:
|
|
586
|
+
prompt: repairChecks
|
|
587
|
+
? buildPullRequestCheckRepairPrompt(dispatch, worktree.branchName)
|
|
588
|
+
: addressComments
|
|
589
|
+
? buildPullRequestCommentsPrompt(dispatch, worktree.branchName)
|
|
590
|
+
: buildConflictResolvePrompt(dispatch, worktree.branchName)
|
|
577
591
|
});
|
|
578
592
|
await context.emit({
|
|
579
593
|
role: "runner",
|
|
580
594
|
kind: "checkpoint",
|
|
581
|
-
content: `Starting provider CLI ${addressComments ? "PR comment address" : "conflict resolve"}: ${formatProviderCliCommandForCheckpoint(command)} in ${worktree.worktreePath}`,
|
|
595
|
+
content: `Starting provider CLI ${repairChecks ? "CI repair" : addressComments ? "PR comment address" : "conflict resolve"}: ${formatProviderCliCommandForCheckpoint(command)} in ${worktree.worktreePath}`,
|
|
582
596
|
eventType: "provider_cli_started"
|
|
583
597
|
});
|
|
584
598
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
@@ -604,15 +618,19 @@ export class ProcessBackedProviderCliService {
|
|
|
604
618
|
await context.emit({
|
|
605
619
|
role: "assistant",
|
|
606
620
|
kind: "message",
|
|
607
|
-
content: outputState.finalMessage || (
|
|
608
|
-
? `${dispatch.execution.provider} CLI
|
|
609
|
-
:
|
|
610
|
-
|
|
621
|
+
content: outputState.finalMessage || (repairChecks
|
|
622
|
+
? `${dispatch.execution.provider} CLI repaired pull request checks for ${worktree.branchName}.`
|
|
623
|
+
: addressComments
|
|
624
|
+
? `${dispatch.execution.provider} CLI addressed pull request comments for ${worktree.branchName}.`
|
|
625
|
+
: `${dispatch.execution.provider} CLI resolved merge conflicts for ${worktree.branchName}.`),
|
|
626
|
+
eventType: repairChecks ? "pr_checks_complete" : addressComments ? "pr_comments_complete" : "conflict_resolve_complete"
|
|
611
627
|
});
|
|
612
628
|
return {
|
|
613
|
-
summary:
|
|
614
|
-
? `${dispatch.execution.provider} CLI
|
|
615
|
-
:
|
|
629
|
+
summary: repairChecks
|
|
630
|
+
? `${dispatch.execution.provider} CLI repaired PR checks on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`
|
|
631
|
+
: addressComments
|
|
632
|
+
? `${dispatch.execution.provider} CLI addressed PR comments on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`
|
|
633
|
+
: `${dispatch.execution.provider} CLI resolved conflicts on ${worktree.branchName} (${committed.commitSha.slice(0, 12)}).`
|
|
616
634
|
};
|
|
617
635
|
}
|
|
618
636
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { redactSecrets } from "../shared/redaction/redaction.js";
|
|
2
2
|
import { RoutingProviderCliService } from "./provider-cli/provider-cli.service.js";
|
|
3
3
|
import { GitWorktreeService } from "../worktree/worktree.service.js";
|
|
4
|
+
import { SandboxWorkspaceService } from "../sandbox/workspace.service.js";
|
|
4
5
|
export class BridgeSessionService {
|
|
5
6
|
worktrees;
|
|
6
7
|
providerCli;
|
|
7
|
-
constructor(worktrees =
|
|
8
|
+
constructor(worktrees = process.env.FORGE_SANDBOX_ID
|
|
9
|
+
? new SandboxWorkspaceService()
|
|
10
|
+
: new GitWorktreeService(), providerCli = new RoutingProviderCliService()) {
|
|
8
11
|
this.worktrees = worktrees;
|
|
9
12
|
this.providerCli = providerCli;
|
|
10
13
|
}
|
|
@@ -75,10 +78,12 @@ export class BridgeSessionService {
|
|
|
75
78
|
}, redactionSecrets);
|
|
76
79
|
}
|
|
77
80
|
throwIfAborted(signal);
|
|
78
|
-
const lane = dispatch.execution.lane.toLowerCase();
|
|
79
|
-
const role = dispatch.execution.role.toLowerCase();
|
|
81
|
+
const lane = dispatch.execution.lane.trim().toLowerCase();
|
|
82
|
+
const role = dispatch.execution.role.trim().toLowerCase();
|
|
83
|
+
const cycleKind = dispatch.cycleRun.kind.trim().toLowerCase();
|
|
80
84
|
const isQa = role === "qa" || lane === "qa";
|
|
81
|
-
const isArchitect = role === "architect" || lane === "architect" ||
|
|
85
|
+
const isArchitect = role === "architect" || lane === "architect" || cycleKind === "architect";
|
|
86
|
+
const isCiRepair = role === "ci_repair" || lane === "ci_repair" || cycleKind === "pr_checks";
|
|
82
87
|
const isConflictResolve = role === "conflict_resolve" || lane === "conflict_resolve";
|
|
83
88
|
const context = {
|
|
84
89
|
dispatch: providerDispatch,
|
|
@@ -90,9 +95,11 @@ export class BridgeSessionService {
|
|
|
90
95
|
? await this.providerCli.runQa(context)
|
|
91
96
|
: isArchitect
|
|
92
97
|
? await this.providerCli.runArchitect(context)
|
|
93
|
-
:
|
|
94
|
-
? await this.providerCli.
|
|
95
|
-
:
|
|
98
|
+
: isCiRepair
|
|
99
|
+
? await this.providerCli.runCiRepair(context)
|
|
100
|
+
: isConflictResolve
|
|
101
|
+
? await this.providerCli.runConflictResolve(context)
|
|
102
|
+
: await this.providerCli.runImplementation(context);
|
|
96
103
|
throwIfAborted(signal);
|
|
97
104
|
const phaseArtifacts = run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : [];
|
|
98
105
|
return redactSecrets({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export interface RunOnceDispatchPacket {
|
|
2
|
-
readonly schemaVersion: 1;
|
|
2
|
+
readonly schemaVersion: 1 | 2;
|
|
3
3
|
readonly userId: string;
|
|
4
4
|
readonly workItem: DispatchWorkItem;
|
|
5
5
|
readonly repo: DispatchRepo;
|
|
@@ -8,8 +8,10 @@ export interface RunOnceDispatchPacket {
|
|
|
8
8
|
readonly lease: DispatchLease;
|
|
9
9
|
readonly execution: DispatchExecution;
|
|
10
10
|
readonly serverLocal: DispatchServerLocalConfig;
|
|
11
|
+
readonly sandbox?: DispatchSandboxContext | null;
|
|
11
12
|
readonly pullRequest?: DispatchPullRequestContext | null;
|
|
12
13
|
readonly apiKeySecretGrant?: ApiKeySecretGrant | null;
|
|
14
|
+
readonly apiKeySecretGrantHandle?: ApiKeySecretGrantHandle | null;
|
|
13
15
|
readonly artifacts: readonly DispatchResolvedArtifact[];
|
|
14
16
|
readonly warmPathOverrides?: readonly string[];
|
|
15
17
|
readonly qaBrowser?: DispatchQaBrowserContext | null;
|
|
@@ -22,6 +24,39 @@ export interface RunOnceDispatchPacket {
|
|
|
22
24
|
readonly designerRefs?: DispatchDesignerRefHandoffContext | null;
|
|
23
25
|
readonly draft?: DispatchDraftPrompt | null;
|
|
24
26
|
}
|
|
27
|
+
export interface DispatchSandboxContext {
|
|
28
|
+
readonly dispatchAttemptId: string;
|
|
29
|
+
readonly executionLeaseId: string;
|
|
30
|
+
readonly runnerInstanceId: string;
|
|
31
|
+
readonly executionMode: "host" | "docker";
|
|
32
|
+
readonly executionPolicy: "host_allowed" | "sandbox_preferred" | "sandbox_required";
|
|
33
|
+
readonly networkPolicy: DispatchSandboxNetworkPolicy;
|
|
34
|
+
readonly requestedResources: DispatchSandboxResourceRequest;
|
|
35
|
+
readonly runtime?: string | null;
|
|
36
|
+
readonly sandboxConfigCommitSha?: string | null;
|
|
37
|
+
readonly inputCommitSha?: string | null;
|
|
38
|
+
readonly contractDigest?: string | null;
|
|
39
|
+
readonly imageDigest?: string | null;
|
|
40
|
+
readonly publicationMode: "bridge_push" | "shared_filesystem";
|
|
41
|
+
readonly publishGrantHandle?: DispatchPublishGrantHandle | null;
|
|
42
|
+
}
|
|
43
|
+
export interface DispatchPublishGrantHandle {
|
|
44
|
+
readonly grantId: string;
|
|
45
|
+
readonly branch: string;
|
|
46
|
+
readonly expiresAt: string;
|
|
47
|
+
}
|
|
48
|
+
export interface DispatchSandboxResourceRequest {
|
|
49
|
+
readonly sandboxSlots: number;
|
|
50
|
+
readonly cpuMillis: number;
|
|
51
|
+
readonly memoryBytes: number;
|
|
52
|
+
readonly workspaceBytes: number;
|
|
53
|
+
readonly pids: number;
|
|
54
|
+
readonly buildSlots: number;
|
|
55
|
+
}
|
|
56
|
+
export interface DispatchSandboxNetworkPolicy {
|
|
57
|
+
readonly mode: "offline" | "public_allowlist" | "open_public";
|
|
58
|
+
readonly allowedHosts: readonly string[];
|
|
59
|
+
}
|
|
25
60
|
export interface DispatchDraftPrompt {
|
|
26
61
|
readonly kind: string;
|
|
27
62
|
readonly systemPrompt: string;
|
|
@@ -279,6 +314,11 @@ export interface ApiKeySecretGrant {
|
|
|
279
314
|
readonly secret: string;
|
|
280
315
|
readonly expiresAt: string;
|
|
281
316
|
}
|
|
317
|
+
export interface ApiKeySecretGrantHandle {
|
|
318
|
+
readonly grantId: string;
|
|
319
|
+
readonly provider: string;
|
|
320
|
+
readonly expiresAt: string;
|
|
321
|
+
}
|
|
282
322
|
export interface DispatchServerLocalConfig {
|
|
283
323
|
readonly repoRoot: string;
|
|
284
324
|
readonly cleanupWorktrees: boolean;
|
|
@@ -2,11 +2,11 @@ import path from "node:path";
|
|
|
2
2
|
export function parseRunOnceDispatchPacket(value) {
|
|
3
3
|
const packet = asObject(value, "dispatch packet");
|
|
4
4
|
const schemaVersion = requiredNumber(packet, "schemaVersion");
|
|
5
|
-
if (schemaVersion !== 1) {
|
|
5
|
+
if (schemaVersion !== 1 && schemaVersion !== 2) {
|
|
6
6
|
throw new Error(`Unsupported dispatch schemaVersion: ${schemaVersion}`);
|
|
7
7
|
}
|
|
8
8
|
return {
|
|
9
|
-
schemaVersion
|
|
9
|
+
schemaVersion,
|
|
10
10
|
userId: requiredString(packet, "userId"),
|
|
11
11
|
workItem: parseWorkItem(packet.workItem),
|
|
12
12
|
repo: parseRepo(packet.repo),
|
|
@@ -15,8 +15,10 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
15
15
|
lease: parseLease(packet.lease),
|
|
16
16
|
execution: parseExecution(packet.execution),
|
|
17
17
|
serverLocal: parseServerLocal(packet.serverLocal),
|
|
18
|
+
sandbox: parseSandbox(packet.sandbox, schemaVersion),
|
|
18
19
|
pullRequest: parsePullRequest(packet.pullRequest),
|
|
19
20
|
apiKeySecretGrant: parseApiKeySecretGrant(packet.apiKeySecretGrant),
|
|
21
|
+
apiKeySecretGrantHandle: parseApiKeySecretGrantHandle(packet.apiKeySecretGrantHandle),
|
|
20
22
|
artifacts: parseArtifacts(packet.artifacts),
|
|
21
23
|
warmPathOverrides: parseWarmPathOverrides(packet.warmPathOverrides),
|
|
22
24
|
qaBrowser: parseQaBrowser(packet.qaBrowser),
|
|
@@ -30,6 +32,104 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
30
32
|
draft: parseDraftPrompt(packet.draft)
|
|
31
33
|
};
|
|
32
34
|
}
|
|
35
|
+
function parseSandbox(value, schemaVersion) {
|
|
36
|
+
if (value === undefined || value === null) {
|
|
37
|
+
if (schemaVersion === 2) {
|
|
38
|
+
throw new Error("sandbox is required for dispatch schemaVersion 2.");
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const sandbox = asObject(value, "sandbox");
|
|
43
|
+
const executionMode = requiredString(sandbox, "executionMode");
|
|
44
|
+
if (executionMode !== "host" && executionMode !== "docker") {
|
|
45
|
+
throw new Error("sandbox.executionMode must be host or docker.");
|
|
46
|
+
}
|
|
47
|
+
const executionPolicy = requiredString(sandbox, "executionPolicy");
|
|
48
|
+
if (executionPolicy !== "host_allowed" && executionPolicy !== "sandbox_preferred" && executionPolicy !== "sandbox_required") {
|
|
49
|
+
throw new Error("sandbox.executionPolicy is invalid.");
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
dispatchAttemptId: requiredString(sandbox, "dispatchAttemptId"),
|
|
53
|
+
executionLeaseId: requiredString(sandbox, "executionLeaseId"),
|
|
54
|
+
runnerInstanceId: requiredString(sandbox, "runnerInstanceId"),
|
|
55
|
+
executionMode,
|
|
56
|
+
executionPolicy,
|
|
57
|
+
networkPolicy: parseSandboxNetworkPolicy(sandbox.networkPolicy),
|
|
58
|
+
requestedResources: parseSandboxResourceRequest(sandbox.requestedResources),
|
|
59
|
+
runtime: optionalString(sandbox, "runtime"),
|
|
60
|
+
sandboxConfigCommitSha: optionalString(sandbox, "sandboxConfigCommitSha"),
|
|
61
|
+
inputCommitSha: optionalString(sandbox, "inputCommitSha"),
|
|
62
|
+
contractDigest: optionalString(sandbox, "contractDigest"),
|
|
63
|
+
imageDigest: optionalString(sandbox, "imageDigest"),
|
|
64
|
+
publicationMode: parsePublicationMode(sandbox.publicationMode),
|
|
65
|
+
publishGrantHandle: parsePublishGrantHandle(sandbox.publishGrantHandle)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function parsePublicationMode(value) {
|
|
69
|
+
if (value === undefined || value === null)
|
|
70
|
+
return "shared_filesystem";
|
|
71
|
+
if (value !== "bridge_push" && value !== "shared_filesystem")
|
|
72
|
+
throw new Error("sandbox.publicationMode is invalid.");
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
function parsePublishGrantHandle(value) {
|
|
76
|
+
if (value === undefined || value === null)
|
|
77
|
+
return null;
|
|
78
|
+
const handle = asObject(value, "sandbox.publishGrantHandle");
|
|
79
|
+
const grantId = requiredString(handle, "grantId");
|
|
80
|
+
const branch = requiredString(handle, "branch");
|
|
81
|
+
const expiresAt = requiredString(handle, "expiresAt");
|
|
82
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(grantId)) {
|
|
83
|
+
throw new Error("sandbox.publishGrantHandle.grantId is invalid.");
|
|
84
|
+
}
|
|
85
|
+
if (branch.length > 240)
|
|
86
|
+
throw new Error("sandbox.publishGrantHandle.branch is too long.");
|
|
87
|
+
if (!Number.isFinite(Date.parse(expiresAt)))
|
|
88
|
+
throw new Error("sandbox.publishGrantHandle.expiresAt is invalid.");
|
|
89
|
+
return { grantId, branch, expiresAt };
|
|
90
|
+
}
|
|
91
|
+
function parseSandboxResourceRequest(value) {
|
|
92
|
+
if (value === undefined || value === null) {
|
|
93
|
+
return {
|
|
94
|
+
sandboxSlots: 1,
|
|
95
|
+
cpuMillis: 2000,
|
|
96
|
+
memoryBytes: 4 * 1024 ** 3,
|
|
97
|
+
workspaceBytes: 10 * 1024 ** 3,
|
|
98
|
+
pids: 512,
|
|
99
|
+
buildSlots: 1
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const resources = asObject(value, "sandbox.requestedResources");
|
|
103
|
+
return {
|
|
104
|
+
sandboxSlots: boundedInteger(resources, "sandboxSlots", 1, 4),
|
|
105
|
+
cpuMillis: boundedInteger(resources, "cpuMillis", 100, 64_000),
|
|
106
|
+
memoryBytes: boundedInteger(resources, "memoryBytes", 128 * 1024 ** 2, 512 * 1024 ** 3),
|
|
107
|
+
workspaceBytes: boundedInteger(resources, "workspaceBytes", 1024 ** 3, 10 * 1024 ** 4),
|
|
108
|
+
pids: boundedInteger(resources, "pids", 32, 65_536),
|
|
109
|
+
buildSlots: boundedInteger(resources, "buildSlots", 0, 4)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function parseSandboxNetworkPolicy(value) {
|
|
113
|
+
if (value === undefined || value === null)
|
|
114
|
+
return { mode: "public_allowlist", allowedHosts: [] };
|
|
115
|
+
const policy = asObject(value, "sandbox.networkPolicy");
|
|
116
|
+
const mode = requiredString(policy, "mode");
|
|
117
|
+
if (mode !== "offline" && mode !== "public_allowlist" && mode !== "open_public") {
|
|
118
|
+
throw new Error("sandbox.networkPolicy.mode is invalid.");
|
|
119
|
+
}
|
|
120
|
+
const rawHosts = policy.allowedHosts ?? [];
|
|
121
|
+
if (!Array.isArray(rawHosts) || rawHosts.some((host) => typeof host !== "string")) {
|
|
122
|
+
throw new Error("sandbox.networkPolicy.allowedHosts must be a string array.");
|
|
123
|
+
}
|
|
124
|
+
const allowedHosts = [...new Set(rawHosts.map(host => host.trim().toLowerCase()).filter(Boolean))];
|
|
125
|
+
if (allowedHosts.length > 128 || allowedHosts.some(host => !/^(?:\*\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/u.test(host))) {
|
|
126
|
+
throw new Error("sandbox.networkPolicy.allowedHosts contains an invalid hostname.");
|
|
127
|
+
}
|
|
128
|
+
if (mode !== "public_allowlist" && allowedHosts.length > 0) {
|
|
129
|
+
throw new Error("sandbox.networkPolicy.allowedHosts requires public_allowlist mode.");
|
|
130
|
+
}
|
|
131
|
+
return { mode, allowedHosts };
|
|
132
|
+
}
|
|
33
133
|
export function failureResult(summary, events = []) {
|
|
34
134
|
return {
|
|
35
135
|
schemaVersion: 1,
|
|
@@ -172,6 +272,16 @@ function parseApiKeySecretGrant(value) {
|
|
|
172
272
|
expiresAt: requiredString(grant, "expiresAt")
|
|
173
273
|
};
|
|
174
274
|
}
|
|
275
|
+
function parseApiKeySecretGrantHandle(value) {
|
|
276
|
+
if (value === undefined || value === null)
|
|
277
|
+
return null;
|
|
278
|
+
const grant = asObject(value, "apiKeySecretGrantHandle");
|
|
279
|
+
return {
|
|
280
|
+
grantId: requiredString(grant, "grantId"),
|
|
281
|
+
provider: requiredString(grant, "provider"),
|
|
282
|
+
expiresAt: requiredString(grant, "expiresAt")
|
|
283
|
+
};
|
|
284
|
+
}
|
|
175
285
|
function parseServerLocal(value) {
|
|
176
286
|
const serverLocal = asObject(value, "serverLocal");
|
|
177
287
|
return {
|
|
@@ -616,3 +726,10 @@ function requiredNumber(source, key) {
|
|
|
616
726
|
}
|
|
617
727
|
return value;
|
|
618
728
|
}
|
|
729
|
+
function boundedInteger(source, key, minimum, maximum) {
|
|
730
|
+
const value = requiredNumber(source, key);
|
|
731
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
732
|
+
throw new Error(`${key} must be an integer from ${minimum} through ${maximum}.`);
|
|
733
|
+
}
|
|
734
|
+
return value;
|
|
735
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const forgeBridgeVersion = "0.1.3-dev.
|
|
1
|
+
export declare const forgeBridgeVersion = "0.1.3-dev.1fe428dd78ab";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const forgeBridgeVersion = "0.1.3-dev.
|
|
1
|
+
export const forgeBridgeVersion = "0.1.3-dev.1fe428dd78ab";
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loucompanion/forge-bridge",
|
|
3
|
-
"version": "0.1.3-dev.
|
|
3
|
+
"version": "0.1.3-dev.1fe428dd78ab",
|
|
4
4
|
"description": "Forge runner bridge CLI for connecting local developer machines to Forge dispatch.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
+
"forge": "dist/cli/bin.js",
|
|
8
9
|
"forge-bridge": "dist/cli/bin.js"
|
|
9
10
|
},
|
|
10
11
|
"main": "./dist/index.js",
|
|
@@ -43,7 +44,8 @@
|
|
|
43
44
|
"lint": "eslint \"src/**/*.ts\" \"scripts/**/*.mjs\" \"vitest.config.ts\""
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@microsoft/signalr": "^8.0.17"
|
|
47
|
+
"@microsoft/signalr": "^8.0.17",
|
|
48
|
+
"yaml": "^2.9.0"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@eslint/js": "^10.0.1",
|