@elie-laloum/outpost 2.0.0 → 3.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.
- package/CHANGELOG.md +24 -0
- package/dist/adapters/agents/claude-diagnostics.d.ts +1 -0
- package/dist/adapters/agents/claude-diagnostics.js +7 -0
- package/dist/adapters/agents/claude-diagnostics.js.map +1 -0
- package/dist/adapters/agents/cli-diagnostics.constants.d.ts +12 -0
- package/dist/adapters/agents/cli-diagnostics.constants.js +24 -0
- package/dist/adapters/agents/cli-diagnostics.constants.js.map +1 -0
- package/dist/adapters/agents/cli-diagnostics.d.ts +4 -0
- package/dist/adapters/agents/cli-diagnostics.js +14 -0
- package/dist/adapters/agents/cli-diagnostics.js.map +1 -0
- package/dist/adapters/agents/cli-diagnostics.types.d.ts +13 -0
- package/dist/adapters/agents/cli-diagnostics.types.js +2 -0
- package/dist/adapters/agents/cli-diagnostics.types.js.map +1 -0
- package/dist/adapters/agents/codex-diagnostics.d.ts +1 -0
- package/dist/adapters/agents/codex-diagnostics.js +7 -0
- package/dist/adapters/agents/codex-diagnostics.js.map +1 -0
- package/dist/adapters/agents/protocol-fixtures.constants.d.ts +2 -0
- package/dist/adapters/agents/protocol-fixtures.constants.js +68 -0
- package/dist/adapters/agents/protocol-fixtures.constants.js.map +1 -0
- package/dist/adapters/agents/protocol-fixtures.types.d.ts +6 -0
- package/dist/adapters/agents/protocol-fixtures.types.js +2 -0
- package/dist/adapters/agents/protocol-fixtures.types.js.map +1 -0
- package/dist/application/diagnostic-probe.d.ts +3 -0
- package/dist/application/diagnostic-probe.js +55 -0
- package/dist/application/diagnostic-probe.js.map +1 -0
- package/dist/application/doctor-agent.constants.d.ts +9 -0
- package/dist/application/doctor-agent.constants.js +8 -0
- package/dist/application/doctor-agent.constants.js.map +1 -0
- package/dist/application/doctor-agent.d.ts +3 -0
- package/dist/application/doctor-agent.js +53 -0
- package/dist/application/doctor-agent.js.map +1 -0
- package/dist/application/doctor-image.d.ts +3 -0
- package/dist/application/doctor-image.js +133 -0
- package/dist/application/doctor-image.js.map +1 -0
- package/dist/application/doctor-protocol.d.ts +3 -0
- package/dist/application/doctor-protocol.js +29 -0
- package/dist/application/doctor-protocol.js.map +1 -0
- package/dist/application/doctor-protocol.types.d.ts +10 -0
- package/dist/application/doctor-protocol.types.js +2 -0
- package/dist/application/doctor-protocol.types.js.map +1 -0
- package/dist/application/doctor-sandbox.constants.d.ts +5 -0
- package/dist/application/doctor-sandbox.constants.js +6 -0
- package/dist/application/doctor-sandbox.constants.js.map +1 -0
- package/dist/application/doctor-sandbox.d.ts +4 -0
- package/dist/application/doctor-sandbox.js +133 -0
- package/dist/application/doctor-sandbox.js.map +1 -0
- package/dist/application/doctor-sandbox.types.d.ts +23 -0
- package/dist/application/doctor-sandbox.types.js +2 -0
- package/dist/application/doctor-sandbox.types.js.map +1 -0
- package/dist/application/doctor-transfers.constants.d.ts +6 -0
- package/dist/application/doctor-transfers.constants.js +7 -0
- package/dist/application/doctor-transfers.constants.js.map +1 -0
- package/dist/application/doctor-transfers.d.ts +4 -0
- package/dist/application/doctor-transfers.js +88 -0
- package/dist/application/doctor-transfers.js.map +1 -0
- package/dist/application/doctor.constants.d.ts +9 -0
- package/dist/application/doctor.constants.js +15 -0
- package/dist/application/doctor.constants.js.map +1 -0
- package/dist/application/doctor.d.ts +3 -0
- package/dist/application/doctor.js +106 -0
- package/dist/application/doctor.js.map +1 -0
- package/dist/application/doctor.types.d.ts +39 -0
- package/dist/application/doctor.types.js +2 -0
- package/dist/application/doctor.types.js.map +1 -0
- package/dist/application/execution.js +1 -0
- package/dist/application/execution.js.map +1 -1
- package/dist/application/outpost.types.d.ts +2 -0
- package/dist/application/outpost.types.js.map +1 -1
- package/dist/application/recovery-checksum-capture.d.ts +2 -0
- package/dist/application/recovery-checksum-capture.js +32 -0
- package/dist/application/recovery-checksum-capture.js.map +1 -0
- package/dist/application/recovery-checksum-paths.d.ts +2 -0
- package/dist/application/recovery-checksum-paths.js +11 -0
- package/dist/application/recovery-checksum-paths.js.map +1 -0
- package/dist/application/recovery-checksums.constants.d.ts +5 -0
- package/dist/application/recovery-checksums.constants.js +6 -0
- package/dist/application/recovery-checksums.constants.js.map +1 -0
- package/dist/application/recovery-checksums.d.ts +3 -0
- package/dist/application/recovery-checksums.js +94 -0
- package/dist/application/recovery-checksums.js.map +1 -0
- package/dist/application/recovery-checksums.types.d.ts +17 -0
- package/dist/application/recovery-checksums.types.js +2 -0
- package/dist/application/recovery-checksums.types.js.map +1 -0
- package/dist/application/recovery-inspection.d.ts +2 -0
- package/dist/application/recovery-inspection.js +27 -0
- package/dist/application/recovery-inspection.js.map +1 -0
- package/dist/application/recovery-inspection.types.d.ts +15 -0
- package/dist/application/recovery-inspection.types.js +2 -0
- package/dist/application/recovery-inspection.types.js.map +1 -0
- package/dist/application/recovery-restorability.d.ts +2 -0
- package/dist/application/recovery-restorability.js +137 -0
- package/dist/application/recovery-restorability.js.map +1 -0
- package/dist/application/recovery-retention-policy.d.ts +2 -0
- package/dist/application/recovery-retention-policy.js +21 -0
- package/dist/application/recovery-retention-policy.js.map +1 -0
- package/dist/application/recovery-retention.constants.d.ts +3 -0
- package/dist/application/recovery-retention.constants.js +2 -0
- package/dist/application/recovery-retention.constants.js.map +1 -0
- package/dist/application/recovery-retention.d.ts +4 -0
- package/dist/application/recovery-retention.js +202 -0
- package/dist/application/recovery-retention.js.map +1 -0
- package/dist/application/recovery-retention.types.d.ts +52 -0
- package/dist/application/recovery-retention.types.js +2 -0
- package/dist/application/recovery-retention.types.js.map +1 -0
- package/dist/application/recovery-verification.constants.d.ts +5 -0
- package/dist/application/recovery-verification.constants.js +10 -0
- package/dist/application/recovery-verification.constants.js.map +1 -0
- package/dist/application/recovery-verification.d.ts +2 -0
- package/dist/application/recovery-verification.js +107 -0
- package/dist/application/recovery-verification.js.map +1 -0
- package/dist/application/recovery-verification.types.d.ts +26 -0
- package/dist/application/recovery-verification.types.js +2 -0
- package/dist/application/recovery-verification.types.js.map +1 -0
- package/dist/application/remote-apply.js +24 -6
- package/dist/application/remote-apply.js.map +1 -1
- package/dist/application/remote-backup.js +6 -2
- package/dist/application/remote-backup.js.map +1 -1
- package/dist/application/remote-download.js +36 -4
- package/dist/application/remote-download.js.map +1 -1
- package/dist/application/remote-validation.js +19 -0
- package/dist/application/remote-validation.js.map +1 -1
- package/dist/application/remote-workspace.constants.d.ts +3 -0
- package/dist/application/remote-workspace.constants.js +2 -0
- package/dist/application/remote-workspace.constants.js.map +1 -0
- package/dist/application/remote-workspace.js +6 -0
- package/dist/application/remote-workspace.js.map +1 -1
- package/dist/application/remote-workspace.types.d.ts +3 -1
- package/dist/application/remote-workspace.types.js.map +1 -1
- package/dist/application/sandbox.js +11 -0
- package/dist/application/sandbox.js.map +1 -1
- package/dist/application/task-usage.d.ts +4 -0
- package/dist/application/task-usage.js +41 -0
- package/dist/application/task-usage.js.map +1 -0
- package/dist/application/task-usage.types.d.ts +5 -0
- package/dist/application/task-usage.types.js +2 -0
- package/dist/application/task-usage.types.js.map +1 -0
- package/dist/application/tasks.js +23 -2
- package/dist/application/tasks.js.map +1 -1
- package/dist/cli/commands.js +4 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/doctor-command.d.ts +3 -0
- package/dist/cli/doctor-command.js +38 -0
- package/dist/cli/doctor-command.js.map +1 -0
- package/dist/cli/main.constants.d.ts +28 -1
- package/dist/cli/main.constants.js +15 -0
- package/dist/cli/main.constants.js.map +1 -1
- package/dist/cli/recovery-command.d.ts +2 -0
- package/dist/cli/recovery-command.js +69 -0
- package/dist/cli/recovery-command.js.map +1 -0
- package/dist/cli/recovery-prune-command.d.ts +2 -0
- package/dist/cli/recovery-prune-command.js +34 -0
- package/dist/cli/recovery-prune-command.js.map +1 -0
- package/dist/cli/recovery-verify-command.d.ts +2 -0
- package/dist/cli/recovery-verify-command.js +43 -0
- package/dist/cli/recovery-verify-command.js.map +1 -0
- package/dist/domain/ports.d.ts +1 -1
- package/dist/domain/ports.js.map +1 -1
- package/dist/domain/sandbox.types.d.ts +12 -0
- package/dist/domain/sandbox.types.js.map +1 -1
- package/dist/domain/workflow/budget.constants.d.ts +1 -0
- package/dist/domain/workflow/budget.constants.js +7 -0
- package/dist/domain/workflow/budget.constants.js.map +1 -0
- package/dist/domain/workflow/budget.d.ts +9 -0
- package/dist/domain/workflow/budget.js +67 -0
- package/dist/domain/workflow/budget.js.map +1 -0
- package/dist/domain/workflow/budget.types.d.ts +15 -0
- package/dist/domain/workflow/budget.types.js +2 -0
- package/dist/domain/workflow/budget.types.js.map +1 -0
- package/dist/domain/workflow/task-runner.js +8 -1
- package/dist/domain/workflow/task-runner.js.map +1 -1
- package/dist/domain/workflow/workflow-scheduler.js +4 -2
- package/dist/domain/workflow/workflow-scheduler.js.map +1 -1
- package/dist/domain/workflow/workflow-state.js +29 -0
- package/dist/domain/workflow/workflow-state.js.map +1 -1
- package/dist/domain/workflow.d.ts +2 -0
- package/dist/domain/workflow.js +1 -0
- package/dist/domain/workflow.js.map +1 -1
- package/dist/domain/workflow.types.d.ts +10 -1
- package/dist/domain/workflow.types.js.map +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/file-manifest.d.ts +6 -0
- package/dist/infrastructure/file-manifest.js +91 -0
- package/dist/infrastructure/file-manifest.js.map +1 -0
- package/dist/infrastructure/git/lock-inspection.constants.d.ts +4 -0
- package/dist/infrastructure/git/lock-inspection.constants.js +5 -0
- package/dist/infrastructure/git/lock-inspection.constants.js.map +1 -0
- package/dist/infrastructure/git/lock-inspection.d.ts +3 -0
- package/dist/infrastructure/git/lock-inspection.js +64 -0
- package/dist/infrastructure/git/lock-inspection.js.map +1 -0
- package/dist/infrastructure/git/lock-inspection.types.d.ts +27 -0
- package/dist/infrastructure/git/lock-inspection.types.js +2 -0
- package/dist/infrastructure/git/lock-inspection.types.js.map +1 -0
- package/dist/infrastructure/git/lock.d.ts +1 -0
- package/dist/infrastructure/git/lock.js +70 -29
- package/dist/infrastructure/git/lock.js.map +1 -1
- package/dist/infrastructure/git/process-identity.d.ts +3 -0
- package/dist/infrastructure/git/process-identity.js +73 -0
- package/dist/infrastructure/git/process-identity.js.map +1 -0
- package/dist/infrastructure/git/process-identity.types.d.ts +10 -0
- package/dist/infrastructure/git/process-identity.types.js +2 -0
- package/dist/infrastructure/git/process-identity.types.js.map +1 -0
- package/dist/infrastructure/git/verification-command.d.ts +1 -0
- package/dist/infrastructure/git/verification-command.js +36 -0
- package/dist/infrastructure/git/verification-command.js.map +1 -0
- package/dist/infrastructure/git/workspace-lease.js +1 -0
- package/dist/infrastructure/git/workspace-lease.js.map +1 -1
- package/dist/infrastructure/git/workspace.js +5 -1
- package/dist/infrastructure/git/workspace.js.map +1 -1
- package/dist/infrastructure/git/worktree-inspection.constants.d.ts +5 -0
- package/dist/infrastructure/git/worktree-inspection.constants.js +16 -0
- package/dist/infrastructure/git/worktree-inspection.constants.js.map +1 -0
- package/dist/infrastructure/git/worktree-inspection.d.ts +3 -0
- package/dist/infrastructure/git/worktree-inspection.js +120 -0
- package/dist/infrastructure/git/worktree-inspection.js.map +1 -0
- package/dist/infrastructure/git/worktree-inspection.types.d.ts +19 -0
- package/dist/infrastructure/git/worktree-inspection.types.js +2 -0
- package/dist/infrastructure/git/worktree-inspection.types.js.map +1 -0
- package/dist/infrastructure/inspection-file.constants.d.ts +1 -0
- package/dist/infrastructure/inspection-file.constants.js +5 -0
- package/dist/infrastructure/inspection-file.constants.js.map +1 -0
- package/dist/infrastructure/inspection-file.d.ts +1 -0
- package/dist/infrastructure/inspection-file.js +43 -0
- package/dist/infrastructure/inspection-file.js.map +1 -0
- package/dist/infrastructure/inspection-hash.constants.d.ts +1 -0
- package/dist/infrastructure/inspection-hash.constants.js +2 -0
- package/dist/infrastructure/inspection-hash.constants.js.map +1 -0
- package/dist/infrastructure/inspection-hash.d.ts +2 -0
- package/dist/infrastructure/inspection-hash.js +62 -0
- package/dist/infrastructure/inspection-hash.js.map +1 -0
- package/dist/infrastructure/inspection-hash.types.d.ts +5 -0
- package/dist/infrastructure/inspection-hash.types.js +2 -0
- package/dist/infrastructure/inspection-hash.types.js.map +1 -0
- package/dist/infrastructure/journal-retention.constants.d.ts +4 -0
- package/dist/infrastructure/journal-retention.constants.js +5 -0
- package/dist/infrastructure/journal-retention.constants.js.map +1 -0
- package/dist/infrastructure/journal-retention.d.ts +2 -0
- package/dist/infrastructure/journal-retention.js +51 -0
- package/dist/infrastructure/journal-retention.js.map +1 -0
- package/dist/infrastructure/journal.js +37 -13
- package/dist/infrastructure/journal.js.map +1 -1
- package/dist/infrastructure/opentelemetry.constants.d.ts +10 -0
- package/dist/infrastructure/opentelemetry.constants.js +11 -0
- package/dist/infrastructure/opentelemetry.constants.js.map +1 -0
- package/dist/infrastructure/opentelemetry.d.ts +3 -0
- package/dist/infrastructure/opentelemetry.js +131 -0
- package/dist/infrastructure/opentelemetry.js.map +1 -0
- package/dist/infrastructure/opentelemetry.types.d.ts +21 -0
- package/dist/infrastructure/opentelemetry.types.js +2 -0
- package/dist/infrastructure/opentelemetry.types.js.map +1 -0
- package/dist/infrastructure/storage-inventory.constants.d.ts +6 -0
- package/dist/infrastructure/storage-inventory.constants.js +11 -0
- package/dist/infrastructure/storage-inventory.constants.js.map +1 -0
- package/dist/infrastructure/storage-inventory.d.ts +2 -0
- package/dist/infrastructure/storage-inventory.js +123 -0
- package/dist/infrastructure/storage-inventory.js.map +1 -0
- package/dist/infrastructure/storage-inventory.types.d.ts +38 -0
- package/dist/infrastructure/storage-inventory.types.js +2 -0
- package/dist/infrastructure/storage-inventory.types.js.map +1 -0
- package/dist/infrastructure/transfer.js +12 -0
- package/dist/infrastructure/transfer.js.map +1 -1
- package/dist/providers/cloud.constants.d.ts +1 -0
- package/dist/providers/cloud.constants.js +1 -0
- package/dist/providers/cloud.constants.js.map +1 -1
- package/dist/providers/container-cache.constants.d.ts +11 -0
- package/dist/providers/container-cache.constants.js +9 -0
- package/dist/providers/container-cache.constants.js.map +1 -0
- package/dist/providers/container-cache.d.ts +4 -0
- package/dist/providers/container-cache.js +48 -0
- package/dist/providers/container-cache.js.map +1 -0
- package/dist/providers/container-cache.types.d.ts +8 -0
- package/dist/providers/container-cache.types.js +2 -0
- package/dist/providers/container-cache.types.js.map +1 -0
- package/dist/providers/container-plan.js +3 -1
- package/dist/providers/container-plan.js.map +1 -1
- package/dist/providers/container.js +31 -1
- package/dist/providers/container.js.map +1 -1
- package/dist/providers/container.types.d.ts +2 -0
- package/dist/providers/container.types.js.map +1 -1
- package/dist/providers/daytona-command.js +7 -4
- package/dist/providers/daytona-command.js.map +1 -1
- package/dist/providers/daytona.js +13 -4
- package/dist/providers/daytona.js.map +1 -1
- package/dist/providers/diagnostic-container.d.ts +4 -0
- package/dist/providers/diagnostic-container.js +15 -0
- package/dist/providers/diagnostic-container.js.map +1 -0
- package/dist/providers/docker.d.ts +1 -0
- package/dist/providers/docker.js.map +1 -1
- package/dist/providers/file-batches.constants.d.ts +10 -0
- package/dist/providers/file-batches.constants.js +57 -0
- package/dist/providers/file-batches.constants.js.map +1 -0
- package/dist/providers/file-batches.d.ts +2 -0
- package/dist/providers/file-batches.js +151 -0
- package/dist/providers/file-batches.js.map +1 -0
- package/dist/providers/podman.d.ts +1 -0
- package/dist/providers/podman.js.map +1 -1
- package/dist/providers/vercel.js +3 -1
- package/dist/providers/vercel.js.map +1 -1
- package/package.json +12 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cp, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, posix } from "node:path";
|
|
2
3
|
import { OutpostError } from "../domain/errors.js";
|
|
4
|
+
import { fileManifest, parseManifest, sameFile, sameLocalFile, } from "../infrastructure/file-manifest.js";
|
|
3
5
|
import { safeDestination } from "../infrastructure/files.js";
|
|
4
6
|
export async function downloadChanges(context, synchronized, transfer) {
|
|
5
7
|
const { workspace, lease, remoteBundle, run } = context;
|
|
@@ -12,13 +14,43 @@ export async function downloadChanges(context, synchronized, transfer) {
|
|
|
12
14
|
for (const file of incoming) {
|
|
13
15
|
if (/^\.outpost\/(locks|recovery|workspaces|logs)(\/|$)/i.test(file))
|
|
14
16
|
throw new OutpostError("workspace", "Remote file overlaps Outpost runtime state", { file });
|
|
15
|
-
await safeDestination(
|
|
16
|
-
await lease.download(posix.join(lease.root, file), await safeDestination(join(transfer, "incoming"), file));
|
|
17
|
+
await safeDestination(join(transfer, "incoming"), file);
|
|
17
18
|
}
|
|
19
|
+
let manifest;
|
|
20
|
+
if (lease.fileTransfers) {
|
|
21
|
+
manifest = parseManifest(await lease.fileTransfers.manifest(lease.root, incoming), incoming);
|
|
22
|
+
await writeFile(join(transfer, "manifest.json"), JSON.stringify(manifest), {
|
|
23
|
+
mode: 0o600,
|
|
24
|
+
});
|
|
25
|
+
const changed = [];
|
|
26
|
+
for (const entry of manifest) {
|
|
27
|
+
const previous = context.transferred?.get(entry.path);
|
|
28
|
+
if (previous &&
|
|
29
|
+
sameFile(previous, entry) &&
|
|
30
|
+
sameLocalFile(entry, await fileManifest(workspace.directory, entry.path))) {
|
|
31
|
+
const target = await safeDestination(join(transfer, "incoming"), entry.path);
|
|
32
|
+
await mkdir(dirname(target), { recursive: true });
|
|
33
|
+
await cp(await safeDestination(workspace.directory, entry.path), target, { dereference: false, verbatimSymlinks: true });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
changed.push(entry);
|
|
37
|
+
}
|
|
38
|
+
await lease.fileTransfers.downloadBatch(lease.root, changed, join(transfer, "incoming"));
|
|
39
|
+
for (const entry of manifest) {
|
|
40
|
+
if (!sameLocalFile(entry, await fileManifest(join(transfer, "incoming"), entry.path)))
|
|
41
|
+
throw new OutpostError("workspace", "Incoming file checksum mismatch", {
|
|
42
|
+
file: entry.path,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else
|
|
47
|
+
for (const file of incoming) {
|
|
48
|
+
await lease.download(posix.join(lease.root, file), await safeDestination(join(transfer, "incoming"), file));
|
|
49
|
+
}
|
|
18
50
|
if (head !== synchronized) {
|
|
19
51
|
await run(["bundle", "create", remoteBundle, "HEAD"]);
|
|
20
52
|
await lease.download(remoteBundle, join(transfer, "commits.bundle"));
|
|
21
53
|
}
|
|
22
|
-
return { head, patch, incoming };
|
|
54
|
+
return { head, patch, incoming, ...(manifest ? { manifest } : {}) };
|
|
23
55
|
}
|
|
24
56
|
//# sourceMappingURL=remote-download.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-download.js","sourceRoot":"","sources":["../../src/application/remote-download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"remote-download.js","sourceRoot":"","sources":["../../src/application/remote-download.ts"],"names":[],"mappings":"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","sourcesContent":["import { cp, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport {\n fileManifest,\n parseManifest,\n sameFile,\n sameLocalFile,\n} from \"../infrastructure/file-manifest.ts\";\nimport type { FileManifestEntry } from \"../domain/sandbox.types.ts\";\nimport { safeDestination } from \"../infrastructure/files.ts\";\nimport type {\n RemoteChanges,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\n\nexport async function downloadChanges(\n context: RemoteWorkspaceContext,\n synchronized: string,\n transfer: string,\n): Promise<RemoteChanges> {\n const { workspace, lease, remoteBundle, run } = context;\n const head = (await run([\"rev-parse\", \"HEAD\"])).trim(),\n patch = join(transfer, \"remote.patch\");\n await run([\"diff\", \"--binary\", \"HEAD\", `--output=${remoteBundle}.patch`]);\n await lease.download(`${remoteBundle}.patch`, patch);\n const incoming = (\n await run([\"ls-files\", \"--others\", \"--exclude-standard\", \"-z\"])\n )\n .split(\"\\0\")\n .filter(Boolean);\n for (const file of incoming) {\n if (/^\\.outpost\\/(locks|recovery|workspaces|logs)(\\/|$)/i.test(file))\n throw new OutpostError(\n \"workspace\",\n \"Remote file overlaps Outpost runtime state\",\n { file },\n );\n await safeDestination(join(transfer, \"incoming\"), file);\n }\n let manifest: readonly FileManifestEntry[] | undefined;\n if (lease.fileTransfers) {\n manifest = parseManifest(\n await lease.fileTransfers.manifest(lease.root, incoming),\n incoming,\n );\n await writeFile(join(transfer, \"manifest.json\"), JSON.stringify(manifest), {\n mode: 0o600,\n });\n const changed: FileManifestEntry[] = [];\n for (const entry of manifest) {\n const previous = context.transferred?.get(entry.path);\n if (\n previous &&\n sameFile(previous, entry) &&\n sameLocalFile(\n entry,\n await fileManifest(workspace.directory, entry.path),\n )\n ) {\n const target = await safeDestination(\n join(transfer, \"incoming\"),\n entry.path,\n );\n await mkdir(dirname(target), { recursive: true });\n await cp(\n await safeDestination(workspace.directory, entry.path),\n target,\n { dereference: false, verbatimSymlinks: true },\n );\n continue;\n }\n changed.push(entry);\n }\n await lease.fileTransfers.downloadBatch(\n lease.root,\n changed,\n join(transfer, \"incoming\"),\n );\n for (const entry of manifest) {\n if (\n !sameLocalFile(\n entry,\n await fileManifest(join(transfer, \"incoming\"), entry.path),\n )\n )\n throw new OutpostError(\"workspace\", \"Incoming file checksum mismatch\", {\n file: entry.path,\n });\n }\n } else\n for (const file of incoming) {\n await lease.download(\n posix.join(lease.root, file),\n await safeDestination(join(transfer, \"incoming\"), file),\n );\n }\n if (head !== synchronized) {\n await run([\"bundle\", \"create\", remoteBundle, \"HEAD\"]);\n await lease.download(remoteBundle, join(transfer, \"commits.bundle\"));\n }\n\n return { head, patch, incoming, ...(manifest ? { manifest } : {}) };\n}\n"]}
|
|
@@ -2,10 +2,29 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { OutpostError } from "../domain/errors.js";
|
|
4
4
|
import { git } from "../infrastructure/git/command.js";
|
|
5
|
+
import { remoteSyncLimits } from "./remote-workspace.constants.js";
|
|
5
6
|
import { digest } from "./workspace-fingerprint.js";
|
|
6
7
|
export async function validateChanges(context, changes, synchronized, expected, transfer) {
|
|
7
8
|
const { workspace, options, recovery, run, protectedFiles, originalHead, initialPatch, initialIndex, } = context;
|
|
8
9
|
const { head, patch, incoming } = changes;
|
|
10
|
+
const ignoredOverlaps = [];
|
|
11
|
+
for (let offset = 0; offset < incoming.length; offset += remoteSyncLimits.pathspecs) {
|
|
12
|
+
ignoredOverlaps.push(...(await git(workspace.directory, [
|
|
13
|
+
"ls-files",
|
|
14
|
+
"--others",
|
|
15
|
+
"--ignored",
|
|
16
|
+
"--exclude-standard",
|
|
17
|
+
"-z",
|
|
18
|
+
"--",
|
|
19
|
+
...incoming
|
|
20
|
+
.slice(offset, offset + remoteSyncLimits.pathspecs)
|
|
21
|
+
.map((path) => `:(literal)${path}`),
|
|
22
|
+
]))
|
|
23
|
+
.split("\0")
|
|
24
|
+
.filter(Boolean));
|
|
25
|
+
}
|
|
26
|
+
if (ignoredOverlaps.length)
|
|
27
|
+
throw new OutpostError("conflict", "Remote changes overlap ignored host files", { files: ignoredOverlaps, recovery: transfer });
|
|
9
28
|
if (protectedFiles.length) {
|
|
10
29
|
const changed = new Set([
|
|
11
30
|
...(await run(["diff", "--name-only", "-z", originalHead]))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-validation.js","sourceRoot":"","sources":["../../src/application/remote-validation.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 { git } from \"../infrastructure/git/command.ts\";\nimport type {\n RemoteChanges,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\nimport { digest } from \"./workspace-fingerprint.ts\";\n\nexport async function validateChanges(\n context: RemoteWorkspaceContext,\n changes: RemoteChanges,\n synchronized: string,\n expected: string,\n transfer: string,\n): Promise<void> {\n const {\n workspace,\n options,\n recovery,\n run,\n protectedFiles,\n originalHead,\n initialPatch,\n initialIndex,\n } = context;\n const { head, patch, incoming } = changes;\n if (protectedFiles.length) {\n const changed = new Set([\n ...(await run([\"diff\", \"--name-only\", \"-z\", originalHead]))\n .split(\"\\0\")\n .filter(Boolean),\n ...incoming,\n ]);\n const overlaps = protectedFiles.filter((file) =>\n [...changed].some(\n (path) =>\n path === file ||\n path.startsWith(`${file}/`) ||\n file.startsWith(`${path}/`),\n ),\n );\n if (overlaps.length)\n throw new OutpostError(\n \"conflict\",\n \"Remote changes overlap uncommitted host files\",\n { files: overlaps, recovery: transfer },\n );\n }\n if ((await digest(workspace.directory, recovery)) !== expected)\n throw new OutpostError(\n \"conflict\",\n \"Host workspace changed while the remote sandbox was active\",\n { recovery },\n );\n if (head !== synchronized) {\n await git(workspace.directory, [\n \"fetch\",\n join(transfer, \"commits.bundle\"),\n \"HEAD\",\n ]);\n await git(workspace.directory, [\n \"merge-base\",\n \"--is-ancestor\",\n \"HEAD\",\n \"FETCH_HEAD\",\n ]);\n }\n const validation = join(transfer, \"validation\");\n await git(workspace.directory, [\n \"worktree\",\n \"add\",\n \"--detach\",\n validation,\n head,\n ]);\n try {\n if ((await readFile(patch)).length)\n await git(validation, [\"apply\", \"--binary\", patch]);\n if (!options.includeUncommitted && (await readFile(initialPatch)).length)\n await git(validation, [\"apply\", \"--binary\", initialPatch]);\n if (!options.includeUncommitted && (await readFile(initialIndex)).length)\n await git(validation, [\"apply\", \"--cached\", \"--binary\", initialIndex]);\n } finally {\n await git(workspace.directory, [\n \"worktree\",\n \"remove\",\n \"--force\",\n validation,\n ]);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-validation.js","sourceRoot":"","sources":["../../src/application/remote-validation.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 { git } from \"../infrastructure/git/command.ts\";\nimport type {\n RemoteChanges,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\nimport { remoteSyncLimits } from \"./remote-workspace.constants.ts\";\nimport { digest } from \"./workspace-fingerprint.ts\";\n\nexport async function validateChanges(\n context: RemoteWorkspaceContext,\n changes: RemoteChanges,\n synchronized: string,\n expected: string,\n transfer: string,\n): Promise<void> {\n const {\n workspace,\n options,\n recovery,\n run,\n protectedFiles,\n originalHead,\n initialPatch,\n initialIndex,\n } = context;\n const { head, patch, incoming } = changes;\n const ignoredOverlaps: string[] = [];\n for (\n let offset = 0;\n offset < incoming.length;\n offset += remoteSyncLimits.pathspecs\n ) {\n ignoredOverlaps.push(\n ...(\n await git(workspace.directory, [\n \"ls-files\",\n \"--others\",\n \"--ignored\",\n \"--exclude-standard\",\n \"-z\",\n \"--\",\n ...incoming\n .slice(offset, offset + remoteSyncLimits.pathspecs)\n .map((path) => `:(literal)${path}`),\n ])\n )\n .split(\"\\0\")\n .filter(Boolean),\n );\n }\n if (ignoredOverlaps.length)\n throw new OutpostError(\n \"conflict\",\n \"Remote changes overlap ignored host files\",\n { files: ignoredOverlaps, recovery: transfer },\n );\n if (protectedFiles.length) {\n const changed = new Set([\n ...(await run([\"diff\", \"--name-only\", \"-z\", originalHead]))\n .split(\"\\0\")\n .filter(Boolean),\n ...incoming,\n ]);\n const overlaps = protectedFiles.filter((file) =>\n [...changed].some(\n (path) =>\n path === file ||\n path.startsWith(`${file}/`) ||\n file.startsWith(`${path}/`),\n ),\n );\n if (overlaps.length)\n throw new OutpostError(\n \"conflict\",\n \"Remote changes overlap uncommitted host files\",\n { files: overlaps, recovery: transfer },\n );\n }\n if ((await digest(workspace.directory, recovery)) !== expected)\n throw new OutpostError(\n \"conflict\",\n \"Host workspace changed while the remote sandbox was active\",\n { recovery },\n );\n if (head !== synchronized) {\n await git(workspace.directory, [\n \"fetch\",\n join(transfer, \"commits.bundle\"),\n \"HEAD\",\n ]);\n await git(workspace.directory, [\n \"merge-base\",\n \"--is-ancestor\",\n \"HEAD\",\n \"FETCH_HEAD\",\n ]);\n }\n const validation = join(transfer, \"validation\");\n await git(workspace.directory, [\n \"worktree\",\n \"add\",\n \"--detach\",\n validation,\n head,\n ]);\n try {\n if ((await readFile(patch)).length)\n await git(validation, [\"apply\", \"--binary\", patch]);\n if (!options.includeUncommitted && (await readFile(initialPatch)).length)\n await git(validation, [\"apply\", \"--binary\", initialPatch]);\n if (!options.includeUncommitted && (await readFile(initialIndex)).length)\n await git(validation, [\"apply\", \"--cached\", \"--binary\", initialIndex]);\n } finally {\n await git(workspace.directory, [\n \"worktree\",\n \"remove\",\n \"--force\",\n validation,\n ]);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-workspace.constants.js","sourceRoot":"","sources":["../../src/application/remote-workspace.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,SAAS,EAAE,EAAE,EAAW,CAAC","sourcesContent":["export const remoteSyncLimits = { pathspecs: 32 } as const;\n"]}
|
|
@@ -40,6 +40,8 @@ export async function seedRemote(workspace, lease, options = {}) {
|
|
|
40
40
|
...result,
|
|
41
41
|
recovery,
|
|
42
42
|
});
|
|
43
|
+
if (result.stdout.length >= gitDefaults.retainBytes)
|
|
44
|
+
throw new OutpostError("workspace", "Remote Git output exceeds synchronization limit", { recovery });
|
|
43
45
|
return result.stdout;
|
|
44
46
|
};
|
|
45
47
|
await run(["init"]);
|
|
@@ -86,6 +88,7 @@ export async function seedRemote(workspace, lease, options = {}) {
|
|
|
86
88
|
initializing = false;
|
|
87
89
|
let expected = await digest(workspace.directory, recovery);
|
|
88
90
|
const context = {
|
|
91
|
+
transferred: new Map(),
|
|
89
92
|
workspace,
|
|
90
93
|
lease,
|
|
91
94
|
options,
|
|
@@ -110,6 +113,9 @@ export async function seedRemote(workspace, lease, options = {}) {
|
|
|
110
113
|
await validateChanges(context, changes, synchronized, expected, transfer);
|
|
111
114
|
const backup = await backupHost(context, changes, synchronized, transfer);
|
|
112
115
|
await applyChanges(context, changes, backup, synchronized, expected, transfer);
|
|
116
|
+
context.transferred?.clear();
|
|
117
|
+
for (const entry of changes.manifest ?? [])
|
|
118
|
+
context.transferred?.set(entry.path, entry);
|
|
113
119
|
const { head } = changes;
|
|
114
120
|
synchronized = head;
|
|
115
121
|
expected = await digest(workspace.directory, recovery);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-workspace.js","sourceRoot":"","sources":["../../src/application/remote-workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rm } from \"node:fs/promises\";\nimport { join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport type { WorkspaceRecord } from \"../domain/workspace.types.ts\";\nimport { safeDestination } from \"../infrastructure/files.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { gitDefaults } from \"../infrastructure/git/git.constants.ts\";\nimport { applyChanges } from \"./remote-apply.ts\";\nimport { backupHost } from \"./remote-backup.ts\";\nimport { downloadChanges } from \"./remote-download.ts\";\nimport { validateChanges } from \"./remote-validation.ts\";\nimport type {\n RemoteSync,\n RemoteSyncOptions,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\nimport { digest, extras } from \"./workspace-fingerprint.ts\";\n\nexport type {\n RemoteSync,\n RemoteSyncOptions,\n} from \"./remote-workspace.types.ts\";\n\nexport async function seedRemote(\n workspace: WorkspaceRecord,\n lease: SandboxLease,\n options: RemoteSyncOptions = {},\n): Promise<RemoteSync> {\n options.signal?.throwIfAborted();\n const recovery = join(\n workspace.repository,\n \".outpost\",\n \"recovery\",\n randomUUID(),\n );\n await mkdir(recovery, { recursive: true });\n const bundle = join(recovery, \"initial.bundle\");\n await git(workspace.directory, [\"bundle\", \"create\", bundle, \"--all\", \"HEAD\"]);\n const remoteBundle = posix.join(\n lease.root.replaceAll(\"\\\\\", \"/\"),\n \"..\",\n `outpost-${randomUUID()}.bundle`,\n );\n await lease.upload(bundle, remoteBundle);\n let initializing = true,\n failed = false;\n const run = async (args: readonly string[]) => {\n let result;\n for (let attempt = 0; attempt < 3; attempt++) {\n result = await lease.invoke({\n executable: \"git\",\n arguments: args,\n directory: lease.root,\n retain: gitDefaults.retainBytes,\n ...(initializing && options.signal ? { signal: options.signal } : {}),\n ...(options.limits?.gitMs ? { deadlineMs: options.limits.gitMs } : {}),\n });\n if (!initializing || result.status !== 126) break;\n }\n if (!result)\n throw new OutpostError(\"workspace\", \"Remote Git did not start\");\n if (result.status !== 0)\n throw new OutpostError(\"workspace\", \"Remote Git operation failed\", {\n ...result,\n recovery,\n });\n return result.stdout;\n };\n await run([\"init\"]);\n await run([\"config\", \"core.autocrlf\", \"false\"]);\n await run([\"fetch\", remoteBundle, \"HEAD\"]);\n await run([\"checkout\", \"-B\", workspace.branch, \"FETCH_HEAD\"]);\n for (const key of [\"user.name\", \"user.email\"] as const) {\n const fallback = key === \"user.name\" ? \"Outpost\" : \"outpost@localhost\";\n const value =\n (\n await git(workspace.repository, [\"config\", \"--get\", key]).catch(\n () => fallback,\n )\n ).trim() || fallback;\n await run([\"config\", key, value]);\n }\n const initialPatch = join(recovery, \"initial.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--binary\",\n \"HEAD\",\n `--output=${initialPatch}`,\n ]);\n const initialIndex = join(recovery, \"initial-index.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--cached\",\n \"--binary\",\n `--output=${initialIndex}`,\n ]);\n const protectedFiles = options.includeUncommitted\n ? []\n : [\n ...(\n await git(workspace.directory, [\"diff\", \"--name-only\", \"-z\", \"HEAD\"])\n )\n .split(\"\\0\")\n .filter(Boolean),\n ...(await extras(workspace.directory)),\n ];\n const originalHead = (\n await git(workspace.directory, [\"rev-parse\", \"HEAD\"])\n ).trim();\n if (options.includeUncommitted && (await readFile(initialPatch)).length) {\n await lease.upload(initialPatch, `${remoteBundle}.patch`);\n await run([\"apply\", \"--binary\", `${remoteBundle}.patch`]);\n }\n for (const file of options.includeUncommitted\n ? await extras(workspace.directory)\n : [])\n await lease.upload(\n await safeDestination(workspace.directory, file),\n posix.join(lease.root, file),\n );\n let synchronized = (await run([\"rev-parse\", \"HEAD\"])).trim();\n initializing = false;\n let expected = await digest(workspace.directory, recovery);\n const context: RemoteWorkspaceContext = {\n workspace,\n lease,\n options,\n recovery,\n remoteBundle,\n run,\n protectedFiles,\n originalHead,\n initialPatch,\n initialIndex,\n };\n return {\n async close() {\n if (!failed) await rm(recovery, { recursive: true, force: true });\n },\n async pull() {\n const transfer = join(recovery, randomUUID());\n await mkdir(transfer, { recursive: true });\n try {\n const changes = await downloadChanges(context, synchronized, transfer);\n await validateChanges(\n context,\n changes,\n synchronized,\n expected,\n transfer,\n );\n const backup = await backupHost(\n context,\n changes,\n synchronized,\n transfer,\n );\n await applyChanges(\n context,\n changes,\n backup,\n synchronized,\n expected,\n transfer,\n );\n const { head } = changes;\n synchronized = head;\n expected = await digest(workspace.directory, recovery);\n await rm(transfer, { recursive: true, force: true });\n } catch (cause) {\n failed = true;\n throw new OutpostError(\n \"workspace\",\n \"Remote changes could not be fully synchronized; recovery files retained\",\n { recovery: transfer, directory: workspace.directory },\n cause,\n );\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-workspace.js","sourceRoot":"","sources":["../../src/application/remote-workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rm } from \"node:fs/promises\";\nimport { join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport type { WorkspaceRecord } from \"../domain/workspace.types.ts\";\nimport { safeDestination } from \"../infrastructure/files.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { gitDefaults } from \"../infrastructure/git/git.constants.ts\";\nimport { applyChanges } from \"./remote-apply.ts\";\nimport { backupHost } from \"./remote-backup.ts\";\nimport { downloadChanges } from \"./remote-download.ts\";\nimport { validateChanges } from \"./remote-validation.ts\";\nimport type {\n RemoteSync,\n RemoteSyncOptions,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\nimport { digest, extras } from \"./workspace-fingerprint.ts\";\n\nexport type {\n RemoteSync,\n RemoteSyncOptions,\n} from \"./remote-workspace.types.ts\";\n\nexport async function seedRemote(\n workspace: WorkspaceRecord,\n lease: SandboxLease,\n options: RemoteSyncOptions = {},\n): Promise<RemoteSync> {\n options.signal?.throwIfAborted();\n const recovery = join(\n workspace.repository,\n \".outpost\",\n \"recovery\",\n randomUUID(),\n );\n await mkdir(recovery, { recursive: true });\n const bundle = join(recovery, \"initial.bundle\");\n await git(workspace.directory, [\"bundle\", \"create\", bundle, \"--all\", \"HEAD\"]);\n const remoteBundle = posix.join(\n lease.root.replaceAll(\"\\\\\", \"/\"),\n \"..\",\n `outpost-${randomUUID()}.bundle`,\n );\n await lease.upload(bundle, remoteBundle);\n let initializing = true,\n failed = false;\n const run = async (args: readonly string[]) => {\n let result;\n for (let attempt = 0; attempt < 3; attempt++) {\n result = await lease.invoke({\n executable: \"git\",\n arguments: args,\n directory: lease.root,\n retain: gitDefaults.retainBytes,\n ...(initializing && options.signal ? { signal: options.signal } : {}),\n ...(options.limits?.gitMs ? { deadlineMs: options.limits.gitMs } : {}),\n });\n if (!initializing || result.status !== 126) break;\n }\n if (!result)\n throw new OutpostError(\"workspace\", \"Remote Git did not start\");\n if (result.status !== 0)\n throw new OutpostError(\"workspace\", \"Remote Git operation failed\", {\n ...result,\n recovery,\n });\n if (result.stdout.length >= gitDefaults.retainBytes)\n throw new OutpostError(\n \"workspace\",\n \"Remote Git output exceeds synchronization limit\",\n { recovery },\n );\n return result.stdout;\n };\n await run([\"init\"]);\n await run([\"config\", \"core.autocrlf\", \"false\"]);\n await run([\"fetch\", remoteBundle, \"HEAD\"]);\n await run([\"checkout\", \"-B\", workspace.branch, \"FETCH_HEAD\"]);\n for (const key of [\"user.name\", \"user.email\"] as const) {\n const fallback = key === \"user.name\" ? \"Outpost\" : \"outpost@localhost\";\n const value =\n (\n await git(workspace.repository, [\"config\", \"--get\", key]).catch(\n () => fallback,\n )\n ).trim() || fallback;\n await run([\"config\", key, value]);\n }\n const initialPatch = join(recovery, \"initial.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--binary\",\n \"HEAD\",\n `--output=${initialPatch}`,\n ]);\n const initialIndex = join(recovery, \"initial-index.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--cached\",\n \"--binary\",\n `--output=${initialIndex}`,\n ]);\n const protectedFiles = options.includeUncommitted\n ? []\n : [\n ...(\n await git(workspace.directory, [\"diff\", \"--name-only\", \"-z\", \"HEAD\"])\n )\n .split(\"\\0\")\n .filter(Boolean),\n ...(await extras(workspace.directory)),\n ];\n const originalHead = (\n await git(workspace.directory, [\"rev-parse\", \"HEAD\"])\n ).trim();\n if (options.includeUncommitted && (await readFile(initialPatch)).length) {\n await lease.upload(initialPatch, `${remoteBundle}.patch`);\n await run([\"apply\", \"--binary\", `${remoteBundle}.patch`]);\n }\n for (const file of options.includeUncommitted\n ? await extras(workspace.directory)\n : [])\n await lease.upload(\n await safeDestination(workspace.directory, file),\n posix.join(lease.root, file),\n );\n let synchronized = (await run([\"rev-parse\", \"HEAD\"])).trim();\n initializing = false;\n let expected = await digest(workspace.directory, recovery);\n const context: RemoteWorkspaceContext = {\n transferred: new Map(),\n workspace,\n lease,\n options,\n recovery,\n remoteBundle,\n run,\n protectedFiles,\n originalHead,\n initialPatch,\n initialIndex,\n };\n return {\n async close() {\n if (!failed) await rm(recovery, { recursive: true, force: true });\n },\n async pull() {\n const transfer = join(recovery, randomUUID());\n await mkdir(transfer, { recursive: true });\n try {\n const changes = await downloadChanges(context, synchronized, transfer);\n await validateChanges(\n context,\n changes,\n synchronized,\n expected,\n transfer,\n );\n const backup = await backupHost(\n context,\n changes,\n synchronized,\n transfer,\n );\n await applyChanges(\n context,\n changes,\n backup,\n synchronized,\n expected,\n transfer,\n );\n context.transferred?.clear();\n for (const entry of changes.manifest ?? [])\n context.transferred?.set(entry.path, entry);\n const { head } = changes;\n synchronized = head;\n expected = await digest(workspace.directory, recovery);\n await rm(transfer, { recursive: true, force: true });\n } catch (cause) {\n failed = true;\n throw new OutpostError(\n \"workspace\",\n \"Remote changes could not be fully synchronized; recovery files retained\",\n { recovery: transfer, directory: workspace.directory },\n cause,\n );\n }\n },\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SandboxLease } from "../domain/sandbox.types.ts";
|
|
1
|
+
import type { FileManifestEntry, SandboxLease } from "../domain/sandbox.types.ts";
|
|
2
2
|
import type { StageLimits, WorkspaceRecord } from "../domain/workspace.types.ts";
|
|
3
3
|
export interface RemoteSync {
|
|
4
4
|
pull(): Promise<void>;
|
|
@@ -10,6 +10,7 @@ export interface RemoteSyncOptions {
|
|
|
10
10
|
readonly limits?: StageLimits;
|
|
11
11
|
}
|
|
12
12
|
export interface RemoteWorkspaceContext {
|
|
13
|
+
readonly transferred?: Map<string, FileManifestEntry>;
|
|
13
14
|
readonly workspace: WorkspaceRecord;
|
|
14
15
|
readonly lease: SandboxLease;
|
|
15
16
|
readonly options: RemoteSyncOptions;
|
|
@@ -22,6 +23,7 @@ export interface RemoteWorkspaceContext {
|
|
|
22
23
|
readonly initialIndex: string;
|
|
23
24
|
}
|
|
24
25
|
export interface RemoteChanges {
|
|
26
|
+
readonly manifest?: readonly FileManifestEntry[];
|
|
25
27
|
readonly head: string;
|
|
26
28
|
readonly patch: string;
|
|
27
29
|
readonly incoming: readonly string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-workspace.types.js","sourceRoot":"","sources":["../../src/application/remote-workspace.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"remote-workspace.types.js","sourceRoot":"","sources":["../../src/application/remote-workspace.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n FileManifestEntry,\n SandboxLease,\n} from \"../domain/sandbox.types.ts\";\nimport type {\n StageLimits,\n WorkspaceRecord,\n} from \"../domain/workspace.types.ts\";\n\nexport interface RemoteSync {\n pull(): Promise<void>;\n close(): Promise<void>;\n}\n\nexport interface RemoteSyncOptions {\n readonly includeUncommitted?: boolean;\n readonly signal?: AbortSignal;\n readonly limits?: StageLimits;\n}\n\nexport interface RemoteWorkspaceContext {\n readonly transferred?: Map<string, FileManifestEntry>;\n readonly workspace: WorkspaceRecord;\n readonly lease: SandboxLease;\n readonly options: RemoteSyncOptions;\n readonly recovery: string;\n readonly remoteBundle: string;\n readonly run: (args: readonly string[]) => Promise<string>;\n readonly protectedFiles: readonly string[];\n readonly originalHead: string;\n readonly initialPatch: string;\n readonly initialIndex: string;\n}\nexport interface RemoteChanges {\n readonly manifest?: readonly FileManifestEntry[];\n readonly head: string;\n readonly patch: string;\n readonly incoming: readonly string[];\n}\nexport interface HostBackup {\n readonly previousPatch: string;\n readonly previousExtras: readonly string[];\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import { OutpostError } from "../domain/errors.js";
|
|
|
2
2
|
import { validateBrief } from "../domain/prompts.js";
|
|
3
3
|
import { registerCleanup } from "../infrastructure/shutdown.js";
|
|
4
4
|
import { validateDispatch } from "./dispatch-validation.js";
|
|
5
|
+
import { diagnoseSandbox } from "./doctor-sandbox.js";
|
|
5
6
|
import { operationGate } from "./operation-gate.js";
|
|
6
7
|
import { sandboxAgents } from "./sandbox-agents.js";
|
|
7
8
|
import { attachInSandbox } from "./sandbox-attach.js";
|
|
@@ -32,6 +33,16 @@ export async function createSandbox(options = {}) {
|
|
|
32
33
|
validateBrief(settings.brief, true);
|
|
33
34
|
return exclusive(() => attachInSandbox(context, agents, settings));
|
|
34
35
|
},
|
|
36
|
+
diagnose(settings = {}) {
|
|
37
|
+
settings.signal?.throwIfAborted();
|
|
38
|
+
return exclusive(() => diagnoseSandbox(runtime, {
|
|
39
|
+
...settings,
|
|
40
|
+
provider: context.provider,
|
|
41
|
+
signal: settings.signal
|
|
42
|
+
? AbortSignal.any([settings.signal, stop.signal])
|
|
43
|
+
: stop.signal,
|
|
44
|
+
}));
|
|
45
|
+
},
|
|
35
46
|
command(command) {
|
|
36
47
|
command.signal?.throwIfAborted();
|
|
37
48
|
return exclusive(async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/application/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAA0B,EAAE;IAE5B,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IACjE,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC;IAC3B,IAAI,OAAsC,CAAC;IAC3C,MAAM,MAAM,GAAY;QACtB,SAAS;QACT,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,QAAQ,CAAC,QAAQ;YACf,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC3B,OAAO,SAAS,CAAC,GAAG,EAAE,CACpB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CACrD,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,EAAE,EAAE,QAAQ;YACjB,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,CAAC,EAAE,EAAE,QAAQ;YACf,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,CAAC,QAAQ,GAAG,EAAE;YAClB,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;YAClC,aAAa,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACpC,OAAO,SAAS,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,CAAC,OAAO;YACb,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;YACjC,OAAO,SAAS,CAAC,KAAK,IAAI,EAAE;gBAC1B,IAAI,CAAC;oBACH,OAAO,MAAM,OAAO,CAAC,MAAM,CAAC;wBAC1B,GAAG,OAAO;wBACV,MAAM,EAAE,OAAO,CAAC,MAAM;4BACpB,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;4BAChD,CAAC,CAAC,IAAI,CAAC,MAAM;qBAChB,CAAC,CAAC;gBACL,CAAC;wBAAS,CAAC;oBACT,MAAM,IAAI,EAAE,IAAI,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,QAAQ,GAAG,EAAE;YACjB,IAAI,OAAO;gBAAE,OAAO,OAAO,CAAC;YAC5B,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE;gBACpB,IAAI,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC;gBAC1D,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;gBACnB,IAAI,OAAgB,CAAC;gBACrB,IAAI,CAAC;oBACH,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;oBACxB,MAAM,IAAI,EAAE,KAAK,EAAE,CAAC;gBACtB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,GAAG,KAAK,CAAC;gBAClB,CAAC;gBACD,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;gBAErB,UAAU,EAAE,CAAC;gBACb,MAAM,QAAQ,GAAG,KAAK;oBACpB,CAAC,CAAC,MAAM,SAAS,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;oBACrE,CAAC,CAAC,EAAE,CAAC;gBACP,IAAI,OAAO;oBAAE,MAAM,OAAO,CAAC;gBAC3B,OAAO,QAAQ,CAAC;YAClB,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;YACzB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;IACF,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3E,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { OutpostError } from \"../domain/errors.ts\";\nimport { validateBrief } from \"../domain/prompts.ts\";\nimport type { Disposal } from \"../domain/workspace.types.ts\";\nimport { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { validateDispatch } from \"./dispatch-validation.ts\";\nimport { operationGate } from \"./operation-gate.ts\";\nimport type { Sandbox, SandboxOptions } from \"./outpost.types.ts\";\nimport { sandboxAgents } from \"./sandbox-agents.ts\";\nimport { attachInSandbox } from \"./sandbox-attach.ts\";\nimport { dispatchInSandbox } from \"./sandbox-dispatch.ts\";\nimport { provisionSandbox } from \"./sandbox-provision.ts\";\n\nexport async function createSandbox(\n options: SandboxOptions = {},\n): Promise<Sandbox> {\n const context = await provisionSandbox(options);\n const { workspace, runtime, sync, stop, state, owned } = context;\n const agents = sandboxAgents(context);\n const gate = operationGate();\n const exclusive = gate.run;\n let closing: Promise<Disposal> | undefined;\n const result: Sandbox = {\n workspace,\n root: runtime.root,\n dispatch(settings) {\n validateDispatch(settings);\n return exclusive(() =>\n dispatchInSandbox(context, agents, result, settings),\n );\n },\n resume(id, settings) {\n return result.dispatch({ ...settings, continuation: { id } });\n },\n fork(id, settings) {\n return result.dispatch({ ...settings, continuation: { id, fork: true } });\n },\n attach(settings = {}) {\n settings.signal?.throwIfAborted();\n validateBrief(settings.brief, true);\n return exclusive(() => attachInSandbox(context, agents, settings));\n },\n command(command) {\n command.signal?.throwIfAborted();\n return exclusive(async () => {\n try {\n return await runtime.invoke({\n ...command,\n signal: command.signal\n ? AbortSignal.any([command.signal, stop.signal])\n : stop.signal,\n });\n } finally {\n await sync?.pull();\n }\n });\n },\n close(settings = {}) {\n if (closing) return closing;\n closing = (async () => {\n stop.abort(new OutpostError(\"aborted\", \"Sandbox closed\"));\n await gate.close();\n let failure: unknown;\n try {\n await runtime.release();\n await sync?.close();\n } catch (cause) {\n failure = cause;\n }\n state.active = false;\n\n unregister();\n const disposal = owned\n ? await workspace.close({ preserve: settings.preserve || !!failure })\n : {};\n if (failure) throw failure;\n return disposal;\n })();\n return closing;\n },\n async [Symbol.asyncDispose]() {\n await result.close();\n },\n };\n const unregister = registerCleanup(() => result.close({ preserve: true }));\n return result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/application/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { OutpostError } from \"../domain/errors.ts\";\nimport { validateBrief } from \"../domain/prompts.ts\";\nimport type { Disposal } from \"../domain/workspace.types.ts\";\nimport { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { validateDispatch } from \"./dispatch-validation.ts\";\nimport { diagnoseSandbox } from \"./doctor-sandbox.ts\";\nimport { operationGate } from \"./operation-gate.ts\";\nimport type { Sandbox, SandboxOptions } from \"./outpost.types.ts\";\nimport { sandboxAgents } from \"./sandbox-agents.ts\";\nimport { attachInSandbox } from \"./sandbox-attach.ts\";\nimport { dispatchInSandbox } from \"./sandbox-dispatch.ts\";\nimport { provisionSandbox } from \"./sandbox-provision.ts\";\n\nexport async function createSandbox(\n options: SandboxOptions = {},\n): Promise<Sandbox> {\n const context = await provisionSandbox(options);\n const { workspace, runtime, sync, stop, state, owned } = context;\n const agents = sandboxAgents(context);\n const gate = operationGate();\n const exclusive = gate.run;\n let closing: Promise<Disposal> | undefined;\n const result: Sandbox = {\n workspace,\n root: runtime.root,\n dispatch(settings) {\n validateDispatch(settings);\n return exclusive(() =>\n dispatchInSandbox(context, agents, result, settings),\n );\n },\n resume(id, settings) {\n return result.dispatch({ ...settings, continuation: { id } });\n },\n fork(id, settings) {\n return result.dispatch({ ...settings, continuation: { id, fork: true } });\n },\n attach(settings = {}) {\n settings.signal?.throwIfAborted();\n validateBrief(settings.brief, true);\n return exclusive(() => attachInSandbox(context, agents, settings));\n },\n diagnose(settings = {}) {\n settings.signal?.throwIfAborted();\n return exclusive(() =>\n diagnoseSandbox(runtime, {\n ...settings,\n provider: context.provider,\n signal: settings.signal\n ? AbortSignal.any([settings.signal, stop.signal])\n : stop.signal,\n }),\n );\n },\n command(command) {\n command.signal?.throwIfAborted();\n return exclusive(async () => {\n try {\n return await runtime.invoke({\n ...command,\n signal: command.signal\n ? AbortSignal.any([command.signal, stop.signal])\n : stop.signal,\n });\n } finally {\n await sync?.pull();\n }\n });\n },\n close(settings = {}) {\n if (closing) return closing;\n closing = (async () => {\n stop.abort(new OutpostError(\"aborted\", \"Sandbox closed\"));\n await gate.close();\n let failure: unknown;\n try {\n await runtime.release();\n await sync?.close();\n } catch (cause) {\n failure = cause;\n }\n state.active = false;\n\n unregister();\n const disposal = owned\n ? await workspace.close({ preserve: settings.preserve || !!failure })\n : {};\n if (failure) throw failure;\n return disposal;\n })();\n return closing;\n },\n async [Symbol.asyncDispose]() {\n await result.close();\n },\n };\n const unregister = registerCleanup(() => result.close({ preserve: true }));\n return result;\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AgentObservation } from "../domain/agent.types.ts";
|
|
2
|
+
import type { TaskContext } from "../domain/workflow.types.ts";
|
|
3
|
+
import type { TaskUsageObserver } from "./task-usage.types.ts";
|
|
4
|
+
export declare function taskUsage(context: TaskContext, observer: ((event: AgentObservation) => void) | undefined): TaskUsageObserver;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { addUsage } from "../domain/usage.js";
|
|
2
|
+
import { notify } from "./observation.js";
|
|
3
|
+
export function taskUsage(context, observer) {
|
|
4
|
+
const passes = new Map();
|
|
5
|
+
let total = { input: 0, cached: 0, output: 0 };
|
|
6
|
+
function difference(next, previous) {
|
|
7
|
+
return {
|
|
8
|
+
input: Math.max(0, next.input - previous.input),
|
|
9
|
+
cached: Math.max(0, next.cached - previous.cached),
|
|
10
|
+
output: Math.max(0, next.output - previous.output),
|
|
11
|
+
cacheCreated: Math.max(0, (next.cacheCreated ?? 0) - (previous.cacheCreated ?? 0)),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function report(usage) {
|
|
15
|
+
if (!Object.values(usage).some((value) => value > 0))
|
|
16
|
+
return;
|
|
17
|
+
total = addUsage(total, usage);
|
|
18
|
+
context.reportUsage(usage);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
observe(event) {
|
|
22
|
+
if (event.kind === "usage" || event.kind === "summary") {
|
|
23
|
+
const previous = passes.get(event.pass) ?? {
|
|
24
|
+
input: 0,
|
|
25
|
+
cached: 0,
|
|
26
|
+
output: 0,
|
|
27
|
+
};
|
|
28
|
+
const delta = event.kind === "usage"
|
|
29
|
+
? event.tokens
|
|
30
|
+
: difference(event.tokens, previous);
|
|
31
|
+
passes.set(event.pass, addUsage(previous, delta));
|
|
32
|
+
report(delta);
|
|
33
|
+
}
|
|
34
|
+
notify(observer, event);
|
|
35
|
+
},
|
|
36
|
+
reconcile(usage) {
|
|
37
|
+
report(difference(usage, total));
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=task-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-usage.js","sourceRoot":"","sources":["../../src/application/task-usage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,UAAU,SAAS,CACvB,OAAoB,EACpB,QAAyD;IAEzD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IACxC,IAAI,KAAK,GAAU,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACtD,SAAS,UAAU,CAAC,IAAW,EAAE,QAAe;QAC9C,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;YAC/C,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAClD,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAClD,YAAY,EAAE,IAAI,CAAC,GAAG,CACpB,CAAC,EACD,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,IAAI,CAAC,CAAC,CACxD;SACF,CAAC;IACJ,CAAC;IACD,SAAS,MAAM,CAAC,KAAY;QAC1B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;YAAE,OAAO;QAC7D,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC/B,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO;QACL,OAAO,CAAC,KAAK;YACX,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI;oBACzC,KAAK,EAAE,CAAC;oBACR,MAAM,EAAE,CAAC;oBACT,MAAM,EAAE,CAAC;iBACV,CAAC;gBACF,MAAM,KAAK,GACT,KAAK,CAAC,IAAI,KAAK,OAAO;oBACpB,CAAC,CAAC,KAAK,CAAC,MAAM;oBACd,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBACzC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;gBAClD,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;YACD,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,SAAS,CAAC,KAAK;YACb,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentObservation, Usage } from \"../domain/agent.types.ts\";\nimport { addUsage } from \"../domain/usage.ts\";\nimport type { TaskContext } from \"../domain/workflow.types.ts\";\nimport { notify } from \"./observation.ts\";\nimport type { TaskUsageObserver } from \"./task-usage.types.ts\";\n\nexport function taskUsage(\n context: TaskContext,\n observer: ((event: AgentObservation) => void) | undefined,\n): TaskUsageObserver {\n const passes = new Map<number, Usage>();\n let total: Usage = { input: 0, cached: 0, output: 0 };\n function difference(next: Usage, previous: Usage): Usage {\n return {\n input: Math.max(0, next.input - previous.input),\n cached: Math.max(0, next.cached - previous.cached),\n output: Math.max(0, next.output - previous.output),\n cacheCreated: Math.max(\n 0,\n (next.cacheCreated ?? 0) - (previous.cacheCreated ?? 0),\n ),\n };\n }\n function report(usage: Usage): void {\n if (!Object.values(usage).some((value) => value > 0)) return;\n total = addUsage(total, usage);\n context.reportUsage(usage);\n }\n return {\n observe(event) {\n if (event.kind === \"usage\" || event.kind === \"summary\") {\n const previous = passes.get(event.pass) ?? {\n input: 0,\n cached: 0,\n output: 0,\n };\n const delta =\n event.kind === \"usage\"\n ? event.tokens\n : difference(event.tokens, previous);\n passes.set(event.pass, addUsage(previous, delta));\n report(delta);\n }\n notify(observer, event);\n },\n reconcile(usage) {\n report(difference(usage, total));\n },\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-usage.types.js","sourceRoot":"","sources":["../../src/application/task-usage.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AgentObservation, Usage } from \"../domain/agent.types.ts\";\nexport interface TaskUsageObserver {\n observe(event: AgentObservation): void;\n reconcile(usage: Usage): void;\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { taskUsage } from "./task-usage.js";
|
|
1
2
|
import { OutpostError } from "../domain/errors.js";
|
|
2
3
|
import { task } from "../domain/workflow.js";
|
|
3
4
|
import { dispatch } from "./outpost.js";
|
|
@@ -5,14 +6,34 @@ export function agentTask(options) {
|
|
|
5
6
|
const { sandbox, request, ...definition } = options;
|
|
6
7
|
return task({
|
|
7
8
|
...definition,
|
|
8
|
-
perform
|
|
9
|
+
async perform(context) {
|
|
10
|
+
const options = request(context);
|
|
11
|
+
const usage = taskUsage(context, options.observe);
|
|
12
|
+
const result = await sandbox.dispatch({
|
|
13
|
+
...options,
|
|
14
|
+
signal: context.signal,
|
|
15
|
+
observe: usage.observe,
|
|
16
|
+
});
|
|
17
|
+
usage.reconcile(result.usage);
|
|
18
|
+
return result;
|
|
19
|
+
},
|
|
9
20
|
});
|
|
10
21
|
}
|
|
11
22
|
export function isolatedTask(options) {
|
|
12
23
|
const { request, ...definition } = options;
|
|
13
24
|
return task({
|
|
14
25
|
...definition,
|
|
15
|
-
perform
|
|
26
|
+
async perform(context) {
|
|
27
|
+
const options = request(context);
|
|
28
|
+
const usage = taskUsage(context, options.observe);
|
|
29
|
+
const result = await dispatch({
|
|
30
|
+
...options,
|
|
31
|
+
signal: context.signal,
|
|
32
|
+
observe: usage.observe,
|
|
33
|
+
});
|
|
34
|
+
usage.reconcile(result.usage);
|
|
35
|
+
return result;
|
|
36
|
+
},
|
|
16
37
|
});
|
|
17
38
|
}
|
|
18
39
|
export function commandTask(options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/application/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,IAAI,EAA+B,MAAM,uBAAuB,CAAC;AAE1E,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAOxC,MAAM,UAAU,SAAS,CACvB,OACqB;IAErB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;IACpD,OAAO,IAAI,CAAC;QACV,GAAG,UAAU;QACb,OAAO,
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/application/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,IAAI,EAA+B,MAAM,uBAAuB,CAAC;AAE1E,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAOxC,MAAM,UAAU,SAAS,CACvB,OACqB;IAErB,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;IACpD,OAAO,IAAI,CAAC;QACV,GAAG,UAAU;QACb,KAAK,CAAC,OAAO,CAAC,OAAO;YACnB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC;gBACpC,GAAG,OAAO;gBACV,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;YACH,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9B,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,OACwB;IAExB,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;IAC3C,OAAO,IAAI,CAAC;QACV,GAAG,UAAU;QACb,KAAK,CAAC,OAAO,CAAC,OAAO;YACnB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;gBAC5B,GAAG,OAAO;gBACV,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;YACH,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9B,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,OAAyE;IAEzE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC;IACpD,OAAO,IAAI,CAAC;QACV,GAAG,UAAU;QACb,KAAK,CAAC,OAAO,CAAC,OAAO;YACnB,MAAM,UAAU,GACd,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC;gBACnC,GAAG,UAAU;gBACb,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;YACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBACrB,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,yBAAyB,EAAE;oBAC3D,GAAG,MAAM;iBACV,CAAC,CAAC;YACL,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { taskUsage } from \"./task-usage.ts\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { CommandResult } from \"../domain/ports.ts\";\nimport { task, type Task, type TaskOptions } from \"../domain/workflow.ts\";\nimport type { DispatchResult } from \"./outpost.ts\";\nimport { dispatch } from \"./outpost.ts\";\nimport type {\n AgentTaskOptions,\n CommandTaskOptions,\n IsolatedTaskOptions,\n} from \"./tasks.types.ts\";\n\nexport function agentTask<T>(\n options: Omit<TaskOptions<DispatchResult<T>>, \"perform\"> &\n AgentTaskOptions<T>,\n): Task<DispatchResult<T>> {\n const { sandbox, request, ...definition } = options;\n return task({\n ...definition,\n async perform(context) {\n const options = request(context);\n const usage = taskUsage(context, options.observe);\n const result = await sandbox.dispatch({\n ...options,\n signal: context.signal,\n observe: usage.observe,\n });\n usage.reconcile(result.usage);\n return result;\n },\n });\n}\n\nexport function isolatedTask<T>(\n options: Omit<TaskOptions<DispatchResult<T>>, \"perform\"> &\n IsolatedTaskOptions<T>,\n): Task<DispatchResult<T>> {\n const { request, ...definition } = options;\n return task({\n ...definition,\n async perform(context) {\n const options = request(context);\n const usage = taskUsage(context, options.observe);\n const result = await dispatch({\n ...options,\n signal: context.signal,\n observe: usage.observe,\n });\n usage.reconcile(result.usage);\n return result;\n },\n });\n}\n\nexport function commandTask(\n options: Omit<TaskOptions<CommandResult>, \"perform\"> & CommandTaskOptions,\n): Task<CommandResult> {\n const { sandbox, command, ...definition } = options;\n return task({\n ...definition,\n async perform(context) {\n const invocation =\n typeof command === \"function\" ? command(context) : command;\n const result = await sandbox.command({\n ...invocation,\n signal: context.signal,\n });\n if (result.status !== 0)\n throw new OutpostError(\"process\", \"Workflow command failed\", {\n ...result,\n });\n return result;\n },\n });\n}\n"]}
|
package/dist/cli/commands.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
|
+
import { recoveryCommand } from "./recovery-command.js";
|
|
3
|
+
import { doctorCommand } from "./doctor-command.js";
|
|
2
4
|
import { imageCommand } from "./image-command.js";
|
|
3
5
|
import { initializeCommand } from "./init-command.js";
|
|
4
6
|
import { cliOptions, help } from "./main.constants.js";
|
|
@@ -13,6 +15,8 @@ export async function runCli(args = process.argv.slice(2)) {
|
|
|
13
15
|
return;
|
|
14
16
|
}
|
|
15
17
|
const commands = {
|
|
18
|
+
doctor: doctorCommand,
|
|
19
|
+
recovery: recoveryCommand,
|
|
16
20
|
init: initializeCommand,
|
|
17
21
|
image: imageCommand,
|
|
18
22
|
};
|
package/dist/cli/commands.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAA0B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,SAAS,CAAC;QAC3B,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE,UAAU;KACpB,CAAC,CAAC;IACH,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,GAAyC;QACrD,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,YAAY;KACpB,CAAC;IACF,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,MAAM,QAAQ,CAAC,IAAI,CAAE,CAAC,UAAU,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import { parseArgs } from \"node:util\";\nimport { imageCommand } from \"./image-command.ts\";\nimport { initializeCommand } from \"./init-command.ts\";\nimport { cliOptions, help } from \"./main.constants.ts\";\nimport type { CliCommand } from \"./main.types.ts\";\n\nexport async function runCli(\n args: readonly string[] = process.argv.slice(2),\n): Promise<void> {\n const invocation = parseArgs({\n args,\n allowPositionals: true,\n options: cliOptions,\n });\n if (invocation.values.help || !invocation.positionals.length) {\n process.stdout.write(help);\n return;\n }\n const commands: Readonly<Record<string, CliCommand>> = {\n init: initializeCommand,\n image: imageCommand,\n };\n const name = invocation.positionals[0]!;\n if (!Object.hasOwn(commands, name))\n throw new Error(\"Unknown command. Run outpost --help.\");\n await commands[name]!(invocation);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAA0B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,SAAS,CAAC;QAC3B,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE,UAAU;KACpB,CAAC,CAAC;IACH,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,GAAyC;QACrD,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,eAAe;QACzB,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,YAAY;KACpB,CAAC;IACF,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,MAAM,QAAQ,CAAC,IAAI,CAAE,CAAC,UAAU,CAAC,CAAC;AACpC,CAAC","sourcesContent":["import { parseArgs } from \"node:util\";\nimport { recoveryCommand } from \"./recovery-command.ts\";\nimport { doctorCommand } from \"./doctor-command.ts\";\nimport { imageCommand } from \"./image-command.ts\";\nimport { initializeCommand } from \"./init-command.ts\";\nimport { cliOptions, help } from \"./main.constants.ts\";\nimport type { CliCommand } from \"./main.types.ts\";\n\nexport async function runCli(\n args: readonly string[] = process.argv.slice(2),\n): Promise<void> {\n const invocation = parseArgs({\n args,\n allowPositionals: true,\n options: cliOptions,\n });\n if (invocation.values.help || !invocation.positionals.length) {\n process.stdout.write(help);\n return;\n }\n const commands: Readonly<Record<string, CliCommand>> = {\n doctor: doctorCommand,\n recovery: recoveryCommand,\n init: initializeCommand,\n image: imageCommand,\n };\n const name = invocation.positionals[0]!;\n if (!Object.hasOwn(commands, name))\n throw new Error(\"Unknown command. Run outpost --help.\");\n await commands[name]!(invocation);\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { doctorDefaults, providerDiagnostics, } from "../application/doctor.constants.js";
|
|
2
|
+
import { diagnose } from "../application/doctor.js";
|
|
3
|
+
import { invariant } from "../domain/errors.js";
|
|
4
|
+
function isProvider(value) {
|
|
5
|
+
return Object.hasOwn(providerDiagnostics, value);
|
|
6
|
+
}
|
|
7
|
+
export async function doctorCommand({ values, positionals }, execute, write = (text) => {
|
|
8
|
+
process.stdout.write(text);
|
|
9
|
+
}) {
|
|
10
|
+
invariant(positionals.length === 1, "Usage: outpost doctor [--provider NAME] [--agent NAME] [--image NAME] [--json]");
|
|
11
|
+
for (const key of Object.keys(values))
|
|
12
|
+
invariant(["provider", "agent", "image", "json"].includes(key), `Unsupported doctor option: --${key}`);
|
|
13
|
+
const provider = values.provider ?? doctorDefaults.provider;
|
|
14
|
+
const agent = values.agent ?? doctorDefaults.agent;
|
|
15
|
+
invariant(isProvider(provider), "Unknown provider. Choose docker, podman, local, vercel or daytona.");
|
|
16
|
+
invariant(agent === "codex" || agent === "claude", "Unknown agent. Choose codex or claude.");
|
|
17
|
+
const report = await diagnose({
|
|
18
|
+
provider,
|
|
19
|
+
agent,
|
|
20
|
+
...(values.image !== undefined ? { image: values.image } : {}),
|
|
21
|
+
}, execute);
|
|
22
|
+
if (values.json)
|
|
23
|
+
write(`${JSON.stringify(report, null, 2)}\n`);
|
|
24
|
+
else {
|
|
25
|
+
write(`Outpost doctor — ${report.scope === "host" ? "host checks" : "host and image checks"} (${provider}, ${agent})\n`);
|
|
26
|
+
write(`Provider contract: ${report.placement}; interactive terminal: ${report.interactiveTerminal ? "supported" : "unsupported"}. Workflow execution is not tested.\n`);
|
|
27
|
+
if (report.image)
|
|
28
|
+
write(`Image: ${report.image}\n`);
|
|
29
|
+
for (const check of report.checks)
|
|
30
|
+
write(`[${check.status.toUpperCase()}] ${check.id}${check.version ? ` ${check.version}` : ""}${check.referenceVersion ? ` (pinned: ${check.referenceVersion})` : ""}: ${check.message}\n`);
|
|
31
|
+
write(report.hasFailures
|
|
32
|
+
? "Diagnostic checks found failures.\n"
|
|
33
|
+
: "No blocking check failed; warnings and skipped checks still need review.\n");
|
|
34
|
+
}
|
|
35
|
+
if (report.hasFailures)
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=doctor-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-command.js","sourceRoot":"","sources":["../../src/cli/doctor-command.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n doctorDefaults,\n providerDiagnostics,\n} from \"../application/doctor.constants.ts\";\nimport { diagnose } from \"../application/doctor.ts\";\nimport type { DoctorProvider } from \"../application/doctor.types.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport type { CliInvocation } from \"./main.types.ts\";\n\nfunction isProvider(value: string): value is DoctorProvider {\n return Object.hasOwn(providerDiagnostics, value);\n}\nexport async function doctorCommand(\n { values, positionals }: CliInvocation,\n execute?: Executor,\n write: (text: string) => void = (text) => {\n process.stdout.write(text);\n },\n): Promise<void> {\n invariant(\n positionals.length === 1,\n \"Usage: outpost doctor [--provider NAME] [--agent NAME] [--image NAME] [--json]\",\n );\n for (const key of Object.keys(values))\n invariant(\n [\"provider\", \"agent\", \"image\", \"json\"].includes(key),\n `Unsupported doctor option: --${key}`,\n );\n const provider = values.provider ?? doctorDefaults.provider;\n const agent = values.agent ?? doctorDefaults.agent;\n invariant(\n isProvider(provider),\n \"Unknown provider. Choose docker, podman, local, vercel or daytona.\",\n );\n invariant(\n agent === \"codex\" || agent === \"claude\",\n \"Unknown agent. Choose codex or claude.\",\n );\n const report = await diagnose(\n {\n provider,\n agent,\n ...(values.image !== undefined ? { image: values.image } : {}),\n },\n execute,\n );\n if (values.json) write(`${JSON.stringify(report, null, 2)}\\n`);\n else {\n write(\n `Outpost doctor — ${report.scope === \"host\" ? \"host checks\" : \"host and image checks\"} (${provider}, ${agent})\\n`,\n );\n write(\n `Provider contract: ${report.placement}; interactive terminal: ${report.interactiveTerminal ? \"supported\" : \"unsupported\"}. Workflow execution is not tested.\\n`,\n );\n if (report.image) write(`Image: ${report.image}\\n`);\n for (const check of report.checks)\n write(\n `[${check.status.toUpperCase()}] ${check.id}${check.version ? ` ${check.version}` : \"\"}${check.referenceVersion ? ` (pinned: ${check.referenceVersion})` : \"\"}: ${check.message}\\n`,\n );\n write(\n report.hasFailures\n ? \"Diagnostic checks found failures.\\n\"\n : \"No blocking check failed; warnings and skipped checks still need review.\\n\",\n );\n }\n if (report.hasFailures) process.exitCode = 1;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const help = "Outpost \u2014 sandboxed coding agents and workflows\n\noutpost init [--yes] [--agent codex|claude] [--provider docker|podman|vercel|daytona|local]\n [--manager npm|pnpm|yarn|bun] [--model NAME] [--install] [--build] [--image NAME] [--directory PATH] [--repository PATH]\noutpost image build|remove [--engine docker|podman] [--file PATH] [--image NAME] [--uid ID] [--gid ID]\n\nNode.js 24+ and Git are required. Run generated scripts with Node.js.\nInitialization writes run.ts and project files directly into --directory (default: current directory).\n--repository selects the target Git repository; relative paths are resolved from the workflow directory.\n";
|
|
1
|
+
export declare const help = "Outpost \u2014 sandboxed coding agents and workflows\n\noutpost init [--yes] [--agent codex|claude] [--provider docker|podman|vercel|daytona|local]\n [--manager npm|pnpm|yarn|bun] [--model NAME] [--install] [--build] [--image NAME] [--directory PATH] [--repository PATH]\noutpost image build|remove [--engine docker|podman] [--file PATH] [--image NAME] [--uid ID] [--gid ID]\n\noutpost doctor [--provider docker|podman|local|vercel|daytona] [--agent codex|claude] [--image NAME] [--json]\n\noutpost recovery inspect [--repository PATH] [--max-entries NUMBER] [--git] [--locks] [--json]\noutpost recovery verify --directory TRANSFER_PATH [--checksums] [--max-bytes NUMBER] [--restorability --repository PATH] [--json]\noutpost recovery prune --policy FILE [--repository PATH] [--apply] [--json]\n\nNode.js 24+ and Git are required. Run generated scripts with Node.js.\nInitialization writes run.ts and project files directly into --directory (default: current directory).\n--repository selects the target Git repository; relative paths are resolved from the workflow directory.\n";
|
|
2
2
|
export declare const cliOptions: {
|
|
3
3
|
readonly help: {
|
|
4
4
|
readonly type: "boolean";
|
|
@@ -14,6 +14,33 @@ export declare const cliOptions: {
|
|
|
14
14
|
readonly build: {
|
|
15
15
|
readonly type: "boolean";
|
|
16
16
|
};
|
|
17
|
+
readonly json: {
|
|
18
|
+
readonly type: "boolean";
|
|
19
|
+
};
|
|
20
|
+
readonly git: {
|
|
21
|
+
readonly type: "boolean";
|
|
22
|
+
};
|
|
23
|
+
readonly locks: {
|
|
24
|
+
readonly type: "boolean";
|
|
25
|
+
};
|
|
26
|
+
readonly restorability: {
|
|
27
|
+
readonly type: "boolean";
|
|
28
|
+
};
|
|
29
|
+
readonly apply: {
|
|
30
|
+
readonly type: "boolean";
|
|
31
|
+
};
|
|
32
|
+
readonly policy: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
};
|
|
35
|
+
readonly checksums: {
|
|
36
|
+
readonly type: "boolean";
|
|
37
|
+
};
|
|
38
|
+
readonly "max-bytes": {
|
|
39
|
+
readonly type: "string";
|
|
40
|
+
};
|
|
41
|
+
readonly "max-entries": {
|
|
42
|
+
readonly type: "string";
|
|
43
|
+
};
|
|
17
44
|
readonly agent: {
|
|
18
45
|
readonly type: "string";
|
|
19
46
|
};
|