@heyditto/cli 2.4.0 → 2.5.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.
Files changed (50) hide show
  1. package/README.md +89 -1
  2. package/dist/agents/launch.js +6 -1
  3. package/dist/agents/launch.js.map +1 -1
  4. package/dist/cli.js +2 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands.js +1 -1
  7. package/dist/commands.js.map +1 -1
  8. package/dist/teleport/api.d.ts +139 -0
  9. package/dist/teleport/api.js +61 -0
  10. package/dist/teleport/api.js.map +1 -0
  11. package/dist/teleport/bundle.d.ts +35 -0
  12. package/dist/teleport/bundle.js +102 -0
  13. package/dist/teleport/bundle.js.map +1 -0
  14. package/dist/teleport/chunks.d.ts +13 -0
  15. package/dist/teleport/chunks.js +57 -0
  16. package/dist/teleport/chunks.js.map +1 -0
  17. package/dist/teleport/commands.d.ts +74 -0
  18. package/dist/teleport/commands.js +488 -0
  19. package/dist/teleport/commands.js.map +1 -0
  20. package/dist/teleport/discover.d.ts +13 -0
  21. package/dist/teleport/discover.js +64 -0
  22. package/dist/teleport/discover.js.map +1 -0
  23. package/dist/teleport/git.d.ts +18 -0
  24. package/dist/teleport/git.js +41 -0
  25. package/dist/teleport/git.js.map +1 -0
  26. package/dist/teleport/harness.d.ts +29 -0
  27. package/dist/teleport/harness.js +121 -0
  28. package/dist/teleport/harness.js.map +1 -0
  29. package/dist/teleport/offload.d.ts +32 -0
  30. package/dist/teleport/offload.js +83 -0
  31. package/dist/teleport/offload.js.map +1 -0
  32. package/dist/teleport/pull.d.ts +12 -0
  33. package/dist/teleport/pull.js +118 -0
  34. package/dist/teleport/pull.js.map +1 -0
  35. package/dist/teleport/push.d.ts +49 -0
  36. package/dist/teleport/push.js +261 -0
  37. package/dist/teleport/push.js.map +1 -0
  38. package/dist/teleport/restore.d.ts +16 -0
  39. package/dist/teleport/restore.js +163 -0
  40. package/dist/teleport/restore.js.map +1 -0
  41. package/dist/teleport/storage.d.ts +53 -0
  42. package/dist/teleport/storage.js +43 -0
  43. package/dist/teleport/storage.js.map +1 -0
  44. package/dist/teleport/types.d.ts +100 -0
  45. package/dist/teleport/types.js +85 -0
  46. package/dist/teleport/types.js.map +1 -0
  47. package/dist/teleport/worktree.d.ts +22 -0
  48. package/dist/teleport/worktree.js +94 -0
  49. package/dist/teleport/worktree.js.map +1 -0
  50. package/package.json +1 -1
@@ -0,0 +1,261 @@
1
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { packageVersion } from "../config.js";
5
+ import * as tapi from "./api.js";
6
+ import { basisFromPrevious, createBundle, readRepoState } from "./bundle.js";
7
+ import { chunkFile, dedupedBytes, readChunk, sha256 } from "./chunks.js";
8
+ import { discoverRepos } from "./discover.js";
9
+ import { captureHarness, locateHarness } from "./harness.js";
10
+ import { captureWorktree, dirtyPaths, pickCompression } from "./worktree.js";
11
+ import { DEFAULT_EXCLUDES, MANIFEST_VERSION, machineInfo, } from "./types.js";
12
+ const PUT_CONCURRENCY = 4;
13
+ const NEGOTIATE_BATCH = 200;
14
+ /** Thin packs chained past this length are compacted into a fresh full bundle. */
15
+ const MAX_PACK_CHAIN = 8;
16
+ /**
17
+ * Builds a generation, negotiates which chunks are missing, uploads them via
18
+ * presigned PUTs, then commits the manifest. The manifest is content-addressed
19
+ * too, so a re-push with no changes uploads nothing.
20
+ *
21
+ * Every generation's manifest carries each repo's complete pack chain (the
22
+ * basis packs by reference plus this generation's thin pack), so a pull of any
23
+ * generation rebuilds full history from that one manifest. When the server no
24
+ * longer holds a referenced basis chunk, the affected repos are re-bundled in
25
+ * full and the push retried once.
26
+ */
27
+ export async function pushCapsule(input, forceFull = new Set()) {
28
+ const tmp = await mkdtemp(path.join(os.tmpdir(), "teleport-push-"));
29
+ const compression = pickCompression();
30
+ const generation = (input.parentGeneration ?? 0) + 1;
31
+ const sources = new Map();
32
+ /** Chunks referenced from a previous generation (no local bytes) → their repo. */
33
+ const carried = new Map();
34
+ const allChunks = [];
35
+ try {
36
+ const discovery = await discoverRepos(input.root);
37
+ const prevByRel = new Map();
38
+ for (const r of input.previousManifest?.repos ?? [])
39
+ prevByRel.set(r.relPath, r);
40
+ const repos = [];
41
+ for (const rel of discovery.repos) {
42
+ const repoDir = rel === "." ? discovery.root : path.join(discovery.root, rel);
43
+ const state = readRepoState(repoDir);
44
+ const prev = prevByRel.get(rel);
45
+ // Thin against the previous generation unless forced, unless there is no
46
+ // usable basis, or unless the chain is long enough that a restore would
47
+ // crawl; those cases compact into a fresh full bundle.
48
+ const useBasis = !forceFull.has(rel) && !!prev && prev.packs.length > 0 && prev.packs.length < MAX_PACK_CHAIN;
49
+ const basis = useBasis ? basisFromPrevious(prev) : [];
50
+ const bundleFile = path.join(tmp, `bundle-${safe(rel)}.bundle`);
51
+ const bundle = await createBundle(repoDir, bundleFile, basis, Object.keys(state.upstreamTips).map((u) => `refs/remotes/${u}`));
52
+ const packs = [];
53
+ if (useBasis && (bundle === null || bundle.kind === "thin")) {
54
+ // Carry the basis chain by reference: the server already holds these
55
+ // chunks and dedups them, and a pull needs them to rebuild history.
56
+ for (const pack of prev.packs) {
57
+ packs.push(pack);
58
+ for (const c of pack.chunks) {
59
+ allChunks.push(c);
60
+ if (!sources.has(c.sha256))
61
+ carried.set(c.sha256, rel);
62
+ }
63
+ }
64
+ }
65
+ const packChunks = bundle ? await addFileChunks(bundle.file, sources, allChunks) : [];
66
+ for (const c of packChunks)
67
+ carried.delete(c.sha256);
68
+ const dirty = dirtyPaths(repoDir, input.ignoredIncludes);
69
+ const wtFile = path.join(tmp, `worktree-${safe(rel)}.tar`);
70
+ const capture = await captureWorktree(repoDir, dirty, wtFile, compression);
71
+ const wtChunks = capture ? await addFileChunks(capture.file, sources, allChunks) : [];
72
+ if (bundle) {
73
+ const pack = { kind: bundle.kind, chunks: packChunks };
74
+ if (bundle.kind === "thin" && input.parentGeneration)
75
+ pack.basisGeneration = input.parentGeneration;
76
+ packs.push(pack);
77
+ }
78
+ const repo = {
79
+ head: state.head,
80
+ relPath: rel,
81
+ remotes: state.remotes,
82
+ packs,
83
+ worktree: capture
84
+ ? { chunks: wtChunks, entries: capture.entries, bytes: bundleBytes(wtChunks) }
85
+ : { chunks: [], entries: 0, bytes: 0 },
86
+ };
87
+ if (state.branches.length)
88
+ repo.branches = state.branches;
89
+ if (state.tags.length)
90
+ repo.tags = state.tags;
91
+ // Per-branch upstreams, restricted to remotes the manifest carries so a
92
+ // restore can always recreate the tracking ref.
93
+ const remoteNames = new Set(state.remotes.map((r) => r.name));
94
+ const upstreams = {};
95
+ for (const [b, up] of Object.entries(state.branchUpstreams)) {
96
+ if (remoteNames.has(up.split("/")[0]))
97
+ upstreams[b] = up;
98
+ }
99
+ if (Object.keys(upstreams).length)
100
+ repo.branchUpstreams = upstreams;
101
+ // Remote-tracking tips for every upstream the manifest names.
102
+ const tips = {};
103
+ for (const up of new Set([...(state.head.upstream ? [state.head.upstream] : []), ...Object.values(upstreams)])) {
104
+ const tip = state.upstreamTips[up];
105
+ if (tip && remoteNames.has(up.split("/")[0]))
106
+ tips[up] = tip;
107
+ }
108
+ if (Object.keys(tips).length)
109
+ repo.upstreamTips = tips;
110
+ if (state.stashes.length)
111
+ repo.stashes = state.stashes;
112
+ if (input.ignoredIncludes.length)
113
+ repo.ignoredIncludes = input.ignoredIncludes;
114
+ repos.push(repo);
115
+ }
116
+ // Harness transcript.
117
+ const harness = { kind: input.harness.kind, chunks: [] };
118
+ if (input.harness.sessionId)
119
+ harness.sessionId = input.harness.sessionId;
120
+ if (input.harness.cwd)
121
+ harness.cwd = input.harness.cwd;
122
+ if (input.harness.kind !== "none" && input.harness.sessionId && input.harness.cwd) {
123
+ const loc = await locateHarness(input.harness.kind, input.harness.sessionId, input.harness.cwd);
124
+ if (loc) {
125
+ const hFile = path.join(tmp, "harness.tar");
126
+ const cap = await captureHarness(loc, hFile, compression);
127
+ if (cap)
128
+ harness.chunks = await addFileChunks(cap.file, sources, allChunks);
129
+ }
130
+ }
131
+ const bytesTotal = allChunks.reduce((n, c) => n + c.size, 0);
132
+ const manifest = {
133
+ v: MANIFEST_VERSION,
134
+ capsuleId: input.capsuleId,
135
+ generation,
136
+ parentGeneration: input.parentGeneration ?? 0,
137
+ createdAt: new Date().toISOString(),
138
+ machine: machineInfo(packageVersion),
139
+ root: { kind: input.rootKind, name: input.rootName },
140
+ repos,
141
+ excludes: [...DEFAULT_EXCLUDES],
142
+ harness,
143
+ totals: { chunks: allChunks.length, bytes: bytesTotal, dedupedBytes: dedupedBytes(allChunks) },
144
+ };
145
+ // The manifest is itself a chunk: write it, hash it, and negotiate it with
146
+ // the rest so commit can reference it by sha256.
147
+ const manifestBytes = Buffer.from(JSON.stringify(manifest));
148
+ const manifestSha256 = sha256(manifestBytes);
149
+ const manifestFile = path.join(tmp, "manifest.json");
150
+ await writeFile(manifestFile, manifestBytes);
151
+ const manifestRef = { sha256: manifestSha256, size: manifestBytes.length };
152
+ allChunks.push(manifestRef);
153
+ if (!sources.has(manifestSha256))
154
+ sources.set(manifestSha256, { file: manifestFile, index: 0, size: manifestBytes.length });
155
+ // Distinct chunks, negotiate in batches of ≤ 200.
156
+ const distinct = new Map();
157
+ for (const c of allChunks)
158
+ distinct.set(c.sha256, c);
159
+ const wanted = [...distinct.values()];
160
+ let uploaded = 0;
161
+ let uploadedBytes = 0;
162
+ for (let i = 0; i < wanted.length; i += NEGOTIATE_BATCH) {
163
+ const batch = wanted.slice(i, i + NEGOTIATE_BATCH);
164
+ const neg = await tapi.negotiate(input.capsuleId, batch.map((c) => ({ sha256: c.sha256, size: c.size })));
165
+ // A basis chunk the server no longer has cannot be re-sent by reference:
166
+ // rebuild those repos with a full bundle and push again (once).
167
+ const lost = neg.missing.filter((m) => !sources.has(m.sha256) && carried.has(m.sha256));
168
+ if (lost.length > 0) {
169
+ const repos = new Set(lost.map((m) => carried.get(m.sha256)));
170
+ if ([...repos].every((r) => forceFull.has(r))) {
171
+ throw new Error(`server is missing basis chunks for ${[...repos].join(", ")} even after a full re-bundle`);
172
+ }
173
+ await rm(tmp, { recursive: true, force: true });
174
+ return pushCapsule(input, new Set([...forceFull, ...repos]));
175
+ }
176
+ await uploadMissing(neg.missing, sources);
177
+ uploaded += neg.missing.length;
178
+ for (const m of neg.missing)
179
+ uploadedBytes += m.size ?? distinct.get(m.sha256)?.size ?? 0;
180
+ }
181
+ const committed = await tapi.commit(input.capsuleId, {
182
+ manifest,
183
+ manifestSha256,
184
+ committedBy: input.committedBy ?? detectCommitter(),
185
+ });
186
+ const logicalBytes = allChunks.reduce((n, c) => n + c.size, 0);
187
+ const reusedBytes = Math.max(0, logicalBytes - uploadedBytes);
188
+ return {
189
+ // The server's generation record is authoritative for the number.
190
+ generation: committed.generation?.generation ?? generation,
191
+ bytesTotal,
192
+ dedupedBytes: manifest.totals.dedupedBytes ?? 0,
193
+ chunkCount: distinct.size,
194
+ uploaded,
195
+ reused: distinct.size - uploaded,
196
+ uploadedBytes,
197
+ logicalBytes,
198
+ reusedBytes,
199
+ savingsRatio: logicalBytes > 0 ? reusedBytes / logicalBytes : 0,
200
+ };
201
+ }
202
+ finally {
203
+ await rm(tmp, { recursive: true, force: true });
204
+ }
205
+ }
206
+ async function addFileChunks(file, sources, all) {
207
+ const refs = await chunkFile(file);
208
+ refs.forEach((r, index) => {
209
+ all.push(r);
210
+ if (!sources.has(r.sha256))
211
+ sources.set(r.sha256, { file, index, size: r.size });
212
+ });
213
+ return refs;
214
+ }
215
+ async function uploadMissing(missing, sources) {
216
+ let cursor = 0;
217
+ const workers = Array.from({ length: Math.min(PUT_CONCURRENCY, missing.length) }, async () => {
218
+ for (;;) {
219
+ const i = cursor++;
220
+ if (i >= missing.length)
221
+ return;
222
+ const item = missing[i];
223
+ const src = sources.get(item.sha256);
224
+ if (!src)
225
+ throw new Error(`server asked for an unknown chunk ${item.sha256}`);
226
+ const body = await readChunk(src.file, src.index);
227
+ await putWithRetry(item.putUrl, body);
228
+ }
229
+ });
230
+ await Promise.all(workers);
231
+ }
232
+ async function putWithRetry(url, body, attempts = 4) {
233
+ let delay = 400;
234
+ for (let attempt = 1;; attempt++) {
235
+ const res = await fetch(url, { method: "PUT", body });
236
+ if (res.ok)
237
+ return;
238
+ if (attempt >= attempts || (res.status < 500 && res.status !== 429)) {
239
+ throw new Error(`chunk upload failed: HTTP ${res.status}`);
240
+ }
241
+ await sleep(delay);
242
+ delay *= 2;
243
+ }
244
+ }
245
+ /** The Ditto Code runner authenticates with a ditto_agent_ token and pins the harness session via env. */
246
+ export function detectCommitter() {
247
+ const key = process.env.DITTO_API_KEY ?? "";
248
+ if (key.startsWith("ditto_agent_") || process.env.TELEPORT_HARNESS_SESSION_ID)
249
+ return "runner";
250
+ return "cli";
251
+ }
252
+ function sleep(ms) {
253
+ return new Promise((r) => setTimeout(r, ms));
254
+ }
255
+ function bundleBytes(chunks) {
256
+ return chunks.reduce((n, c) => n + c.size, 0);
257
+ }
258
+ function safe(rel) {
259
+ return rel.replace(/[^A-Za-z0-9._-]/g, "_") || "root";
260
+ }
261
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/teleport/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Manifest } from "./types.js";
2
+ export interface RestoreResult {
3
+ root: string;
4
+ repos: string[];
5
+ harnessSessionId: string | null;
6
+ harnessCwd: string | null;
7
+ }
8
+ /**
9
+ * Rebuilds a capsule from its manifest and the downloaded chunk files (keyed by
10
+ * sha256). Each repo is reconstructed from its bundle(s), branches and
11
+ * upstreams are restored, the dirty worktree is untarred over the checkout, and
12
+ * the harness transcript (if any) is placed under the restored cwd.
13
+ */
14
+ export declare function restoreCapsule(manifest: Manifest, chunkPath: (sha256: string) => string, destRoot: string, opts?: {
15
+ restoreHarness?: boolean;
16
+ }): Promise<RestoreResult>;
@@ -0,0 +1,163 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { git, gitOrThrow, isGitRepo } from "./git.js";
5
+ import { restoreClaudeTranscript } from "./harness.js";
6
+ import { detectCompression, extractWorktree } from "./worktree.js";
7
+ /**
8
+ * Rebuilds a capsule from its manifest and the downloaded chunk files (keyed by
9
+ * sha256). Each repo is reconstructed from its bundle(s), branches and
10
+ * upstreams are restored, the dirty worktree is untarred over the checkout, and
11
+ * the harness transcript (if any) is placed under the restored cwd.
12
+ */
13
+ export async function restoreCapsule(manifest, chunkPath, destRoot, opts = {}) {
14
+ await mkdir(destRoot, { recursive: true });
15
+ const tmp = await mkdtemp(path.join(os.tmpdir(), "teleport-restore-"));
16
+ try {
17
+ const repos = [];
18
+ for (const repo of manifest.repos) {
19
+ const repoDest = path.resolve(destRoot, repo.relPath);
20
+ await mkdir(repoDest, { recursive: true });
21
+ await restoreRepo(repo, chunkPath, repoDest, tmp);
22
+ repos.push(repo.relPath);
23
+ }
24
+ let harnessCwd = null;
25
+ if (opts.restoreHarness && manifest.harness.kind !== "none" && manifest.harness.chunks.length > 0) {
26
+ const tar = path.join(tmp, "harness.tar");
27
+ await concatChunks(manifest.harness.chunks.map((c) => c.sha256), chunkPath, tar);
28
+ const originalCwd = manifest.harness.cwd ?? destRoot;
29
+ const targetCwd = manifest.root.kind === "repo" ? path.resolve(destRoot, manifest.repos[0]?.relPath ?? ".") : destRoot;
30
+ const compression = detectCompression(tar);
31
+ if (manifest.harness.kind === "claude-code" && manifest.harness.sessionId) {
32
+ await restoreClaudeTranscript(tar, compression, manifest.harness.sessionId, originalCwd, targetCwd);
33
+ }
34
+ else {
35
+ // Codex keys transcripts on absolute cwd inside a shared home; extract as-is.
36
+ extractWorktree(tar, os.homedir(), compression);
37
+ }
38
+ harnessCwd = targetCwd;
39
+ }
40
+ return { root: destRoot, repos, harnessSessionId: manifest.harness.sessionId ?? null, harnessCwd };
41
+ }
42
+ finally {
43
+ await rm(tmp, { recursive: true, force: true });
44
+ }
45
+ }
46
+ async function restoreRepo(repo, chunkPath, repoDest, tmp) {
47
+ const hasBundle = repo.packs.some((p) => p.chunks.length > 0);
48
+ if (hasBundle) {
49
+ // Reassemble every pack into one bare prerequisite repo, then clone from it
50
+ // (a fetch into a repo whose branch is checked out is refused by git).
51
+ const bundleDir = path.join(tmp, `bundles-${sanitize(repo.relPath)}`);
52
+ await mkdir(bundleDir, { recursive: true });
53
+ const scratch = path.join(tmp, `scratch-${sanitize(repo.relPath)}`);
54
+ gitOrThrow(["init", "--bare", scratch], tmp);
55
+ // Full packs first, then thin packs oldest basis → newest, so every
56
+ // bundle's prerequisites are present before it is fetched.
57
+ const ordered = [...repo.packs].sort((a, b) => {
58
+ if (a.kind !== b.kind)
59
+ return a.kind === "full" ? -1 : 1;
60
+ return (a.basisGeneration ?? 0) - (b.basisGeneration ?? 0);
61
+ });
62
+ for (let i = 0; i < ordered.length; i++) {
63
+ const bundleFile = path.join(bundleDir, `pack-${i}.bundle`);
64
+ await concatChunks(ordered[i].chunks.map((c) => c.sha256), chunkPath, bundleFile);
65
+ gitOrThrow(["fetch", "--tags", bundleFile, "+refs/*:refs/*"], scratch);
66
+ }
67
+ // Clone the bare repo into the destination: this copies every branch and
68
+ // tag with a real working tree, without a fetch-into-checked-out-branch.
69
+ gitOrThrow(["clone", scratch, repoDest], tmp);
70
+ // Local tracking branches for every branch the capsule carried, so a later
71
+ // checkout of any of them works offline.
72
+ for (const branch of repo.branches ?? []) {
73
+ if (git(["rev-parse", "--verify", `refs/heads/${branch}`], repoDest).ok)
74
+ continue;
75
+ git(["branch", "--no-track", branch, `refs/remotes/origin/${branch}`], repoDest);
76
+ }
77
+ git(["remote", "remove", "origin"], repoDest);
78
+ }
79
+ else if (!isGitRepo(repoDest)) {
80
+ gitOrThrow(["init", repoDest], tmp);
81
+ }
82
+ // Remotes.
83
+ for (const remote of repo.remotes) {
84
+ if (git(["remote", "get-url", remote.name], repoDest).ok) {
85
+ git(["remote", "set-url", remote.name, remote.url], repoDest);
86
+ }
87
+ else {
88
+ git(["remote", "add", remote.name, remote.url], repoDest);
89
+ }
90
+ }
91
+ // Check out the recorded HEAD/branch and re-establish upstream tracking.
92
+ if (repo.head.branch && (repo.branches ?? []).includes(repo.head.branch)) {
93
+ gitOrThrow(["checkout", "-f", repo.head.branch], repoDest);
94
+ if (repo.head.upstream)
95
+ restoreUpstream(repoDest, repo, repo.head.branch, repo.head.upstream);
96
+ }
97
+ else if (repo.head.sha) {
98
+ gitOrThrow(["checkout", "-f", repo.head.sha], repoDest);
99
+ }
100
+ // Every other branch that tracked a remote gets its tracking ref back too.
101
+ for (const [branch, upstream] of Object.entries(repo.branchUpstreams ?? {})) {
102
+ if (branch === repo.head.branch && repo.head.upstream)
103
+ continue;
104
+ if (!git(["rev-parse", "--verify", `refs/heads/${branch}`], repoDest).ok)
105
+ continue;
106
+ restoreUpstream(repoDest, repo, branch, upstream);
107
+ }
108
+ // Dirty worktree over the checkout.
109
+ if (repo.worktree?.chunks?.length) {
110
+ const tar = path.join(tmp, `worktree-${sanitize(repo.relPath)}.tar`);
111
+ await concatChunks(repo.worktree.chunks.map((c) => c.sha256), chunkPath, tar);
112
+ extractWorktree(tar, repoDest);
113
+ }
114
+ }
115
+ /**
116
+ * Recreates `<remote>/<branch>` tracking without a network fetch. The clone we
117
+ * restore from has no remote-tracking refs (its origin was the scratch repo),
118
+ * so `branch --set-upstream-to` would fail; instead the tracking ref is
119
+ * pointed at the branch tip the capsule recorded, then the upstream is set.
120
+ * The tip is the last state this machine knew of the upstream, so ahead/behind
121
+ * reads 0 until the next fetch. Any failure here is a hard error: silently
122
+ * losing tracking is exactly the bug this guards against.
123
+ */
124
+ function restoreUpstream(repoDest, repo, branch, upstream) {
125
+ const remote = repo.remotes.find((r) => upstream === r.name || upstream.startsWith(`${r.name}/`));
126
+ if (!remote) {
127
+ throw new Error(`cannot restore upstream ${upstream} for ${branch}: remote is not in the capsule (${repo.remotes.map((r) => r.name).join(", ") || "none"})`);
128
+ }
129
+ const remoteBranch = upstream.slice(remote.name.length + 1);
130
+ if (!remoteBranch)
131
+ throw new Error(`cannot restore upstream ${upstream}: no branch component`);
132
+ const tip = repo.upstreamTips?.[upstream];
133
+ if (tip) {
134
+ // The real remote-tracking sha captured at push time; its objects rode in
135
+ // the bundle, so ahead/behind is exactly what the source machine saw.
136
+ gitOrThrow(["update-ref", `refs/remotes/${remote.name}/${remoteBranch}`, tip], repoDest);
137
+ gitOrThrow(["branch", `--set-upstream-to=${upstream}`, branch], repoDest);
138
+ return;
139
+ }
140
+ // Older capsules carry no tip: configure the upstream but never fabricate the
141
+ // tracking ref — offload treats "configured, ref missing" as unknown.
142
+ gitOrThrow(["config", `branch.${branch}.remote`, remote.name], repoDest);
143
+ gitOrThrow(["config", `branch.${branch}.merge`, `refs/heads/${remoteBranch}`], repoDest);
144
+ }
145
+ async function concatChunks(shas, chunkPath, outFile) {
146
+ const { createWriteStream } = await import("node:fs");
147
+ const { readFile } = await import("node:fs/promises");
148
+ const ws = createWriteStream(outFile);
149
+ try {
150
+ for (const sha of shas) {
151
+ const buf = await readFile(chunkPath(sha));
152
+ await new Promise((resolve, reject) => ws.write(buf, (err) => (err ? reject(err) : resolve())));
153
+ }
154
+ }
155
+ finally {
156
+ await new Promise((resolve) => ws.end(resolve));
157
+ }
158
+ void writeFile; // keep import set stable if unused in future edits
159
+ }
160
+ function sanitize(rel) {
161
+ return rel.replace(/[^A-Za-z0-9._-]/g, "_") || "root";
162
+ }
163
+ //# sourceMappingURL=restore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/teleport/restore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Bring-your-own buckets that capsules can mirror to, managed through the
3
+ * teleport API (`/api/v5/teleport/buckets`), which accepts the CLI's
4
+ * ditto_mcp_ key. Any S3-compatible endpoint works (AWS S3, R2, B2, MinIO,
5
+ * Hippius); `providerKind` is detected server-side from the endpoint.
6
+ */
7
+ export interface StorageBucket {
8
+ id: string;
9
+ name?: string;
10
+ providerKind?: "s3" | "hippius";
11
+ bucket: string;
12
+ endpoint: string;
13
+ region: string;
14
+ enabled: boolean;
15
+ default: boolean;
16
+ teleportMirror?: boolean;
17
+ keyHint?: string;
18
+ credentialState?: string;
19
+ }
20
+ export interface AddBucketInput {
21
+ name?: string;
22
+ providerKind?: "s3" | "hippius";
23
+ accessKeyID: string;
24
+ secretAccessKey: string;
25
+ bucket: string;
26
+ endpoint: string;
27
+ region?: string;
28
+ enabled?: boolean;
29
+ default?: boolean;
30
+ teleportMirror?: boolean;
31
+ }
32
+ export interface UpdateBucketInput {
33
+ name?: string;
34
+ enabled?: boolean;
35
+ default?: boolean;
36
+ teleportMirror?: boolean;
37
+ }
38
+ export interface TestResult {
39
+ ok: boolean;
40
+ error?: string;
41
+ }
42
+ export declare function listBuckets(): Promise<StorageBucket[]>;
43
+ export declare function addBucket(input: AddBucketInput): Promise<StorageBucket>;
44
+ export declare function updateBucket(id: string, input: UpdateBucketInput): Promise<StorageBucket>;
45
+ export declare function removeBucket(id: string): Promise<void>;
46
+ /** Tests a stored bucket's credentials. */
47
+ export declare function testBucket(id: string): Promise<TestResult>;
48
+ /** Tests credentials before they are saved. */
49
+ export declare function testDraft(input: AddBucketInput): Promise<TestResult>;
50
+ /** Resolves a bucket by id or by its friendly name (or, failing that, the bucket name). */
51
+ export declare function resolveBucket(ref: string): Promise<StorageBucket>;
52
+ /** Human summary of where a bucket lives, for `storage list`. */
53
+ export declare function bucketEndpointLabel(b: StorageBucket): string;
@@ -0,0 +1,43 @@
1
+ import { apiBase, apiFetch } from "../api.js";
2
+ const BUCKETS = "/api/v5/teleport/buckets";
3
+ export async function listBuckets() {
4
+ const res = await apiFetch(BUCKETS);
5
+ return res.buckets ?? [];
6
+ }
7
+ export async function addBucket(input) {
8
+ return apiFetch(BUCKETS, { method: "POST", body: input });
9
+ }
10
+ export async function updateBucket(id, input) {
11
+ return apiFetch(`${BUCKETS}/${encodeURIComponent(id)}`, { method: "PATCH", body: input });
12
+ }
13
+ export async function removeBucket(id) {
14
+ await apiFetch(`${BUCKETS}/${encodeURIComponent(id)}`, { method: "DELETE" });
15
+ }
16
+ /** Tests a stored bucket's credentials. */
17
+ export async function testBucket(id) {
18
+ return apiFetch(`${BUCKETS}/test`, { method: "POST", body: { bucketId: id } });
19
+ }
20
+ /** Tests credentials before they are saved. */
21
+ export async function testDraft(input) {
22
+ return apiFetch(`${BUCKETS}/test`, { method: "POST", body: { settings: input } });
23
+ }
24
+ /** Resolves a bucket by id or by its friendly name (or, failing that, the bucket name). */
25
+ export async function resolveBucket(ref) {
26
+ const buckets = await listBuckets();
27
+ const hit = buckets.find((b) => b.id === ref) ??
28
+ buckets.find((b) => b.name === ref) ??
29
+ buckets.find((b) => b.bucket === ref);
30
+ if (!hit)
31
+ throw new Error(`no bucket named "${ref}"; see \`heyditto storage list\``);
32
+ return hit;
33
+ }
34
+ /** Human summary of where a bucket lives, for `storage list`. */
35
+ export function bucketEndpointLabel(b) {
36
+ try {
37
+ return new URL(b.endpoint).host;
38
+ }
39
+ catch {
40
+ return b.endpoint || apiBase();
41
+ }
42
+ }
43
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/teleport/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AA+C9C,MAAM,OAAO,GAAG,0BAA0B,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAgC,OAAO,CAAC,CAAC;IACnE,OAAO,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAqB;IACnD,OAAO,QAAQ,CAAgB,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAU,EAAE,KAAwB;IACrE,OAAO,QAAQ,CAAgB,GAAG,OAAO,IAAI,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAU;IAC3C,MAAM,QAAQ,CAAO,GAAG,OAAO,IAAI,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAU;IACzC,OAAO,QAAQ,CAAa,GAAG,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7F,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAqB;IACnD,OAAO,QAAQ,CAAa,GAAG,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AAChG,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,MAAM,GAAG,GACP,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;IACxC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,kCAAkC,CAAC,CAAC;IACrF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,mBAAmB,CAAC,CAAgB;IAClD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC,QAAQ,IAAI,OAAO,EAAE,CAAC;IACjC,CAAC;AACH,CAAC"}