@heyditto/cli 2.10.0 → 2.12.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/README.md +74 -0
- package/dist/agents/launch.d.ts +11 -0
- package/dist/agents/launch.js +207 -9
- package/dist/agents/launch.js.map +1 -1
- package/dist/api.d.ts +129 -0
- package/dist/api.js +82 -0
- package/dist/api.js.map +1 -1
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +5 -1
- package/dist/commands.js.map +1 -1
- package/dist/org-commands.d.ts +52 -0
- package/dist/org-commands.js +511 -0
- package/dist/org-commands.js.map +1 -0
- package/dist/remote/attachments.d.ts +22 -0
- package/dist/remote/attachments.js +0 -0
- package/dist/remote/attachments.js.map +1 -0
- package/dist/remote/catalog.d.ts +18 -0
- package/dist/remote/catalog.js +309 -0
- package/dist/remote/catalog.js.map +1 -0
- package/dist/remote/controller.d.ts +76 -0
- package/dist/remote/controller.js +352 -0
- package/dist/remote/controller.js.map +1 -0
- package/dist/remote/fork-command.d.ts +8 -0
- package/dist/remote/fork-command.js +50 -0
- package/dist/remote/fork-command.js.map +1 -0
- package/dist/remote/fork.d.ts +22 -0
- package/dist/remote/fork.js +113 -0
- package/dist/remote/fork.js.map +1 -0
- package/dist/remote/headless.d.ts +30 -0
- package/dist/remote/headless.js +75 -0
- package/dist/remote/headless.js.map +1 -0
- package/dist/remote/hook.d.ts +2 -0
- package/dist/remote/hook.js +69 -0
- package/dist/remote/hook.js.map +1 -0
- package/dist/remote/hooks.d.ts +30 -0
- package/dist/remote/hooks.js +95 -0
- package/dist/remote/hooks.js.map +1 -0
- package/dist/remote/host.d.ts +63 -0
- package/dist/remote/host.js +240 -0
- package/dist/remote/host.js.map +1 -0
- package/dist/remote/protocol.d.ts +163 -0
- package/dist/remote/protocol.js +11 -0
- package/dist/remote/protocol.js.map +1 -0
- package/dist/remote/pty.d.ts +56 -0
- package/dist/remote/pty.js +100 -0
- package/dist/remote/pty.js.map +1 -0
- package/dist/store.d.ts +11 -0
- package/dist/store.js.map +1 -1
- package/dist/teleport/commands.d.ts +6 -0
- package/dist/teleport/commands.js +11 -0
- package/dist/teleport/commands.js.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { buildPrompt, downloadAttachments } from "./attachments.js";
|
|
3
|
+
import { commandInvocation, discoverCommands, watchCatalog } from "./catalog.js";
|
|
4
|
+
import { HostClient } from "./host.js";
|
|
5
|
+
export function harnessName(harness) {
|
|
6
|
+
return harness === "claude" ? "claude-code" : "codex";
|
|
7
|
+
}
|
|
8
|
+
const CLAUDE_PERMISSION_OPTIONS = [
|
|
9
|
+
{ id: "1", label: "Yes" },
|
|
10
|
+
{ id: "2", label: "Yes, and don't ask again this session" },
|
|
11
|
+
{ id: "3", label: "No, and tell Claude what to do differently" },
|
|
12
|
+
];
|
|
13
|
+
export class RemoteSession {
|
|
14
|
+
host;
|
|
15
|
+
ctx;
|
|
16
|
+
hooks;
|
|
17
|
+
pty;
|
|
18
|
+
runHeadless;
|
|
19
|
+
queue = [];
|
|
20
|
+
current;
|
|
21
|
+
draining = false;
|
|
22
|
+
stopped = false;
|
|
23
|
+
catalog = [];
|
|
24
|
+
stopWatching;
|
|
25
|
+
pending;
|
|
26
|
+
constructor(ctx, hooks) {
|
|
27
|
+
this.ctx = ctx;
|
|
28
|
+
this.hooks = hooks;
|
|
29
|
+
this.host = new HostClient({
|
|
30
|
+
baseUrl: ctx.baseUrl,
|
|
31
|
+
apiKey: ctx.apiKey,
|
|
32
|
+
capabilities: {
|
|
33
|
+
harnesses: ["claude-code", "codex"],
|
|
34
|
+
attachments: true,
|
|
35
|
+
headless: true,
|
|
36
|
+
teleport: Boolean(ctx.checkpoint),
|
|
37
|
+
},
|
|
38
|
+
log: ctx.log,
|
|
39
|
+
handlers: {
|
|
40
|
+
onTurn: (frame) => this.onTurn(frame),
|
|
41
|
+
onInterrupt: (turnId) => this.onInterrupt(turnId),
|
|
42
|
+
onCheckpoint: (request) => void this.onCheckpoint(request),
|
|
43
|
+
onPromptAnswer: (answer) => this.answerPrompt(answer),
|
|
44
|
+
},
|
|
45
|
+
...ctx.hostOptions,
|
|
46
|
+
});
|
|
47
|
+
hooks?.events.on("hook", (event) => this.onHook(event));
|
|
48
|
+
}
|
|
49
|
+
attachTui(pty) {
|
|
50
|
+
this.pty = pty;
|
|
51
|
+
}
|
|
52
|
+
attachHeadless(run) {
|
|
53
|
+
this.runHeadless = run;
|
|
54
|
+
}
|
|
55
|
+
/** The catalog last announced (tests and diagnostics). */
|
|
56
|
+
get commands() {
|
|
57
|
+
return this.catalog;
|
|
58
|
+
}
|
|
59
|
+
/** Connects (best effort), announces the session and its command catalog. Returns whether the backend answered in time. */
|
|
60
|
+
async start() {
|
|
61
|
+
const ok = await this.host.start();
|
|
62
|
+
this.host.announce({
|
|
63
|
+
sessionId: this.ctx.sessionId,
|
|
64
|
+
harness: harnessName(this.ctx.harness),
|
|
65
|
+
cwd: this.ctx.cwd,
|
|
66
|
+
mode: this.ctx.mode,
|
|
67
|
+
status: "idle",
|
|
68
|
+
});
|
|
69
|
+
await this.refreshCatalog();
|
|
70
|
+
this.stopWatching = watchCatalog(this.ctx.harness, this.ctx.cwd, () => void this.refreshCatalog());
|
|
71
|
+
return ok;
|
|
72
|
+
}
|
|
73
|
+
stop() {
|
|
74
|
+
this.stopped = true;
|
|
75
|
+
this.stopWatching?.();
|
|
76
|
+
this.host.close();
|
|
77
|
+
}
|
|
78
|
+
async refreshCatalog() {
|
|
79
|
+
try {
|
|
80
|
+
this.catalog = await discoverCommands(this.ctx.harness, this.ctx.cwd);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
this.ctx.log(`remote control: could not read the command catalog (${err instanceof Error ? err.message : String(err)})`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.host.commands({
|
|
87
|
+
sessionId: this.ctx.sessionId,
|
|
88
|
+
harness: harnessName(this.ctx.harness),
|
|
89
|
+
commands: this.catalog,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
finish(turnId, fields = {}) {
|
|
93
|
+
this.host.send({ type: "turn.finished", turnId, ...fields });
|
|
94
|
+
}
|
|
95
|
+
onTurn(frame) {
|
|
96
|
+
this.host.send({ type: "turn.ack", turnId: frame.turnId });
|
|
97
|
+
if (frame.sessionId !== this.ctx.sessionId) {
|
|
98
|
+
this.finish(frame.turnId, { error: "unknown session on this host" });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if ((frame.kind ?? "prompt") === "answer") {
|
|
102
|
+
// Not a turn of its own: the running turn is waiting on this.
|
|
103
|
+
this.deliverAnswer(frame);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.queue.push(frame);
|
|
107
|
+
void this.drain();
|
|
108
|
+
}
|
|
109
|
+
onInterrupt(turnId) {
|
|
110
|
+
const current = this.current;
|
|
111
|
+
if (!current || current.turnId !== turnId)
|
|
112
|
+
return;
|
|
113
|
+
current.interrupted = true;
|
|
114
|
+
if (current.headless)
|
|
115
|
+
current.headless.interrupt();
|
|
116
|
+
else
|
|
117
|
+
this.pty?.interrupt(this.ctx.harness);
|
|
118
|
+
}
|
|
119
|
+
async onCheckpoint(request) {
|
|
120
|
+
if (!this.ctx.checkpoint) {
|
|
121
|
+
this.host.send({ type: "checkpoint.failed", sessionId: request.sessionId, error: "teleport is not available on this host" });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const generation = await this.ctx.checkpoint(this.ctx.cwd);
|
|
126
|
+
this.host.send({ type: "checkpoint.done", sessionId: request.sessionId, generation });
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
this.host.send({ type: "checkpoint.failed", sessionId: request.sessionId, error: err instanceof Error ? err.message : String(err) });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// ---------------------------------------------------------------------
|
|
133
|
+
// Harness prompts (permission dialogs, questions)
|
|
134
|
+
// ---------------------------------------------------------------------
|
|
135
|
+
onHook(event) {
|
|
136
|
+
const payload = event.payload ?? {};
|
|
137
|
+
if (event.source === "claude") {
|
|
138
|
+
switch (event.event) {
|
|
139
|
+
case "UserPromptSubmit":
|
|
140
|
+
// Local typing also moves the session between running and idle, so
|
|
141
|
+
// the app sees the same state whether the prompt came from the
|
|
142
|
+
// phone or the keyboard.
|
|
143
|
+
this.clearPending();
|
|
144
|
+
this.host.status(this.ctx.sessionId, "running");
|
|
145
|
+
return;
|
|
146
|
+
case "Notification":
|
|
147
|
+
if (payload.notification_type === "permission_prompt") {
|
|
148
|
+
this.requestPrompt({
|
|
149
|
+
kind: "permission",
|
|
150
|
+
text: String(payload.message ?? payload.title ?? "Claude Code is asking for permission"),
|
|
151
|
+
options: CLAUDE_PERMISSION_OPTIONS,
|
|
152
|
+
default: "1",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
case "PreToolUse":
|
|
157
|
+
if (payload.tool_name === "AskUserQuestion")
|
|
158
|
+
this.requestQuestion(payload.tool_input);
|
|
159
|
+
return;
|
|
160
|
+
case "Stop":
|
|
161
|
+
this.turnEnded();
|
|
162
|
+
return;
|
|
163
|
+
default:
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (/turn-complete|turn-ended|agent-turn/.test(event.event))
|
|
168
|
+
this.turnEnded();
|
|
169
|
+
}
|
|
170
|
+
turnEnded() {
|
|
171
|
+
this.clearPending();
|
|
172
|
+
if (!this.current)
|
|
173
|
+
this.host.status(this.ctx.sessionId, "idle");
|
|
174
|
+
this.current?.done();
|
|
175
|
+
}
|
|
176
|
+
requestQuestion(toolInput) {
|
|
177
|
+
const input = (toolInput ?? {});
|
|
178
|
+
const q = input.questions?.[0];
|
|
179
|
+
if (!q)
|
|
180
|
+
return;
|
|
181
|
+
const options = (q.options ?? []).map((o, i) => ({ id: String(i + 1), label: o.label ?? `option ${i + 1}` }));
|
|
182
|
+
this.requestPrompt({
|
|
183
|
+
kind: options.length > 0 ? "choice" : "question",
|
|
184
|
+
text: [q.header, q.question].filter(Boolean).join(": "),
|
|
185
|
+
...(options.length > 0 ? { options } : {}),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
requestPrompt(prompt) {
|
|
189
|
+
const pending = { promptId: randomUUID(), sessionId: this.ctx.sessionId, ...prompt };
|
|
190
|
+
this.pending = pending;
|
|
191
|
+
const { keys: _keys, ...frame } = pending;
|
|
192
|
+
this.host.send({ type: "prompt.request", ...frame });
|
|
193
|
+
}
|
|
194
|
+
clearPending() {
|
|
195
|
+
this.pending = undefined;
|
|
196
|
+
}
|
|
197
|
+
/** `prompt.answer` frame: inject the keystrokes for the pending prompt. */
|
|
198
|
+
answerPrompt(answer) {
|
|
199
|
+
const pending = this.pending;
|
|
200
|
+
if (!pending || pending.promptId !== answer.promptId)
|
|
201
|
+
return false;
|
|
202
|
+
if (!this.pty)
|
|
203
|
+
return false;
|
|
204
|
+
const option = pending.options?.find((o) => o.id === answer.value || o.label.toLowerCase() === answer.value.trim().toLowerCase());
|
|
205
|
+
const keys = option ? (pending.keys?.[option.id] ?? option.id) : answer.value;
|
|
206
|
+
this.pty.write(keys);
|
|
207
|
+
// A digit hotkey confirms on its own in Claude Code; the extra Enter is a
|
|
208
|
+
// no-op there and submits typed text everywhere else.
|
|
209
|
+
setTimeout(() => this.pty?.write("\r"), 150).unref?.();
|
|
210
|
+
this.pending = undefined;
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
/** `turn.deliver {kind: "answer"}`: same as a `prompt.answer`, reported as an instant turn. */
|
|
214
|
+
deliverAnswer(frame) {
|
|
215
|
+
if (!frame.answer)
|
|
216
|
+
return this.finish(frame.turnId, { error: "answer turn without an answer" });
|
|
217
|
+
if (!this.pty)
|
|
218
|
+
return this.finish(frame.turnId, { exitCode: 0, unsupported: "headless sessions cannot answer harness prompts" });
|
|
219
|
+
if (!this.pending)
|
|
220
|
+
return this.finish(frame.turnId, { error: "no prompt is pending" });
|
|
221
|
+
if (!this.answerPrompt({ type: "prompt.answer", ...frame.answer }))
|
|
222
|
+
return this.finish(frame.turnId, { error: `prompt ${frame.answer.promptId} is not pending` });
|
|
223
|
+
this.finish(frame.turnId, { exitCode: 0 });
|
|
224
|
+
}
|
|
225
|
+
// ---------------------------------------------------------------------
|
|
226
|
+
// Turns
|
|
227
|
+
// ---------------------------------------------------------------------
|
|
228
|
+
async drain() {
|
|
229
|
+
if (this.draining)
|
|
230
|
+
return;
|
|
231
|
+
this.draining = true;
|
|
232
|
+
try {
|
|
233
|
+
while (this.queue.length > 0 && !this.stopped) {
|
|
234
|
+
const frame = this.queue.shift();
|
|
235
|
+
await this.runTurn(frame);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
this.draining = false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
lookupCommand(name) {
|
|
243
|
+
const bare = name.replace(/^[/$]/, "");
|
|
244
|
+
return this.catalog.find((c) => c.name === name || c.name === bare || c.name === `$${bare}` || c.name === `/${bare}`);
|
|
245
|
+
}
|
|
246
|
+
/** The text that goes into the harness for a turn, or a reason it cannot run here. */
|
|
247
|
+
async resolveText(frame) {
|
|
248
|
+
const attachments = await downloadAttachments(this.ctx.cwd, frame.turnId, frame.attachments);
|
|
249
|
+
if (attachments.length > 0) {
|
|
250
|
+
this.ctx.log(`remote control: ${attachments.length} attachment(s) saved under .tmp/ditto/attachments/${frame.turnId}`);
|
|
251
|
+
}
|
|
252
|
+
if ((frame.kind ?? "prompt") !== "command")
|
|
253
|
+
return { text: buildPrompt(frame.text, attachments) };
|
|
254
|
+
const name = frame.command?.name?.trim() || frame.text.trim().replace(/^[/$]/, "").split(/\s+/)[0];
|
|
255
|
+
if (!name)
|
|
256
|
+
return { unsupported: "command turn without a command name" };
|
|
257
|
+
const entry = this.lookupCommand(name);
|
|
258
|
+
const args = frame.command?.args ?? (frame.command ? undefined : frame.text.trim().split(/\s+/).slice(1).join(" "));
|
|
259
|
+
const invocation = commandInvocation(this.ctx.harness, entry, entry?.name ?? name, args);
|
|
260
|
+
if (this.ctx.mode === "headless") {
|
|
261
|
+
const headlessOk = entry ? entry.headless : false;
|
|
262
|
+
if (!headlessOk) {
|
|
263
|
+
return { unsupported: `${invocation.split(" ")[0]} only works in a terminal session (${entry ? "TUI-only builtin" : "not in this session's catalog"})` };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return { text: buildPrompt(invocation, attachments), entry };
|
|
267
|
+
}
|
|
268
|
+
async runTurn(frame) {
|
|
269
|
+
const { turnId } = frame;
|
|
270
|
+
this.host.status(this.ctx.sessionId, "running");
|
|
271
|
+
let resolved;
|
|
272
|
+
try {
|
|
273
|
+
resolved = await this.resolveText(frame);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
this.finish(turnId, { error: err instanceof Error ? err.message : String(err) });
|
|
277
|
+
this.host.status(this.ctx.sessionId, "idle");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if ("unsupported" in resolved) {
|
|
281
|
+
this.finish(turnId, { exitCode: 0, unsupported: resolved.unsupported });
|
|
282
|
+
this.host.status(this.ctx.sessionId, "idle");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const { text, entry } = resolved;
|
|
286
|
+
let finish = () => undefined;
|
|
287
|
+
const finished = new Promise((resolve) => {
|
|
288
|
+
finish = resolve;
|
|
289
|
+
});
|
|
290
|
+
this.current = { turnId, interrupted: false, done: finish };
|
|
291
|
+
try {
|
|
292
|
+
if (this.pty) {
|
|
293
|
+
this.ctx.log(`remote control: ${entry ? `command ${text.split("\n")[0]}` : "prompt"} received from the app (turn ${turnId.slice(0, 8)})`);
|
|
294
|
+
await this.pty.inject(text);
|
|
295
|
+
this.host.send({ type: "turn.started", turnId });
|
|
296
|
+
// Builtins (/model, /clear …) run no model turn, so no Stop hook
|
|
297
|
+
// fires; a short quiet period ends them.
|
|
298
|
+
const quiet = entry?.source === "builtin" ? Math.min(this.ctx.quietFallbackMs ?? 8000, 1500) : undefined;
|
|
299
|
+
await Promise.race([finished, this.pty.exit, this.quietFallback(finished, quiet)]);
|
|
300
|
+
this.finish(turnId, { exitCode: 0, ...(this.current.interrupted ? { interrupted: true } : {}) });
|
|
301
|
+
}
|
|
302
|
+
else if (this.runHeadless) {
|
|
303
|
+
const turn = this.runHeadless(text);
|
|
304
|
+
this.current.headless = turn;
|
|
305
|
+
this.host.send({ type: "turn.started", turnId });
|
|
306
|
+
const exitCode = await turn.exit;
|
|
307
|
+
this.finish(turnId, { exitCode, ...(this.current.interrupted ? { interrupted: true } : {}) });
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
this.finish(turnId, { error: "no harness attached" });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
this.finish(turnId, { error: err instanceof Error ? err.message : String(err) });
|
|
315
|
+
}
|
|
316
|
+
finally {
|
|
317
|
+
this.current = undefined;
|
|
318
|
+
this.clearPending();
|
|
319
|
+
this.host.status(this.ctx.sessionId, "idle");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Safety net when a harness never reports its turn end (older builds, hooks
|
|
324
|
+
* disabled, builtin commands): once the screen has been quiet for the limit
|
|
325
|
+
* after the prompt went in, the turn counts as finished. Hooks are the
|
|
326
|
+
* primary signal; a pending prompt never times out this way.
|
|
327
|
+
*/
|
|
328
|
+
quietFallback(finished, limitMs) {
|
|
329
|
+
const limit = limitMs ?? this.ctx.quietFallbackMs ?? 8000;
|
|
330
|
+
return new Promise((resolve) => {
|
|
331
|
+
let settled = false;
|
|
332
|
+
void finished.then(() => {
|
|
333
|
+
settled = true;
|
|
334
|
+
resolve();
|
|
335
|
+
});
|
|
336
|
+
const started = Date.now();
|
|
337
|
+
const timer = setInterval(() => {
|
|
338
|
+
if (settled)
|
|
339
|
+
return clearInterval(timer);
|
|
340
|
+
const pty = this.pty;
|
|
341
|
+
if (!pty || this.pending)
|
|
342
|
+
return;
|
|
343
|
+
if (Date.now() - started > Math.min(2000, limit) && pty.quietFor() >= limit) {
|
|
344
|
+
clearInterval(timer);
|
|
345
|
+
resolve();
|
|
346
|
+
}
|
|
347
|
+
}, 250);
|
|
348
|
+
timer.unref?.();
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/remote/controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type ForkOptions } from "./fork.js";
|
|
3
|
+
/**
|
|
4
|
+
* `heyditto fork <session>`: copy a coding session's conversation into a new
|
|
5
|
+
* session (optionally in a new worktree), locally or straight into Ditto Cloud.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cmdFork(ref: string, options: ForkOptions): Promise<void>;
|
|
8
|
+
export declare function registerForkCommand(program: Command, addExamples: (c: Command, ex: string) => Command): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { cmdTeleport } from "../teleport/commands.js";
|
|
2
|
+
import { forkSession } from "./fork.js";
|
|
3
|
+
/**
|
|
4
|
+
* `heyditto fork <session>`: copy a coding session's conversation into a new
|
|
5
|
+
* session (optionally in a new worktree), locally or straight into Ditto Cloud.
|
|
6
|
+
*/
|
|
7
|
+
export async function cmdFork(ref, options) {
|
|
8
|
+
const result = await forkSession(ref, options);
|
|
9
|
+
const { session } = result;
|
|
10
|
+
const asJson = options.output === "json";
|
|
11
|
+
if (options.cloud) {
|
|
12
|
+
const root = session.worktree ?? session.cwd;
|
|
13
|
+
process.stderr.write(`Forked ${result.from} → ${session.id}; teleporting ${root} to Ditto Cloud…\n`);
|
|
14
|
+
await cmdTeleport(root, {
|
|
15
|
+
cloud: true,
|
|
16
|
+
endpoint: options.endpoint,
|
|
17
|
+
prompt: options.prompt,
|
|
18
|
+
session: session.harnessSessionId,
|
|
19
|
+
harness: session.harness,
|
|
20
|
+
output: options.output,
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (asJson) {
|
|
25
|
+
process.stdout.write(`${JSON.stringify({ from: result.from, session, transcript: result.transcript }, null, 2)}\n`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
process.stdout.write(`Forked ${result.from} → ${session.id} (${session.harness}${session.worktree ? `, worktree ${session.worktree}` : ""}).\n`);
|
|
29
|
+
process.stdout.write(`Open it with: heyditto ${session.harness} --resume ${session.id}\n`);
|
|
30
|
+
}
|
|
31
|
+
export function registerForkCommand(program, addExamples) {
|
|
32
|
+
const fork = program
|
|
33
|
+
.command("fork <session>")
|
|
34
|
+
.description("start a new session from a copy of an existing conversation, locally or in Ditto Cloud")
|
|
35
|
+
.summary("fork a coding session")
|
|
36
|
+
.option("-w, --worktree [name]", "put the fork in <repo>/.worktrees/<name> (a fresh branch of the same name)")
|
|
37
|
+
.option("--cloud", "teleport the fork and resume it in a Ditto Code cloud job")
|
|
38
|
+
.option("-e, --endpoint <slug>", "inference endpoint for the cloud session (with --cloud)")
|
|
39
|
+
.option("--prompt <text>", "first instruction for the cloud session (with --cloud)")
|
|
40
|
+
.option("-o, --output <format>", "text (default) or json")
|
|
41
|
+
.option("--json", "print machine-readable output")
|
|
42
|
+
.action(async (ref, options) => {
|
|
43
|
+
await cmdFork(ref, { ...options, output: options.json ? "json" : options.output });
|
|
44
|
+
});
|
|
45
|
+
addExamples(fork, ` heyditto fork 3f1c… copy the conversation into a new local session
|
|
46
|
+
heyditto fork 3f1c… --worktree try-b same, on a fresh branch in .worktrees/try-b
|
|
47
|
+
heyditto fork 3f1c… --cloud continue the copy in Ditto Cloud
|
|
48
|
+
heyditto sessions find session ids`);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=fork-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork-command.js","sourceRoot":"","sources":["../../src/remote/fork-command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAoB,WAAW,EAAE,MAAM,WAAW,CAAC;AAE1D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,OAAoB;IAC7D,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC;IACzC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC;QAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO,CAAC,EAAE,iBAAiB,IAAI,oBAAoB,CAAC,CAAC;QACrG,MAAM,WAAW,CAAC,IAAI,EAAE;YACtB,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,gBAAgB;YACjC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACpH,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACjJ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,OAAO,CAAC,OAAO,aAAa,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB,EAAE,WAAgD;IACpG,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,wFAAwF,CAAC;SACrG,OAAO,CAAC,uBAAuB,CAAC;SAChC,MAAM,CAAC,uBAAuB,EAAE,4EAA4E,CAAC;SAC7G,MAAM,CAAC,SAAS,EAAE,2DAA2D,CAAC;SAC9E,MAAM,CAAC,uBAAuB,EAAE,yDAAyD,CAAC;SAC1F,MAAM,CAAC,iBAAiB,EAAE,wDAAwD,CAAC;SACnF,MAAM,CAAC,uBAAuB,EAAE,wBAAwB,CAAC;SACzD,MAAM,CAAC,QAAQ,EAAE,+BAA+B,CAAC;SACjD,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,OAAyC,EAAE,EAAE;QACvE,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;IACL,WAAW,CACT,IAAI,EACJ;;;2DAGuD,CACxD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SessionRecord } from "../agents/sessions.js";
|
|
2
|
+
/**
|
|
3
|
+
* `heyditto fork <session>`: a new session that starts from a copy of an
|
|
4
|
+
* existing conversation. Locally that is a copy of the harness transcript
|
|
5
|
+
* under a fresh id (optionally in a new git worktree); `--cloud` then
|
|
6
|
+
* teleports the fork and resumes it in Ditto Code.
|
|
7
|
+
*/
|
|
8
|
+
export interface ForkOptions {
|
|
9
|
+
worktree?: string | boolean;
|
|
10
|
+
cloud?: boolean;
|
|
11
|
+
endpoint?: string;
|
|
12
|
+
prompt?: string;
|
|
13
|
+
output?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ForkResult {
|
|
16
|
+
from: string;
|
|
17
|
+
session: SessionRecord;
|
|
18
|
+
transcript?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function forkSession(ref: string, options: ForkOptions): Promise<ForkResult>;
|
|
21
|
+
/** Lists transcripts present for a project dir; used by tests and diagnostics. */
|
|
22
|
+
export declare function listClaudeTranscripts(cwd: string): Promise<string[]>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { readSession, writeSession } from "../agents/sessions.js";
|
|
6
|
+
import { defaultWorktreeName, ensureWorktree, repoRoot, validWorktreeName } from "../agents/worktree.js";
|
|
7
|
+
import { canonicalCwd, locateClaude, locateCodex } from "../teleport/harness.js";
|
|
8
|
+
import { cwdSlug } from "../teleport/types.js";
|
|
9
|
+
async function exists(p) {
|
|
10
|
+
try {
|
|
11
|
+
await access(p);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function claudeProjectsDir() {
|
|
19
|
+
const home = process.env.CLAUDE_CONFIG_DIR?.trim() || path.join(os.homedir(), ".claude");
|
|
20
|
+
return path.join(home, "projects");
|
|
21
|
+
}
|
|
22
|
+
/** Copies a Claude transcript to a new session id (and, when the cwd moved, the new project slug). */
|
|
23
|
+
async function forkClaudeTranscript(oldId, newId, fromCwd, toCwd) {
|
|
24
|
+
const loc = await locateClaude(oldId, fromCwd);
|
|
25
|
+
if (!loc)
|
|
26
|
+
throw new Error(`no local Claude Code transcript for session ${oldId} under ${fromCwd}`);
|
|
27
|
+
const fromCanon = canonicalCwd(fromCwd);
|
|
28
|
+
const toCanon = canonicalCwd(toCwd);
|
|
29
|
+
const toDir = path.join(claudeProjectsDir(), cwdSlug(toCanon));
|
|
30
|
+
await mkdir(toDir, { recursive: true });
|
|
31
|
+
const jsonl = loc.files.find((f) => f.endsWith(`${oldId}.jsonl`));
|
|
32
|
+
if (!jsonl)
|
|
33
|
+
throw new Error(`transcript ${oldId}.jsonl not found`);
|
|
34
|
+
let text = await readFile(jsonl, "utf8");
|
|
35
|
+
text = text.split(oldId).join(newId);
|
|
36
|
+
if (fromCanon !== toCanon)
|
|
37
|
+
text = text.split(fromCanon).join(toCanon).split(fromCwd).join(toCwd);
|
|
38
|
+
const target = path.join(toDir, `${newId}.jsonl`);
|
|
39
|
+
await writeFile(target, text);
|
|
40
|
+
const subdir = path.join(path.dirname(jsonl), oldId);
|
|
41
|
+
if (await exists(subdir)) {
|
|
42
|
+
for (const f of loc.files.filter((x) => x.startsWith(`${subdir}${path.sep}`))) {
|
|
43
|
+
const dest = path.join(toDir, newId, path.relative(subdir, f));
|
|
44
|
+
await mkdir(path.dirname(dest), { recursive: true });
|
|
45
|
+
await writeFile(dest, (await readFile(f, "utf8")).split(oldId).join(newId));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return target;
|
|
49
|
+
}
|
|
50
|
+
/** Copies a Codex rollout to a new thread id: same directory, id rewritten in the file name and body. */
|
|
51
|
+
async function forkCodexTranscript(oldId, newId) {
|
|
52
|
+
const loc = await locateCodex(oldId);
|
|
53
|
+
if (!loc)
|
|
54
|
+
throw new Error(`no local Codex transcript for thread ${oldId}`);
|
|
55
|
+
let target = "";
|
|
56
|
+
for (const f of loc.files) {
|
|
57
|
+
const dest = path.join(path.dirname(f), path.basename(f).split(oldId).join(newId));
|
|
58
|
+
await writeFile(dest, (await readFile(f, "utf8")).split(oldId).join(newId));
|
|
59
|
+
target = target || dest;
|
|
60
|
+
}
|
|
61
|
+
return target;
|
|
62
|
+
}
|
|
63
|
+
export async function forkSession(ref, options) {
|
|
64
|
+
const record = await readSession(ref);
|
|
65
|
+
if (!record)
|
|
66
|
+
throw new Error(`no local session "${ref}"; see \`heyditto sessions\``);
|
|
67
|
+
const oldHarnessId = record.harnessSessionId ?? record.id;
|
|
68
|
+
const newId = randomUUID();
|
|
69
|
+
const sourceCwd = record.worktree ?? record.cwd;
|
|
70
|
+
let cwd = sourceCwd;
|
|
71
|
+
let worktree;
|
|
72
|
+
if (options.worktree !== undefined && options.worktree !== false) {
|
|
73
|
+
const name = typeof options.worktree === "string" ? options.worktree : defaultWorktreeName(record.harness);
|
|
74
|
+
if (!validWorktreeName(name))
|
|
75
|
+
throw new Error(`invalid worktree name "${name}"`);
|
|
76
|
+
const root = repoRoot(record.cwd);
|
|
77
|
+
if (!root)
|
|
78
|
+
throw new Error(`${record.cwd} is not inside a git repository; forks into a worktree need one`);
|
|
79
|
+
const wt = await ensureWorktree(root, name);
|
|
80
|
+
cwd = wt.path;
|
|
81
|
+
worktree = wt.path;
|
|
82
|
+
}
|
|
83
|
+
const transcript = record.harness === "claude"
|
|
84
|
+
? await forkClaudeTranscript(oldHarnessId, newId, sourceCwd, cwd)
|
|
85
|
+
: await forkCodexTranscript(oldHarnessId, newId);
|
|
86
|
+
const now = new Date().toISOString();
|
|
87
|
+
const session = {
|
|
88
|
+
id: newId,
|
|
89
|
+
harness: record.harness,
|
|
90
|
+
endpointId: record.endpointId,
|
|
91
|
+
endpointSlug: record.endpointSlug,
|
|
92
|
+
harnessSessionId: newId,
|
|
93
|
+
cwd: worktree ? record.cwd : cwd,
|
|
94
|
+
worktree,
|
|
95
|
+
model: record.model,
|
|
96
|
+
createdAt: now,
|
|
97
|
+
lastLaunchedAt: now,
|
|
98
|
+
launches: 1,
|
|
99
|
+
};
|
|
100
|
+
await writeSession(session);
|
|
101
|
+
return { from: record.id, session, transcript };
|
|
102
|
+
}
|
|
103
|
+
/** Lists transcripts present for a project dir; used by tests and diagnostics. */
|
|
104
|
+
export async function listClaudeTranscripts(cwd) {
|
|
105
|
+
const dir = path.join(claudeProjectsDir(), cwdSlug(canonicalCwd(cwd)));
|
|
106
|
+
try {
|
|
107
|
+
return (await readdir(dir)).filter((f) => f.endsWith(".jsonl")).sort();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=fork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.js","sourceRoot":"","sources":["../../src/remote/fork.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
import { type Harness, type PlanInput } from "../agents/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Headless Remote Control: no terminal UI. Each delivered turn runs one
|
|
5
|
+
* harness invocation (`claude -p --resume …` / `codex exec resume --last …`)
|
|
6
|
+
* with the same endpoint, key and session id as an interactive launch, so the
|
|
7
|
+
* conversation lands in the same Ditto thread and can be picked up again from
|
|
8
|
+
* a TUI later.
|
|
9
|
+
*/
|
|
10
|
+
export interface HeadlessTurnInput {
|
|
11
|
+
harness: Harness;
|
|
12
|
+
/** Everything but prompt/resume, which this module fills in per turn. */
|
|
13
|
+
base: Omit<PlanInput, "prompt" | "resumeId" | "resumeLast" | "passthrough">;
|
|
14
|
+
prompt: string;
|
|
15
|
+
/** Claude: the session id to resume (after the first turn). Codex: resume the last thread. */
|
|
16
|
+
resumeId?: string;
|
|
17
|
+
resumeLast?: boolean;
|
|
18
|
+
cwd: string;
|
|
19
|
+
/** Extra args (e.g. Codex `-c notify=…`, Claude `--settings …`). */
|
|
20
|
+
extraArgs?: string[];
|
|
21
|
+
onProgress?: (line: string) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface HeadlessTurn {
|
|
24
|
+
child: ChildProcess;
|
|
25
|
+
exit: Promise<number | null>;
|
|
26
|
+
interrupt(): void;
|
|
27
|
+
}
|
|
28
|
+
/** One line of progress from Claude's stream-json output, or undefined when the event is not worth showing. */
|
|
29
|
+
export declare function summarizeClaudeEvent(line: string): string | undefined;
|
|
30
|
+
export declare function startHeadlessTurn(input: HeadlessTurnInput): HeadlessTurn;
|