@elie-laloum/outpost 1.1.3 → 2.0.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.
Files changed (95) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +12 -6
  3. package/dist/cli/image.js +1 -1
  4. package/dist/cli/image.js.map +1 -1
  5. package/dist/cli/init-command.js +4 -3
  6. package/dist/cli/init-command.js.map +1 -1
  7. package/dist/cli/main.constants.d.ts +4 -10
  8. package/dist/cli/main.constants.js +4 -7
  9. package/dist/cli/main.constants.js.map +1 -1
  10. package/dist/cli/project-settings.js +8 -2
  11. package/dist/cli/project-settings.js.map +1 -1
  12. package/dist/cli/scaffold-files.d.ts +1 -1
  13. package/dist/cli/scaffold-files.js +26 -20
  14. package/dist/cli/scaffold-files.js.map +1 -1
  15. package/dist/cli/scaffold-validation.js +2 -5
  16. package/dist/cli/scaffold-validation.js.map +1 -1
  17. package/dist/cli/scaffold.constants.d.ts +0 -2
  18. package/dist/cli/scaffold.constants.js +2 -13
  19. package/dist/cli/scaffold.constants.js.map +1 -1
  20. package/dist/cli/scaffold.d.ts +1 -1
  21. package/dist/cli/scaffold.js +37 -29
  22. package/dist/cli/scaffold.js.map +1 -1
  23. package/dist/cli/scaffold.types.d.ts +3 -5
  24. package/dist/cli/scaffold.types.js.map +1 -1
  25. package/dist/cli/starters.d.ts +1 -2
  26. package/dist/cli/starters.js +42 -30
  27. package/dist/cli/starters.js.map +1 -1
  28. package/dist/index.d.ts +0 -5
  29. package/dist/index.js +0 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/infrastructure/binary-process.d.ts +3 -0
  32. package/dist/infrastructure/binary-process.js +53 -0
  33. package/dist/infrastructure/binary-process.js.map +1 -0
  34. package/dist/infrastructure/binary-process.types.d.ts +5 -0
  35. package/dist/infrastructure/binary-process.types.js +2 -0
  36. package/dist/infrastructure/binary-process.types.js.map +1 -0
  37. package/dist/infrastructure/transfer-copy.d.ts +3 -0
  38. package/dist/infrastructure/transfer-copy.js +51 -0
  39. package/dist/infrastructure/transfer-copy.js.map +1 -0
  40. package/dist/providers/container-archive.d.ts +3 -0
  41. package/dist/providers/container-archive.js +27 -0
  42. package/dist/providers/container-archive.js.map +1 -0
  43. package/dist/providers/container-command.js +3 -3
  44. package/dist/providers/container-command.js.map +1 -1
  45. package/dist/providers/container-files.d.ts +2 -1
  46. package/dist/providers/container-files.js +100 -11
  47. package/dist/providers/container-files.js.map +1 -1
  48. package/dist/providers/container.js +11 -2
  49. package/dist/providers/container.js.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/adapters/backlogs/backlog.constants.d.ts +0 -4
  52. package/dist/adapters/backlogs/backlog.constants.js +0 -5
  53. package/dist/adapters/backlogs/backlog.constants.js.map +0 -1
  54. package/dist/adapters/backlogs/beads-adapter.d.ts +0 -4
  55. package/dist/adapters/backlogs/beads-adapter.js +0 -31
  56. package/dist/adapters/backlogs/beads-adapter.js.map +0 -1
  57. package/dist/adapters/backlogs/cli.d.ts +0 -3
  58. package/dist/adapters/backlogs/cli.js +0 -20
  59. package/dist/adapters/backlogs/cli.js.map +0 -1
  60. package/dist/adapters/backlogs/github-adapter.d.ts +0 -4
  61. package/dist/adapters/backlogs/github-adapter.js +0 -28
  62. package/dist/adapters/backlogs/github-adapter.js.map +0 -1
  63. package/dist/adapters/backlogs/issue.d.ts +0 -2
  64. package/dist/adapters/backlogs/issue.js +0 -13
  65. package/dist/adapters/backlogs/issue.js.map +0 -1
  66. package/dist/adapters/backlogs/settings.types.d.ts +0 -5
  67. package/dist/adapters/backlogs/settings.types.js +0 -2
  68. package/dist/adapters/backlogs/settings.types.js.map +0 -1
  69. package/dist/application/campaign-integration.d.ts +0 -2
  70. package/dist/application/campaign-integration.js +0 -44
  71. package/dist/application/campaign-integration.js.map +0 -1
  72. package/dist/application/campaign-planner.d.ts +0 -3
  73. package/dist/application/campaign-planner.js +0 -35
  74. package/dist/application/campaign-planner.js.map +0 -1
  75. package/dist/application/campaign-worker.d.ts +0 -3
  76. package/dist/application/campaign-worker.js +0 -54
  77. package/dist/application/campaign-worker.js.map +0 -1
  78. package/dist/application/campaign.constants.d.ts +0 -7
  79. package/dist/application/campaign.constants.js +0 -8
  80. package/dist/application/campaign.constants.js.map +0 -1
  81. package/dist/application/campaign.d.ts +0 -3
  82. package/dist/application/campaign.js +0 -96
  83. package/dist/application/campaign.js.map +0 -1
  84. package/dist/application/campaign.types.d.ts +0 -43
  85. package/dist/application/campaign.types.js +0 -2
  86. package/dist/application/campaign.types.js.map +0 -1
  87. package/dist/domain/backlog.d.ts +0 -3
  88. package/dist/domain/backlog.js +0 -23
  89. package/dist/domain/backlog.js.map +0 -1
  90. package/dist/domain/backlog.types.d.ts +0 -15
  91. package/dist/domain/backlog.types.js +0 -2
  92. package/dist/domain/backlog.types.js.map +0 -1
  93. package/dist/providers/backlogs.d.ts +0 -3
  94. package/dist/providers/backlogs.js +0 -3
  95. package/dist/providers/backlogs.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/cli/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { requireSuccess } from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { manageImage } from \"./image.ts\";\nimport { projectSettings } from \"./project-settings.ts\";\nimport { scaffoldFiles } from \"./scaffold-files.ts\";\nimport { validateInitialization } from \"./scaffold-validation.ts\";\nimport { providerPackages } from \"./scaffold.constants.ts\";\nimport type { InitOptions, ScaffoldResult } from \"./scaffold.types.ts\";\n\nexport { manageImage } from \"./image.ts\";\nexport { imageRecipe } from \"./scaffold.constants.ts\";\nexport type { InitOptions, Template } from \"./scaffold.types.ts\";\n\nexport async function initialize(\n options: InitOptions = {},\n executor?: Executor,\n): Promise<ScaffoldResult> {\n const root = resolve(options.directory ?? process.cwd()),\n folder = join(root, \".outpost\");\n const provider = options.provider ?? \"docker\";\n validateInitialization(options);\n const { extension, manager } = await projectSettings(root, options);\n const files = scaffoldFiles(options, extension);\n for (const name of Object.keys(files))\n if (\n await access(join(folder, name))\n .then(() => true)\n .catch(() => false)\n )\n throw new OutpostError(\n \"configuration\",\n `Initialization would overwrite .outpost/${name}`,\n );\n await mkdir(folder, { recursive: true });\n for (const [name, content] of Object.entries(files))\n await writeFile(join(folder, name), content, { flag: \"wx\" });\n if (options.install) {\n const packages = [\"@elie-laloum/outpost\", ...providerPackages[provider]];\n const args =\n manager === \"npm\"\n ? [\"install\", \"--save-dev\", ...packages]\n : [\"add\", \"--dev\", ...packages];\n await requireSuccess(\n process.platform === \"win32\"\n ? {\n executable: \"cmd.exe\",\n arguments: [\"/d\", \"/c\", [manager, ...args].join(\" \")],\n directory: root,\n }\n : { executable: manager, arguments: args, directory: root },\n executor,\n );\n }\n if (options.label) {\n invariant(\n options.tracker === \"github\",\n \"Labels require the GitHub tracker\",\n );\n await requireSuccess(\n {\n executable: \"gh\",\n arguments: [\n \"label\",\n \"create\",\n options.label,\n \"--color\",\n \"4969ED\",\n \"--description\",\n \"Ready for Outpost\",\n \"--force\",\n ],\n directory: root,\n },\n executor,\n );\n }\n if (options.build && (provider === \"docker\" || provider === \"podman\"))\n await manageImage(\n \"build\",\n {\n directory: root,\n engine: provider,\n ...(options.image ? { image: options.image } : {}),\n },\n executor,\n );\n return {\n files: Object.keys(files).map((name) => join(folder, name)),\n run: `node .outpost/run.${extension}`,\n };\n}\n"]}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/cli/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n appendFile,\n lstat,\n mkdir,\n readFile,\n writeFile,\n} from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport { requireSuccess } from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { imageName } from \"../providers/container.ts\";\nimport { manageImage } from \"./image.ts\";\nimport { projectSettings } from \"./project-settings.ts\";\nimport { scaffoldFiles } from \"./scaffold-files.ts\";\nimport { validateInitialization } from \"./scaffold-validation.ts\";\nimport { providerPackages } from \"./scaffold.constants.ts\";\nimport type { InitOptions, ScaffoldResult } from \"./scaffold.types.ts\";\n\nexport { manageImage } from \"./image.ts\";\nexport { imageRecipe } from \"./scaffold.constants.ts\";\nexport type { InitOptions } from \"./scaffold.types.ts\";\n\nexport async function initialize(\n options: InitOptions = {},\n executor?: Executor,\n): Promise<ScaffoldResult> {\n const root = resolve(options.directory ?? process.cwd());\n const provider = options.provider ?? \"docker\";\n validateInitialization(options);\n const { hasPackage, manager, extension } = await projectSettings(\n root,\n options,\n );\n const files = await scaffoldFiles(\n { ...options, image: options.image ?? imageName(root) },\n hasPackage,\n extension,\n );\n const existingIgnore = await readFile(join(root, \".gitignore\"), \"utf8\").catch(\n (error) => {\n if (error.code === \"ENOENT\") return undefined;\n throw error;\n },\n );\n const ignore = files[\".gitignore\"]!;\n if (existingIgnore !== undefined) delete files[\".gitignore\"];\n for (const name of Object.keys(files))\n if (\n await lstat(join(root, name))\n .then(() => true)\n .catch((error) => {\n if (error.code === \"ENOENT\") return false;\n throw error;\n })\n )\n throw new OutpostError(\n \"configuration\",\n `Initialization would overwrite ${name}`,\n );\n await mkdir(root, { recursive: true });\n for (const [name, content] of Object.entries(files))\n await writeFile(join(root, name), content, { flag: \"wx\" });\n if (existingIgnore !== undefined) {\n const rules = new Set(existingIgnore.split(/\\r?\\n/));\n const additions = ignore\n .trimEnd()\n .split(\"\\n\")\n .filter((rule) => !rules.has(rule));\n if (additions.length) {\n const suffix = `${existingIgnore && !existingIgnore.endsWith(\"\\n\") ? \"\\n\" : \"\"}${additions.join(\"\\n\")}\\n`;\n await appendFile(join(root, \".gitignore\"), suffix);\n files[\".gitignore\"] = existingIgnore + suffix;\n }\n }\n if (options.install) {\n const packages = [\"@elie-laloum/outpost\", ...providerPackages[provider]];\n const args =\n manager === \"npm\"\n ? [\"install\", \"--save-dev\", ...packages]\n : [\"add\", \"--dev\", ...packages];\n await requireSuccess(\n process.platform === \"win32\"\n ? {\n executable: \"cmd.exe\",\n arguments: [\"/d\", \"/c\", [manager, ...args].join(\" \")],\n directory: root,\n }\n : { executable: manager, arguments: args, directory: root },\n executor,\n );\n }\n if (options.build && (provider === \"docker\" || provider === \"podman\"))\n await manageImage(\n \"build\",\n {\n directory: root,\n engine: provider,\n ...(options.image ? { image: options.image } : {}),\n },\n executor,\n );\n return {\n files: Object.keys(files).map((name) => join(root, name)),\n run: `node run.${extension}`,\n };\n}\n"]}
@@ -1,14 +1,11 @@
1
- export type Template = "blank" | "iterate" | "review" | "plan" | "plan-review";
2
1
  export interface InitOptions {
3
2
  readonly directory?: string;
3
+ readonly repository?: string;
4
4
  readonly agent?: "claude" | "codex";
5
5
  readonly provider?: "docker" | "podman" | "vercel" | "daytona" | "local";
6
- readonly template?: Template;
7
- readonly tracker?: "github" | "beads" | "custom";
8
6
  readonly manager?: "npm" | "pnpm" | "yarn" | "bun";
9
7
  readonly model?: string;
10
8
  readonly install?: boolean;
11
- readonly label?: string;
12
9
  readonly build?: boolean;
13
10
  readonly image?: string;
14
11
  }
@@ -17,7 +14,8 @@ export interface PackageManifest {
17
14
  readonly packageManager?: string;
18
15
  }
19
16
  export interface ProjectSettings {
20
- readonly extension: string;
17
+ readonly extension: "ts" | "mts";
18
+ readonly hasPackage: boolean;
21
19
  readonly manager: string;
22
20
  }
23
21
  export interface ScaffoldResult {
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.types.js","sourceRoot":"","sources":["../../src/cli/scaffold.types.ts"],"names":[],"mappings":"","sourcesContent":["export type Template = \"blank\" | \"iterate\" | \"review\" | \"plan\" | \"plan-review\";\n\nexport interface InitOptions {\n readonly directory?: string;\n readonly agent?: \"claude\" | \"codex\";\n readonly provider?: \"docker\" | \"podman\" | \"vercel\" | \"daytona\" | \"local\";\n readonly template?: Template;\n readonly tracker?: \"github\" | \"beads\" | \"custom\";\n readonly manager?: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n readonly model?: string;\n readonly install?: boolean;\n readonly label?: string;\n readonly build?: boolean;\n readonly image?: string;\n}\n\nexport interface PackageManifest {\n readonly type?: string;\n readonly packageManager?: string;\n}\nexport interface ProjectSettings {\n readonly extension: string;\n readonly manager: string;\n}\nexport interface ScaffoldResult {\n files: readonly string[];\n run: string;\n}\n"]}
1
+ {"version":3,"file":"scaffold.types.js","sourceRoot":"","sources":["../../src/cli/scaffold.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface InitOptions {\n readonly directory?: string;\n readonly repository?: string;\n readonly agent?: \"claude\" | \"codex\";\n readonly provider?: \"docker\" | \"podman\" | \"vercel\" | \"daytona\" | \"local\";\n readonly manager?: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\n readonly model?: string;\n readonly install?: boolean;\n readonly build?: boolean;\n readonly image?: string;\n}\n\nexport interface PackageManifest {\n readonly type?: string;\n readonly packageManager?: string;\n}\nexport interface ProjectSettings {\n readonly extension: \"ts\" | \"mts\";\n readonly hasPackage: boolean;\n readonly manager: string;\n}\nexport interface ScaffoldResult {\n files: readonly string[];\n run: string;\n}\n"]}
@@ -1,3 +1,2 @@
1
1
  import type { InitOptions } from "./scaffold.types.ts";
2
- export declare function starter(options: InitOptions, extension: string): string;
3
- export declare function tracker(kind: NonNullable<InitOptions["tracker"]>, label?: string): string;
2
+ export declare function starter(options: InitOptions): string;
@@ -1,34 +1,46 @@
1
- export function starter(options, extension) {
2
- const agent = options.agent ?? "codex", provider = options.provider ?? "docker", template = options.template ?? "blank";
3
- const planning = template === "plan" || template === "plan-review";
4
- const reviewing = template === "review" || template === "plan-review";
5
- const imports = `import { campaign, dispatch, ${agent} } from "@elie-laloum/outpost";\nimport type { Backlog } from "@elie-laloum/outpost";\nimport { ${provider} } from "@elie-laloum/outpost/providers/${provider}";\nimport { readFile } from "node:fs/promises";\n${options.tracker ? `import { backlog } from "./tickets.${extension}";\n` : ""}`;
6
- const config = `const runtime = { agent: ${agent}(${options.model ? `{ model: ${JSON.stringify(options.model)} }` : ""}), provider: ${provider}(${options.image && (provider === "docker" || provider === "podman") ? `{ image: ${JSON.stringify(options.image)} }` : ""}) };\nconst objective = process.argv.slice(2).join(" ") || "Inspect this repository and implement one useful improvement.";\n`;
7
- if (template === "blank")
8
- return `${imports}\n${config}\nconst result = await dispatch({ ...runtime, branch: { mode: "integrate" }, brief: { file: ".outpost/brief.md", values: { OBJECTIVE: objective } } });\nconsole.log({ branch: result.branch, commits: result.commits, conversation: result.conversation });\n`;
9
- const backlog = options.tracker
10
- ? ""
11
- : `let pending = true;\nconst requested = { id: "objective", title: objective };\nconst backlog: Backlog = {\n async list() { return pending ? [requested] : []; },\n async get() { return requested; },\n async close() { pending = false; },\n};\n`;
12
- return `${imports}\n${config}\n${backlog}\nconst result = await campaign({\n ...runtime, backlog,\n cycles: 10, concurrency: ${planning ? 3 : 1},\n implementationPasses: ${planning ? 100 : 1}, reviewPasses: 1,\n planner: ${planning ? "runtime.agent" : "false"}, reviewer: ${reviewing ? "runtime.agent" : "false"},\n copies: ["node_modules"],\n standards: await readFile(new URL("./STANDARDS.md", import.meta.url), "utf8"),\n observe: event => console.log(\`[cycle \${event.cycle}] \${event.phase}\${event.issue ? " " + event.issue : ""}\`),\n});\nconsole.log(result);\nif (result.issues.some(issue => issue.state === "failed")) process.exitCode = 1;\n`;
13
- }
14
- export function tracker(kind, label) {
15
- if (kind !== "custom") {
16
- const factory = kind === "github" ? "githubBacklog" : "beadsBacklog";
17
- return `import { ${factory} } from "@elie-laloum/outpost";\nexport const backlog = ${factory}(${label ? `{ label: ${JSON.stringify(label)} }` : ""});\n`;
18
- }
19
- return `import type { Backlog, Issue } from "@elie-laloum/outpost";
20
- async function request(path: string, method = "GET", signal?: AbortSignal) {
21
- const base = process.env.OUTPOST_TRACKER_URL;
22
- if (!base) throw new Error("Set OUTPOST_TRACKER_URL");
23
- const reply = await fetch(new URL(path, base.endsWith("/") ? base : base + "/"), { method, signal });
24
- if (!reply.ok) throw new Error(\`Tracker returned \${reply.status}\`);
25
- return reply;
26
- }
27
- export const backlog: Backlog = {
28
- async list(signal) { return await (await request("issues?state=open", "GET", signal)).json() as Issue[]; },
29
- async get(id, signal) { return await (await request("issues/" + encodeURIComponent(id), "GET", signal)).json() as Issue; },
30
- async close(id, signal) { await request("issues/" + encodeURIComponent(id) + "/close", "POST", signal); },
1
+ export function starter(options) {
2
+ const agent = options.agent ?? "codex", provider = options.provider ?? "docker";
3
+ const model = options.model ? `model: ${JSON.stringify(options.model)}` : "";
4
+ const image = options.image && (provider === "docker" || provider === "podman")
5
+ ? `image: ${JSON.stringify(options.image)}, `
6
+ : "";
7
+ return `import { readFile } from "node:fs/promises";
8
+ import { resolve } from "node:path";
9
+ import { parseEnv } from "node:util";
10
+ import { dispatch, ${agent}, OutpostError, reporter } from "@elie-laloum/outpost";
11
+ import { ${provider} } from "@elie-laloum/outpost/providers/${provider}";
12
+
13
+ // Paths are relative to this workflow, regardless of where Node is launched.
14
+ const repository = resolve(import.meta.dirname, ${JSON.stringify(options.repository ?? ".")});
15
+ const environment = await readFile(new URL(".env", import.meta.url), "utf8").catch((error) => {
16
+ if (error.code === "ENOENT") return "";
17
+ throw error;
18
+ });
19
+ const variables = Object.fromEntries(
20
+ Object.entries(parseEnv(environment)).map(([key, value]) => [key, value || process.env[key] || ""]),
21
+ );
22
+ const runtime = {
23
+ agent: ${agent}({ ${model} }),
24
+ provider: ${provider}({ ${image}variables }),
31
25
  };
26
+ const objective = process.argv.slice(2).join(" ") || "Inspect this repository and implement one useful improvement.";
27
+
28
+ console.error("[outpost] Preparing sandbox...");
29
+ try {
30
+ const result = await dispatch({
31
+ ...runtime,
32
+ repository,
33
+ branch: { mode: "integrate" },
34
+ brief: { file: resolve(import.meta.dirname, "brief.md"), values: { OBJECTIVE: objective } },
35
+ observe: reporter(),
36
+ warn: (message) => console.error(message),
37
+ });
38
+ console.log({ branch: result.branch, commits: result.commits, conversation: result.conversation });
39
+ } catch (error) {
40
+ if (!(error instanceof OutpostError) || error.code !== "aborted") throw error;
41
+ process.exitCode ||= 130;
42
+ console.error("[outpost] Cancelled. Recovery details:", error.recovery);
43
+ }
32
44
  `;
33
45
  }
34
46
  //# sourceMappingURL=starters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"starters.js","sourceRoot":"","sources":["../../src/cli/starters.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CAAC,OAAoB,EAAE,SAAiB;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,EACpC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,EACvC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC;IACzC,MAAM,QAAQ,GAAG,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,aAAa,CAAC;IACnE,MAAM,SAAS,GAAG,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,aAAa,CAAC;IACtE,MAAM,OAAO,GAAG,gCAAgC,KAAK,mGAAmG,QAAQ,2CAA2C,QAAQ,qDAAqD,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,sCAAsC,SAAS,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACvV,MAAM,MAAM,GAAG,4BAA4B,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,gBAAgB,QAAQ,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,+HAA+H,CAAC;IACxY,IAAI,QAAQ,KAAK,OAAO;QACtB,OAAO,GAAG,OAAO,KAAK,MAAM,gQAAgQ,CAAC;IAC/R,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;QAC7B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,sPAAsP,CAAC;IAC3P,OAAO,GAAG,OAAO,KAAK,MAAM,KAAK,OAAO,yFAAyF,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,8BAA8B,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,kCAAkC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,eAAe,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,wVAAwV,CAAC;AACtpB,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,IAAyC,EACzC,KAAc;IAEd,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC;QACrE,OAAO,YAAY,OAAO,2DAA2D,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3J,CAAC;IACD,OAAO;;;;;;;;;;;;;CAaR,CAAC;AACF,CAAC","sourcesContent":["import type { InitOptions } from \"./scaffold.types.ts\";\n\nexport function starter(options: InitOptions, extension: string): string {\n const agent = options.agent ?? \"codex\",\n provider = options.provider ?? \"docker\",\n template = options.template ?? \"blank\";\n const planning = template === \"plan\" || template === \"plan-review\";\n const reviewing = template === \"review\" || template === \"plan-review\";\n const imports = `import { campaign, dispatch, ${agent} } from \"@elie-laloum/outpost\";\\nimport type { Backlog } from \"@elie-laloum/outpost\";\\nimport { ${provider} } from \"@elie-laloum/outpost/providers/${provider}\";\\nimport { readFile } from \"node:fs/promises\";\\n${options.tracker ? `import { backlog } from \"./tickets.${extension}\";\\n` : \"\"}`;\n const config = `const runtime = { agent: ${agent}(${options.model ? `{ model: ${JSON.stringify(options.model)} }` : \"\"}), provider: ${provider}(${options.image && (provider === \"docker\" || provider === \"podman\") ? `{ image: ${JSON.stringify(options.image)} }` : \"\"}) };\\nconst objective = process.argv.slice(2).join(\" \") || \"Inspect this repository and implement one useful improvement.\";\\n`;\n if (template === \"blank\")\n return `${imports}\\n${config}\\nconst result = await dispatch({ ...runtime, branch: { mode: \"integrate\" }, brief: { file: \".outpost/brief.md\", values: { OBJECTIVE: objective } } });\\nconsole.log({ branch: result.branch, commits: result.commits, conversation: result.conversation });\\n`;\n const backlog = options.tracker\n ? \"\"\n : `let pending = true;\\nconst requested = { id: \"objective\", title: objective };\\nconst backlog: Backlog = {\\n async list() { return pending ? [requested] : []; },\\n async get() { return requested; },\\n async close() { pending = false; },\\n};\\n`;\n return `${imports}\\n${config}\\n${backlog}\\nconst result = await campaign({\\n ...runtime, backlog,\\n cycles: 10, concurrency: ${planning ? 3 : 1},\\n implementationPasses: ${planning ? 100 : 1}, reviewPasses: 1,\\n planner: ${planning ? \"runtime.agent\" : \"false\"}, reviewer: ${reviewing ? \"runtime.agent\" : \"false\"},\\n copies: [\"node_modules\"],\\n standards: await readFile(new URL(\"./STANDARDS.md\", import.meta.url), \"utf8\"),\\n observe: event => console.log(\\`[cycle \\${event.cycle}] \\${event.phase}\\${event.issue ? \" \" + event.issue : \"\"}\\`),\\n});\\nconsole.log(result);\\nif (result.issues.some(issue => issue.state === \"failed\")) process.exitCode = 1;\\n`;\n}\n\nexport function tracker(\n kind: NonNullable<InitOptions[\"tracker\"]>,\n label?: string,\n): string {\n if (kind !== \"custom\") {\n const factory = kind === \"github\" ? \"githubBacklog\" : \"beadsBacklog\";\n return `import { ${factory} } from \"@elie-laloum/outpost\";\\nexport const backlog = ${factory}(${label ? `{ label: ${JSON.stringify(label)} }` : \"\"});\\n`;\n }\n return `import type { Backlog, Issue } from \"@elie-laloum/outpost\";\nasync function request(path: string, method = \"GET\", signal?: AbortSignal) {\n const base = process.env.OUTPOST_TRACKER_URL;\n if (!base) throw new Error(\"Set OUTPOST_TRACKER_URL\");\n const reply = await fetch(new URL(path, base.endsWith(\"/\") ? base : base + \"/\"), { method, signal });\n if (!reply.ok) throw new Error(\\`Tracker returned \\${reply.status}\\`);\n return reply;\n}\nexport const backlog: Backlog = {\n async list(signal) { return await (await request(\"issues?state=open\", \"GET\", signal)).json() as Issue[]; },\n async get(id, signal) { return await (await request(\"issues/\" + encodeURIComponent(id), \"GET\", signal)).json() as Issue; },\n async close(id, signal) { await request(\"issues/\" + encodeURIComponent(id) + \"/close\", \"POST\", signal); },\n};\n`;\n}\n"]}
1
+ {"version":3,"file":"starters.js","sourceRoot":"","sources":["../../src/cli/starters.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CAAC,OAAoB;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,EACpC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7E,MAAM,KAAK,GACT,OAAO,CAAC,KAAK,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,CAAC;QAC/D,CAAC,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI;QAC7C,CAAC,CAAC,EAAE,CAAC;IACT,OAAO;;;qBAGY,KAAK;WACf,QAAQ,2CAA2C,QAAQ;;;kDAGpB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC;;;;;;;;;WAShF,KAAK,MAAM,KAAK;cACb,QAAQ,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;CAoBhC,CAAC;AACF,CAAC","sourcesContent":["import type { InitOptions } from \"./scaffold.types.ts\";\n\nexport function starter(options: InitOptions): string {\n const agent = options.agent ?? \"codex\",\n provider = options.provider ?? \"docker\";\n const model = options.model ? `model: ${JSON.stringify(options.model)}` : \"\";\n const image =\n options.image && (provider === \"docker\" || provider === \"podman\")\n ? `image: ${JSON.stringify(options.image)}, `\n : \"\";\n return `import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseEnv } from \"node:util\";\nimport { dispatch, ${agent}, OutpostError, reporter } from \"@elie-laloum/outpost\";\nimport { ${provider} } from \"@elie-laloum/outpost/providers/${provider}\";\n\n// Paths are relative to this workflow, regardless of where Node is launched.\nconst repository = resolve(import.meta.dirname, ${JSON.stringify(options.repository ?? \".\")});\nconst environment = await readFile(new URL(\".env\", import.meta.url), \"utf8\").catch((error) => {\n if (error.code === \"ENOENT\") return \"\";\n throw error;\n});\nconst variables = Object.fromEntries(\n Object.entries(parseEnv(environment)).map(([key, value]) => [key, value || process.env[key] || \"\"]),\n);\nconst runtime = {\n agent: ${agent}({ ${model} }),\n provider: ${provider}({ ${image}variables }),\n};\nconst objective = process.argv.slice(2).join(\" \") || \"Inspect this repository and implement one useful improvement.\";\n\nconsole.error(\"[outpost] Preparing sandbox...\");\ntry {\n const result = await dispatch({\n ...runtime,\n repository,\n branch: { mode: \"integrate\" },\n brief: { file: resolve(import.meta.dirname, \"brief.md\"), values: { OBJECTIVE: objective } },\n observe: reporter(),\n warn: (message) => console.error(message),\n });\n console.log({ branch: result.branch, commits: result.commits, conversation: result.conversation });\n} catch (error) {\n if (!(error instanceof OutpostError) || error.code !== \"aborted\") throw error;\n process.exitCode ||= 130;\n console.error(\"[outpost] Cancelled. Recovery details:\", error.recovery);\n}\n`;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -4,11 +4,6 @@ export type { DispatchOptions, Execution, Turn, } from "./application/execution.
4
4
  export { WorkflowFailure, task, workflow } from "./domain/workflow.ts";
5
5
  export type { Retry, Task, TaskContext, TaskOptions, TaskRecord, TaskStatus, Workflow, WorkflowEvent, WorkflowOptions, WorkflowResult, } from "./domain/workflow.ts";
6
6
  export { agentTask, commandTask, isolatedTask } from "./application/tasks.ts";
7
- export { campaign } from "./application/campaign.ts";
8
- export type { CampaignEvent, CampaignOptions, CampaignResult, IssueOutcome, } from "./application/campaign.ts";
9
- export type { Assignment, Backlog, Issue } from "./domain/backlog.ts";
10
- export { beadsBacklog, githubBacklog } from "./providers/backlogs.ts";
11
- export type { BacklogSettings } from "./providers/backlogs.ts";
12
7
  export { claude, codex } from "./providers/agents.ts";
13
8
  export { agentVersions } from "./providers/versions.ts";
14
9
  export { conversations } from "./infrastructure/conversations.ts";
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  export { attach, createSandbox, dispatch, openWorkspace, } from "./application/outpost.js";
2
2
  export { WorkflowFailure, task, workflow } from "./domain/workflow.js";
3
3
  export { agentTask, commandTask, isolatedTask } from "./application/tasks.js";
4
- export { campaign } from "./application/campaign.js";
5
- export { beadsBacklog, githubBacklog } from "./providers/backlogs.js";
6
4
  export { claude, codex } from "./providers/agents.js";
7
5
  export { agentVersions } from "./providers/versions.js";
8
6
  export { conversations } from "./infrastructure/conversations.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,aAAa,EACb,QAAQ,EACR,aAAa,GACd,MAAM,0BAA0B,CAAC;AAoBlC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAevE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAWrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAItE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AASlE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAkCnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC","sourcesContent":["export {\n attach,\n createSandbox,\n dispatch,\n openWorkspace,\n} from \"./application/outpost.ts\";\n\nexport type {\n AttachOptions,\n AttachResult,\n ContinuationOptions,\n DispatchResult,\n Sandbox,\n SandboxOptions,\n WarmDispatchResult,\n Workspace,\n WorkspaceOptions,\n} from \"./application/outpost.ts\";\n\nexport type {\n DispatchOptions,\n Execution,\n Turn,\n} from \"./application/execution.ts\";\n\nexport { WorkflowFailure, task, workflow } from \"./domain/workflow.ts\";\n\nexport type {\n Retry,\n Task,\n TaskContext,\n TaskOptions,\n TaskRecord,\n TaskStatus,\n Workflow,\n WorkflowEvent,\n WorkflowOptions,\n WorkflowResult,\n} from \"./domain/workflow.ts\";\n\nexport { agentTask, commandTask, isolatedTask } from \"./application/tasks.ts\";\n\nexport { campaign } from \"./application/campaign.ts\";\n\nexport type {\n CampaignEvent,\n CampaignOptions,\n CampaignResult,\n IssueOutcome,\n} from \"./application/campaign.ts\";\n\nexport type { Assignment, Backlog, Issue } from \"./domain/backlog.ts\";\n\nexport { beadsBacklog, githubBacklog } from \"./providers/backlogs.ts\";\n\nexport type { BacklogSettings } from \"./providers/backlogs.ts\";\n\nexport { claude, codex } from \"./providers/agents.ts\";\n\nexport { agentVersions } from \"./providers/versions.ts\";\n\nexport { conversations } from \"./infrastructure/conversations.ts\";\n\nexport type {\n ConversationFormat,\n ConversationLocation,\n} from \"./infrastructure/conversations.ts\";\n\nexport type { ClaudeSettings, CodexSettings } from \"./providers/agents.ts\";\n\nexport { mountedProvider, remoteProvider } from \"./providers/factories.ts\";\n\nexport { ResponseError, response } from \"./domain/response.ts\";\n\nexport type { ResponseSpec, StandardValidator } from \"./domain/response.ts\";\n\nexport { OutpostError, recoveryDetails } from \"./domain/errors.ts\";\n\nexport type { FaultCode } from \"./domain/errors.ts\";\n\nexport type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentObservation,\n BranchPolicy,\n Channel,\n Command,\n CommandResult,\n Commit,\n ConversationContext,\n ConversationRecord,\n ConversationStore,\n Disposal,\n LifecycleHooks,\n SandboxContext,\n SandboxLease,\n SandboxProvider,\n StageLimits,\n TransferOptions,\n Usage,\n Variables,\n Volume,\n WorkspaceRecord,\n} from \"./domain/ports.ts\";\n\nexport type { Brief, PromptVariables } from \"./domain/prompts.ts\";\n\nexport type { Logging } from \"./infrastructure/journal.ts\";\n\nexport { reporter } from \"./infrastructure/reporter.ts\";\n\nexport type { ReporterOptions } from \"./infrastructure/reporter.ts\";\n\nexport type { VariableQuestion } from \"./application/interactive-brief.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,aAAa,EACb,QAAQ,EACR,aAAa,GACd,MAAM,0BAA0B,CAAC;AAoBlC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAevE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE9E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AASlE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAkCnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC","sourcesContent":["export {\n attach,\n createSandbox,\n dispatch,\n openWorkspace,\n} from \"./application/outpost.ts\";\n\nexport type {\n AttachOptions,\n AttachResult,\n ContinuationOptions,\n DispatchResult,\n Sandbox,\n SandboxOptions,\n WarmDispatchResult,\n Workspace,\n WorkspaceOptions,\n} from \"./application/outpost.ts\";\n\nexport type {\n DispatchOptions,\n Execution,\n Turn,\n} from \"./application/execution.ts\";\n\nexport { WorkflowFailure, task, workflow } from \"./domain/workflow.ts\";\n\nexport type {\n Retry,\n Task,\n TaskContext,\n TaskOptions,\n TaskRecord,\n TaskStatus,\n Workflow,\n WorkflowEvent,\n WorkflowOptions,\n WorkflowResult,\n} from \"./domain/workflow.ts\";\n\nexport { agentTask, commandTask, isolatedTask } from \"./application/tasks.ts\";\n\nexport { claude, codex } from \"./providers/agents.ts\";\n\nexport { agentVersions } from \"./providers/versions.ts\";\n\nexport { conversations } from \"./infrastructure/conversations.ts\";\n\nexport type {\n ConversationFormat,\n ConversationLocation,\n} from \"./infrastructure/conversations.ts\";\n\nexport type { ClaudeSettings, CodexSettings } from \"./providers/agents.ts\";\n\nexport { mountedProvider, remoteProvider } from \"./providers/factories.ts\";\n\nexport { ResponseError, response } from \"./domain/response.ts\";\n\nexport type { ResponseSpec, StandardValidator } from \"./domain/response.ts\";\n\nexport { OutpostError, recoveryDetails } from \"./domain/errors.ts\";\n\nexport type { FaultCode } from \"./domain/errors.ts\";\n\nexport type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n AgentObservation,\n BranchPolicy,\n Channel,\n Command,\n CommandResult,\n Commit,\n ConversationContext,\n ConversationRecord,\n ConversationStore,\n Disposal,\n LifecycleHooks,\n SandboxContext,\n SandboxLease,\n SandboxProvider,\n StageLimits,\n TransferOptions,\n Usage,\n Variables,\n Volume,\n WorkspaceRecord,\n} from \"./domain/ports.ts\";\n\nexport type { Brief, PromptVariables } from \"./domain/prompts.ts\";\n\nexport type { Logging } from \"./infrastructure/journal.ts\";\n\nexport { reporter } from \"./infrastructure/reporter.ts\";\n\nexport type { ReporterOptions } from \"./infrastructure/reporter.ts\";\n\nexport type { VariableQuestion } from \"./application/interactive-brief.ts\";\n"]}
@@ -0,0 +1,3 @@
1
+ import type { BinaryStreams } from "./binary-process.types.ts";
2
+ import type { Executor } from "./process.types.ts";
3
+ export declare function binaryExecutor(streams: BinaryStreams): Executor;
@@ -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":"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","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,5 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ export interface BinaryStreams {
3
+ readonly input?: Readable;
4
+ readonly output?: Writable;
5
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=binary-process.types.js.map
@@ -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,3 @@
1
+ export declare function transferDestination(destination: string): Promise<void>;
2
+ export declare function copyTransfer(source: string, destination: string, signal: AbortSignal): Promise<void>;
3
+ export declare function removeTransferDirectory(root: string): Promise<void>;
@@ -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
- "setsid",
41
- ...(command.interactive ? ["--wait"] : []),
42
- ...(command.interactive && process.stdin.isTTY ? ["--ctty"] : []),
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":"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","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 \"setsid\",\n ...(command.interactive ? [\"--wait\"] : []),\n ...(command.interactive && process.stdin.isTTY ? [\"--ctty\"] : []),\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
+ {"version":3,"file":"container-command.js","sourceRoot":"","sources":["../../src/providers/container-command.ts"],"names":[],"mappings":"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","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(call: ContainerRuntime["call"], name: string): Pick<SandboxLease, "upload" | "download">;
4
+ export declare function containerFiles(runtime: ContainerRuntime, archive?: typeof containerArchive): Pick<SandboxLease, "upload" | "download">;