@heyditto/cli 2.42.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.
@@ -2,7 +2,7 @@ import type { Manifest } from "./types.js";
2
2
  export interface Capsule {
3
3
  id: string;
4
4
  name: string;
5
- rootKind: "repo" | "folder";
5
+ rootKind: "repo" | "folder" | "archive";
6
6
  harnessKind?: string;
7
7
  harnessSessionId?: string | null;
8
8
  headGeneration: number;
@@ -96,7 +96,7 @@ export type CommittedBy = "cli" | "runner";
96
96
  export declare function listCapsules(): Promise<Capsule[]>;
97
97
  export declare function createCapsule(input: {
98
98
  name: string;
99
- rootKind: "repo" | "folder";
99
+ rootKind: "repo" | "folder" | "archive";
100
100
  harnessKind?: string;
101
101
  harnessSessionId?: string;
102
102
  mirrorPolicy?: Capsule["mirrorPolicy"];
@@ -0,0 +1,88 @@
1
+ import { type ChunkRef } from "./types.js";
2
+ /**
3
+ * Archive capsules back up trees that only grow: coding-agent session
4
+ * history and local recovery data. Each source is one tar stream, cut into
5
+ * content-defined chunks (gear rolling hash) so an append or an edit only
6
+ * changes the chunks around it, and every chunk is zstd-compressed on its own
7
+ * so it dedupes by content and restores without its neighbours. The manifest
8
+ * lists the chunks in order; restore decompresses and concatenates them.
9
+ */
10
+ export interface ArchiveSource {
11
+ /** Unique name in the capsule, e.g. "claude-code". */
12
+ name: string;
13
+ /** Directory whose contents are archived (paths stored relative to it). */
14
+ dir: string;
15
+ }
16
+ export interface ArchiveEntry {
17
+ name: string;
18
+ format: "tar";
19
+ chunkCompression: "zstd";
20
+ chunks: ChunkRef[];
21
+ entries: number;
22
+ bytes: number;
23
+ }
24
+ export interface ArchiveManifest {
25
+ v: number;
26
+ capsuleId: string;
27
+ generation: number;
28
+ parentGeneration: number;
29
+ createdAt: string;
30
+ machine: Record<string, unknown>;
31
+ root: {
32
+ kind: "archive";
33
+ name: string;
34
+ };
35
+ repos: [];
36
+ archives: ArchiveEntry[];
37
+ harness: {
38
+ kind: "none";
39
+ chunks: [];
40
+ };
41
+ totals: {
42
+ chunks: number;
43
+ bytes: number;
44
+ dedupedBytes?: number;
45
+ };
46
+ }
47
+ /**
48
+ * Content-defined boundaries over a buffer: a cut where the rolling hash's low
49
+ * bits are zero, bounded by MIN_CHUNK and MAX_CHUNK. Exported for tests.
50
+ */
51
+ export declare function cdcBoundaries(buf: Buffer): number[];
52
+ export interface BuiltArchive {
53
+ entry: ArchiveEntry;
54
+ /** sha256 → compressed bytes on disk, for upload. */
55
+ files: Map<string, string>;
56
+ }
57
+ /**
58
+ * Tars `src.dir` and writes each compressed chunk into `outDir`. The tar is
59
+ * read through a window of at most MAX_CHUNK + one read, so memory stays
60
+ * bounded however large the source grows; boundaries match cdcBoundaries.
61
+ */
62
+ export declare function buildArchive(src: ArchiveSource, outDir: string): Promise<BuiltArchive>;
63
+ export interface ArchivePushOutput {
64
+ generation: number;
65
+ logicalBytes: number;
66
+ storedBytes: number;
67
+ uploadedBytes: number;
68
+ uploaded: number;
69
+ reused: number;
70
+ archives: {
71
+ name: string;
72
+ entries: number;
73
+ bytes: number;
74
+ chunks: number;
75
+ }[];
76
+ }
77
+ /** Builds every source, uploads only chunks the server lacks, commits a generation. */
78
+ export declare function pushArchive(capsuleId: string, rootName: string, sources: ArchiveSource[], parentGeneration: number): Promise<ArchivePushOutput>;
79
+ /**
80
+ * Restores one archive of a generation into `dest` (which must not exist):
81
+ * downloads each chunk, checks its sha256, decompresses and untars.
82
+ */
83
+ export declare function restoreArchive(capsuleId: string, name: string, dest: string, generation?: number): Promise<{
84
+ entries: number;
85
+ bytes: number;
86
+ }>;
87
+ /** Default sources: agent session history and infinite-disk's retained recovery data. */
88
+ export declare function defaultArchiveSources(home?: string): ArchiveSource[];
@@ -0,0 +1,280 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { createReadStream, createWriteStream } from "node:fs";
4
+ import { mkdir, mkdtemp, open, readFile, rm, stat, writeFile } from "node:fs/promises";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { zstdCompressSync, zstdDecompressSync } from "node:zlib";
8
+ import { packageVersion } from "../config.js";
9
+ import * as tapi from "./api.js";
10
+ import { CHUNK_BYTES, MANIFEST_VERSION, machineInfo } from "./types.js";
11
+ const MIN_CHUNK = 512 * 1024;
12
+ const AVG_BITS = 21; // ~2 MiB average
13
+ const MAX_CHUNK = 8 * 1024 * 1024;
14
+ const MASK = (1 << AVG_BITS) - 1;
15
+ const NEGOTIATE_BATCH = 200;
16
+ const PUT_CONCURRENCY = 4;
17
+ /** Deterministic gear table: chunk boundaries must match across machines. */
18
+ const GEAR = (() => {
19
+ const t = new Uint32Array(256);
20
+ for (let i = 0; i < 256; i++) {
21
+ t[i] = createHash("sha256").update(`ditto-archive-gear-${i}`).digest().readUInt32LE(0);
22
+ }
23
+ return t;
24
+ })();
25
+ /**
26
+ * Content-defined boundaries over a buffer: a cut where the rolling hash's low
27
+ * bits are zero, bounded by MIN_CHUNK and MAX_CHUNK. Exported for tests.
28
+ */
29
+ export function cdcBoundaries(buf) {
30
+ const cuts = [];
31
+ let start = 0;
32
+ while (start < buf.length) {
33
+ const end = Math.min(buf.length, start + MAX_CHUNK);
34
+ let cut = end;
35
+ let h = 0;
36
+ for (let i = start + MIN_CHUNK; i < end; i++) {
37
+ h = ((h << 1) + GEAR[buf[i]]) >>> 0;
38
+ if ((h & MASK) === 0) {
39
+ cut = i + 1;
40
+ break;
41
+ }
42
+ }
43
+ cuts.push(cut);
44
+ start = cut;
45
+ }
46
+ return cuts;
47
+ }
48
+ /**
49
+ * Tars `src.dir` and writes each compressed chunk into `outDir`. The tar is
50
+ * read through a window of at most MAX_CHUNK + one read, so memory stays
51
+ * bounded however large the source grows; boundaries match cdcBoundaries.
52
+ */
53
+ export async function buildArchive(src, outDir) {
54
+ const tarFile = path.join(outDir, `${safe(src.name)}.tar`);
55
+ const entries = await tarDirectory(src.dir, tarFile);
56
+ const chunks = [];
57
+ const files = new Map();
58
+ let bytes = 0;
59
+ const emit = async (piece) => {
60
+ const z = zstdCompressSync(piece);
61
+ if (z.length > CHUNK_BYTES)
62
+ throw new Error(`compressed chunk exceeds ${CHUNK_BYTES} bytes`);
63
+ const sha = createHash("sha256").update(z).digest("hex");
64
+ chunks.push({ sha256: sha, size: z.length });
65
+ if (!files.has(sha)) {
66
+ const file = path.join(outDir, sha);
67
+ await writeFile(file, z);
68
+ files.set(sha, file);
69
+ }
70
+ };
71
+ try {
72
+ let pending = Buffer.alloc(0);
73
+ for await (const piece of createReadStream(tarFile, { highWaterMark: 1 << 20 })) {
74
+ const buf = piece;
75
+ bytes += buf.length;
76
+ pending = pending.length === 0 ? buf : Buffer.concat([pending, buf]);
77
+ // A cut is final once MAX_CHUNK bytes follow its start: later data
78
+ // can't move it. Keep the unfinished tail.
79
+ while (pending.length >= MAX_CHUNK) {
80
+ const cut = cdcBoundaries(pending.subarray(0, MAX_CHUNK))[0];
81
+ await emit(Buffer.from(pending.subarray(0, cut)));
82
+ pending = Buffer.from(pending.subarray(cut));
83
+ }
84
+ }
85
+ // The tail is under MAX_CHUNK; its cuts are offsets into this buffer.
86
+ let start = 0;
87
+ for (const cut of cdcBoundaries(pending)) {
88
+ await emit(Buffer.from(pending.subarray(start, cut)));
89
+ start = cut;
90
+ }
91
+ }
92
+ finally {
93
+ await rm(tarFile, { force: true });
94
+ }
95
+ return { entry: { name: src.name, format: "tar", chunkCompression: "zstd", chunks, entries, bytes }, files };
96
+ }
97
+ /** tar with paths relative to `dir`; returns the entry count. */
98
+ async function tarDirectory(dir, outFile) {
99
+ const info = await stat(dir);
100
+ if (!info.isDirectory())
101
+ throw new Error(`${dir} is not a directory`);
102
+ const list = await run("tar", ["-c", "-f", outFile, "-C", dir, "."]);
103
+ if (list.code !== 0)
104
+ throw new Error(`tar failed for ${dir}: ${list.stderr.trim()}`);
105
+ const count = await run("tar", ["-t", "-f", outFile]);
106
+ return count.stdout.split("\n").filter((l) => l && !l.endsWith("/")).length;
107
+ }
108
+ /** Builds every source, uploads only chunks the server lacks, commits a generation. */
109
+ export async function pushArchive(capsuleId, rootName, sources, parentGeneration) {
110
+ const tmp = await mkdtemp(path.join(os.tmpdir(), "teleport-archive-"));
111
+ try {
112
+ const archives = [];
113
+ const files = new Map();
114
+ for (const src of sources) {
115
+ const built = await buildArchive(src, tmp);
116
+ archives.push(built.entry);
117
+ for (const [sha, file] of built.files)
118
+ files.set(sha, file);
119
+ }
120
+ const all = archives.flatMap((a) => a.chunks);
121
+ const distinct = new Map();
122
+ for (const c of all)
123
+ distinct.set(c.sha256, c);
124
+ const generation = parentGeneration + 1;
125
+ const manifest = {
126
+ v: MANIFEST_VERSION,
127
+ capsuleId,
128
+ generation,
129
+ parentGeneration,
130
+ createdAt: new Date().toISOString(),
131
+ machine: machineInfo(packageVersion),
132
+ root: { kind: "archive", name: rootName },
133
+ repos: [],
134
+ archives,
135
+ harness: { kind: "none", chunks: [] },
136
+ totals: { chunks: distinct.size, bytes: [...distinct.values()].reduce((n, c) => n + c.size, 0) },
137
+ };
138
+ const manifestBytes = Buffer.from(JSON.stringify(manifest));
139
+ const manifestSha = createHash("sha256").update(manifestBytes).digest("hex");
140
+ const manifestFile = path.join(tmp, "manifest.json");
141
+ const fh = await open(manifestFile, "w");
142
+ await fh.writeFile(manifestBytes);
143
+ await fh.close();
144
+ files.set(manifestSha, manifestFile);
145
+ distinct.set(manifestSha, { sha256: manifestSha, size: manifestBytes.length });
146
+ const wanted = [...distinct.values()];
147
+ let uploaded = 0;
148
+ let uploadedBytes = 0;
149
+ for (let i = 0; i < wanted.length; i += NEGOTIATE_BATCH) {
150
+ const neg = await tapi.negotiate(capsuleId, wanted.slice(i, i + NEGOTIATE_BATCH));
151
+ await uploadAll(neg.missing, files);
152
+ uploaded += neg.missing.length;
153
+ for (const m of neg.missing)
154
+ uploadedBytes += m.size;
155
+ }
156
+ const committed = await tapi.commit(capsuleId, { manifest: manifest, manifestSha256: manifestSha, committedBy: "cli" });
157
+ return {
158
+ generation: committed.generation?.generation ?? generation,
159
+ logicalBytes: archives.reduce((n, a) => n + a.bytes, 0),
160
+ storedBytes: manifest.totals.bytes,
161
+ uploadedBytes,
162
+ uploaded,
163
+ reused: wanted.length - uploaded,
164
+ archives: archives.map((a) => ({ name: a.name, entries: a.entries, bytes: a.bytes, chunks: a.chunks.length })),
165
+ };
166
+ }
167
+ finally {
168
+ await rm(tmp, { recursive: true, force: true });
169
+ }
170
+ }
171
+ /**
172
+ * Restores one archive of a generation into `dest` (which must not exist):
173
+ * downloads each chunk, checks its sha256, decompresses and untars.
174
+ */
175
+ export async function restoreArchive(capsuleId, name, dest, generation) {
176
+ const res = await tapi.resolveGeneration(capsuleId, generation);
177
+ const manifest = res.manifest;
178
+ const archive = manifest.archives?.find((a) => a.name === name);
179
+ if (!archive)
180
+ throw new Error(`generation ${res.generation} has no archive named "${name}"`);
181
+ const urls = new Map(res.chunks.map((c) => [c.sha256, c.getUrl]));
182
+ // Claim the destination atomically: a non-recursive mkdir fails if it exists.
183
+ await mkdir(path.dirname(dest), { recursive: true });
184
+ try {
185
+ await mkdir(dest);
186
+ }
187
+ catch (e) {
188
+ if (e.code === "EEXIST")
189
+ throw new Error(`${dest} already exists; restore never overwrites`);
190
+ throw e;
191
+ }
192
+ const tmp = await mkdtemp(path.join(os.tmpdir(), "teleport-archive-restore-"));
193
+ try {
194
+ const tarFile = path.join(tmp, "archive.tar");
195
+ const out = createWriteStream(tarFile);
196
+ const finished = new Promise((resolve, reject) => {
197
+ out.once("error", reject);
198
+ out.once("finish", resolve);
199
+ });
200
+ finished.catch(() => undefined);
201
+ let bytes = 0;
202
+ for (const c of archive.chunks) {
203
+ const url = urls.get(c.sha256);
204
+ if (!url)
205
+ throw new Error(`no download URL for chunk ${c.sha256.slice(0, 12)}`);
206
+ const body = Buffer.from(await (await fetchOk(url)).arrayBuffer());
207
+ if (createHash("sha256").update(body).digest("hex") !== c.sha256)
208
+ throw new Error(`chunk ${c.sha256.slice(0, 12)} failed its checksum`);
209
+ const plain = zstdDecompressSync(body);
210
+ bytes += plain.length;
211
+ if (!out.write(plain))
212
+ await Promise.race([new Promise((r) => out.once("drain", r)), finished]);
213
+ }
214
+ out.end();
215
+ await finished;
216
+ const x = await run("tar", ["-x", "-k", "-f", tarFile, "-C", dest]);
217
+ if (x.code !== 0)
218
+ throw new Error(`tar extract failed: ${x.stderr.trim()}`);
219
+ return { entries: archive.entries, bytes };
220
+ }
221
+ finally {
222
+ await rm(tmp, { recursive: true, force: true });
223
+ }
224
+ }
225
+ async function uploadAll(missing, files) {
226
+ let cursor = 0;
227
+ const workers = Array.from({ length: Math.min(PUT_CONCURRENCY, missing.length) }, async () => {
228
+ for (;;) {
229
+ const i = cursor++;
230
+ if (i >= missing.length)
231
+ return;
232
+ const file = files.get(missing[i].sha256);
233
+ if (!file)
234
+ throw new Error(`server asked for an unknown chunk ${missing[i].sha256}`);
235
+ const body = await readFile(file);
236
+ let delay = 400;
237
+ for (let attempt = 1;; attempt++) {
238
+ const r = await fetch(missing[i].putUrl, { method: "PUT", body });
239
+ if (r.ok)
240
+ break;
241
+ await r.body?.cancel().catch(() => undefined);
242
+ if (attempt >= 4 || (r.status < 500 && r.status !== 429))
243
+ throw new Error(`chunk upload failed: HTTP ${r.status}`);
244
+ await new Promise((s) => setTimeout(s, delay));
245
+ delay *= 2;
246
+ }
247
+ }
248
+ });
249
+ await Promise.all(workers);
250
+ }
251
+ async function fetchOk(url) {
252
+ const r = await fetch(url);
253
+ if (!r.ok)
254
+ throw new Error(`chunk download failed: HTTP ${r.status}`);
255
+ return r;
256
+ }
257
+ function run(cmd, args) {
258
+ return new Promise((resolve, reject) => {
259
+ const p = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
260
+ let stdout = "";
261
+ let stderr = "";
262
+ p.stdout.on("data", (d) => (stdout += d));
263
+ p.stderr.on("data", (d) => (stderr += d));
264
+ p.on("error", reject);
265
+ p.on("close", (code) => resolve({ code: code ?? 1, stdout, stderr }));
266
+ });
267
+ }
268
+ function safe(name) {
269
+ return name.replace(/[^A-Za-z0-9._-]/g, "_") || "archive";
270
+ }
271
+ /** Default sources: agent session history and infinite-disk's retained recovery data. */
272
+ export function defaultArchiveSources(home = os.homedir()) {
273
+ return [
274
+ { name: "claude-code", dir: path.join(home, ".claude", "projects") },
275
+ { name: "codex", dir: path.join(home, ".codex", "sessions") },
276
+ { name: "infinite-disk-recovery", dir: path.join(home, ".local", "share", "infinite-disk", "recovery-consolidated") },
277
+ { name: "infinite-disk-sessions", dir: path.join(home, ".local", "share", "infinite-disk", "session-backups") },
278
+ ];
279
+ }
280
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/teleport/archive.ts"],"names":[],"mappings":"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"}
@@ -75,6 +75,15 @@ export declare function cmdStorageList(options: OutputOptions): Promise<void>;
75
75
  export declare function cmdStorageTest(ref: string, options: OutputOptions): Promise<void>;
76
76
  export declare function cmdStorageRemove(ref: string): Promise<void>;
77
77
  export declare function cmdStorageMirror(ref: string, targetsArg: string): Promise<void>;
78
+ interface ArchiveOptions extends OutputOptions {
79
+ name?: string;
80
+ source?: string[];
81
+ }
82
+ export declare function cmdTeleportArchive(options: ArchiveOptions): Promise<void>;
83
+ export declare function cmdTeleportArchiveRestore(archiveName: string, dest: string, options: {
84
+ name?: string;
85
+ generation?: string;
86
+ }): Promise<void>;
78
87
  export declare function registerTeleportCommands(program: Command, addExamples: (c: Command, ex: string) => Command): void;
79
88
  /**
80
89
  * Remote Control checkpoint: push the session's root as a new generation and
@@ -14,6 +14,8 @@ import { discoverRepos } from "../teleport/discover.js";
14
14
  import { offloadBlockers, planCapture } from "../teleport/workspace.js";
15
15
  import { formatCapturePlan } from "../dittoconfig/commands.js";
16
16
  import { formatBytes } from "../teleport/types.js";
17
+ import { defaultArchiveSources, pushArchive, restoreArchive } from "../teleport/archive.js";
18
+ import { stat } from "node:fs/promises";
17
19
  function out(line) {
18
20
  process.stdout.write(`${line}\n`);
19
21
  }
@@ -438,6 +440,63 @@ function jsonAlias() {
438
440
  function withOutput(c) {
439
441
  return c.addOption(outputOption()).addOption(jsonAlias());
440
442
  }
443
+ /** Parses repeatable --source name=dir; defaults to agent history and infinite-disk recovery data. */
444
+ async function archiveSources(options) {
445
+ const names = new Set();
446
+ const sources = options.source?.length
447
+ ? options.source.map((s) => {
448
+ const at = s.indexOf("=");
449
+ const dir = s.slice(at + 1);
450
+ if (at <= 0 || !dir)
451
+ throw new Error(`--source must be name=dir, got "${s}"`);
452
+ const name = s.slice(0, at);
453
+ if (names.has(name))
454
+ throw new Error(`duplicate --source name "${name}"`);
455
+ names.add(name);
456
+ return { name, dir: path.resolve(dir) };
457
+ })
458
+ : defaultArchiveSources();
459
+ const present = [];
460
+ for (const src of sources) {
461
+ const info = await stat(src.dir).catch(() => undefined);
462
+ if (info?.isDirectory())
463
+ present.push(src);
464
+ else if (options.source?.length)
465
+ throw new Error(`${src.dir} is not a directory`);
466
+ }
467
+ if (present.length === 0)
468
+ throw new Error("nothing to archive: no source directory exists");
469
+ return present;
470
+ }
471
+ export async function cmdTeleportArchive(options) {
472
+ const name = options.name ?? "agent-traces";
473
+ const sources = await archiveSources(options);
474
+ let capsule = (await tapi.listCapsules()).find((c) => c.name === name && c.status !== "deleted");
475
+ if (capsule && capsule.rootKind !== "archive") {
476
+ throw new Error(`capsule "${name}" is a ${capsule.rootKind} capsule; choose another --name for the archive`);
477
+ }
478
+ if (!capsule)
479
+ capsule = await tapi.createCapsule({ name, rootKind: "archive" });
480
+ if (!json(options)) {
481
+ err(`Archiving ${sources.map((s) => s.name).join(", ")} into "${name}" (generation ${capsule.headGeneration + 1})…`);
482
+ }
483
+ const res = await pushArchive(capsule.id, name, sources, capsule.headGeneration);
484
+ if (json(options)) {
485
+ out(JSON.stringify({ capsule: capsule.id, ...res }));
486
+ return;
487
+ }
488
+ for (const a of res.archives)
489
+ out(` ${pad(a.name, 24)} ${pad(String(a.entries), 7)} files ${formatBytes(a.bytes)}`);
490
+ out(`Generation ${res.generation}: ${formatBytes(res.logicalBytes)} archived, ${formatBytes(res.storedBytes)} stored compressed, ${formatBytes(res.uploadedBytes)} uploaded (${res.reused} chunks already stored).`);
491
+ out("Billed as storage on your plan; restore with `heyditto teleport archive-restore`.");
492
+ }
493
+ export async function cmdTeleportArchiveRestore(archiveName, dest, options) {
494
+ const generation = options.generation === undefined ? undefined : Number(options.generation);
495
+ if (generation !== undefined && (!Number.isInteger(generation) || generation < 1))
496
+ throw new Error("--generation must be a positive integer");
497
+ const res = await restoreArchive(options.name ?? "agent-traces", archiveName, path.resolve(dest), generation);
498
+ out(`Restored ${res.entries} files (${formatBytes(res.bytes)}) into ${path.resolve(dest)}.`);
499
+ }
441
500
  export function registerTeleportCommands(program, addExamples) {
442
501
  const teleport = withOutput(program
443
502
  .command("teleport")
@@ -491,6 +550,18 @@ export function registerTeleportCommands(program, addExamples) {
491
550
  for (const line of formatCapturePlan(plan))
492
551
  out(line);
493
552
  }));
553
+ withOutput(teleport
554
+ .command("archive")
555
+ .description("back up agent session history and recovery data as storage (no capsule slot)")
556
+ .option("--name <capsule>", "archive capsule name", "agent-traces")
557
+ .option("--source <name=dir...>", "archive these directories instead of the defaults (~/.claude/projects, ~/.codex/sessions, infinite-disk recovery data)")
558
+ .action(cmdTeleportArchive));
559
+ teleport
560
+ .command("archive-restore <archive> <dest>")
561
+ .description("restore one archive (e.g. claude-code) into a new directory; never overwrites")
562
+ .option("--name <capsule>", "archive capsule name", "agent-traces")
563
+ .option("--generation <n>", "generation to restore (default: latest)")
564
+ .action(cmdTeleportArchiveRestore);
494
565
  withOutput(teleport.command("rm <capsule>").description("delete a capsule and its generations").option("--yes", "skip the confirmation").action(cmdTeleportRm));
495
566
  addExamples(teleport, ` heyditto teleport push the current directory
496
567
  heyditto teleport --cloud --endpoint work push, then resume in Ditto Code