@loucompanion/forge-bridge 0.1.3-dev.c28767e94230 → 0.1.3-dev.d46a8e73d03d
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/sandbox/runtime.js +19 -13
- package/dist/session/session.types.js +4 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/sandbox/runtime.js
CHANGED
|
@@ -556,13 +556,14 @@ export class DockerSandboxRuntime {
|
|
|
556
556
|
const helperImageDigest = (await this.docker(["image", "inspect", helperTag, "--format", "{{.Id}}"], 15_000)).stdout.trim();
|
|
557
557
|
if (!/^sha256:[0-9a-f]{64}$/i.test(helperImageDigest))
|
|
558
558
|
throw new Error("sandbox_build_failed: helper image did not resolve to an immutable digest.");
|
|
559
|
+
const projectContextRef = ociLayoutContextRef(path.relative(overlayRoot, projectOciRoot));
|
|
559
560
|
await this.runBuildStage("agent", key, buildxBuildArgs(builder, path.join(metadataRoot, "agent.json"), [
|
|
560
561
|
"--pull=false",
|
|
561
|
-
"--build-context", `${projectContextName}=${
|
|
562
|
+
"--build-context", `${projectContextName}=${projectContextRef}`,
|
|
562
563
|
...imageLabelArgs(project, key, "agent"),
|
|
563
564
|
"--tag", overlayTag,
|
|
564
|
-
|
|
565
|
-
]), sink, signal);
|
|
565
|
+
"."
|
|
566
|
+
]), sink, signal, { cwd: overlayRoot });
|
|
566
567
|
const digest = (await this.docker(["image", "inspect", overlayTag, "--format", "{{.Id}}"], 15_000)).stdout.trim();
|
|
567
568
|
if (!/^sha256:[0-9a-f]{64}$/i.test(digest))
|
|
568
569
|
throw new Error("sandbox_build_failed: overlay image did not resolve to an immutable digest.");
|
|
@@ -602,10 +603,11 @@ export class DockerSandboxRuntime {
|
|
|
602
603
|
}));
|
|
603
604
|
return { imageDigest: digest, helperImageDigest };
|
|
604
605
|
}
|
|
605
|
-
async runBuildStage(stage, key, args, sink, signal) {
|
|
606
|
+
async runBuildStage(stage, key, args, sink, signal, options = {}) {
|
|
606
607
|
const result = await runSandboxProcess({
|
|
607
608
|
command: this.config.dockerExecutable,
|
|
608
609
|
args: this.dockerArgs(args),
|
|
610
|
+
cwd: options.cwd,
|
|
609
611
|
timeoutMs: 30 * 60_000,
|
|
610
612
|
maxOutputBytes: 2 * 1024 * 1024,
|
|
611
613
|
terminateOnOutputLimit: true,
|
|
@@ -658,10 +660,10 @@ export class DockerSandboxRuntime {
|
|
|
658
660
|
}
|
|
659
661
|
async initializeVolumes(helperImage, resources, includeCredentials, signal) {
|
|
660
662
|
const cacheMounts = Object.entries(resources.cacheVolumes).flatMap(([name, volume]) => ["--volume", `${volume}:/cache/${name}`]);
|
|
661
|
-
const credentialSetup = includeCredentials ? "f.
|
|
662
|
-
const script = `const f=require('fs');f.mkdirSync('/control/input',{recursive:true});f.mkdirSync('/control/output',{recursive:true});for(const p of ['/control','/control/input','/control/output','/workspace'])f.chmodSync(p,0o777);${credentialSetup}for(const p of f.readdirSync('/cache').map(n=>'/cache/'+n)){f.
|
|
663
|
+
const credentialSetup = includeCredentials ? "f.chmodSync('/credentials',0o700);" : "";
|
|
664
|
+
const script = `const f=require('fs');f.mkdirSync('/control/input',{recursive:true});f.mkdirSync('/control/output',{recursive:true});for(const p of ['/control','/control/input','/control/output','/workspace'])f.chmodSync(p,0o777);${credentialSetup}for(const p of f.readdirSync('/cache').map(n=>'/cache/'+n)){f.chmodSync(p,0o700);f.chownSync(p,10001,10001)}`;
|
|
663
665
|
const credentialMount = includeCredentials ? ["--volume", `${resources.credentialsVolume}:/credentials`] : [];
|
|
664
|
-
await this.docker(["run", "--rm", "--network", "none", "--read-only", "--cap-drop", "ALL", "--security-opt", "no-new-privileges", "--pids-limit", "64", "--memory", "128m", "--memory-swap", "128m", "--cpus", "0.25", "--log-driver", "none", "--user", "0", "--entrypoint", "node", "--volume", `${resources.workspaceVolume}:/workspace`, "--volume", `${resources.controlVolume}:/control`, ...credentialMount, ...cacheMounts, "--tmpfs", "/cache:rw,nosuid,nodev,noexec,size=1m", helperImage, "-e", script], 60_000, signal);
|
|
666
|
+
await this.docker(["run", "--rm", "--network", "none", "--read-only", "--cap-drop", "ALL", "--cap-add", "CHOWN", "--security-opt", "no-new-privileges", "--pids-limit", "64", "--memory", "128m", "--memory-swap", "128m", "--cpus", "0.25", "--log-driver", "none", "--user", "0", "--entrypoint", "node", "--volume", `${resources.workspaceVolume}:/workspace`, "--volume", `${resources.controlVolume}:/control`, ...credentialMount, ...cacheMounts, "--tmpfs", "/cache:rw,nosuid,nodev,noexec,size=1m", helperImage, "-e", script], 60_000, signal);
|
|
665
667
|
}
|
|
666
668
|
async inspectContainerState(containerId) {
|
|
667
669
|
const result = await this.docker(["container", "inspect", containerId, "--format", "{{json .State}}"], 15_000, undefined, 256 * 1024);
|
|
@@ -692,10 +694,10 @@ export class DockerSandboxRuntime {
|
|
|
692
694
|
if (!providerAuth) {
|
|
693
695
|
return;
|
|
694
696
|
}
|
|
695
|
-
const script = "let s='';process.stdin.setEncoding('utf8');process.stdin.on('data',c=>s+=c);process.stdin.on('end',()=>{const f=require('fs'),p=require('path'),root='/credentials';for(const e of f.readdirSync(root))f.rmSync(p.join(root,e),{recursive:true,force:true});const packet=JSON.parse(s);for(const file of packet.files){if(!/^[a-z0-9_.-]+$/i.test(file.name))throw new Error('invalid credential file');const target=p.resolve(root,file.name);if(!target.startsWith(root+'/'))throw new Error('invalid credential file');const body=Buffer.from(file.contentBase64,'base64');f.writeFileSync(target,body,{mode:0o600});f.chownSync(target,10001,10001)}f.
|
|
697
|
+
const script = "let s='';process.stdin.setEncoding('utf8');process.stdin.on('data',c=>s+=c);process.stdin.on('end',()=>{const f=require('fs'),p=require('path'),root='/credentials';for(const e of f.readdirSync(root))f.rmSync(p.join(root,e),{recursive:true,force:true});const packet=JSON.parse(s);for(const file of packet.files){if(!/^[a-z0-9_.-]+$/i.test(file.name))throw new Error('invalid credential file');const target=p.resolve(root,file.name);if(!target.startsWith(root+'/'))throw new Error('invalid credential file');const body=Buffer.from(file.contentBase64,'base64');f.writeFileSync(target,body,{mode:0o600});f.chmodSync(target,0o600);f.chownSync(target,10001,10001)}f.chmodSync(root,0o700);f.chownSync(root,10001,10001)})";
|
|
696
698
|
await requireSandboxProcess({
|
|
697
699
|
command: this.config.dockerExecutable,
|
|
698
|
-
args: this.dockerArgs(["run", "--rm", "--interactive", "--network", "none", "--read-only", "--cap-drop", "ALL", "--security-opt", "no-new-privileges", "--pids-limit", "64", "--memory", "128m", "--memory-swap", "128m", "--cpus", "0.25", "--log-driver", "none", "--user", "0", "--entrypoint", "node", "--volume", `${resources.credentialsVolume}:/credentials`, helperImage, "-e", script]),
|
|
700
|
+
args: this.dockerArgs(["run", "--rm", "--interactive", "--network", "none", "--read-only", "--cap-drop", "ALL", "--cap-add", "CHOWN", "--security-opt", "no-new-privileges", "--pids-limit", "64", "--memory", "128m", "--memory-swap", "128m", "--cpus", "0.25", "--log-driver", "none", "--user", "0", "--entrypoint", "node", "--volume", `${resources.credentialsVolume}:/credentials`, helperImage, "-e", script]),
|
|
699
701
|
input: JSON.stringify({ files: providerAuth.files }),
|
|
700
702
|
timeoutMs: 60_000,
|
|
701
703
|
maxOutputBytes: 64 * 1024,
|
|
@@ -1453,11 +1455,15 @@ export async function extractOciLayoutArchive(archivePath, destination, signal)
|
|
|
1453
1455
|
await fs.rm(archivePath, { force: true }).catch(() => undefined);
|
|
1454
1456
|
}
|
|
1455
1457
|
export function ociLayoutContextRef(layoutPath) {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
+
if (/^[A-Za-z]:[\\/]/u.test(layoutPath)) {
|
|
1459
|
+
throw new Error("sandbox_build_failed: Windows absolute OCI layout paths are not BuildKit-safe; use a relative path from the build working directory.");
|
|
1460
|
+
}
|
|
1461
|
+
const normalized = layoutPath.replace(/\\/gu, "/").replace(/^\.\//u, "");
|
|
1458
1462
|
// BuildKit's oci-layout resolver expects a raw filesystem path here; it does not percent-decode
|
|
1459
|
-
// URI path escapes such as %20 before opening index.json.
|
|
1460
|
-
|
|
1463
|
+
// URI path escapes such as %20 before opening index.json. On Windows, absolute drive paths
|
|
1464
|
+
// such as D:/state/project-oci are parsed as OCI layout ref/tag syntax, so callers should
|
|
1465
|
+
// run buildx from the layout parent and pass a relative path (for example project-oci).
|
|
1466
|
+
return `oci-layout://${normalized}`;
|
|
1461
1467
|
}
|
|
1462
1468
|
export function formatOciExtractionFailure(result) {
|
|
1463
1469
|
const output = result.stderr.trim() || result.stdout.trim() || "no tar output";
|
|
@@ -111,7 +111,7 @@ function parseSandboxResourceRequest(value) {
|
|
|
111
111
|
}
|
|
112
112
|
function parseSandboxNetworkPolicy(value) {
|
|
113
113
|
if (value === undefined || value === null)
|
|
114
|
-
return { mode: "
|
|
114
|
+
return { mode: "offline", allowedHosts: [] };
|
|
115
115
|
const policy = asObject(value, "sandbox.networkPolicy");
|
|
116
116
|
const mode = requiredString(policy, "mode");
|
|
117
117
|
if (mode !== "offline" && mode !== "public_allowlist" && mode !== "open_public") {
|
|
@@ -128,6 +128,9 @@ function parseSandboxNetworkPolicy(value) {
|
|
|
128
128
|
if (mode !== "public_allowlist" && allowedHosts.length > 0) {
|
|
129
129
|
throw new Error("sandbox.networkPolicy.allowedHosts requires public_allowlist mode.");
|
|
130
130
|
}
|
|
131
|
+
if (mode === "public_allowlist" && allowedHosts.length === 0) {
|
|
132
|
+
return { mode: "offline", allowedHosts: [] };
|
|
133
|
+
}
|
|
131
134
|
return { mode, allowedHosts };
|
|
132
135
|
}
|
|
133
136
|
export function failureResult(summary, events = []) {
|
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.d46a8e73d03d";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const forgeBridgeVersion = "0.1.3-dev.
|
|
1
|
+
export const forgeBridgeVersion = "0.1.3-dev.d46a8e73d03d";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loucompanion/forge-bridge",
|
|
3
|
-
"version": "0.1.3-dev.
|
|
3
|
+
"version": "0.1.3-dev.d46a8e73d03d",
|
|
4
4
|
"description": "Forge runner bridge CLI for connecting local developer machines to Forge dispatch.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|