@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,30 @@
|
|
|
1
|
+
import type { SandboxLease, ConversationStore } from "../domain/ports.ts";
|
|
2
|
+
export type ConversationFormat = "claude" | "codex";
|
|
3
|
+
export interface ConversationLocation {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly file: string;
|
|
6
|
+
readonly format: ConversationFormat;
|
|
7
|
+
}
|
|
8
|
+
export declare function nativeConversations(format: ConversationFormat): ConversationStore;
|
|
9
|
+
export declare const conversations: {
|
|
10
|
+
native: typeof nativeConversations;
|
|
11
|
+
locate: typeof locateConversation;
|
|
12
|
+
capture: typeof captureConversation;
|
|
13
|
+
restore: typeof restoreConversation;
|
|
14
|
+
rewrite: typeof relocateTranscript;
|
|
15
|
+
projectKey: typeof projectKey;
|
|
16
|
+
claudePath(id: string, repository: string, home?: string): string;
|
|
17
|
+
directory(format: ConversationFormat, repository: string, home?: string): string;
|
|
18
|
+
destination: typeof remotePath;
|
|
19
|
+
};
|
|
20
|
+
export declare function projectKey(path: string): string;
|
|
21
|
+
export declare function locateConversation(format: ConversationFormat, id: string, repository: string, home?: string): Promise<ConversationLocation>;
|
|
22
|
+
export declare function relocateTranscript(text: string, destination: string, source?: string): string;
|
|
23
|
+
declare function remotePath(format: ConversationFormat, id: string, lease: SandboxLease, original: string): string;
|
|
24
|
+
export declare function restoreConversation(location: ConversationLocation, lease: SandboxLease, staging: string): Promise<void>;
|
|
25
|
+
export declare function captureConversation(format: ConversationFormat, id: string, repository: string, lease: SandboxLease, staging: string, options?: {
|
|
26
|
+
home?: string;
|
|
27
|
+
warn?: (message: string) => void;
|
|
28
|
+
local?: boolean;
|
|
29
|
+
}): Promise<ConversationLocation>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, rm, stat, writeFile, } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, join, posix, resolve, relative } from "node:path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { OutpostError, invariant } from "../domain/errors.js";
|
|
6
|
+
import { safeDestination } from "./files.js";
|
|
7
|
+
export function nativeConversations(format) {
|
|
8
|
+
return {
|
|
9
|
+
name: format,
|
|
10
|
+
locate(id, repository, home) {
|
|
11
|
+
return locateConversation(format, id, repository, home);
|
|
12
|
+
},
|
|
13
|
+
capture(id, context) {
|
|
14
|
+
return captureConversation(format, id, context.repository, context.sandbox, context.staging, context);
|
|
15
|
+
},
|
|
16
|
+
restore(record, context) {
|
|
17
|
+
invariant(record.format === format, "Conversation format does not match its storage");
|
|
18
|
+
return restoreConversation({ ...record, format }, context.sandbox, context.staging);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export const conversations = {
|
|
23
|
+
native: nativeConversations,
|
|
24
|
+
locate: locateConversation,
|
|
25
|
+
capture: captureConversation,
|
|
26
|
+
restore: restoreConversation,
|
|
27
|
+
rewrite: relocateTranscript,
|
|
28
|
+
projectKey,
|
|
29
|
+
claudePath(id, repository, home = homedir()) {
|
|
30
|
+
validId(id);
|
|
31
|
+
return join(home, ".claude", "projects", projectKey(resolve(repository)), `${id}.jsonl`);
|
|
32
|
+
},
|
|
33
|
+
directory(format, repository, home = homedir()) {
|
|
34
|
+
return format === "claude"
|
|
35
|
+
? join(home, ".claude", "projects", projectKey(resolve(repository)))
|
|
36
|
+
: join(home, ".codex", "sessions");
|
|
37
|
+
},
|
|
38
|
+
destination: remotePath,
|
|
39
|
+
};
|
|
40
|
+
export function projectKey(path) {
|
|
41
|
+
return path.replace(/[^a-zA-Z0-9]/g, "-");
|
|
42
|
+
}
|
|
43
|
+
function validId(id) {
|
|
44
|
+
invariant(/^[A-Za-z0-9_-]+$/.test(id), "Invalid conversation identifier");
|
|
45
|
+
}
|
|
46
|
+
async function files(root) {
|
|
47
|
+
const result = [];
|
|
48
|
+
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
49
|
+
if (error.code === "ENOENT")
|
|
50
|
+
return [];
|
|
51
|
+
throw error;
|
|
52
|
+
});
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const path = join(root, entry.name);
|
|
55
|
+
if (entry.isDirectory())
|
|
56
|
+
result.push(...(await files(path)));
|
|
57
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
58
|
+
result.push(path);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
export async function locateConversation(format, id, repository, home = homedir()) {
|
|
63
|
+
validId(id);
|
|
64
|
+
if (format === "claude") {
|
|
65
|
+
const expected = join(home, ".claude", "projects", projectKey(resolve(repository)), `${id}.jsonl`);
|
|
66
|
+
if ((await stat(expected).catch(() => undefined))?.isFile())
|
|
67
|
+
return { id, file: expected, format };
|
|
68
|
+
const found = (await files(join(home, ".claude", "projects"))).find((path) => basename(path) === `${id}.jsonl`);
|
|
69
|
+
if (found)
|
|
70
|
+
return { id, file: found, format };
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const found = (await files(join(home, ".codex", "sessions"))).find((path) => path.endsWith(`-${id}.jsonl`));
|
|
74
|
+
if (found)
|
|
75
|
+
return { id, file: found, format };
|
|
76
|
+
}
|
|
77
|
+
throw new OutpostError("session", `Conversation ${id} was not found in native ${format} storage`, { id, repository });
|
|
78
|
+
}
|
|
79
|
+
export function relocateTranscript(text, destination, source) {
|
|
80
|
+
const records = text.split("\n");
|
|
81
|
+
const origin = source ??
|
|
82
|
+
records.reduce((found, line) => {
|
|
83
|
+
if (found)
|
|
84
|
+
return found;
|
|
85
|
+
try {
|
|
86
|
+
const item = JSON.parse(line);
|
|
87
|
+
return item.cwd ?? item.payload?.cwd;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}, undefined);
|
|
93
|
+
function visit(value) {
|
|
94
|
+
if (Array.isArray(value))
|
|
95
|
+
return value.map(visit);
|
|
96
|
+
if (value && typeof value === "object")
|
|
97
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
98
|
+
key,
|
|
99
|
+
key === "cwd" && typeof item === "string" && item === origin
|
|
100
|
+
? destination
|
|
101
|
+
: visit(item),
|
|
102
|
+
]));
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
return records
|
|
106
|
+
.map((line) => {
|
|
107
|
+
if (!line.trim())
|
|
108
|
+
return line;
|
|
109
|
+
try {
|
|
110
|
+
const value = JSON.parse(line);
|
|
111
|
+
const rewritten = visit(value);
|
|
112
|
+
return JSON.stringify(value) === JSON.stringify(rewritten)
|
|
113
|
+
? line
|
|
114
|
+
: JSON.stringify(rewritten);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return line;
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
.join("\n");
|
|
121
|
+
}
|
|
122
|
+
function remotePath(format, id, lease, original) {
|
|
123
|
+
validId(id);
|
|
124
|
+
return format === "claude"
|
|
125
|
+
? posix.join(lease.home, ".claude", "projects", projectKey(lease.root), `${id}.jsonl`)
|
|
126
|
+
: posix.join(lease.home, ".codex", "sessions", ...new Date().toISOString().slice(0, 10).split("-"), basename(original));
|
|
127
|
+
}
|
|
128
|
+
export async function restoreConversation(location, lease, staging) {
|
|
129
|
+
const target = remotePath(location.format, location.id, lease, location.file);
|
|
130
|
+
const contents = relocateTranscript(await readFile(location.file, "utf8"), lease.root);
|
|
131
|
+
const temporary = join(staging, `${randomUUID()}.jsonl`);
|
|
132
|
+
await mkdir(staging, { recursive: true });
|
|
133
|
+
await writeFile(temporary, contents, { mode: 0o600 });
|
|
134
|
+
try {
|
|
135
|
+
await lease.upload(temporary, target);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
await rm(temporary, { force: true });
|
|
139
|
+
}
|
|
140
|
+
if (location.format === "claude") {
|
|
141
|
+
const sidecars = join(dirname(location.file), location.id, "subagents");
|
|
142
|
+
for (const file of await files(sidecars)) {
|
|
143
|
+
const name = relative(sidecars, file).replaceAll("\\", "/");
|
|
144
|
+
const local = join(staging, `${randomUUID()}.jsonl`);
|
|
145
|
+
await writeFile(local, relocateTranscript(await readFile(file, "utf8"), lease.root), { mode: 0o600 });
|
|
146
|
+
try {
|
|
147
|
+
await lease.upload(local, posix.join(posix.dirname(target), location.id, "subagents", name));
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
await rm(local, { force: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export async function captureConversation(format, id, repository, lease, staging, options = {}) {
|
|
156
|
+
validId(id);
|
|
157
|
+
const home = options.home ?? homedir();
|
|
158
|
+
const search = format === "claude"
|
|
159
|
+
? posix.join(lease.home, ".claude", "projects")
|
|
160
|
+
: posix.join(lease.home, ".codex", "sessions");
|
|
161
|
+
const pattern = format === "claude" ? `${id}.jsonl` : `*-${id}.jsonl`;
|
|
162
|
+
const result = options.local
|
|
163
|
+
? {
|
|
164
|
+
status: 0,
|
|
165
|
+
stdout: (await locateConversation(format, id, lease.root, lease.home))
|
|
166
|
+
.file,
|
|
167
|
+
}
|
|
168
|
+
: await lease.invoke({
|
|
169
|
+
executable: "find",
|
|
170
|
+
arguments: [search, "-type", "f", "-name", pattern],
|
|
171
|
+
retain: 1_048_576,
|
|
172
|
+
});
|
|
173
|
+
const remote = result.stdout.trim().split("\n").filter(Boolean)[0];
|
|
174
|
+
if (result.status !== 0 || !remote)
|
|
175
|
+
throw new OutpostError("session", `Agent emitted conversation ${id} but its transcript is unavailable`, { id, search });
|
|
176
|
+
const file = format === "claude"
|
|
177
|
+
? join(home, ".claude", "projects", projectKey(resolve(repository)), `${id}.jsonl`)
|
|
178
|
+
: join(home, ".codex", "sessions", ...new Date().toISOString().slice(0, 10).split("-"), posix.basename(remote.replaceAll("\\", "/")));
|
|
179
|
+
await mkdir(staging, { recursive: true });
|
|
180
|
+
const temporary = join(staging, `${randomUUID()}.jsonl`);
|
|
181
|
+
await lease.download(remote, temporary);
|
|
182
|
+
await mkdir(dirname(file), { recursive: true });
|
|
183
|
+
try {
|
|
184
|
+
await writeFile(file, relocateTranscript(await readFile(temporary, "utf8"), resolve(repository)), { mode: 0o600 });
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await rm(temporary, { force: true });
|
|
188
|
+
}
|
|
189
|
+
if (format === "claude") {
|
|
190
|
+
const remoteSidecars = posix.join(posix.dirname(remote), id, "subagents");
|
|
191
|
+
const listed = options.local
|
|
192
|
+
? {
|
|
193
|
+
status: 0,
|
|
194
|
+
stdout: (await files(join(dirname(remote), id, "subagents"))).join("\n"),
|
|
195
|
+
}
|
|
196
|
+
: await lease.invoke({
|
|
197
|
+
executable: "find",
|
|
198
|
+
arguments: [remoteSidecars, "-type", "f", "-name", "*.jsonl"],
|
|
199
|
+
retain: 1_048_576,
|
|
200
|
+
});
|
|
201
|
+
if (listed.status === 0)
|
|
202
|
+
for (const child of listed.stdout.split("\n").filter(Boolean)) {
|
|
203
|
+
try {
|
|
204
|
+
const scratch = join(staging, `${randomUUID()}.jsonl`);
|
|
205
|
+
await lease.download(child, scratch);
|
|
206
|
+
const destination = await safeDestination(join(dirname(file), id, "subagents"), posix.relative(remoteSidecars.replaceAll("\\", "/"), child.replaceAll("\\", "/")));
|
|
207
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
208
|
+
try {
|
|
209
|
+
await writeFile(destination, relocateTranscript(await readFile(scratch, "utf8"), resolve(repository)), { mode: 0o600 });
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
await rm(scratch, { force: true });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (cause) {
|
|
216
|
+
options.warn?.(`Could not capture child transcript: ${String(cause)}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return { id, file, format };
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/infrastructure/conversations.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n mkdir,\n readFile,\n readdir,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, posix, resolve, relative } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { OutpostError, invariant } from \"../domain/errors.ts\";\nimport type { SandboxLease, ConversationStore } from \"../domain/ports.ts\";\nimport { safeDestination } from \"./files.ts\";\n\nexport type ConversationFormat = \"claude\" | \"codex\";\nexport interface ConversationLocation {\n readonly id: string;\n readonly file: string;\n readonly format: ConversationFormat;\n}\n\nexport function nativeConversations(\n format: ConversationFormat,\n): ConversationStore {\n return {\n name: format,\n locate(id, repository, home) {\n return locateConversation(format, id, repository, home);\n },\n capture(id, context) {\n return captureConversation(\n format,\n id,\n context.repository,\n context.sandbox,\n context.staging,\n context,\n );\n },\n restore(record, context) {\n invariant(\n record.format === format,\n \"Conversation format does not match its storage\",\n );\n return restoreConversation(\n { ...record, format },\n context.sandbox,\n context.staging,\n );\n },\n };\n}\n\nexport const conversations = {\n native: nativeConversations,\n locate: locateConversation,\n capture: captureConversation,\n restore: restoreConversation,\n rewrite: relocateTranscript,\n projectKey,\n claudePath(id: string, repository: string, home = homedir()): string {\n validId(id);\n return join(\n home,\n \".claude\",\n \"projects\",\n projectKey(resolve(repository)),\n `${id}.jsonl`,\n );\n },\n directory(\n format: ConversationFormat,\n repository: string,\n home = homedir(),\n ): string {\n return format === \"claude\"\n ? join(home, \".claude\", \"projects\", projectKey(resolve(repository)))\n : join(home, \".codex\", \"sessions\");\n },\n destination: remotePath,\n};\n\nexport function projectKey(path: string): string {\n return path.replace(/[^a-zA-Z0-9]/g, \"-\");\n}\nfunction validId(id: string): void {\n invariant(/^[A-Za-z0-9_-]+$/.test(id), \"Invalid conversation identifier\");\n}\n\nasync function files(root: string): Promise<string[]> {\n const result: string[] = [];\n const entries = await readdir(root, { withFileTypes: true }).catch(\n (error) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw error;\n },\n );\n for (const entry of entries) {\n const path = join(root, entry.name);\n if (entry.isDirectory()) result.push(...(await files(path)));\n else if (entry.isFile() && entry.name.endsWith(\".jsonl\")) result.push(path);\n }\n return result;\n}\n\nexport async function locateConversation(\n format: ConversationFormat,\n id: string,\n repository: string,\n home = homedir(),\n): Promise<ConversationLocation> {\n validId(id);\n if (format === \"claude\") {\n const expected = join(\n home,\n \".claude\",\n \"projects\",\n projectKey(resolve(repository)),\n `${id}.jsonl`,\n );\n if ((await stat(expected).catch(() => undefined))?.isFile())\n return { id, file: expected, format };\n const found = (await files(join(home, \".claude\", \"projects\"))).find(\n (path) => basename(path) === `${id}.jsonl`,\n );\n if (found) return { id, file: found, format };\n } else {\n const found = (await files(join(home, \".codex\", \"sessions\"))).find((path) =>\n path.endsWith(`-${id}.jsonl`),\n );\n if (found) return { id, file: found, format };\n }\n throw new OutpostError(\n \"session\",\n `Conversation ${id} was not found in native ${format} storage`,\n { id, repository },\n );\n}\n\nexport function relocateTranscript(\n text: string,\n destination: string,\n source?: string,\n): string {\n const records = text.split(\"\\n\");\n const origin =\n source ??\n records.reduce<string | undefined>((found, line) => {\n if (found) return found;\n try {\n const item = JSON.parse(line);\n return item.cwd ?? item.payload?.cwd;\n } catch {\n return undefined;\n }\n }, undefined);\n function visit(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(visit);\n if (value && typeof value === \"object\")\n return Object.fromEntries(\n Object.entries(value).map(([key, item]) => [\n key,\n key === \"cwd\" && typeof item === \"string\" && item === origin\n ? destination\n : visit(item),\n ]),\n );\n return value;\n }\n return records\n .map((line) => {\n if (!line.trim()) return line;\n try {\n const value = JSON.parse(line);\n const rewritten = visit(value);\n return JSON.stringify(value) === JSON.stringify(rewritten)\n ? line\n : JSON.stringify(rewritten);\n } catch {\n return line;\n }\n })\n .join(\"\\n\");\n}\n\nfunction remotePath(\n format: ConversationFormat,\n id: string,\n lease: SandboxLease,\n original: string,\n): string {\n validId(id);\n return format === \"claude\"\n ? posix.join(\n lease.home,\n \".claude\",\n \"projects\",\n projectKey(lease.root),\n `${id}.jsonl`,\n )\n : posix.join(\n lease.home,\n \".codex\",\n \"sessions\",\n ...new Date().toISOString().slice(0, 10).split(\"-\"),\n basename(original),\n );\n}\n\nexport async function restoreConversation(\n location: ConversationLocation,\n lease: SandboxLease,\n staging: string,\n): Promise<void> {\n const target = remotePath(location.format, location.id, lease, location.file);\n const contents = relocateTranscript(\n await readFile(location.file, \"utf8\"),\n lease.root,\n );\n const temporary = join(staging, `${randomUUID()}.jsonl`);\n await mkdir(staging, { recursive: true });\n await writeFile(temporary, contents, { mode: 0o600 });\n try {\n await lease.upload(temporary, target);\n } finally {\n await rm(temporary, { force: true });\n }\n if (location.format === \"claude\") {\n const sidecars = join(dirname(location.file), location.id, \"subagents\");\n for (const file of await files(sidecars)) {\n const name = relative(sidecars, file).replaceAll(\"\\\\\", \"/\");\n const local = join(staging, `${randomUUID()}.jsonl`);\n await writeFile(\n local,\n relocateTranscript(await readFile(file, \"utf8\"), lease.root),\n { mode: 0o600 },\n );\n try {\n await lease.upload(\n local,\n posix.join(posix.dirname(target), location.id, \"subagents\", name),\n );\n } finally {\n await rm(local, { force: true });\n }\n }\n }\n}\n\nexport async function captureConversation(\n format: ConversationFormat,\n id: string,\n repository: string,\n lease: SandboxLease,\n staging: string,\n options: {\n home?: string;\n warn?: (message: string) => void;\n local?: boolean;\n } = {},\n): Promise<ConversationLocation> {\n validId(id);\n const home = options.home ?? homedir();\n const search =\n format === \"claude\"\n ? posix.join(lease.home, \".claude\", \"projects\")\n : posix.join(lease.home, \".codex\", \"sessions\");\n const pattern = format === \"claude\" ? `${id}.jsonl` : `*-${id}.jsonl`;\n const result = options.local\n ? {\n status: 0,\n stdout: (await locateConversation(format, id, lease.root, lease.home))\n .file,\n }\n : await lease.invoke({\n executable: \"find\",\n arguments: [search, \"-type\", \"f\", \"-name\", pattern],\n retain: 1_048_576,\n });\n const remote = result.stdout.trim().split(\"\\n\").filter(Boolean)[0];\n if (result.status !== 0 || !remote)\n throw new OutpostError(\n \"session\",\n `Agent emitted conversation ${id} but its transcript is unavailable`,\n { id, search },\n );\n const file =\n format === \"claude\"\n ? join(\n home,\n \".claude\",\n \"projects\",\n projectKey(resolve(repository)),\n `${id}.jsonl`,\n )\n : join(\n home,\n \".codex\",\n \"sessions\",\n ...new Date().toISOString().slice(0, 10).split(\"-\"),\n posix.basename(remote.replaceAll(\"\\\\\", \"/\")),\n );\n await mkdir(staging, { recursive: true });\n const temporary = join(staging, `${randomUUID()}.jsonl`);\n await lease.download(remote, temporary);\n await mkdir(dirname(file), { recursive: true });\n try {\n await writeFile(\n file,\n relocateTranscript(\n await readFile(temporary, \"utf8\"),\n resolve(repository),\n ),\n { mode: 0o600 },\n );\n } finally {\n await rm(temporary, { force: true });\n }\n if (format === \"claude\") {\n const remoteSidecars = posix.join(posix.dirname(remote), id, \"subagents\");\n const listed = options.local\n ? {\n status: 0,\n stdout: (await files(join(dirname(remote), id, \"subagents\"))).join(\n \"\\n\",\n ),\n }\n : await lease.invoke({\n executable: \"find\",\n arguments: [remoteSidecars, \"-type\", \"f\", \"-name\", \"*.jsonl\"],\n retain: 1_048_576,\n });\n if (listed.status === 0)\n for (const child of listed.stdout.split(\"\\n\").filter(Boolean)) {\n try {\n const scratch = join(staging, `${randomUUID()}.jsonl`);\n await lease.download(child, scratch);\n const destination = await safeDestination(\n join(dirname(file), id, \"subagents\"),\n posix.relative(\n remoteSidecars.replaceAll(\"\\\\\", \"/\"),\n child.replaceAll(\"\\\\\", \"/\"),\n ),\n );\n await mkdir(dirname(destination), { recursive: true });\n try {\n await writeFile(\n destination,\n relocateTranscript(\n await readFile(scratch, \"utf8\"),\n resolve(repository),\n ),\n { mode: 0o600 },\n );\n } finally {\n await rm(scratch, { force: true });\n }\n } catch (cause) {\n options.warn?.(\n `Could not capture child transcript: ${String(cause)}`,\n );\n }\n }\n }\n return { id, file, format };\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function expandPath(value: string, base?: string): string;
|
|
2
|
+
export declare function directory(value?: string): Promise<string>;
|
|
3
|
+
export declare function inside(root: string, path: string): boolean;
|
|
4
|
+
export declare function safeDestination(root: string, entry: string): Promise<string>;
|
|
5
|
+
export declare function copySelected(source: string, target: string, entries: readonly string[], deadlineMs?: number): Promise<void>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { cp, lstat, mkdir, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { OutpostError } from "../domain/errors.js";
|
|
5
|
+
export function expandPath(value, base = process.cwd()) {
|
|
6
|
+
const expanded = value === "~"
|
|
7
|
+
? homedir()
|
|
8
|
+
: /^~[\\/]/.test(value)
|
|
9
|
+
? resolve(homedir(), value.slice(2))
|
|
10
|
+
: value;
|
|
11
|
+
return resolve(base, expanded);
|
|
12
|
+
}
|
|
13
|
+
export async function directory(value = process.cwd()) {
|
|
14
|
+
const path = expandPath(value);
|
|
15
|
+
try {
|
|
16
|
+
if (!(await stat(path)).isDirectory())
|
|
17
|
+
throw new Error("Not a directory");
|
|
18
|
+
return await realpath(path);
|
|
19
|
+
}
|
|
20
|
+
catch (cause) {
|
|
21
|
+
throw new OutpostError("workspace", `Directory is unavailable: ${path}`, { path }, cause);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function inside(root, path) {
|
|
25
|
+
const rel = relative(resolve(root), resolve(path));
|
|
26
|
+
return (rel === "" ||
|
|
27
|
+
(!isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`)));
|
|
28
|
+
}
|
|
29
|
+
export async function safeDestination(root, entry) {
|
|
30
|
+
if (!entry ||
|
|
31
|
+
isAbsolute(entry) ||
|
|
32
|
+
entry
|
|
33
|
+
.split(/[\\/]/)
|
|
34
|
+
.some((part) => part === ".." || part.toLowerCase() === ".git") ||
|
|
35
|
+
entry.includes("\0"))
|
|
36
|
+
throw new OutpostError("workspace", `Unsafe workspace path: ${entry}`);
|
|
37
|
+
const target = resolve(root, entry);
|
|
38
|
+
if (!inside(root, target))
|
|
39
|
+
throw new OutpostError("workspace", "Path escapes workspace");
|
|
40
|
+
let parent = dirname(target);
|
|
41
|
+
while (inside(root, parent)) {
|
|
42
|
+
const info = await lstat(parent).catch((error) => {
|
|
43
|
+
if (error.code === "ENOENT")
|
|
44
|
+
return undefined;
|
|
45
|
+
throw error;
|
|
46
|
+
});
|
|
47
|
+
if (info?.isSymbolicLink())
|
|
48
|
+
throw new OutpostError("workspace", `Workspace path traverses a symlink: ${entry}`);
|
|
49
|
+
if (resolve(parent) === resolve(root))
|
|
50
|
+
break;
|
|
51
|
+
parent = dirname(parent);
|
|
52
|
+
}
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
export async function copySelected(source, target, entries, deadlineMs = 60_000) {
|
|
56
|
+
const started = Date.now();
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
if (!entry ||
|
|
59
|
+
isAbsolute(entry) ||
|
|
60
|
+
entry.split(/[\\/]/).some((part) => part === ".." || part === ".git"))
|
|
61
|
+
throw new OutpostError("configuration", `Unsafe copied path: ${entry}`);
|
|
62
|
+
const from = await safeDestination(source, entry), to = await safeDestination(target, entry);
|
|
63
|
+
if (!inside(source, from) || !inside(target, to))
|
|
64
|
+
throw new OutpostError("configuration", "Copy must stay within workspace boundaries");
|
|
65
|
+
const info = await lstat(from).catch((error) => {
|
|
66
|
+
if (error.code === "ENOENT")
|
|
67
|
+
return undefined;
|
|
68
|
+
throw error;
|
|
69
|
+
});
|
|
70
|
+
if (!info || resolve(from) === resolve(to))
|
|
71
|
+
continue;
|
|
72
|
+
await mkdir(dirname(to), { recursive: true });
|
|
73
|
+
await cp(from, to, {
|
|
74
|
+
recursive: true,
|
|
75
|
+
force: true,
|
|
76
|
+
dereference: false,
|
|
77
|
+
filter: () => {
|
|
78
|
+
if (Date.now() - started > deadlineMs)
|
|
79
|
+
throw new OutpostError("timeout", "Workspace copy deadline exceeded", { deadlineMs });
|
|
80
|
+
return true;
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/infrastructure/files.ts"],"names":[],"mappings":"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","sourcesContent":["import { cp, lstat, mkdir, realpath, stat } from \"node:fs/promises\";\nimport { dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { OutpostError } from \"../domain/errors.ts\";\n\nexport function expandPath(value: string, base = process.cwd()): string {\n const expanded =\n value === \"~\"\n ? homedir()\n : /^~[\\\\/]/.test(value)\n ? resolve(homedir(), value.slice(2))\n : value;\n return resolve(base, expanded);\n}\n\nexport async function directory(value = process.cwd()): Promise<string> {\n const path = expandPath(value);\n try {\n if (!(await stat(path)).isDirectory()) throw new Error(\"Not a directory\");\n return await realpath(path);\n } catch (cause) {\n throw new OutpostError(\n \"workspace\",\n `Directory is unavailable: ${path}`,\n { path },\n cause,\n );\n }\n}\n\nexport function inside(root: string, path: string): boolean {\n const rel = relative(resolve(root), resolve(path));\n return (\n rel === \"\" ||\n (!isAbsolute(rel) && rel !== \"..\" && !rel.startsWith(`..${sep}`))\n );\n}\n\nexport async function safeDestination(\n root: string,\n entry: string,\n): Promise<string> {\n if (\n !entry ||\n isAbsolute(entry) ||\n entry\n .split(/[\\\\/]/)\n .some((part) => part === \"..\" || part.toLowerCase() === \".git\") ||\n entry.includes(\"\\0\")\n )\n throw new OutpostError(\"workspace\", `Unsafe workspace path: ${entry}`);\n const target = resolve(root, entry);\n if (!inside(root, target))\n throw new OutpostError(\"workspace\", \"Path escapes workspace\");\n let parent = dirname(target);\n while (inside(root, parent)) {\n const info = await lstat(parent).catch((error) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw error;\n });\n if (info?.isSymbolicLink())\n throw new OutpostError(\n \"workspace\",\n `Workspace path traverses a symlink: ${entry}`,\n );\n if (resolve(parent) === resolve(root)) break;\n parent = dirname(parent);\n }\n return target;\n}\n\nexport async function copySelected(\n source: string,\n target: string,\n entries: readonly string[],\n deadlineMs = 60_000,\n): Promise<void> {\n const started = Date.now();\n for (const entry of entries) {\n if (\n !entry ||\n isAbsolute(entry) ||\n entry.split(/[\\\\/]/).some((part) => part === \"..\" || part === \".git\")\n )\n throw new OutpostError(\"configuration\", `Unsafe copied path: ${entry}`);\n const from = await safeDestination(source, entry),\n to = await safeDestination(target, entry);\n if (!inside(source, from) || !inside(target, to))\n throw new OutpostError(\n \"configuration\",\n \"Copy must stay within workspace boundaries\",\n );\n const info = await lstat(from).catch((error) => {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return undefined;\n throw error;\n });\n if (!info || resolve(from) === resolve(to)) continue;\n await mkdir(dirname(to), { recursive: true });\n await cp(from, to, {\n recursive: true,\n force: true,\n dereference: false,\n filter: () => {\n if (Date.now() - started > deadlineMs)\n throw new OutpostError(\n \"timeout\",\n \"Workspace copy deadline exceeded\",\n { deadlineMs },\n );\n return true;\n },\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BranchPolicy, Commit, Disposal, StageLimits, WorkspaceRecord } from "../domain/ports.ts";
|
|
2
|
+
export declare function git(cwd: string, args: readonly string[], deadlineMs?: number, stdin?: string): Promise<string>;
|
|
3
|
+
export declare function commits(cwd: string, baseline: string, deadlineMs?: number): Promise<Commit[]>;
|
|
4
|
+
export interface WorkspaceLease extends WorkspaceRecord {
|
|
5
|
+
integrate(): Promise<void>;
|
|
6
|
+
dispose(preserve?: boolean): Promise<Disposal>;
|
|
7
|
+
}
|
|
8
|
+
export declare function acquireWorkspace(options: {
|
|
9
|
+
repository?: string;
|
|
10
|
+
branch?: BranchPolicy;
|
|
11
|
+
copies?: readonly string[];
|
|
12
|
+
limits?: StageLimits;
|
|
13
|
+
label?: string;
|
|
14
|
+
}): Promise<WorkspaceLease>;
|