@elie-laloum/outpost 1.1.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.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.fr.md +251 -0
- package/README.md +226 -0
- package/ROADMAP.md +34 -0
- package/SECURITY.md +15 -0
- package/dist/application/campaign.d.ts +33 -0
- package/dist/application/campaign.js +195 -0
- package/dist/application/campaign.js.map +1 -0
- package/dist/application/execution.d.ts +48 -0
- package/dist/application/execution.js +322 -0
- package/dist/application/execution.js.map +1 -0
- package/dist/application/interactive-brief.d.ts +3 -0
- package/dist/application/interactive-brief.js +35 -0
- package/dist/application/interactive-brief.js.map +1 -0
- package/dist/application/outpost.d.ts +91 -0
- package/dist/application/outpost.js +629 -0
- package/dist/application/outpost.js.map +1 -0
- package/dist/application/remote-workspace.d.ts +11 -0
- package/dist/application/remote-workspace.js +264 -0
- package/dist/application/remote-workspace.js.map +1 -0
- package/dist/application/tasks.d.ts +17 -0
- package/dist/application/tasks.js +36 -0
- package/dist/application/tasks.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +94 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/scaffold.d.ts +28 -0
- package/dist/cli/scaffold.js +169 -0
- package/dist/cli/scaffold.js.map +1 -0
- package/dist/cli/starters.d.ts +3 -0
- package/dist/cli/starters.js +34 -0
- package/dist/cli/starters.js.map +1 -0
- package/dist/domain/backlog.d.ts +16 -0
- package/dist/domain/backlog.js +23 -0
- package/dist/domain/backlog.js.map +1 -0
- package/dist/domain/errors.d.ts +11 -0
- package/dist/domain/errors.js +37 -0
- package/dist/domain/errors.js.map +1 -0
- package/dist/domain/ports.d.ts +183 -0
- package/dist/domain/ports.js +2 -0
- package/dist/domain/ports.js.map +1 -0
- package/dist/domain/prompts.d.ts +22 -0
- package/dist/domain/prompts.js +45 -0
- package/dist/domain/prompts.js.map +1 -0
- package/dist/domain/response.d.ts +37 -0
- package/dist/domain/response.js +50 -0
- package/dist/domain/response.js.map +1 -0
- package/dist/domain/workflow.d.ts +68 -0
- package/dist/domain/workflow.js +246 -0
- package/dist/domain/workflow.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/abort.d.ts +1 -0
- package/dist/infrastructure/abort.js +16 -0
- package/dist/infrastructure/abort.js.map +1 -0
- package/dist/infrastructure/conversations.d.ts +30 -0
- package/dist/infrastructure/conversations.js +222 -0
- package/dist/infrastructure/conversations.js.map +1 -0
- package/dist/infrastructure/files.d.ts +5 -0
- package/dist/infrastructure/files.js +85 -0
- package/dist/infrastructure/files.js.map +1 -0
- package/dist/infrastructure/git.d.ts +14 -0
- package/dist/infrastructure/git.js +304 -0
- package/dist/infrastructure/git.js.map +1 -0
- package/dist/infrastructure/journal.d.ts +10 -0
- package/dist/infrastructure/journal.js +53 -0
- package/dist/infrastructure/journal.js.map +1 -0
- package/dist/infrastructure/process.d.ts +6 -0
- package/dist/infrastructure/process.js +133 -0
- package/dist/infrastructure/process.js.map +1 -0
- package/dist/infrastructure/reporter.d.ts +10 -0
- package/dist/infrastructure/reporter.js +47 -0
- package/dist/infrastructure/reporter.js.map +1 -0
- package/dist/infrastructure/settings.d.ts +3 -0
- package/dist/infrastructure/settings.js +41 -0
- package/dist/infrastructure/settings.js.map +1 -0
- package/dist/infrastructure/shutdown.d.ts +1 -0
- package/dist/infrastructure/shutdown.js +67 -0
- package/dist/infrastructure/shutdown.js.map +1 -0
- package/dist/infrastructure/terminal.d.ts +11 -0
- package/dist/infrastructure/terminal.js +19 -0
- package/dist/infrastructure/terminal.js.map +1 -0
- package/dist/infrastructure/transfer.d.ts +3 -0
- package/dist/infrastructure/transfer.js +29 -0
- package/dist/infrastructure/transfer.js.map +1 -0
- package/dist/providers/agents.d.ts +17 -0
- package/dist/providers/agents.js +204 -0
- package/dist/providers/agents.js.map +1 -0
- package/dist/providers/backlogs.d.ts +9 -0
- package/dist/providers/backlogs.js +82 -0
- package/dist/providers/backlogs.js.map +1 -0
- package/dist/providers/cloud-files.d.ts +4 -0
- package/dist/providers/cloud-files.js +66 -0
- package/dist/providers/cloud-files.js.map +1 -0
- package/dist/providers/container.d.ts +21 -0
- package/dist/providers/container.js +310 -0
- package/dist/providers/container.js.map +1 -0
- package/dist/providers/daytona.d.ts +10 -0
- package/dist/providers/daytona.js +153 -0
- package/dist/providers/daytona.js.map +1 -0
- package/dist/providers/docker.d.ts +3 -0
- package/dist/providers/docker.js +3 -0
- package/dist/providers/docker.js.map +1 -0
- package/dist/providers/factories.d.ts +9 -0
- package/dist/providers/factories.js +12 -0
- package/dist/providers/factories.js.map +1 -0
- package/dist/providers/local.d.ts +4 -0
- package/dist/providers/local.js +64 -0
- package/dist/providers/local.js.map +1 -0
- package/dist/providers/podman.d.ts +3 -0
- package/dist/providers/podman.js +3 -0
- package/dist/providers/podman.js.map +1 -0
- package/dist/providers/vercel.d.ts +9 -0
- package/dist/providers/vercel.js +158 -0
- package/dist/providers/vercel.js.map +1 -0
- package/dist/providers/versions.d.ts +4 -0
- package/dist/providers/versions.js +5 -0
- package/dist/providers/versions.js.map +1 -0
- package/docs/api.md +147 -0
- package/docs/architecture.md +21 -0
- package/docs/migration-1.1.fr.md +44 -0
- package/docs/migration-1.1.md +38 -0
- package/docs/operations.md +101 -0
- package/docs/providers.md +119 -0
- package/docs/workflows.md +101 -0
- package/package.json +95 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function reporter(options = {}) {
|
|
2
|
+
const write = options.write ?? ((text) => process.stdout.write(text));
|
|
3
|
+
let streamed = false;
|
|
4
|
+
return (event) => {
|
|
5
|
+
if (options.quiet)
|
|
6
|
+
return;
|
|
7
|
+
const prefix = `[${options.label ?? "outpost"}${event.pass ? ` · pass ${event.pass}` : ""}]`;
|
|
8
|
+
switch (event.kind) {
|
|
9
|
+
case "phase":
|
|
10
|
+
if (event.name === "running")
|
|
11
|
+
streamed = false;
|
|
12
|
+
write(`${prefix} ${event.name}${event.agent ? ` · ${event.agent}` : ""}${event.branch ? ` · ${event.branch}` : ""}${options.verbose && event.directory ? ` · ${event.directory}` : ""}\n`);
|
|
13
|
+
break;
|
|
14
|
+
case "text":
|
|
15
|
+
streamed = true;
|
|
16
|
+
write(event.text);
|
|
17
|
+
break;
|
|
18
|
+
case "result":
|
|
19
|
+
if (!streamed || options.verbose)
|
|
20
|
+
write(`\n${prefix} result: ${event.text}\n`);
|
|
21
|
+
break;
|
|
22
|
+
case "tool":
|
|
23
|
+
write(`\n${prefix} tool: ${event.name}${options.verbose ? ` ${JSON.stringify(event.input)}` : ""}\n`);
|
|
24
|
+
break;
|
|
25
|
+
case "summary":
|
|
26
|
+
write(`\n${prefix} finished · ${(event.durationMs / 1000).toFixed(2)}s · status ${event.status} · input ${event.tokens.input} · cache read ${event.tokens.cached} · cache write ${event.tokens.cacheCreated ?? 0} · output ${event.tokens.output}\n`);
|
|
27
|
+
break;
|
|
28
|
+
case "failure":
|
|
29
|
+
case "warning":
|
|
30
|
+
write(`\n${prefix} ${event.kind}: ${event.message}\n`);
|
|
31
|
+
break;
|
|
32
|
+
case "conversation":
|
|
33
|
+
if (options.verbose)
|
|
34
|
+
write(`\n${prefix} conversation: ${event.id}\n`);
|
|
35
|
+
break;
|
|
36
|
+
case "prompt":
|
|
37
|
+
if (options.verbose)
|
|
38
|
+
write(`${prefix} prompt:\n${event.text}\n`);
|
|
39
|
+
break;
|
|
40
|
+
case "raw":
|
|
41
|
+
if (options.verbose)
|
|
42
|
+
write(`${prefix} raw: ${typeof event.value === "string" ? event.value : JSON.stringify(event.value)}\n`);
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/infrastructure/reporter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentEvent } from \"../domain/ports.ts\";\n\nexport interface ReporterOptions {\n readonly label?: string;\n readonly verbose?: boolean;\n readonly quiet?: boolean;\n readonly write?: (text: string) => void;\n}\n\nexport function reporter(\n options: ReporterOptions = {},\n): (event: AgentEvent & { readonly pass?: number }) => void {\n const write = options.write ?? ((text) => process.stdout.write(text));\n let streamed = false;\n return (event) => {\n if (options.quiet) return;\n const prefix = `[${options.label ?? \"outpost\"}${event.pass ? ` · pass ${event.pass}` : \"\"}]`;\n switch (event.kind) {\n case \"phase\":\n if (event.name === \"running\") streamed = false;\n write(\n `${prefix} ${event.name}${event.agent ? ` · ${event.agent}` : \"\"}${event.branch ? ` · ${event.branch}` : \"\"}${options.verbose && event.directory ? ` · ${event.directory}` : \"\"}\\n`,\n );\n break;\n case \"text\":\n streamed = true;\n write(event.text);\n break;\n case \"result\":\n if (!streamed || options.verbose)\n write(`\\n${prefix} result: ${event.text}\\n`);\n break;\n case \"tool\":\n write(\n `\\n${prefix} tool: ${event.name}${options.verbose ? ` ${JSON.stringify(event.input)}` : \"\"}\\n`,\n );\n break;\n case \"summary\":\n write(\n `\\n${prefix} finished · ${(event.durationMs / 1000).toFixed(2)}s · status ${event.status} · input ${event.tokens.input} · cache read ${event.tokens.cached} · cache write ${event.tokens.cacheCreated ?? 0} · output ${event.tokens.output}\\n`,\n );\n break;\n case \"failure\":\n case \"warning\":\n write(`\\n${prefix} ${event.kind}: ${event.message}\\n`);\n break;\n case \"conversation\":\n if (options.verbose) write(`\\n${prefix} conversation: ${event.id}\\n`);\n break;\n case \"prompt\":\n if (options.verbose) write(`${prefix} prompt:\\n${event.text}\\n`);\n break;\n case \"raw\":\n if (options.verbose)\n write(\n `${prefix} raw: ${typeof event.value === \"string\" ? event.value : JSON.stringify(event.value)}\\n`,\n );\n break;\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Variables } from "../domain/ports.ts";
|
|
2
|
+
export declare function parseEnvironment(text: string): Record<string, string>;
|
|
3
|
+
export declare function resolveVariables(repository: string, agent?: Variables, provider?: Variables, environment?: NodeJS.ProcessEnv): Promise<Variables>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { OutpostError } from "../domain/errors.js";
|
|
4
|
+
export function parseEnvironment(text) {
|
|
5
|
+
const result = {};
|
|
6
|
+
for (const line of text.split(/\r?\n/)) {
|
|
7
|
+
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
8
|
+
if (!match)
|
|
9
|
+
continue;
|
|
10
|
+
let value = match[2];
|
|
11
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
12
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
13
|
+
const double = value.startsWith('"');
|
|
14
|
+
value = value.slice(1, -1);
|
|
15
|
+
if (double)
|
|
16
|
+
value = value.replace(/\\([nrt"\\])/g, (_, character) => ({ n: "\n", r: "\r", t: "\t", '"': '"', "\\": "\\" })[character]);
|
|
17
|
+
}
|
|
18
|
+
else
|
|
19
|
+
value = value.replace(/\s+#.*$/, "").trim();
|
|
20
|
+
result[match[1]] = value;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
export async function resolveVariables(repository, agent = {}, provider = {}, environment = process.env) {
|
|
25
|
+
const overlap = Object.keys(agent).filter((key) => Object.hasOwn(provider, key));
|
|
26
|
+
if (overlap.length)
|
|
27
|
+
throw new OutpostError("configuration", `Agent and sandbox variables overlap: ${overlap.join(", ")}`, { overlap });
|
|
28
|
+
const optional = async (path) => readFile(path, "utf8").catch((error) => {
|
|
29
|
+
if (error.code === "ENOENT")
|
|
30
|
+
return "";
|
|
31
|
+
throw error;
|
|
32
|
+
});
|
|
33
|
+
const variables = {
|
|
34
|
+
...parseEnvironment(await optional(join(repository, ".outpost", ".env"))),
|
|
35
|
+
};
|
|
36
|
+
for (const key of Object.keys(variables))
|
|
37
|
+
if (!variables[key] && environment[key] !== undefined)
|
|
38
|
+
variables[key] = environment[key];
|
|
39
|
+
return Object.freeze({ ...variables, ...provider, ...agent });
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/infrastructure/settings.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { Variables } from \"../domain/ports.ts\";\n\nexport function parseEnvironment(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of text.split(/\\r?\\n/)) {\n const match = line.match(\n /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*?)\\s*$/,\n );\n if (!match) continue;\n let value = match[2]!;\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n const double = value.startsWith('\"');\n value = value.slice(1, -1);\n if (double)\n value = value.replace(\n /\\\\([nrt\"\\\\])/g,\n (_, character: string) =>\n ({ n: \"\\n\", r: \"\\r\", t: \"\\t\", '\"': '\"', \"\\\\\": \"\\\\\" })[character]!,\n );\n } else value = value.replace(/\\s+#.*$/, \"\").trim();\n result[match[1]!] = value;\n }\n return result;\n}\n\nexport async function resolveVariables(\n repository: string,\n agent: Variables = {},\n provider: Variables = {},\n environment: NodeJS.ProcessEnv = process.env,\n): Promise<Variables> {\n const overlap = Object.keys(agent).filter((key) =>\n Object.hasOwn(provider, key),\n );\n if (overlap.length)\n throw new OutpostError(\n \"configuration\",\n `Agent and sandbox variables overlap: ${overlap.join(\", \")}`,\n { overlap },\n );\n const optional = async (path: string) =>\n readFile(path, \"utf8\").catch((error) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"\";\n throw error;\n });\n const variables = {\n ...parseEnvironment(await optional(join(repository, \".outpost\", \".env\"))),\n };\n for (const key of Object.keys(variables))\n if (!variables[key] && environment[key] !== undefined)\n variables[key] = environment[key]!;\n return Object.freeze({ ...variables, ...provider, ...agent });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerCleanup(clean: () => unknown, immediate?: () => void): () => void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const cleaners = new Map();
|
|
2
|
+
let installed = false, closing = false;
|
|
3
|
+
function uninstall() {
|
|
4
|
+
process.removeListener("SIGINT", onInterrupt);
|
|
5
|
+
process.removeListener("SIGTERM", onTerminate);
|
|
6
|
+
process.removeListener("exit", onExit);
|
|
7
|
+
installed = false;
|
|
8
|
+
}
|
|
9
|
+
async function shutdown(signal) {
|
|
10
|
+
if (closing)
|
|
11
|
+
return;
|
|
12
|
+
closing = true;
|
|
13
|
+
for (const clean of [...cleaners.keys()].reverse()) {
|
|
14
|
+
if (!cleaners.has(clean))
|
|
15
|
+
continue;
|
|
16
|
+
cleaners.delete(clean);
|
|
17
|
+
try {
|
|
18
|
+
await clean();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
/* Cleanup must continue for remaining resources. */
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
uninstall();
|
|
25
|
+
if (signal)
|
|
26
|
+
process.exitCode = signal === "SIGINT" ? 130 : 143;
|
|
27
|
+
closing = false;
|
|
28
|
+
}
|
|
29
|
+
const onInterrupt = () => {
|
|
30
|
+
void shutdown("SIGINT");
|
|
31
|
+
};
|
|
32
|
+
const onTerminate = () => {
|
|
33
|
+
void shutdown("SIGTERM");
|
|
34
|
+
};
|
|
35
|
+
const onExit = () => {
|
|
36
|
+
for (const [clean, immediate] of [...cleaners.entries()].reverse()) {
|
|
37
|
+
if (!cleaners.has(clean))
|
|
38
|
+
continue;
|
|
39
|
+
cleaners.delete(clean);
|
|
40
|
+
try {
|
|
41
|
+
if (immediate)
|
|
42
|
+
immediate();
|
|
43
|
+
else
|
|
44
|
+
void Promise.resolve(clean()).catch(() => undefined);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
/* An exit callback must not hide the original exit status. */
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
uninstall();
|
|
51
|
+
};
|
|
52
|
+
export function registerCleanup(clean, immediate) {
|
|
53
|
+
cleaners.set(clean, immediate);
|
|
54
|
+
if (!installed) {
|
|
55
|
+
installed = true;
|
|
56
|
+
process.on("SIGINT", onInterrupt);
|
|
57
|
+
process.on("SIGTERM", onTerminate);
|
|
58
|
+
process.on("exit", onExit);
|
|
59
|
+
}
|
|
60
|
+
return () => {
|
|
61
|
+
cleaners.delete(clean);
|
|
62
|
+
if (installed && !cleaners.size) {
|
|
63
|
+
uninstall();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=shutdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/infrastructure/shutdown.ts"],"names":[],"mappings":"AAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA2C,CAAC;AACpE,IAAI,SAAS,GAAG,KAAK,EACnB,OAAO,GAAG,KAAK,CAAC;AAElB,SAAS,SAAS;IAChB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC9C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,SAAS,GAAG,KAAK,CAAC;AACpB,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,MAAuB;IAC7C,IAAI,OAAO;QAAE,OAAO;IACpB,OAAO,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QACnC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,KAAK,EAAE,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,oDAAoD;QACtD,CAAC;IACH,CAAC;IACD,SAAS,EAAE,CAAC;IACZ,IAAI,MAAM;QAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC/D,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AACF,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC,CAAC;AACF,MAAM,MAAM,GAAG,GAAG,EAAE;IAClB,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACnE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QACnC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,CAAC;YACH,IAAI,SAAS;gBAAE,SAAS,EAAE,CAAC;;gBACtB,KAAK,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,8DAA8D;QAChE,CAAC;IACH,CAAC;IACD,SAAS,EAAE,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,UAAU,eAAe,CAC7B,KAAoB,EACpB,SAAsB;IAEtB,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,IAAI,CAAC;QACjB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAClC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACnC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YAChC,SAAS,EAAE,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["const cleaners = new Map<() => unknown, (() => void) | undefined>();\nlet installed = false,\n closing = false;\n\nfunction uninstall(): void {\n process.removeListener(\"SIGINT\", onInterrupt);\n process.removeListener(\"SIGTERM\", onTerminate);\n process.removeListener(\"exit\", onExit);\n installed = false;\n}\n\nasync function shutdown(signal?: NodeJS.Signals): Promise<void> {\n if (closing) return;\n closing = true;\n for (const clean of [...cleaners.keys()].reverse()) {\n if (!cleaners.has(clean)) continue;\n cleaners.delete(clean);\n try {\n await clean();\n } catch {\n /* Cleanup must continue for remaining resources. */\n }\n }\n uninstall();\n if (signal) process.exitCode = signal === \"SIGINT\" ? 130 : 143;\n closing = false;\n}\n\nconst onInterrupt = () => {\n void shutdown(\"SIGINT\");\n};\nconst onTerminate = () => {\n void shutdown(\"SIGTERM\");\n};\nconst onExit = () => {\n for (const [clean, immediate] of [...cleaners.entries()].reverse()) {\n if (!cleaners.has(clean)) continue;\n cleaners.delete(clean);\n try {\n if (immediate) immediate();\n else void Promise.resolve(clean()).catch(() => undefined);\n } catch {\n /* An exit callback must not hide the original exit status. */\n }\n }\n uninstall();\n};\n\nexport function registerCleanup(\n clean: () => unknown,\n immediate?: () => void,\n): () => void {\n cleaners.set(clean, immediate);\n if (!installed) {\n installed = true;\n process.on(\"SIGINT\", onInterrupt);\n process.on(\"SIGTERM\", onTerminate);\n process.on(\"exit\", onExit);\n }\n return () => {\n cleaners.delete(clean);\n if (installed && !cleaners.size) {\n uninstall();\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TerminalState {
|
|
2
|
+
readonly input: {
|
|
3
|
+
readonly isTTY?: boolean;
|
|
4
|
+
setRawMode?(mode: boolean): unknown;
|
|
5
|
+
};
|
|
6
|
+
readonly output: {
|
|
7
|
+
readonly isTTY?: boolean;
|
|
8
|
+
write(text: string): unknown;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare function restoreTerminal(terminal?: TerminalState): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function restoreTerminal(terminal = { input: process.stdin, output: process.stdout }) {
|
|
2
|
+
if (terminal.input.isTTY) {
|
|
3
|
+
try {
|
|
4
|
+
terminal.input.setRawMode?.(false);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
/* The terminal may already be disconnected. */
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
if (terminal.output.isTTY) {
|
|
11
|
+
try {
|
|
12
|
+
terminal.output.write("\u001b[?25h");
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
/* Cursor restoration is best effort after disconnect. */
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/infrastructure/terminal.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,eAAe,CAC7B,WAA0B,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;IAE1E,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;QAC3D,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["export interface TerminalState {\n readonly input: {\n readonly isTTY?: boolean;\n setRawMode?(mode: boolean): unknown;\n };\n readonly output: { readonly isTTY?: boolean; write(text: string): unknown };\n}\n\nexport function restoreTerminal(\n terminal: TerminalState = { input: process.stdin, output: process.stdout },\n): void {\n if (terminal.input.isTTY) {\n try {\n terminal.input.setRawMode?.(false);\n } catch {\n /* The terminal may already be disconnected. */\n }\n }\n if (terminal.output.isTTY) {\n try {\n terminal.output.write(\"\\u001b[?25h\");\n } catch {\n /* Cursor restoration is best effort after disconnect. */\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SandboxLease, TransferOptions } from "../domain/ports.ts";
|
|
2
|
+
export declare function transfer<T>(options: TransferOptions, perform: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
3
|
+
export declare function boundedTransfers(lease: SandboxLease, defaults: TransferOptions): SandboxLease;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { invariant, OutpostError } from "../domain/errors.js";
|
|
2
|
+
import { interruptible } from "./abort.js";
|
|
3
|
+
export async function transfer(options, perform) {
|
|
4
|
+
options.signal?.throwIfAborted();
|
|
5
|
+
const deadlineMs = options.deadlineMs ?? 120_000;
|
|
6
|
+
invariant(Number.isFinite(deadlineMs) && deadlineMs > 0, "Transfer deadline must be positive");
|
|
7
|
+
const stop = new AbortController();
|
|
8
|
+
const signal = options.signal
|
|
9
|
+
? AbortSignal.any([options.signal, stop.signal])
|
|
10
|
+
: stop.signal;
|
|
11
|
+
const timer = setTimeout(() => stop.abort(new OutpostError("timeout", "File transfer timed out", { deadlineMs })), deadlineMs);
|
|
12
|
+
try {
|
|
13
|
+
return await interruptible(perform(signal), signal);
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function boundedTransfers(lease, defaults) {
|
|
20
|
+
const copy = (method) => (source, destination, options = {}) => transfer({ ...defaults, ...options }, (signal) => lease[method](source, destination, { ...defaults, ...options, signal }));
|
|
21
|
+
return {
|
|
22
|
+
...lease,
|
|
23
|
+
invoke: lease.invoke.bind(lease),
|
|
24
|
+
release: lease.release.bind(lease),
|
|
25
|
+
upload: copy("upload"),
|
|
26
|
+
download: copy("download"),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/infrastructure/transfer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAwB,EACxB,OAA4C;IAE5C,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC;IACjD,SAAS,CACP,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAC7C,oCAAoC,CACrC,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;QAC3B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAChB,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CACH,IAAI,CAAC,KAAK,CACR,IAAI,YAAY,CAAC,SAAS,EAAE,yBAAyB,EAAE,EAAE,UAAU,EAAE,CAAC,CACvE,EACH,UAAU,CACX,CAAC;IACF,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAmB,EACnB,QAAyB;IAEzB,MAAM,IAAI,GACR,CAAC,MAA6B,EAAE,EAAE,CAClC,CAAC,MAAc,EAAE,WAAmB,EAAE,UAA2B,EAAE,EAAE,EAAE,CACrE,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAC/C,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CACxE,CAAC;IACN,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAClC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;QACtB,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC;KAC3B,CAAC;AACJ,CAAC","sourcesContent":["import type { SandboxLease, TransferOptions } from \"../domain/ports.ts\";\nimport { invariant, OutpostError } from \"../domain/errors.ts\";\nimport { interruptible } from \"./abort.ts\";\n\nexport async function transfer<T>(\n options: TransferOptions,\n perform: (signal: AbortSignal) => Promise<T>,\n): Promise<T> {\n options.signal?.throwIfAborted();\n const deadlineMs = options.deadlineMs ?? 120_000;\n invariant(\n Number.isFinite(deadlineMs) && deadlineMs > 0,\n \"Transfer deadline must be positive\",\n );\n const stop = new AbortController();\n const signal = options.signal\n ? AbortSignal.any([options.signal, stop.signal])\n : stop.signal;\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", \"File transfer timed out\", { deadlineMs }),\n ),\n deadlineMs,\n );\n try {\n return await interruptible(perform(signal), signal);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function boundedTransfers(\n lease: SandboxLease,\n defaults: TransferOptions,\n): SandboxLease {\n const copy =\n (method: \"upload\" | \"download\") =>\n (source: string, destination: string, options: TransferOptions = {}) =>\n transfer({ ...defaults, ...options }, (signal) =>\n lease[method](source, destination, { ...defaults, ...options, signal }),\n );\n return {\n ...lease,\n invoke: lease.invoke.bind(lease),\n release: lease.release.bind(lease),\n upload: copy(\"upload\"),\n download: copy(\"download\"),\n };\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AgentAdapter, Variables } from "../domain/ports.ts";
|
|
2
|
+
interface CommonAgentSettings {
|
|
3
|
+
readonly model?: string;
|
|
4
|
+
readonly variables?: Variables;
|
|
5
|
+
readonly saveConversations?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ClaudeSettings extends CommonAgentSettings {
|
|
8
|
+
readonly reasoning?: "low" | "medium" | "high" | "xhigh" | "max";
|
|
9
|
+
readonly permissions?: "default" | "acceptEdits" | "plan" | "auto" | "dontAsk" | "bypassPermissions";
|
|
10
|
+
}
|
|
11
|
+
export interface CodexSettings extends CommonAgentSettings {
|
|
12
|
+
readonly reasoning?: "low" | "medium" | "high" | "xhigh";
|
|
13
|
+
readonly approvalReviewer?: "user" | "auto_review";
|
|
14
|
+
}
|
|
15
|
+
export declare function claude(settings?: ClaudeSettings): AgentAdapter;
|
|
16
|
+
export declare function codex(settings?: CodexSettings): AgentAdapter;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { invariant } from "../domain/errors.js";
|
|
2
|
+
function record(value) {
|
|
3
|
+
return value !== null && typeof value === "object"
|
|
4
|
+
? value
|
|
5
|
+
: {};
|
|
6
|
+
}
|
|
7
|
+
function number(value) {
|
|
8
|
+
return typeof value === "number" ? value : 0;
|
|
9
|
+
}
|
|
10
|
+
function decode(line) {
|
|
11
|
+
try {
|
|
12
|
+
return record(JSON.parse(line));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function session(input) {
|
|
19
|
+
if (input.continuation)
|
|
20
|
+
invariant(/^[A-Za-z0-9_-]+$/.test(input.continuation.id), "Invalid conversation identifier");
|
|
21
|
+
}
|
|
22
|
+
export function claude(settings = {}) {
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
name: "claude",
|
|
25
|
+
conversations: "claude",
|
|
26
|
+
resumable: true,
|
|
27
|
+
capture: settings.saveConversations ?? true,
|
|
28
|
+
variables: Object.freeze({ ...settings.variables }),
|
|
29
|
+
request(input) {
|
|
30
|
+
session(input);
|
|
31
|
+
const args = [];
|
|
32
|
+
if (!input.interactive)
|
|
33
|
+
args.push("--print", "--verbose", "--output-format", "stream-json");
|
|
34
|
+
if (settings.model)
|
|
35
|
+
args.push("--model", settings.model);
|
|
36
|
+
if (settings.reasoning)
|
|
37
|
+
args.push("--effort", settings.reasoning);
|
|
38
|
+
if (settings.permissions)
|
|
39
|
+
args.push("--permission-mode", settings.permissions);
|
|
40
|
+
else if (!input.interactive)
|
|
41
|
+
args.push("--dangerously-skip-permissions");
|
|
42
|
+
if (input.continuation) {
|
|
43
|
+
args.push("--resume", input.continuation.id);
|
|
44
|
+
if (input.continuation.fork)
|
|
45
|
+
args.push("--fork-session");
|
|
46
|
+
}
|
|
47
|
+
if (input.interactive && input.text !== undefined)
|
|
48
|
+
args.push("--", input.text);
|
|
49
|
+
return {
|
|
50
|
+
executable: "claude",
|
|
51
|
+
arguments: args,
|
|
52
|
+
...(input.interactive
|
|
53
|
+
? { interactive: true }
|
|
54
|
+
: { stdin: input.text ?? "" }),
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
events(line) {
|
|
58
|
+
const event = decode(line);
|
|
59
|
+
if (!event)
|
|
60
|
+
return [{ kind: "raw", value: line }];
|
|
61
|
+
const events = [];
|
|
62
|
+
if (event.type === "system" && typeof event.session_id === "string")
|
|
63
|
+
events.push({ kind: "conversation", id: event.session_id });
|
|
64
|
+
const message = record(event.message);
|
|
65
|
+
if (event.type === "assistant" && Array.isArray(message.content))
|
|
66
|
+
for (const part of message.content) {
|
|
67
|
+
const block = record(part);
|
|
68
|
+
if (block.type === "text" && typeof block.text === "string")
|
|
69
|
+
events.push({ kind: "text", text: block.text });
|
|
70
|
+
if (block.type === "tool_use")
|
|
71
|
+
events.push({
|
|
72
|
+
kind: "tool",
|
|
73
|
+
name: String(block.name),
|
|
74
|
+
input: block.input,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (event.type === "result") {
|
|
78
|
+
if (!event.is_error && typeof event.result === "string")
|
|
79
|
+
events.push({ kind: "result", text: event.result });
|
|
80
|
+
const usage = record(event.usage);
|
|
81
|
+
if (event.usage)
|
|
82
|
+
events.push({
|
|
83
|
+
kind: "usage",
|
|
84
|
+
tokens: {
|
|
85
|
+
input: number(usage.input_tokens),
|
|
86
|
+
cacheCreated: number(usage.cache_creation_input_tokens),
|
|
87
|
+
cached: number(usage.cache_read_input_tokens),
|
|
88
|
+
output: number(usage.output_tokens),
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
events.push(event.is_error
|
|
92
|
+
? {
|
|
93
|
+
kind: "failure",
|
|
94
|
+
message: String(event.result ??
|
|
95
|
+
JSON.stringify(event.errors) ??
|
|
96
|
+
"Agent failed"),
|
|
97
|
+
}
|
|
98
|
+
: { kind: "finished" });
|
|
99
|
+
}
|
|
100
|
+
return events.length ? events : [{ kind: "raw", value: event }];
|
|
101
|
+
},
|
|
102
|
+
transcriptUsage(text) {
|
|
103
|
+
let usage;
|
|
104
|
+
for (const line of text.split(/\r?\n/)) {
|
|
105
|
+
const event = decode(line);
|
|
106
|
+
if (event?.type !== "assistant")
|
|
107
|
+
continue;
|
|
108
|
+
const message = record(event.message);
|
|
109
|
+
if (!message.usage)
|
|
110
|
+
continue;
|
|
111
|
+
const tokens = record(message.usage);
|
|
112
|
+
usage = {
|
|
113
|
+
input: number(tokens.input_tokens),
|
|
114
|
+
cacheCreated: number(tokens.cache_creation_input_tokens),
|
|
115
|
+
cached: number(tokens.cache_read_input_tokens),
|
|
116
|
+
output: number(tokens.output_tokens),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return usage;
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export function codex(settings = {}) {
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
name: "codex",
|
|
126
|
+
conversations: "codex",
|
|
127
|
+
resumable: true,
|
|
128
|
+
capture: settings.saveConversations ?? true,
|
|
129
|
+
variables: Object.freeze({ ...settings.variables }),
|
|
130
|
+
request(input) {
|
|
131
|
+
session(input);
|
|
132
|
+
const args = [];
|
|
133
|
+
if (settings.approvalReviewer === "auto_review")
|
|
134
|
+
args.push("-a", "on-request", "-s", "danger-full-access", "-c", 'approvals_reviewer="auto_review"');
|
|
135
|
+
else if (!input.interactive)
|
|
136
|
+
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
137
|
+
if (settings.model)
|
|
138
|
+
args.push("--model", settings.model);
|
|
139
|
+
if (settings.reasoning)
|
|
140
|
+
args.push("-c", `model_reasoning_effort="${settings.reasoning}"`);
|
|
141
|
+
if (!input.interactive)
|
|
142
|
+
args.push("exec");
|
|
143
|
+
if (input.continuation)
|
|
144
|
+
args.push(input.continuation.fork ? "fork" : "resume", input.continuation.id);
|
|
145
|
+
if (!input.interactive)
|
|
146
|
+
args.push("--json", "-");
|
|
147
|
+
else if (input.text !== undefined)
|
|
148
|
+
args.push("--", input.text);
|
|
149
|
+
return {
|
|
150
|
+
executable: "codex",
|
|
151
|
+
arguments: args,
|
|
152
|
+
...(input.interactive
|
|
153
|
+
? { interactive: true }
|
|
154
|
+
: { stdin: input.text ?? "" }),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
events(line) {
|
|
158
|
+
const event = decode(line);
|
|
159
|
+
if (!event)
|
|
160
|
+
return [{ kind: "raw", value: line }];
|
|
161
|
+
if (event.type === "thread.started" &&
|
|
162
|
+
typeof event.thread_id === "string")
|
|
163
|
+
return [{ kind: "conversation", id: event.thread_id }];
|
|
164
|
+
const item = record(event.item);
|
|
165
|
+
if (event.type === "item.completed" &&
|
|
166
|
+
item.type === "agent_message" &&
|
|
167
|
+
typeof item.text === "string")
|
|
168
|
+
return [{ kind: "text", text: item.text }];
|
|
169
|
+
if (event.type === "item.started" && item.type === "command_execution")
|
|
170
|
+
return [{ kind: "tool", name: "command", input: item.command }];
|
|
171
|
+
if (event.type === "item.started" && item.type === "mcp_tool_call")
|
|
172
|
+
return [
|
|
173
|
+
{ kind: "tool", name: String(item.tool), input: item.arguments },
|
|
174
|
+
];
|
|
175
|
+
if (event.type === "turn.completed") {
|
|
176
|
+
const usage = record(event.usage);
|
|
177
|
+
return [
|
|
178
|
+
{
|
|
179
|
+
kind: "usage",
|
|
180
|
+
tokens: {
|
|
181
|
+
input: number(usage.input_tokens),
|
|
182
|
+
cached: number(usage.cached_input_tokens),
|
|
183
|
+
output: number(usage.output_tokens),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{ kind: "finished" },
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
if (event.type === "error" || event.type === "turn.failed")
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
kind: "failure",
|
|
193
|
+
message: String(event.message ??
|
|
194
|
+
(typeof event.error === "string"
|
|
195
|
+
? event.error
|
|
196
|
+
: record(event.error).message) ??
|
|
197
|
+
"Agent failed"),
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
return [{ kind: "raw", value: event }];
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/providers/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport type {\n AgentAdapter,\n AgentEvent,\n AgentInput,\n Variables,\n} from \"../domain/ports.ts\";\n\ninterface CommonAgentSettings {\n readonly model?: string;\n readonly variables?: Variables;\n readonly saveConversations?: boolean;\n}\nexport interface ClaudeSettings extends CommonAgentSettings {\n readonly reasoning?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n readonly permissions?:\n | \"default\"\n | \"acceptEdits\"\n | \"plan\"\n | \"auto\"\n | \"dontAsk\"\n | \"bypassPermissions\";\n}\nexport interface CodexSettings extends CommonAgentSettings {\n readonly reasoning?: \"low\" | \"medium\" | \"high\" | \"xhigh\";\n readonly approvalReviewer?: \"user\" | \"auto_review\";\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === \"object\"\n ? (value as Record<string, unknown>)\n : {};\n}\nfunction number(value: unknown): number {\n return typeof value === \"number\" ? value : 0;\n}\nfunction decode(line: string): Record<string, unknown> | undefined {\n try {\n return record(JSON.parse(line));\n } catch {\n return undefined;\n }\n}\nfunction session(input: AgentInput): void {\n if (input.continuation)\n invariant(\n /^[A-Za-z0-9_-]+$/.test(input.continuation.id),\n \"Invalid conversation identifier\",\n );\n}\n\nexport function claude(settings: ClaudeSettings = {}): AgentAdapter {\n return Object.freeze({\n name: \"claude\",\n conversations: \"claude\",\n resumable: true,\n capture: settings.saveConversations ?? true,\n variables: Object.freeze({ ...settings.variables }),\n request(input: AgentInput) {\n session(input);\n const args: string[] = [];\n if (!input.interactive)\n args.push(\"--print\", \"--verbose\", \"--output-format\", \"stream-json\");\n if (settings.model) args.push(\"--model\", settings.model);\n if (settings.reasoning) args.push(\"--effort\", settings.reasoning);\n if (settings.permissions)\n args.push(\"--permission-mode\", settings.permissions);\n else if (!input.interactive) args.push(\"--dangerously-skip-permissions\");\n if (input.continuation) {\n args.push(\"--resume\", input.continuation.id);\n if (input.continuation.fork) args.push(\"--fork-session\");\n }\n if (input.interactive && input.text !== undefined)\n args.push(\"--\", input.text);\n return {\n executable: \"claude\",\n arguments: args,\n ...(input.interactive\n ? { interactive: true }\n : { stdin: input.text ?? \"\" }),\n };\n },\n events(line: string): AgentEvent[] {\n const event = decode(line);\n if (!event) return [{ kind: \"raw\", value: line }];\n const events: AgentEvent[] = [];\n if (event.type === \"system\" && typeof event.session_id === \"string\")\n events.push({ kind: \"conversation\", id: event.session_id });\n const message = record(event.message);\n if (event.type === \"assistant\" && Array.isArray(message.content))\n for (const part of message.content) {\n const block = record(part);\n if (block.type === \"text\" && typeof block.text === \"string\")\n events.push({ kind: \"text\", text: block.text });\n if (block.type === \"tool_use\")\n events.push({\n kind: \"tool\",\n name: String(block.name),\n input: block.input,\n });\n }\n if (event.type === \"result\") {\n if (!event.is_error && typeof event.result === \"string\")\n events.push({ kind: \"result\", text: event.result });\n const usage = record(event.usage);\n if (event.usage)\n events.push({\n kind: \"usage\",\n tokens: {\n input: number(usage.input_tokens),\n cacheCreated: number(usage.cache_creation_input_tokens),\n cached: number(usage.cache_read_input_tokens),\n output: number(usage.output_tokens),\n },\n });\n events.push(\n event.is_error\n ? {\n kind: \"failure\",\n message: String(\n event.result ??\n JSON.stringify(event.errors) ??\n \"Agent failed\",\n ),\n }\n : { kind: \"finished\" },\n );\n }\n return events.length ? events : [{ kind: \"raw\", value: event }];\n },\n transcriptUsage(text: string) {\n let usage;\n for (const line of text.split(/\\r?\\n/)) {\n const event = decode(line);\n if (event?.type !== \"assistant\") continue;\n const message = record(event.message);\n if (!message.usage) continue;\n const tokens = record(message.usage);\n usage = {\n input: number(tokens.input_tokens),\n cacheCreated: number(tokens.cache_creation_input_tokens),\n cached: number(tokens.cache_read_input_tokens),\n output: number(tokens.output_tokens),\n };\n }\n return usage;\n },\n });\n}\n\nexport function codex(settings: CodexSettings = {}): AgentAdapter {\n return Object.freeze({\n name: \"codex\",\n conversations: \"codex\",\n resumable: true,\n capture: settings.saveConversations ?? true,\n variables: Object.freeze({ ...settings.variables }),\n request(input: AgentInput) {\n session(input);\n const args: string[] = [];\n if (settings.approvalReviewer === \"auto_review\")\n args.push(\n \"-a\",\n \"on-request\",\n \"-s\",\n \"danger-full-access\",\n \"-c\",\n 'approvals_reviewer=\"auto_review\"',\n );\n else if (!input.interactive)\n args.push(\"--dangerously-bypass-approvals-and-sandbox\");\n if (settings.model) args.push(\"--model\", settings.model);\n if (settings.reasoning)\n args.push(\"-c\", `model_reasoning_effort=\"${settings.reasoning}\"`);\n if (!input.interactive) args.push(\"exec\");\n if (input.continuation)\n args.push(\n input.continuation.fork ? \"fork\" : \"resume\",\n input.continuation.id,\n );\n if (!input.interactive) args.push(\"--json\", \"-\");\n else if (input.text !== undefined) args.push(\"--\", input.text);\n return {\n executable: \"codex\",\n arguments: args,\n ...(input.interactive\n ? { interactive: true }\n : { stdin: input.text ?? \"\" }),\n };\n },\n events(line: string): AgentEvent[] {\n const event = decode(line);\n if (!event) return [{ kind: \"raw\", value: line }];\n if (\n event.type === \"thread.started\" &&\n typeof event.thread_id === \"string\"\n )\n return [{ kind: \"conversation\", id: event.thread_id }];\n const item = record(event.item);\n if (\n event.type === \"item.completed\" &&\n item.type === \"agent_message\" &&\n typeof item.text === \"string\"\n )\n return [{ kind: \"text\", text: item.text }];\n if (event.type === \"item.started\" && item.type === \"command_execution\")\n return [{ kind: \"tool\", name: \"command\", input: item.command }];\n if (event.type === \"item.started\" && item.type === \"mcp_tool_call\")\n return [\n { kind: \"tool\", name: String(item.tool), input: item.arguments },\n ];\n if (event.type === \"turn.completed\") {\n const usage = record(event.usage);\n return [\n {\n kind: \"usage\",\n tokens: {\n input: number(usage.input_tokens),\n cached: number(usage.cached_input_tokens),\n output: number(usage.output_tokens),\n },\n },\n { kind: \"finished\" },\n ];\n }\n if (event.type === \"error\" || event.type === \"turn.failed\")\n return [\n {\n kind: \"failure\",\n message: String(\n event.message ??\n (typeof event.error === \"string\"\n ? event.error\n : record(event.error).message) ??\n \"Agent failed\",\n ),\n },\n ];\n return [{ kind: \"raw\", value: event }];\n },\n });\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Backlog } from "../domain/backlog.ts";
|
|
2
|
+
import { type Executor } from "../infrastructure/process.ts";
|
|
3
|
+
export interface BacklogSettings {
|
|
4
|
+
readonly directory?: string;
|
|
5
|
+
readonly label?: string;
|
|
6
|
+
readonly deadlineMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function githubBacklog(settings?: BacklogSettings, executor?: Executor): Backlog;
|
|
9
|
+
export declare function beadsBacklog(settings?: BacklogSettings, executor?: Executor): Backlog;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { invariant } from "../domain/errors.js";
|
|
2
|
+
import { requireSuccess } from "../infrastructure/process.js";
|
|
3
|
+
import { resolveVariables } from "../infrastructure/settings.js";
|
|
4
|
+
function issue(input) {
|
|
5
|
+
invariant(!!input && typeof input === "object", "Tracker returned an invalid issue");
|
|
6
|
+
const item = input;
|
|
7
|
+
invariant((typeof item.id === "string" || typeof item.number === "number") &&
|
|
8
|
+
typeof item.title === "string", "Tracker issue needs an id and a title");
|
|
9
|
+
return {
|
|
10
|
+
id: String(item.number ?? item.id),
|
|
11
|
+
title: item.title,
|
|
12
|
+
body: String(item.body ?? item.description ?? ""),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function cli(program, settings, executor) {
|
|
16
|
+
const directory = settings.directory ?? process.cwd();
|
|
17
|
+
return async (args, signal) => {
|
|
18
|
+
signal?.throwIfAborted();
|
|
19
|
+
const variables = await resolveVariables(directory);
|
|
20
|
+
return (await requireSuccess({
|
|
21
|
+
executable: program,
|
|
22
|
+
arguments: args,
|
|
23
|
+
directory,
|
|
24
|
+
variables,
|
|
25
|
+
deadlineMs: settings.deadlineMs ?? 60_000,
|
|
26
|
+
retain: 16_777_216,
|
|
27
|
+
...(signal ? { signal } : {}),
|
|
28
|
+
}, executor)).stdout;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function githubBacklog(settings = {}, executor) {
|
|
32
|
+
const run = cli("gh", settings, executor);
|
|
33
|
+
const identifier = (id) => {
|
|
34
|
+
invariant(/^[1-9]\d*$/.test(id), "GitHub issue id must be a positive number");
|
|
35
|
+
return id;
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
async list(signal) {
|
|
39
|
+
const endpoint = `repos/{owner}/{repo}/issues?state=open&per_page=100${settings.label ? `&labels=${encodeURIComponent(settings.label)}` : ""}`;
|
|
40
|
+
const pages = JSON.parse(await run(["api", endpoint, "--paginate", "--slurp"], signal));
|
|
41
|
+
invariant(Array.isArray(pages) && pages.every(Array.isArray), "Invalid GitHub issue pages");
|
|
42
|
+
return pages
|
|
43
|
+
.flat()
|
|
44
|
+
.filter((item) => !item.pull_request)
|
|
45
|
+
.map(issue);
|
|
46
|
+
},
|
|
47
|
+
async get(id, signal) {
|
|
48
|
+
return issue(JSON.parse(await run(["issue", "view", identifier(id), "--json", "number,title,body"], signal)));
|
|
49
|
+
},
|
|
50
|
+
async close(id, signal) {
|
|
51
|
+
await run(["issue", "close", identifier(id), "--reason", "completed"], signal);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function beadsBacklog(settings = {}, executor) {
|
|
56
|
+
const run = cli("bd", settings, executor);
|
|
57
|
+
const identifier = (id) => {
|
|
58
|
+
invariant(/^[\w][\w.-]*$/.test(id), "Invalid Beads issue id");
|
|
59
|
+
return id;
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
async list(signal) {
|
|
63
|
+
const data = JSON.parse(await run([
|
|
64
|
+
"ready",
|
|
65
|
+
"--json",
|
|
66
|
+
"--limit",
|
|
67
|
+
"0",
|
|
68
|
+
...(settings.label ? ["--label", settings.label] : []),
|
|
69
|
+
], signal));
|
|
70
|
+
invariant(Array.isArray(data), "Invalid Beads backlog");
|
|
71
|
+
return data.map(issue);
|
|
72
|
+
},
|
|
73
|
+
async get(id, signal) {
|
|
74
|
+
const data = JSON.parse(await run(["show", identifier(id), "--json"], signal));
|
|
75
|
+
return issue(Array.isArray(data) ? data[0] : data);
|
|
76
|
+
},
|
|
77
|
+
async close(id, signal) {
|
|
78
|
+
await run(["close", identifier(id)], signal);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=backlogs.js.map
|