@savestate/cli 0.2.0 → 0.3.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 (49) hide show
  1. package/README.md +9 -0
  2. package/dist/cli.js +42 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/config.d.ts.map +1 -1
  5. package/dist/commands/config.js +40 -9
  6. package/dist/commands/config.js.map +1 -1
  7. package/dist/commands/login.d.ts +13 -0
  8. package/dist/commands/login.d.ts.map +1 -0
  9. package/dist/commands/login.js +111 -0
  10. package/dist/commands/login.js.map +1 -0
  11. package/dist/commands/migrate.d.ts +21 -0
  12. package/dist/commands/migrate.d.ts.map +1 -0
  13. package/dist/commands/migrate.js +218 -0
  14. package/dist/commands/migrate.js.map +1 -0
  15. package/dist/commands/schedule.d.ts +14 -0
  16. package/dist/commands/schedule.d.ts.map +1 -0
  17. package/dist/commands/schedule.js +294 -0
  18. package/dist/commands/schedule.js.map +1 -0
  19. package/dist/commands/snapshot.d.ts +1 -0
  20. package/dist/commands/snapshot.d.ts.map +1 -1
  21. package/dist/commands/snapshot.js +14 -4
  22. package/dist/commands/snapshot.js.map +1 -1
  23. package/dist/incremental.d.ts +137 -0
  24. package/dist/incremental.d.ts.map +1 -0
  25. package/dist/incremental.js +289 -0
  26. package/dist/incremental.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/restore.d.ts.map +1 -1
  32. package/dist/restore.js +7 -2
  33. package/dist/restore.js.map +1 -1
  34. package/dist/snapshot.d.ts +17 -1
  35. package/dist/snapshot.d.ts.map +1 -1
  36. package/dist/snapshot.js +82 -17
  37. package/dist/snapshot.js.map +1 -1
  38. package/dist/storage/index.d.ts +2 -0
  39. package/dist/storage/index.d.ts.map +1 -1
  40. package/dist/storage/index.js +1 -0
  41. package/dist/storage/index.js.map +1 -1
  42. package/dist/storage/resolve.d.ts.map +1 -1
  43. package/dist/storage/resolve.js +19 -4
  44. package/dist/storage/resolve.js.map +1 -1
  45. package/dist/storage/s3.d.ts +64 -0
  46. package/dist/storage/s3.d.ts.map +1 -0
  47. package/dist/storage/s3.js +360 -0
  48. package/dist/storage/s3.js.map +1 -0
  49. package/package.json +4 -1
@@ -0,0 +1,294 @@
1
+ /**
2
+ * savestate schedule — Configure automatic backup schedules
3
+ *
4
+ * Uses launchd (macOS) or systemd timers (Linux) for reliable scheduling.
5
+ * Requires Pro or Team tier for cloud-backed schedules.
6
+ */
7
+ import chalk from 'chalk';
8
+ import ora from 'ora';
9
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { homedir, platform } from 'node:os';
12
+ import { execSync } from 'node:child_process';
13
+ import { isInitialized } from '../config.js';
14
+ const LABEL = 'dev.savestate.autobackup';
15
+ export async function scheduleCommand(options) {
16
+ console.log();
17
+ if (!isInitialized()) {
18
+ console.log(chalk.red('✗ SaveState not initialized. Run `savestate init` first.'));
19
+ process.exit(1);
20
+ }
21
+ // Status check
22
+ if (options.status || (!options.every && !options.disable)) {
23
+ await showStatus();
24
+ return;
25
+ }
26
+ // Disable
27
+ if (options.disable) {
28
+ await disableSchedule();
29
+ return;
30
+ }
31
+ // Enable with interval
32
+ if (options.every) {
33
+ await enableSchedule(options.every);
34
+ return;
35
+ }
36
+ }
37
+ async function showStatus() {
38
+ const os = platform();
39
+ if (os === 'darwin') {
40
+ // macOS - check launchd
41
+ const plistPath = join(homedir(), 'Library', 'LaunchAgents', `${LABEL}.plist`);
42
+ if (!existsSync(plistPath)) {
43
+ console.log(chalk.yellow('⏸ Scheduled backups: disabled'));
44
+ console.log();
45
+ console.log(chalk.dim(' Enable with: savestate schedule --every 6h'));
46
+ console.log();
47
+ return;
48
+ }
49
+ try {
50
+ const status = execSync(`launchctl list | grep ${LABEL}`, { encoding: 'utf-8' }).trim();
51
+ if (status) {
52
+ const plist = readFileSync(plistPath, 'utf-8');
53
+ const intervalMatch = plist.match(/<key>StartInterval<\/key>\s*<integer>(\d+)<\/integer>/);
54
+ const interval = intervalMatch ? parseInt(intervalMatch[1]) : 0;
55
+ const hours = Math.round(interval / 3600);
56
+ console.log(chalk.green(`✓ Scheduled backups: enabled`));
57
+ console.log();
58
+ console.log(` ${chalk.dim('Interval:')} every ${hours}h`);
59
+ console.log(` ${chalk.dim('Job:')} ${LABEL}`);
60
+ console.log(` ${chalk.dim('Plist:')} ${plistPath}`);
61
+ console.log();
62
+ console.log(chalk.dim(' View logs: tail -f ~/Library/Logs/savestate-autobackup.log'));
63
+ console.log(chalk.dim(' Disable: savestate schedule --disable'));
64
+ console.log();
65
+ }
66
+ }
67
+ catch {
68
+ console.log(chalk.yellow('⏸ Scheduled backups: configured but not running'));
69
+ console.log(chalk.dim(` Try: launchctl load ${plistPath}`));
70
+ console.log();
71
+ }
72
+ return;
73
+ }
74
+ if (os === 'linux') {
75
+ // Linux - check systemd timer
76
+ const timerPath = join(homedir(), '.config', 'systemd', 'user', `${LABEL}.timer`);
77
+ if (!existsSync(timerPath)) {
78
+ console.log(chalk.yellow('⏸ Scheduled backups: disabled'));
79
+ console.log();
80
+ console.log(chalk.dim(' Enable with: savestate schedule --every 6h'));
81
+ console.log();
82
+ return;
83
+ }
84
+ try {
85
+ const status = execSync(`systemctl --user is-active ${LABEL}.timer 2>/dev/null || true`, { encoding: 'utf-8' }).trim();
86
+ if (status === 'active') {
87
+ console.log(chalk.green(`✓ Scheduled backups: enabled`));
88
+ console.log();
89
+ console.log(` ${chalk.dim('Timer:')} ${LABEL}.timer`);
90
+ console.log(chalk.dim(' View: systemctl --user status ' + LABEL + '.timer'));
91
+ console.log(chalk.dim(' Logs: journalctl --user -u ' + LABEL));
92
+ console.log();
93
+ }
94
+ else {
95
+ console.log(chalk.yellow('⏸ Scheduled backups: configured but not running'));
96
+ console.log();
97
+ }
98
+ }
99
+ catch {
100
+ console.log(chalk.yellow('⏸ Scheduled backups: unknown status'));
101
+ console.log();
102
+ }
103
+ return;
104
+ }
105
+ console.log(chalk.yellow(`⚠ Scheduled backups not supported on ${os}`));
106
+ console.log(chalk.dim(' Use cron manually: */360 * * * * savestate snapshot'));
107
+ console.log();
108
+ }
109
+ async function enableSchedule(interval) {
110
+ const seconds = parseInterval(interval);
111
+ if (!seconds) {
112
+ console.log(chalk.red(`✗ Invalid interval: ${interval}`));
113
+ console.log(chalk.dim(' Examples: 1h, 6h, 12h, 1d'));
114
+ process.exit(1);
115
+ }
116
+ const hours = seconds / 3600;
117
+ const os = platform();
118
+ const spinner = ora(`Setting up ${hours}h backup schedule...`).start();
119
+ try {
120
+ if (os === 'darwin') {
121
+ await setupMacOSSchedule(seconds);
122
+ }
123
+ else if (os === 'linux') {
124
+ await setupLinuxSchedule(seconds);
125
+ }
126
+ else {
127
+ spinner.fail(`Scheduled backups not supported on ${os}`);
128
+ console.log(chalk.dim(` Use cron: */${Math.round(seconds / 60)} * * * * savestate snapshot`));
129
+ return;
130
+ }
131
+ spinner.succeed(`Scheduled backups enabled: every ${hours}h`);
132
+ console.log();
133
+ console.log(chalk.dim(' Your AI state will be automatically backed up.'));
134
+ console.log(chalk.dim(' Check status: savestate schedule'));
135
+ console.log(chalk.dim(' Disable: savestate schedule --disable'));
136
+ console.log();
137
+ }
138
+ catch (err) {
139
+ spinner.fail('Failed to set up schedule');
140
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
141
+ process.exit(1);
142
+ }
143
+ }
144
+ async function disableSchedule() {
145
+ const os = platform();
146
+ const spinner = ora('Disabling scheduled backups...').start();
147
+ try {
148
+ if (os === 'darwin') {
149
+ const plistPath = join(homedir(), 'Library', 'LaunchAgents', `${LABEL}.plist`);
150
+ try {
151
+ execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`);
152
+ }
153
+ catch { /* ignore */ }
154
+ if (existsSync(plistPath)) {
155
+ unlinkSync(plistPath);
156
+ }
157
+ }
158
+ else if (os === 'linux') {
159
+ try {
160
+ execSync(`systemctl --user stop ${LABEL}.timer 2>/dev/null || true`);
161
+ execSync(`systemctl --user disable ${LABEL}.timer 2>/dev/null || true`);
162
+ }
163
+ catch { /* ignore */ }
164
+ const configDir = join(homedir(), '.config', 'systemd', 'user');
165
+ const timerPath = join(configDir, `${LABEL}.timer`);
166
+ const servicePath = join(configDir, `${LABEL}.service`);
167
+ if (existsSync(timerPath))
168
+ unlinkSync(timerPath);
169
+ if (existsSync(servicePath))
170
+ unlinkSync(servicePath);
171
+ }
172
+ spinner.succeed('Scheduled backups disabled');
173
+ console.log();
174
+ }
175
+ catch (err) {
176
+ spinner.fail('Failed to disable schedule');
177
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
178
+ }
179
+ }
180
+ // ─── Platform-specific setup ────────────────────────────────
181
+ async function setupMacOSSchedule(intervalSeconds) {
182
+ const launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');
183
+ const plistPath = join(launchAgentsDir, `${LABEL}.plist`);
184
+ const logPath = join(homedir(), 'Library', 'Logs', 'savestate-autobackup.log');
185
+ // Find the savestate binary
186
+ const savestateCmd = findSavestateCommand();
187
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
188
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
189
+ <plist version="1.0">
190
+ <dict>
191
+ <key>Label</key>
192
+ <string>${LABEL}</string>
193
+ <key>ProgramArguments</key>
194
+ <array>
195
+ <string>${savestateCmd}</string>
196
+ <string>snapshot</string>
197
+ <string>--label</string>
198
+ <string>auto</string>
199
+ </array>
200
+ <key>StartInterval</key>
201
+ <integer>${intervalSeconds}</integer>
202
+ <key>WorkingDirectory</key>
203
+ <string>${process.cwd()}</string>
204
+ <key>StandardOutPath</key>
205
+ <string>${logPath}</string>
206
+ <key>StandardErrorPath</key>
207
+ <string>${logPath}</string>
208
+ <key>RunAtLoad</key>
209
+ <true/>
210
+ <key>EnvironmentVariables</key>
211
+ <dict>
212
+ <key>PATH</key>
213
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
214
+ </dict>
215
+ </dict>
216
+ </plist>`;
217
+ // Ensure directory exists
218
+ if (!existsSync(launchAgentsDir)) {
219
+ mkdirSync(launchAgentsDir, { recursive: true });
220
+ }
221
+ // Unload existing if present
222
+ try {
223
+ execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`);
224
+ }
225
+ catch { /* ignore */ }
226
+ // Write and load
227
+ writeFileSync(plistPath, plist);
228
+ execSync(`launchctl load "${plistPath}"`);
229
+ }
230
+ async function setupLinuxSchedule(intervalSeconds) {
231
+ const configDir = join(homedir(), '.config', 'systemd', 'user');
232
+ const timerPath = join(configDir, `${LABEL}.timer`);
233
+ const servicePath = join(configDir, `${LABEL}.service`);
234
+ const savestateCmd = findSavestateCommand();
235
+ const hours = Math.round(intervalSeconds / 3600);
236
+ const service = `[Unit]
237
+ Description=SaveState automatic backup
238
+
239
+ [Service]
240
+ Type=oneshot
241
+ WorkingDirectory=${process.cwd()}
242
+ ExecStart=${savestateCmd} snapshot --label auto
243
+ `;
244
+ const timer = `[Unit]
245
+ Description=SaveState automatic backup timer
246
+
247
+ [Timer]
248
+ OnBootSec=5min
249
+ OnUnitActiveSec=${hours}h
250
+ Persistent=true
251
+
252
+ [Install]
253
+ WantedBy=timers.target
254
+ `;
255
+ // Ensure directory exists
256
+ if (!existsSync(configDir)) {
257
+ mkdirSync(configDir, { recursive: true });
258
+ }
259
+ // Write units
260
+ writeFileSync(servicePath, service);
261
+ writeFileSync(timerPath, timer);
262
+ // Enable and start
263
+ execSync('systemctl --user daemon-reload');
264
+ execSync(`systemctl --user enable ${LABEL}.timer`);
265
+ execSync(`systemctl --user start ${LABEL}.timer`);
266
+ }
267
+ // ─── Helpers ────────────────────────────────────────────────
268
+ function parseInterval(interval) {
269
+ const match = interval.match(/^(\d+)(h|d|m)$/i);
270
+ if (!match)
271
+ return null;
272
+ const value = parseInt(match[1]);
273
+ const unit = match[2].toLowerCase();
274
+ switch (unit) {
275
+ case 'm': return value * 60;
276
+ case 'h': return value * 3600;
277
+ case 'd': return value * 86400;
278
+ default: return null;
279
+ }
280
+ }
281
+ function findSavestateCommand() {
282
+ // Check if we're running via npx/node
283
+ const execPath = process.argv[1];
284
+ // Try to find a global or local installation
285
+ try {
286
+ const which = execSync('which savestate 2>/dev/null || true', { encoding: 'utf-8' }).trim();
287
+ if (which)
288
+ return which;
289
+ }
290
+ catch { /* ignore */ }
291
+ // Fall back to npx
292
+ return 'npx savestate';
293
+ }
294
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/commands/schedule.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ interface SnapshotOptions {
6
6
  tags?: string;
7
7
  adapter?: string;
8
8
  schedule?: string;
9
+ full?: boolean;
9
10
  }
10
11
  export declare function snapshotCommand(options: SnapshotOptions): Promise<void>;
11
12
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE7E"}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF7E"}
@@ -15,9 +15,11 @@ export async function snapshotCommand(options) {
15
15
  process.exit(1);
16
16
  }
17
17
  if (options.schedule) {
18
- console.log(chalk.cyan(`⏰ Scheduled snapshots: every ${options.schedule}`));
19
- console.log(chalk.dim(' [Coming soon] Will run as a background daemon.'));
20
- console.log(chalk.dim(' For now, use cron: */6 * * * * savestate snapshot'));
18
+ console.log(chalk.cyan(`⏰ To set up scheduled backups, use:`));
19
+ console.log();
20
+ console.log(` savestate schedule --every ${options.schedule}`);
21
+ console.log();
22
+ console.log(chalk.dim(' This creates a system job (launchd/systemd) for reliable auto-backups.'));
21
23
  console.log();
22
24
  return;
23
25
  }
@@ -50,14 +52,22 @@ export async function snapshotCommand(options) {
50
52
  const result = await createSnapshot(adapter, storage, passphrase, {
51
53
  label: options.label,
52
54
  tags: options.tags?.split(',').map((t) => t.trim()),
55
+ full: options.full,
53
56
  });
54
- spinner.succeed('Snapshot created!');
57
+ const typeLabel = result.incremental ? 'Incremental snapshot' : 'Full snapshot';
58
+ spinner.succeed(`${typeLabel} created!`);
55
59
  console.log();
56
60
  console.log(` ${chalk.dim('ID:')} ${chalk.cyan(result.snapshot.manifest.id)}`);
57
61
  console.log(` ${chalk.dim('Adapter:')} ${adapter.name}`);
62
+ console.log(` ${chalk.dim('Type:')} ${result.incremental ? chalk.yellow('incremental (delta)') : chalk.blue('full')}`);
58
63
  if (options.label) {
59
64
  console.log(` ${chalk.dim('Label:')} ${options.label}`);
60
65
  }
66
+ if (result.incremental && result.delta) {
67
+ console.log(` ${chalk.dim('Changes:')} ${chalk.green(`+${result.delta.added}`)} added, ${chalk.yellow(`~${result.delta.modified}`)} modified, ${chalk.red(`-${result.delta.removed}`)} removed, ${chalk.dim(`${result.delta.unchanged} unchanged`)}`);
68
+ console.log(` ${chalk.dim('Chain:')} depth ${result.delta.chainDepth} (parent: ${result.snapshot.manifest.parent})`);
69
+ console.log(` ${chalk.dim('Saved:')} ${formatBytes(result.delta.bytesSaved)} vs full snapshot`);
70
+ }
61
71
  console.log(` ${chalk.dim('Files:')} ${result.fileCount} files in archive`);
62
72
  console.log(` ${chalk.dim('Archive:')} ${formatBytes(result.archiveSize)}`);
63
73
  console.log(` ${chalk.dim('Encrypted:')} ${formatBytes(result.encryptedSize)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * SaveState Incremental Snapshots
3
+ *
4
+ * Instead of storing a full copy every time, incremental snapshots
5
+ * only capture what changed since the parent snapshot.
6
+ *
7
+ * How it works:
8
+ * 1. Hash every file in the current snapshot (SHA-256)
9
+ * 2. Load the parent snapshot's content hashes
10
+ * 3. Compare: identify added, modified, removed files
11
+ * 4. Store only the delta (changed + added files)
12
+ * 5. On restore: reconstruct by applying deltas from base → current
13
+ *
14
+ * The delta manifest tracks the relationship:
15
+ * ```
16
+ * base (full) → delta-1 → delta-2 → delta-3 (current)
17
+ * ```
18
+ *
19
+ * Every Nth snapshot (configurable, default 10) forces a full snapshot
20
+ * to keep the chain short and restore fast.
21
+ */
22
+ import type { Snapshot, StorageBackend } from './types.js';
23
+ /** Hash of every file in a snapshot */
24
+ export interface ContentHashes {
25
+ /** Map of relative path → SHA-256 hash */
26
+ files: Record<string, string>;
27
+ /** Total number of files */
28
+ count: number;
29
+ /** Combined hash of all file hashes (for quick comparison) */
30
+ rootHash: string;
31
+ }
32
+ /** A single file change in a delta */
33
+ export interface DeltaEntry {
34
+ /** Relative path within the archive */
35
+ path: string;
36
+ /** Type of change */
37
+ type: 'added' | 'modified' | 'removed';
38
+ /** SHA-256 hash of the new content (absent for 'removed') */
39
+ hash?: string;
40
+ /** Size in bytes of the new content (absent for 'removed') */
41
+ size?: number;
42
+ }
43
+ /** The delta manifest stored inside incremental archives */
44
+ export interface DeltaManifest {
45
+ /** ID of the parent snapshot this delta is relative to */
46
+ parentId: string;
47
+ /** ID of the base (full) snapshot at the root of the chain */
48
+ baseId: string;
49
+ /** Position in the chain (0 = full snapshot, 1 = first delta, etc.) */
50
+ chainDepth: number;
51
+ /** Content hashes of the FULL resulting state after applying this delta */
52
+ resultHashes: ContentHashes;
53
+ /** Individual file changes */
54
+ entries: DeltaEntry[];
55
+ /** Summary stats */
56
+ stats: {
57
+ added: number;
58
+ modified: number;
59
+ removed: number;
60
+ unchanged: number;
61
+ totalFiles: number;
62
+ /** Bytes saved vs full snapshot */
63
+ bytesSaved: number;
64
+ };
65
+ }
66
+ /** Result of computing a delta between two snapshots */
67
+ export interface DeltaResult {
68
+ /** The delta manifest */
69
+ delta: DeltaManifest;
70
+ /** Map of changed file paths → content (only added + modified) */
71
+ changedFiles: Map<string, Buffer>;
72
+ /** Whether a full snapshot should be forced (chain too deep or too much changed) */
73
+ shouldForceFull: boolean;
74
+ }
75
+ /** Max chain depth before forcing a full snapshot */
76
+ export declare const MAX_CHAIN_DEPTH = 10;
77
+ /** If more than this fraction of files changed, just do a full snapshot */
78
+ export declare const FULL_SNAPSHOT_THRESHOLD = 0.7;
79
+ /**
80
+ * Compute SHA-256 hashes for every file in a packed snapshot.
81
+ */
82
+ export declare function computeContentHashes(files: Map<string, Buffer>): ContentHashes;
83
+ /**
84
+ * Compute the delta between the current snapshot files and a parent's content hashes.
85
+ *
86
+ * @param currentFiles - The full file map of the current snapshot
87
+ * @param parentHashes - Content hashes from the parent snapshot
88
+ * @param parentId - ID of the parent snapshot
89
+ * @param baseId - ID of the base (full) snapshot
90
+ * @param chainDepth - Current position in the chain
91
+ * @returns The computed delta and changed files
92
+ */
93
+ export declare function computeDelta(currentFiles: Map<string, Buffer>, parentHashes: ContentHashes, parentId: string, baseId: string, chainDepth: number): DeltaResult;
94
+ /**
95
+ * Pack an incremental snapshot (delta only).
96
+ * The archive contains:
97
+ * - manifest.json (with parent reference)
98
+ * - meta/delta-manifest.json (the delta details)
99
+ * - meta/snapshot-chain.json (chain info)
100
+ * - Only the changed/added files (same paths as full snapshot)
101
+ */
102
+ export declare function packDelta(snapshot: Snapshot, delta: DeltaManifest, changedFiles: Map<string, Buffer>): Map<string, Buffer>;
103
+ /**
104
+ * Reconstruct a full snapshot by walking the chain from base to current.
105
+ *
106
+ * Starting from the base (full) snapshot, applies each delta in order:
107
+ * 1. Load base snapshot → full file map
108
+ * 2. For each delta in the chain:
109
+ * a. Add/overwrite files from the delta
110
+ * b. Remove files marked as 'removed'
111
+ * 3. Return the reconstructed full file map
112
+ *
113
+ * @param snapshotId - The target snapshot to reconstruct
114
+ * @param storage - Storage backend
115
+ * @param passphrase - Decryption passphrase
116
+ * @returns Reconstructed full file map
117
+ */
118
+ export declare function reconstructFromChain(snapshotId: string, storage: StorageBackend, passphrase: string): Promise<Map<string, Buffer>>;
119
+ /**
120
+ * Get content hashes for the latest snapshot (used as parent for incremental).
121
+ * Returns null if no previous snapshots exist.
122
+ */
123
+ export declare function getParentHashes(storage: StorageBackend, passphrase: string): Promise<{
124
+ hashes: ContentHashes;
125
+ parentId: string;
126
+ baseId: string;
127
+ chainDepth: number;
128
+ } | null>;
129
+ /**
130
+ * Check if a snapshot is incremental (has a delta manifest).
131
+ */
132
+ export declare function isIncremental(files: Map<string, Buffer>): boolean;
133
+ /**
134
+ * Get the delta manifest from an unpacked archive, if present.
135
+ */
136
+ export declare function getDeltaManifest(files: Map<string, Buffer>): DeltaManifest | null;
137
+ //# sourceMappingURL=incremental.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental.d.ts","sourceRoot":"","sources":["../src/incremental.ts"],"names":[],"mappings":"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"}