@elie-laloum/outpost 1.1.3 → 1.1.4
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/CHANGELOG.md +9 -0
- package/dist/cli/scaffold.constants.js +1 -0
- package/dist/cli/scaffold.constants.js.map +1 -1
- package/dist/infrastructure/binary-process.d.ts +3 -0
- package/dist/infrastructure/binary-process.js +53 -0
- package/dist/infrastructure/binary-process.js.map +1 -0
- package/dist/infrastructure/binary-process.types.d.ts +5 -0
- package/dist/infrastructure/binary-process.types.js +2 -0
- package/dist/infrastructure/binary-process.types.js.map +1 -0
- package/dist/infrastructure/transfer-copy.d.ts +3 -0
- package/dist/infrastructure/transfer-copy.js +51 -0
- package/dist/infrastructure/transfer-copy.js.map +1 -0
- package/dist/providers/container-archive.d.ts +3 -0
- package/dist/providers/container-archive.js +27 -0
- package/dist/providers/container-archive.js.map +1 -0
- package/dist/providers/container-command.js +3 -3
- package/dist/providers/container-command.js.map +1 -1
- package/dist/providers/container-files.d.ts +2 -1
- package/dist/providers/container-files.js +100 -11
- package/dist/providers/container-files.js.map +1 -1
- package/dist/providers/container.js +11 -2
- package/dist/providers/container.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.4
|
|
4
|
+
|
|
5
|
+
- Wait for noninteractive container commands and preserve their real exit status; reuse the runtime terminal for interactive commands.
|
|
6
|
+
- Stream binary archives through the running Docker/Podman container so uploads and conversation capture/restore see the same tmpfs home as agents.
|
|
7
|
+
- Preserve ordinary file modes and links, reject unsafe download destinations, and support GNU tar and bsdtar hosts.
|
|
8
|
+
- Create a private writable agent home in generated images, including when used without the sandbox tmpfs mount.
|
|
9
|
+
- Add real container, pseudo-terminal, cancellation, binary integrity and native conversation regression checks.
|
|
10
|
+
- Add English/French account-connection guides for Claude Code and Codex, plus seven progressive cookbooks.
|
|
11
|
+
|
|
3
12
|
## 1.1.3
|
|
4
13
|
|
|
5
14
|
- Replace the flat documentation with an Astro Starlight site in English and French, with English as the default language.
|
|
@@ -5,6 +5,7 @@ ARG AGENT_GID=1000
|
|
|
5
5
|
RUN apt-get update && apt-get install -y --no-install-recommends git gh openssh-client ca-certificates curl procps util-linux python3 && rm -rf /var/lib/apt/lists/*
|
|
6
6
|
RUN npm install -g --allow-scripts=@anthropic-ai/claude-code @openai/codex@${agentVersions.codex} @anthropic-ai/claude-code@${agentVersions.claude}
|
|
7
7
|
RUN groupmod -o -g "$AGENT_GID" node && usermod -o -u "$AGENT_UID" -g "$AGENT_GID" node
|
|
8
|
+
RUN mkdir -p /home/agent && chown "$AGENT_UID:$AGENT_GID" /home/agent && chmod 700 /home/agent
|
|
8
9
|
ENV HOME=/home/agent
|
|
9
10
|
USER $AGENT_UID:$AGENT_GID
|
|
10
11
|
WORKDIR /workspace
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.constants.js","sourceRoot":"","sources":["../../src/cli/scaffold.constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,CAAC,MAAM,WAAW,GAAG;;;;6EAIkD,aAAa,CAAC,KAAK,8BAA8B,aAAa,CAAC,MAAM
|
|
1
|
+
{"version":3,"file":"scaffold.constants.js","sourceRoot":"","sources":["../../src/cli/scaffold.constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,CAAC,MAAM,WAAW,GAAG;;;;6EAIkD,aAAa,CAAC,KAAK,8BAA8B,aAAa,CAAC,MAAM;;;;;;CAMjJ,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAsB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,OAAO;CACR,CAAC;AACF,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD,OAAO;IACP,SAAS;IACT,QAAQ;IACR,MAAM;IACN,aAAa;CACd,CAAC;AACF,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,QAAQ;IACR,OAAO;IACP,QAAQ;CACT,CAAC;AACF,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;CACN,CAAC;AACF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAC1B,CAAC,WAAW,EAAE,MAAM,CAAC;IACrB,CAAC,UAAU,EAAE,KAAK,CAAC;IACnB,CAAC,WAAW,EAAE,KAAK,CAAC;CACZ,CAAC;AACX,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE;IACT,MAAM,EAAE,CAAC,iBAAiB,CAAC;IAC3B,OAAO,EAAE,CAAC,cAAc,CAAC;CACjB,CAAC","sourcesContent":["import { agentVersions } from \"../providers/versions.ts\";\n\nexport const imageRecipe = `FROM node:24-bookworm-slim\nARG AGENT_UID=1000\nARG AGENT_GID=1000\nRUN apt-get update && apt-get install -y --no-install-recommends git gh openssh-client ca-certificates curl procps util-linux python3 && rm -rf /var/lib/apt/lists/*\nRUN npm install -g --allow-scripts=@anthropic-ai/claude-code @openai/codex@${agentVersions.codex} @anthropic-ai/claude-code@${agentVersions.claude}\nRUN groupmod -o -g \"$AGENT_GID\" node && usermod -o -u \"$AGENT_UID\" -g \"$AGENT_GID\" node\nRUN mkdir -p /home/agent && chown \"$AGENT_UID:$AGENT_GID\" /home/agent && chmod 700 /home/agent\nENV HOME=/home/agent\nUSER $AGENT_UID:$AGENT_GID\nWORKDIR /workspace\n`;\n\nexport const supportedAgents: readonly string[] = [\"codex\", \"claude\"];\nexport const supportedProviders: readonly string[] = [\n \"docker\",\n \"podman\",\n \"vercel\",\n \"daytona\",\n \"local\",\n];\nexport const supportedTemplates: readonly string[] = [\n \"blank\",\n \"iterate\",\n \"review\",\n \"plan\",\n \"plan-review\",\n];\nexport const supportedTrackers: readonly string[] = [\n \"github\",\n \"beads\",\n \"custom\",\n];\nexport const supportedManagers: readonly string[] = [\n \"npm\",\n \"pnpm\",\n \"yarn\",\n \"bun\",\n];\nexport const managerLocks = [\n [\"pnpm-lock.yaml\", \"pnpm\"],\n [\"yarn.lock\", \"yarn\"],\n [\"bun.lock\", \"bun\"],\n [\"bun.lockb\", \"bun\"],\n] as const;\nexport const providerPackages = {\n docker: [],\n podman: [],\n local: [],\n vercel: [\"@vercel/sandbox\"],\n daytona: [\"@daytona/sdk\"],\n} as const;\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { pipeline } from "node:stream/promises";
|
|
3
|
+
import { OutpostError } from "../domain/errors.js";
|
|
4
|
+
import { processDefaults } from "./process.constants.js";
|
|
5
|
+
import { transfer } from "./transfer.js";
|
|
6
|
+
export function binaryExecutor(streams) {
|
|
7
|
+
return (command) => transfer(command, async (signal) => {
|
|
8
|
+
signal.throwIfAborted();
|
|
9
|
+
const child = spawn(command.executable, [...(command.arguments ?? [])], {
|
|
10
|
+
cwd: command.directory,
|
|
11
|
+
env: { ...process.env, ...command.variables },
|
|
12
|
+
windowsHide: true,
|
|
13
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
14
|
+
signal,
|
|
15
|
+
killSignal: "SIGKILL",
|
|
16
|
+
});
|
|
17
|
+
let stderr = "";
|
|
18
|
+
child.stderr.on("data", (data) => {
|
|
19
|
+
stderr = (stderr + data.toString("utf8")).slice(-processDefaults.retainBytes);
|
|
20
|
+
});
|
|
21
|
+
const completion = new Promise((resolve, reject) => {
|
|
22
|
+
child.on("error", reject);
|
|
23
|
+
child.on("close", (status) => {
|
|
24
|
+
if (status !== 0)
|
|
25
|
+
reject(new OutpostError("process", `${command.executable} exited with status ${status}`, { status, stderr }));
|
|
26
|
+
else
|
|
27
|
+
resolve({ status: 0, stdout: "", stderr });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
const pending = [completion];
|
|
31
|
+
if (streams.input)
|
|
32
|
+
pending.push(pipeline(streams.input, child.stdin, { signal }));
|
|
33
|
+
else
|
|
34
|
+
child.stdin.end();
|
|
35
|
+
if (streams.output)
|
|
36
|
+
pending.push(pipeline(child.stdout, streams.output, { signal }));
|
|
37
|
+
else
|
|
38
|
+
child.stdout.resume();
|
|
39
|
+
try {
|
|
40
|
+
await Promise.all(pending);
|
|
41
|
+
return await completion;
|
|
42
|
+
}
|
|
43
|
+
catch (cause) {
|
|
44
|
+
child.kill("SIGKILL");
|
|
45
|
+
streams.input?.destroy();
|
|
46
|
+
streams.output?.destroy();
|
|
47
|
+
await Promise.allSettled(pending);
|
|
48
|
+
signal.throwIfAborted();
|
|
49
|
+
throw cause;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=binary-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-process.js","sourceRoot":"","sources":["../../src/infrastructure/binary-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,UAAU,cAAc,CAAC,OAAsB;IACnD,OAAO,CAAC,OAAO,EAAE,EAAE,CACjB,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACjC,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,EAAE;YACtE,GAAG,EAAE,OAAO,CAAC,SAAS;YACtB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE;YAC7C,WAAW,EAAE,IAAI;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,MAAM;YACN,UAAU,EAAE,SAAS;SACtB,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACvC,MAAM,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAC7C,CAAC,eAAe,CAAC,WAAW,CAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAChE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC3B,IAAI,MAAM,KAAK,CAAC;oBACd,MAAM,CACJ,IAAI,YAAY,CACd,SAAS,EACT,GAAG,OAAO,CAAC,UAAU,uBAAuB,MAAM,EAAE,EACpD,EAAE,MAAM,EAAE,MAAM,EAAE,CACnB,CACF,CAAC;;oBACC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,MAAM,OAAO,GAAuB,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,KAAK;YACf,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;;YAC5D,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,OAAO,CAAC,MAAM;YAChB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;;YAC9D,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,MAAM,UAAU,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;YACzB,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAClC,MAAM,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { CommandResult } from \"../domain/command.types.ts\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { BinaryStreams } from \"./binary-process.types.ts\";\nimport { processDefaults } from \"./process.constants.ts\";\nimport type { Executor } from \"./process.types.ts\";\nimport { transfer } from \"./transfer.ts\";\n\nexport function binaryExecutor(streams: BinaryStreams): Executor {\n return (command) =>\n transfer(command, async (signal) => {\n signal.throwIfAborted();\n const child = spawn(command.executable, [...(command.arguments ?? [])], {\n cwd: command.directory,\n env: { ...process.env, ...command.variables },\n windowsHide: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n signal,\n killSignal: \"SIGKILL\",\n });\n let stderr = \"\";\n child.stderr.on(\"data\", (data: Buffer) => {\n stderr = (stderr + data.toString(\"utf8\")).slice(\n -processDefaults.retainBytes,\n );\n });\n const completion = new Promise<CommandResult>((resolve, reject) => {\n child.on(\"error\", reject);\n child.on(\"close\", (status) => {\n if (status !== 0)\n reject(\n new OutpostError(\n \"process\",\n `${command.executable} exited with status ${status}`,\n { status, stderr },\n ),\n );\n else resolve({ status: 0, stdout: \"\", stderr });\n });\n });\n const pending: Promise<unknown>[] = [completion];\n if (streams.input)\n pending.push(pipeline(streams.input, child.stdin, { signal }));\n else child.stdin.end();\n if (streams.output)\n pending.push(pipeline(child.stdout, streams.output, { signal }));\n else child.stdout.resume();\n try {\n await Promise.all(pending);\n return await completion;\n } catch (cause) {\n child.kill(\"SIGKILL\");\n streams.input?.destroy();\n streams.output?.destroy();\n await Promise.allSettled(pending);\n signal.throwIfAborted();\n throw cause;\n }\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-process.types.js","sourceRoot":"","sources":["../../src/infrastructure/binary-process.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Readable, Writable } from \"node:stream\";\n\nexport interface BinaryStreams {\n readonly input?: Readable;\n readonly output?: Writable;\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { chmod, copyFile, rm, lstat, mkdir, readdir, readlink, symlink, } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, parse, resolve } from "node:path";
|
|
3
|
+
import { OutpostError } from "../domain/errors.js";
|
|
4
|
+
export async function transferDestination(destination) {
|
|
5
|
+
const absolute = resolve(destination);
|
|
6
|
+
for (let path = absolute; path !== parse(path).root; path = dirname(path)) {
|
|
7
|
+
const info = await lstat(path).catch((cause) => {
|
|
8
|
+
if (cause.code === "ENOENT")
|
|
9
|
+
return undefined;
|
|
10
|
+
throw cause;
|
|
11
|
+
});
|
|
12
|
+
if (info?.isSymbolicLink())
|
|
13
|
+
throw new OutpostError("provider", "Transfer destination traverses a symlink", { destination });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function copyTransfer(source, destination, signal) {
|
|
17
|
+
signal.throwIfAborted();
|
|
18
|
+
await transferDestination(destination);
|
|
19
|
+
const info = await lstat(source);
|
|
20
|
+
if (info.isDirectory()) {
|
|
21
|
+
await mkdir(destination, { recursive: true });
|
|
22
|
+
for (const name of await readdir(source))
|
|
23
|
+
await copyTransfer(join(source, name), join(destination, name), signal);
|
|
24
|
+
await chmod(destination, info.mode & 0o777);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
28
|
+
if (info.isSymbolicLink()) {
|
|
29
|
+
await symlink(await readlink(source), destination);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!info.isFile())
|
|
33
|
+
throw new OutpostError("provider", "Unsupported transfer file type", {
|
|
34
|
+
source,
|
|
35
|
+
});
|
|
36
|
+
await copyFile(source, destination);
|
|
37
|
+
await chmod(destination, info.mode & 0o777);
|
|
38
|
+
}
|
|
39
|
+
export async function removeTransferDirectory(root) {
|
|
40
|
+
async function writable(path) {
|
|
41
|
+
const info = await lstat(path);
|
|
42
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
43
|
+
return;
|
|
44
|
+
await chmod(path, 0o700);
|
|
45
|
+
for (const name of await readdir(path))
|
|
46
|
+
await writable(join(path, name));
|
|
47
|
+
}
|
|
48
|
+
await writable(root);
|
|
49
|
+
await rm(root, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=transfer-copy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer-copy.js","sourceRoot":"","sources":["../../src/infrastructure/transfer-copy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n chmod,\n copyFile,\n rm,\n lstat,\n mkdir,\n readdir,\n readlink,\n symlink,\n} from \"node:fs/promises\";\nimport { dirname, join, parse, resolve } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\n\nexport async function transferDestination(destination: string): Promise<void> {\n const absolute = resolve(destination);\n for (let path = absolute; path !== parse(path).root; path = dirname(path)) {\n const info = await lstat(path).catch((cause: NodeJS.ErrnoException) => {\n if (cause.code === \"ENOENT\") return undefined;\n throw cause;\n });\n if (info?.isSymbolicLink())\n throw new OutpostError(\n \"provider\",\n \"Transfer destination traverses a symlink\",\n { destination },\n );\n }\n}\n\nexport async function copyTransfer(\n source: string,\n destination: string,\n signal: AbortSignal,\n): Promise<void> {\n signal.throwIfAborted();\n await transferDestination(destination);\n const info = await lstat(source);\n if (info.isDirectory()) {\n await mkdir(destination, { recursive: true });\n for (const name of await readdir(source))\n await copyTransfer(join(source, name), join(destination, name), signal);\n await chmod(destination, info.mode & 0o777);\n return;\n }\n await mkdir(dirname(destination), { recursive: true });\n if (info.isSymbolicLink()) {\n await symlink(await readlink(source), destination);\n return;\n }\n if (!info.isFile())\n throw new OutpostError(\"provider\", \"Unsupported transfer file type\", {\n source,\n });\n await copyFile(source, destination);\n await chmod(destination, info.mode & 0o777);\n}\n\nexport async function removeTransferDirectory(root: string): Promise<void> {\n async function writable(path: string): Promise<void> {\n const info = await lstat(path);\n if (!info.isDirectory() || info.isSymbolicLink()) return;\n await chmod(path, 0o700);\n for (const name of await readdir(path)) await writable(join(path, name));\n }\n await writable(root);\n await rm(root, { recursive: true, force: true });\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Command } from "../domain/command.types.ts";
|
|
2
|
+
import type { ContainerRuntime } from "./container.types.ts";
|
|
3
|
+
export declare function containerArchive(runtime: ContainerRuntime, local: Command, remote: Command, upload: boolean, signal: AbortSignal): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PassThrough } from "node:stream";
|
|
2
|
+
import { binaryExecutor } from "../infrastructure/binary-process.js";
|
|
3
|
+
import { containerCommand } from "./container-command.js";
|
|
4
|
+
export async function containerArchive(runtime, local, remote, upload, signal) {
|
|
5
|
+
const pipe = new PassThrough();
|
|
6
|
+
const stop = new AbortController();
|
|
7
|
+
const combined = AbortSignal.any([signal, stop.signal]);
|
|
8
|
+
const host = binaryExecutor(upload ? { output: pipe } : { input: pipe });
|
|
9
|
+
const guest = containerCommand({
|
|
10
|
+
...runtime,
|
|
11
|
+
executor: binaryExecutor(upload ? { input: pipe } : { output: pipe }),
|
|
12
|
+
});
|
|
13
|
+
const pending = [
|
|
14
|
+
host({ ...local, signal: combined }),
|
|
15
|
+
guest({ ...remote, signal: combined }),
|
|
16
|
+
];
|
|
17
|
+
try {
|
|
18
|
+
await Promise.all(pending);
|
|
19
|
+
}
|
|
20
|
+
catch (cause) {
|
|
21
|
+
stop.abort(cause);
|
|
22
|
+
pipe.destroy();
|
|
23
|
+
await Promise.allSettled(pending);
|
|
24
|
+
throw cause;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=container-archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-archive.js","sourceRoot":"","sources":["../../src/providers/container-archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAyB,EACzB,KAAc,EACd,MAAe,EACf,MAAe,EACf,MAAmB;IAEnB,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,gBAAgB,CAAC;QAC7B,GAAG,OAAO;QACV,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KACtE,CAAC,CAAC;IACH,MAAM,OAAO,GAAG;QACd,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACpC,KAAK,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;KACvC,CAAC;IACF,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["import { PassThrough } from \"node:stream\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport { binaryExecutor } from \"../infrastructure/binary-process.ts\";\nimport { containerCommand } from \"./container-command.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport async function containerArchive(\n runtime: ContainerRuntime,\n local: Command,\n remote: Command,\n upload: boolean,\n signal: AbortSignal,\n): Promise<void> {\n const pipe = new PassThrough();\n const stop = new AbortController();\n const combined = AbortSignal.any([signal, stop.signal]);\n const host = binaryExecutor(upload ? { output: pipe } : { input: pipe });\n const guest = containerCommand({\n ...runtime,\n executor: binaryExecutor(upload ? { input: pipe } : { output: pipe }),\n });\n const pending = [\n host({ ...local, signal: combined }),\n guest({ ...remote, signal: combined }),\n ];\n try {\n await Promise.all(pending);\n } catch (cause) {\n stop.abort(cause);\n pipe.destroy();\n await Promise.allSettled(pending);\n throw cause;\n }\n}\n"]}
|
|
@@ -37,9 +37,9 @@ export function containerCommand(runtime) {
|
|
|
37
37
|
executable: engine,
|
|
38
38
|
arguments: [
|
|
39
39
|
...flags,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
...(command.interactive && process.stdin.isTTY
|
|
41
|
+
? []
|
|
42
|
+
: ["setsid", "--wait"]),
|
|
43
43
|
"sh",
|
|
44
44
|
"-c",
|
|
45
45
|
wrapper,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container-command.js","sourceRoot":"","sources":["../../src/providers/container-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,UAAU,gBAAgB,CAC9B,OAAyB;IAEzB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,QAAQ,EAAE;YACZ,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,6BAA6B,CAAC,CAAC;QACpE,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,gBAAgB,EAAE,MAAM,CAAC;QACzC,MAAM,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACnD,SAAS,CACP,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACnC,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CACrC,EACD,yBAAyB,CAC1B,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CACpC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,iBAAiB,KAAK,EAAE;YACxB,KAAK;SACN,CAAC,CACH,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;aACnC,GAAG,CACF,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,UAAU,GAAG,oBAAoB,KAAK,0BAA0B,KAAK,GAAG,CAC3E;aACA,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,MAAM,KAAK,GAAG;YACZ,MAAM;YACN,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YACzD,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,WAAW;YACX,OAAO,CAAC,SAAS,IAAI,IAAI;YACzB,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC5D,IAAI;SACL,CAAC;QACF,MAAM,OAAO,GAAG,GAAG,OAAO,cAAc,KAAK,CAAC,OAAO,CAAC,eAAe,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,yBAAyB,CAAC;QACzH,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;QACzD,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC;gBACpB,GAAG,UAAU;gBACb,UAAU,EAAE,MAAM;gBAClB,SAAS,EAAE;oBACT,GAAG,KAAK;oBACR,
|
|
1
|
+
{"version":3,"file":"container-command.js","sourceRoot":"","sources":["../../src/providers/container-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,UAAU,gBAAgB,CAC9B,OAAyB;IAEzB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC9E,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,QAAQ,EAAE;YACZ,MAAM,IAAI,YAAY,CAAC,UAAU,EAAE,6BAA6B,CAAC,CAAC;QACpE,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,gBAAgB,EAAE,MAAM,CAAC;QACzC,MAAM,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACnD,SAAS,CACP,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACnC,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CACrC,EACD,yBAAyB,CAC1B,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CACpC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,iBAAiB,KAAK,EAAE;YACxB,KAAK;SACN,CAAC,CACH,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;aACnC,GAAG,CACF,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,UAAU,GAAG,oBAAoB,KAAK,0BAA0B,KAAK,GAAG,CAC3E;aACA,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,MAAM,KAAK,GAAG;YACZ,MAAM;YACN,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YACzD,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,WAAW;YACX,OAAO,CAAC,SAAS,IAAI,IAAI;YACzB,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC5D,IAAI;SACL,CAAC;QACF,MAAM,OAAO,GAAG,GAAG,OAAO,cAAc,KAAK,CAAC,OAAO,CAAC,eAAe,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,yBAAyB,CAAC;QACzH,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;QACzD,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC;gBACpB,GAAG,UAAU;gBACb,UAAU,EAAE,MAAM;gBAClB,SAAS,EAAE;oBACT,GAAG,KAAK;oBACR,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK;wBAC5C,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBACzB,IAAI;oBACJ,IAAI;oBACJ,OAAO;oBACP,SAAS;oBACT,OAAO,CAAC,UAAU;oBAClB,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;iBAC7B;gBACD,SAAS,EAAE,WAAW;gBACtB,MAAM,EACJ,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,IAAI,iBAAiB,CAAC,WAAW;aACnE,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC;oBACT,MAAM;oBACN,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9C,IAAI;oBACJ,IAAI;oBACJ,IAAI;oBACJ,SAAS,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,aAAa,KAAK,CAAC,OAAO,CAAC,oBAAoB,KAAK,CAAC,OAAO,CAAC,8FAA8F;iBAC/L,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,OAAO,EAAE,CAAC;gBACjB,MAAM,IAAI,cAAc,CACtB,CAAC,KAAK,EAAE,OAAO,CAAC,EAChB,6CAA6C,CAC9C,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,IAAI,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport function containerCommand(\n runtime: ContainerRuntime,\n): SandboxLease[\"invoke\"] {\n const { engine, config, executor, root, name, env, call, isClosed } = runtime;\n return async (command) => {\n if (isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n command.signal?.throwIfAborted();\n const id = randomUUID();\n const pidFile = `/tmp/outpost-${id}.pid`;\n const variables = { ...env, ...command.variables };\n invariant(\n Object.keys(variables).every((key) =>\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(key),\n ),\n \"Invalid environment key\",\n );\n const environment = Object.fromEntries(\n Object.entries(variables).map(([, value], index) => [\n `OUTPOST_VALUE_${index}`,\n value,\n ]),\n );\n const exports = Object.keys(variables)\n .map(\n (key, index) =>\n `export ${key}=\"$OUTPOST_VALUE_${index}\"; unset OUTPOST_VALUE_${index};`,\n )\n .join(\" \");\n const flags = [\n \"exec\",\n command.interactive && process.stdin.isTTY ? \"-it\" : \"-i\",\n ...(command.elevated ? [\"--user\", \"0:0\"] : []),\n \"--workdir\",\n command.directory ?? root,\n ...Object.keys(environment).flatMap((key) => [\"--env\", key]),\n name,\n ];\n const wrapper = `${exports} echo $$ > ${quote(pidFile)}; test ! -f ${quote(pidFile + \".cancel\")} || exit 130; exec \"$@\"`;\n const { directory: _directory, ...invocation } = command;\n let interrupted = false;\n try {\n return await executor({\n ...invocation,\n executable: engine,\n arguments: [\n ...flags,\n ...(command.interactive && process.stdin.isTTY\n ? []\n : [\"setsid\", \"--wait\"]),\n \"sh\",\n \"-c\",\n wrapper,\n \"outpost\",\n command.executable,\n ...(command.arguments ?? []),\n ],\n variables: environment,\n retain:\n command.retain ?? config.retain ?? containerDefaults.retainBytes,\n });\n } catch (cause) {\n interrupted = true;\n try {\n await call([\n \"exec\",\n ...(command.elevated ? [\"--user\", \"0:0\"] : []),\n name,\n \"sh\",\n \"-c\",\n `touch ${quote(pidFile + \".cancel\")}; if [ -f ${quote(pidFile)} ]; then p=$(cat ${quote(pidFile)}); kill -TERM -\"$p\" 2>/dev/null || true; sleep 0.1; kill -KILL -\"$p\" 2>/dev/null || true; fi`,\n ]);\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Command cancellation could not be confirmed\",\n );\n }\n throw cause;\n } finally {\n if (!isClosed() && !interrupted)\n await call([\"exec\", name, \"rm\", \"-f\", pidFile]).catch(() => undefined);\n }\n };\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { SandboxLease } from "../domain/sandbox.types.ts";
|
|
2
|
+
import { containerArchive } from "./container-archive.ts";
|
|
2
3
|
import type { ContainerRuntime } from "./container.types.ts";
|
|
3
|
-
export declare function containerFiles(
|
|
4
|
+
export declare function containerFiles(runtime: ContainerRuntime, archive?: typeof containerArchive): Pick<SandboxLease, "upload" | "download">;
|
|
@@ -1,16 +1,105 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, mkdir, mkdtemp } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { basename, dirname, join, posix, resolve } from "node:path";
|
|
5
|
+
import { invariant, OutpostError } from "../domain/errors.js";
|
|
6
|
+
import { quote } from "../infrastructure/process.js";
|
|
7
|
+
import { copyTransfer, removeTransferDirectory, transferDestination, } from "../infrastructure/transfer-copy.js";
|
|
8
|
+
import { transfer } from "../infrastructure/transfer.js";
|
|
9
|
+
import { containerArchive } from "./container-archive.js";
|
|
10
|
+
export function containerFiles(runtime, archive = containerArchive) {
|
|
11
|
+
const { call, name } = runtime;
|
|
4
12
|
return {
|
|
5
|
-
|
|
6
|
-
options
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
upload(source, destination, options = {}) {
|
|
14
|
+
return transfer(options, async (signal) => {
|
|
15
|
+
if (runtime.isClosed())
|
|
16
|
+
throw new OutpostError("provider", "Container sandbox is closed");
|
|
17
|
+
const absolute = resolve(source);
|
|
18
|
+
const scratch = `/tmp/outpost-transfer-${randomUUID()}`;
|
|
19
|
+
const entry = basename(absolute);
|
|
20
|
+
invariant(entry, "Upload a directory's contents using a non-root path");
|
|
21
|
+
const contents = /[\\/]\.$/.test(source);
|
|
22
|
+
await call(["exec", name, "mkdir", "-m", "700", scratch], { signal });
|
|
23
|
+
try {
|
|
24
|
+
await archive(runtime, {
|
|
25
|
+
executable: "tar",
|
|
26
|
+
arguments: ["-cf", "-", "-C", dirname(absolute), "--", entry],
|
|
27
|
+
}, {
|
|
28
|
+
executable: "tar",
|
|
29
|
+
arguments: [
|
|
30
|
+
"-xpf",
|
|
31
|
+
"-",
|
|
32
|
+
"--ignore-zeros",
|
|
33
|
+
"--no-same-owner",
|
|
34
|
+
"-C",
|
|
35
|
+
scratch,
|
|
36
|
+
],
|
|
37
|
+
}, true, signal);
|
|
38
|
+
await call([
|
|
39
|
+
"exec",
|
|
40
|
+
name,
|
|
41
|
+
"sh",
|
|
42
|
+
"-c",
|
|
43
|
+
`mkdir -p ${quote(posix.dirname(destination))} && cp -a -- ${quote(posix.join(scratch, entry) + (contents ? "/." : ""))} ${quote(destination)}`,
|
|
44
|
+
], { signal });
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await call([
|
|
48
|
+
"exec",
|
|
49
|
+
name,
|
|
50
|
+
"sh",
|
|
51
|
+
"-c",
|
|
52
|
+
`chmod -R u+rwX ${quote(scratch)} && rm -rf -- ${quote(scratch)}`,
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
9
56
|
},
|
|
10
|
-
|
|
11
|
-
options
|
|
12
|
-
|
|
13
|
-
|
|
57
|
+
download(source, destination, options = {}) {
|
|
58
|
+
return transfer(options, async (signal) => {
|
|
59
|
+
if (runtime.isClosed())
|
|
60
|
+
throw new OutpostError("provider", "Container sandbox is closed");
|
|
61
|
+
await transferDestination(destination);
|
|
62
|
+
const absolute = posix.resolve(runtime.root, source);
|
|
63
|
+
const entry = posix.basename(absolute);
|
|
64
|
+
invariant(entry, "Download a directory's contents using a non-root path");
|
|
65
|
+
const scratch = await mkdtemp(join(tmpdir(), "outpost-transfer-"));
|
|
66
|
+
try {
|
|
67
|
+
await archive(runtime, {
|
|
68
|
+
executable: "tar",
|
|
69
|
+
arguments: [
|
|
70
|
+
"-xpf",
|
|
71
|
+
"-",
|
|
72
|
+
"--ignore-zeros",
|
|
73
|
+
"--no-same-owner",
|
|
74
|
+
"-C",
|
|
75
|
+
scratch,
|
|
76
|
+
],
|
|
77
|
+
}, {
|
|
78
|
+
executable: "tar",
|
|
79
|
+
arguments: [
|
|
80
|
+
"-cf",
|
|
81
|
+
"-",
|
|
82
|
+
"-C",
|
|
83
|
+
posix.dirname(absolute),
|
|
84
|
+
"--",
|
|
85
|
+
entry,
|
|
86
|
+
],
|
|
87
|
+
}, false, signal);
|
|
88
|
+
const existing = await lstat(destination).catch((cause) => {
|
|
89
|
+
if (cause.code === "ENOENT")
|
|
90
|
+
return undefined;
|
|
91
|
+
throw cause;
|
|
92
|
+
});
|
|
93
|
+
const target = existing?.isDirectory() && !source.endsWith("/.")
|
|
94
|
+
? join(destination, entry)
|
|
95
|
+
: destination;
|
|
96
|
+
await mkdir(dirname(target), { recursive: true });
|
|
97
|
+
await copyTransfer(join(scratch, entry), target, signal);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
await removeTransferDirectory(scratch);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
14
103
|
},
|
|
15
104
|
};
|
|
16
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container-files.js","sourceRoot":"","sources":["../../src/providers/container-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"container-files.js","sourceRoot":"","sources":["../../src/providers/container-files.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { lstat, mkdir, mkdtemp } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join, posix, resolve } from \"node:path\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { quote } from \"../infrastructure/process.ts\";\nimport {\n copyTransfer,\n removeTransferDirectory,\n transferDestination,\n} from \"../infrastructure/transfer-copy.ts\";\nimport { transfer } from \"../infrastructure/transfer.ts\";\nimport { containerArchive } from \"./container-archive.ts\";\nimport type { ContainerRuntime } from \"./container.types.ts\";\n\nexport function containerFiles(\n runtime: ContainerRuntime,\n archive = containerArchive,\n): Pick<SandboxLease, \"upload\" | \"download\"> {\n const { call, name } = runtime;\n return {\n upload(source, destination, options = {}) {\n return transfer(options, async (signal) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n const absolute = resolve(source);\n const scratch = `/tmp/outpost-transfer-${randomUUID()}`;\n const entry = basename(absolute);\n invariant(entry, \"Upload a directory's contents using a non-root path\");\n const contents = /[\\\\/]\\.$/.test(source);\n await call([\"exec\", name, \"mkdir\", \"-m\", \"700\", scratch], { signal });\n try {\n await archive(\n runtime,\n {\n executable: \"tar\",\n arguments: [\"-cf\", \"-\", \"-C\", dirname(absolute), \"--\", entry],\n },\n {\n executable: \"tar\",\n arguments: [\n \"-xpf\",\n \"-\",\n \"--ignore-zeros\",\n \"--no-same-owner\",\n \"-C\",\n scratch,\n ],\n },\n true,\n signal,\n );\n await call(\n [\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(posix.dirname(destination))} && cp -a -- ${quote(posix.join(scratch, entry) + (contents ? \"/.\" : \"\"))} ${quote(destination)}`,\n ],\n { signal },\n );\n } finally {\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n `chmod -R u+rwX ${quote(scratch)} && rm -rf -- ${quote(scratch)}`,\n ]);\n }\n });\n },\n download(source, destination, options = {}) {\n return transfer(options, async (signal) => {\n if (runtime.isClosed())\n throw new OutpostError(\"provider\", \"Container sandbox is closed\");\n await transferDestination(destination);\n const absolute = posix.resolve(runtime.root, source);\n const entry = posix.basename(absolute);\n invariant(\n entry,\n \"Download a directory's contents using a non-root path\",\n );\n const scratch = await mkdtemp(join(tmpdir(), \"outpost-transfer-\"));\n try {\n await archive(\n runtime,\n {\n executable: \"tar\",\n arguments: [\n \"-xpf\",\n \"-\",\n \"--ignore-zeros\",\n \"--no-same-owner\",\n \"-C\",\n scratch,\n ],\n },\n {\n executable: \"tar\",\n arguments: [\n \"-cf\",\n \"-\",\n \"-C\",\n posix.dirname(absolute),\n \"--\",\n entry,\n ],\n },\n false,\n signal,\n );\n const existing = await lstat(destination).catch(\n (cause: NodeJS.ErrnoException) => {\n if (cause.code === \"ENOENT\") return undefined;\n throw cause;\n },\n );\n const target =\n existing?.isDirectory() && !source.endsWith(\"/.\")\n ? join(destination, entry)\n : destination;\n await mkdir(dirname(target), { recursive: true });\n await copyTransfer(join(scratch, entry), target, signal);\n } finally {\n await removeTransferDirectory(scratch);\n }\n });\n },\n };\n}\n"]}
|
|
@@ -101,7 +101,7 @@ export function containerProvider(engine, options = {}, executor = executeProces
|
|
|
101
101
|
name,
|
|
102
102
|
"sh",
|
|
103
103
|
"-c",
|
|
104
|
-
"command -v setsid >/dev/null && command -v kill >/dev/null",
|
|
104
|
+
"command -v setsid >/dev/null && command -v kill >/dev/null && command -v tar >/dev/null && command -v cp >/dev/null",
|
|
105
105
|
]);
|
|
106
106
|
}
|
|
107
107
|
catch (cause) {
|
|
@@ -134,7 +134,16 @@ export function containerProvider(engine, options = {}, executor = executeProces
|
|
|
134
134
|
call,
|
|
135
135
|
isClosed: () => closed,
|
|
136
136
|
}),
|
|
137
|
-
...containerFiles(
|
|
137
|
+
...containerFiles({
|
|
138
|
+
engine,
|
|
139
|
+
config,
|
|
140
|
+
executor,
|
|
141
|
+
root,
|
|
142
|
+
name,
|
|
143
|
+
env,
|
|
144
|
+
call,
|
|
145
|
+
isClosed: () => closed,
|
|
146
|
+
}),
|
|
138
147
|
release,
|
|
139
148
|
};
|
|
140
149
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/providers/container.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { basename } from \"node:path\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { SandboxProvider } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { containerCommand } from \"./container-command.ts\";\nimport { containerFiles } from \"./container-files.ts\";\nimport { containerMounts } from \"./container-mounts.ts\";\nimport { containerPlan } from \"./container-plan.ts\";\nimport { containerPreflight } from \"./container-preflight.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerEngine, ContainerOptions } from \"./container.types.ts\";\n\nexport type { ContainerOptions } from \"./container.types.ts\";\n\nexport function imageName(repository: string): string {\n return `outpost:${\n basename(repository)\n .toLowerCase()\n .replace(/[^a-z0-9_.-]/g, \"-\") || \"workspace\"\n }`;\n}\n\nexport function containerProvider(\n engine: ContainerEngine,\n options: ContainerOptions = {},\n executor: Executor = executeProcess,\n platform: NodeJS.Platform = process.platform,\n): SandboxProvider {\n const config = { ...options };\n if (config.cpus !== undefined)\n invariant(\n Number.isFinite(config.cpus) && config.cpus > 0,\n \"cpus must be positive\",\n );\n if (config.memoryMb !== undefined)\n invariant(\n Number.isSafeInteger(config.memoryMb) &&\n config.memoryMb >= containerDefaults.minimumMemoryMb,\n \"memoryMb must be at least 64\",\n );\n return {\n name: engine,\n placement: \"mounted\",\n variables: { ...config.variables },\n async acquire(context) {\n context.signal?.throwIfAborted();\n const name = `outpost-${randomUUID()}`;\n const image = config.image ?? imageName(context.repository);\n const user = config.user ?? {\n uid: process.getuid?.() ?? containerDefaults.uid,\n gid: process.getgid?.() ?? containerDefaults.gid,\n };\n invariant(\n Number.isInteger(user.uid) &&\n user.uid >= 0 &&\n Number.isInteger(user.gid) &&\n user.gid >= 0,\n \"Invalid container user\",\n );\n const call = (args: readonly string[], extra: Partial<Command> = {}) =>\n requireSuccess(\n {\n executable: engine,\n arguments: args,\n deadlineMs: containerDefaults.deadlineMs,\n ...extra,\n },\n executor,\n );\n await containerPreflight(engine, platform, call, config, image, user);\n const root = containerDefaults.root,\n home = containerDefaults.home;\n const { env, volumes, fileParents } = await containerMounts(\n context,\n config,\n platform,\n root,\n home,\n );\n const args = containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n });\n let closed = false,\n releasing: Promise<void> | undefined;\n let unregister = () => {};\n const release = (): Promise<void> => {\n closed = true;\n releasing ??= call([\"rm\", \"--force\", name])\n .then(() => {\n unregister();\n })\n .catch((error) => {\n releasing = undefined;\n throw error;\n });\n return releasing;\n };\n try {\n await call(args, {\n ...(context.signal ? { signal: context.signal } : {}),\n });\n await call(\n [\"start\", name],\n context.signal ? { signal: context.signal } : {},\n );\n for (const parent of fileParents)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(parent)} && chown ${user.uid}:${user.gid} ${quote(parent)}`,\n ]);\n if (fileParents.size)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"chown\",\n `${user.uid}:${user.gid}`,\n home,\n ]);\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n \"command -v setsid >/dev/null && command -v kill >/dev/null\",\n ]);\n } catch (cause) {\n try {\n await release();\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Sandbox provisioning and cleanup failed\",\n );\n }\n throw cause;\n }\n unregister = registerCleanup(release, () => {\n if (!closed)\n execFileSync(engine, [\"rm\", \"--force\", name], {\n timeout: containerDefaults.cleanupMs,\n stdio: \"ignore\",\n windowsHide: true,\n });\n });\n return {\n root,\n home,\n invoke: containerCommand({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n ...containerFiles(call, name),\n release,\n };\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/providers/container.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { basename } from \"node:path\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { SandboxProvider } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { containerCommand } from \"./container-command.ts\";\nimport { containerFiles } from \"./container-files.ts\";\nimport { containerMounts } from \"./container-mounts.ts\";\nimport { containerPlan } from \"./container-plan.ts\";\nimport { containerPreflight } from \"./container-preflight.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerEngine, ContainerOptions } from \"./container.types.ts\";\n\nexport type { ContainerOptions } from \"./container.types.ts\";\n\nexport function imageName(repository: string): string {\n return `outpost:${\n basename(repository)\n .toLowerCase()\n .replace(/[^a-z0-9_.-]/g, \"-\") || \"workspace\"\n }`;\n}\n\nexport function containerProvider(\n engine: ContainerEngine,\n options: ContainerOptions = {},\n executor: Executor = executeProcess,\n platform: NodeJS.Platform = process.platform,\n): SandboxProvider {\n const config = { ...options };\n if (config.cpus !== undefined)\n invariant(\n Number.isFinite(config.cpus) && config.cpus > 0,\n \"cpus must be positive\",\n );\n if (config.memoryMb !== undefined)\n invariant(\n Number.isSafeInteger(config.memoryMb) &&\n config.memoryMb >= containerDefaults.minimumMemoryMb,\n \"memoryMb must be at least 64\",\n );\n return {\n name: engine,\n placement: \"mounted\",\n variables: { ...config.variables },\n async acquire(context) {\n context.signal?.throwIfAborted();\n const name = `outpost-${randomUUID()}`;\n const image = config.image ?? imageName(context.repository);\n const user = config.user ?? {\n uid: process.getuid?.() ?? containerDefaults.uid,\n gid: process.getgid?.() ?? containerDefaults.gid,\n };\n invariant(\n Number.isInteger(user.uid) &&\n user.uid >= 0 &&\n Number.isInteger(user.gid) &&\n user.gid >= 0,\n \"Invalid container user\",\n );\n const call = (args: readonly string[], extra: Partial<Command> = {}) =>\n requireSuccess(\n {\n executable: engine,\n arguments: args,\n deadlineMs: containerDefaults.deadlineMs,\n ...extra,\n },\n executor,\n );\n await containerPreflight(engine, platform, call, config, image, user);\n const root = containerDefaults.root,\n home = containerDefaults.home;\n const { env, volumes, fileParents } = await containerMounts(\n context,\n config,\n platform,\n root,\n home,\n );\n const args = containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n });\n let closed = false,\n releasing: Promise<void> | undefined;\n let unregister = () => {};\n const release = (): Promise<void> => {\n closed = true;\n releasing ??= call([\"rm\", \"--force\", name])\n .then(() => {\n unregister();\n })\n .catch((error) => {\n releasing = undefined;\n throw error;\n });\n return releasing;\n };\n try {\n await call(args, {\n ...(context.signal ? { signal: context.signal } : {}),\n });\n await call(\n [\"start\", name],\n context.signal ? { signal: context.signal } : {},\n );\n for (const parent of fileParents)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(parent)} && chown ${user.uid}:${user.gid} ${quote(parent)}`,\n ]);\n if (fileParents.size)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"chown\",\n `${user.uid}:${user.gid}`,\n home,\n ]);\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n \"command -v setsid >/dev/null && command -v kill >/dev/null && command -v tar >/dev/null && command -v cp >/dev/null\",\n ]);\n } catch (cause) {\n try {\n await release();\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Sandbox provisioning and cleanup failed\",\n );\n }\n throw cause;\n }\n unregister = registerCleanup(release, () => {\n if (!closed)\n execFileSync(engine, [\"rm\", \"--force\", name], {\n timeout: containerDefaults.cleanupMs,\n stdio: \"ignore\",\n windowsHide: true,\n });\n });\n return {\n root,\n home,\n invoke: containerCommand({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n ...containerFiles({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n release,\n };\n },\n };\n}\n"]}
|