island-bridge 2.0.0 → 2.0.1

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 (60) hide show
  1. package/dist/bin/cli.d.ts +2 -0
  2. package/dist/bin/cli.js +327 -0
  3. package/dist/bin/cli.js.map +1 -0
  4. package/dist/lib/args.d.ts +6 -0
  5. package/dist/lib/args.js +128 -0
  6. package/dist/lib/args.js.map +1 -0
  7. package/dist/lib/backup.d.ts +26 -0
  8. package/dist/lib/backup.js +96 -0
  9. package/dist/lib/backup.js.map +1 -0
  10. package/dist/lib/config.d.ts +25 -0
  11. package/dist/lib/config.js +216 -0
  12. package/dist/lib/config.js.map +1 -0
  13. package/dist/lib/history.d.ts +9 -0
  14. package/dist/lib/history.js +84 -0
  15. package/dist/lib/history.js.map +1 -0
  16. package/dist/lib/hooks.d.ts +11 -0
  17. package/dist/lib/hooks.js +36 -0
  18. package/dist/lib/hooks.js.map +1 -0
  19. package/dist/lib/init.d.ts +28 -0
  20. package/dist/lib/init.js +90 -0
  21. package/dist/lib/init.js.map +1 -0
  22. package/dist/lib/interactive.d.ts +9 -0
  23. package/dist/lib/interactive.js +41 -0
  24. package/dist/lib/interactive.js.map +1 -0
  25. package/dist/lib/progress.d.ts +18 -0
  26. package/dist/lib/progress.js +65 -0
  27. package/dist/lib/progress.js.map +1 -0
  28. package/dist/lib/reporter.d.ts +59 -0
  29. package/dist/lib/reporter.js +189 -0
  30. package/dist/lib/reporter.js.map +1 -0
  31. package/dist/lib/status.d.ts +41 -0
  32. package/dist/lib/status.js +123 -0
  33. package/dist/lib/status.js.map +1 -0
  34. package/dist/lib/summary.d.ts +19 -0
  35. package/dist/lib/summary.js +56 -0
  36. package/dist/lib/summary.js.map +1 -0
  37. package/dist/lib/sync.d.ts +32 -0
  38. package/dist/lib/sync.js +237 -0
  39. package/dist/lib/sync.js.map +1 -0
  40. package/dist/lib/types.d.ts +91 -0
  41. package/dist/lib/types.js +2 -0
  42. package/dist/lib/types.js.map +1 -0
  43. package/dist/lib/watch.d.ts +10 -0
  44. package/dist/lib/watch.js +73 -0
  45. package/dist/lib/watch.js.map +1 -0
  46. package/package.json +11 -5
  47. package/bin/cli.js +0 -349
  48. package/lib/args.js +0 -124
  49. package/lib/backup.js +0 -124
  50. package/lib/config.js +0 -234
  51. package/lib/history.js +0 -94
  52. package/lib/hooks.js +0 -33
  53. package/lib/init.js +0 -110
  54. package/lib/interactive.js +0 -52
  55. package/lib/progress.js +0 -67
  56. package/lib/reporter.js +0 -187
  57. package/lib/status.js +0 -127
  58. package/lib/summary.js +0 -62
  59. package/lib/sync.js +0 -267
  60. package/lib/watch.js +0 -78
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from '../lib/args.js';
3
+ import { loadConfig, extractFolderName } from '../lib/config.js';
4
+ import { checkRsync, syncAll, diffPreview } from '../lib/sync.js';
5
+ import { Reporter } from '../lib/reporter.js';
6
+ import { startWatch } from '../lib/watch.js';
7
+ import { recordSync, showHistory } from '../lib/history.js';
8
+ import { runHook } from '../lib/hooks.js';
9
+ import { selectPaths } from '../lib/interactive.js';
10
+ import { runInit } from '../lib/init.js';
11
+ import { runStatus } from '../lib/status.js';
12
+ import { listBackups, restoreBackup, cleanBackups } from '../lib/backup.js';
13
+ import { getErrorHint } from '../lib/summary.js';
14
+ import { readFileSync } from 'node:fs';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { dirname, join } from 'node:path';
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf-8'));
19
+ const USAGE = `
20
+ island-bridge v${pkg.version} - Sync remote folders via rsync over SSH
21
+
22
+ Usage:
23
+ island-bridge <command> [options]
24
+
25
+ Commands:
26
+ pull Pull remote folders to local directory
27
+ push Push local folders to remote server
28
+ watch Watch local folders and auto-push on changes
29
+ diff Preview changes without syncing
30
+ history Show sync history
31
+ init Create island-bridge.json config interactively
32
+ status Show config, SSH, rsync, and paths status
33
+ backup Manage sync backups (list, restore, clean)
34
+
35
+ Options:
36
+ -n, --dry-run Preview sync without making changes
37
+ -v, --verbose Show detailed output
38
+ -q, --quiet Suppress output (exit code only)
39
+ -c, --config <path> Use specific config file
40
+ --env <name> Use named profile from config
41
+ --bwlimit <KB/s> Limit transfer bandwidth
42
+ -s, --select Interactively select folders to sync
43
+ --path <name> Sync specific folder by name (repeatable)
44
+ --json Output in JSON format (for scripts/AI)
45
+ --no-backup Skip backup for this sync
46
+ -V, --version Show version
47
+ -h, --help Show this help message
48
+
49
+ Backup subcommands:
50
+ backup list List available backups
51
+ backup restore <timestamp> Restore files from a backup
52
+ backup clean --keep <N> Remove old backups, keep N most recent
53
+ `.trim();
54
+ async function main() {
55
+ let args;
56
+ try {
57
+ args = parseArgs();
58
+ }
59
+ catch (err) {
60
+ console.error(`\x1b[31mError: ${err.message}\x1b[0m`);
61
+ process.exit(1);
62
+ }
63
+ const reporter = new Reporter(args.json ? 'json' : 'human');
64
+ if (args.help || (!args.command && !args.version)) {
65
+ console.log(USAGE);
66
+ process.exit(0);
67
+ }
68
+ if (args.version) {
69
+ if (args.json) {
70
+ reporter.setCommand('version');
71
+ reporter.info(pkg.version);
72
+ reporter.flush();
73
+ }
74
+ else {
75
+ console.log(`island-bridge v${pkg.version}`);
76
+ }
77
+ process.exit(0);
78
+ }
79
+ const validCommands = ['pull', 'push', 'watch', 'diff', 'history', 'init', 'status', 'backup'];
80
+ if (!validCommands.includes(args.command)) {
81
+ reporter.error(`Unknown command: ${args.command}`, `Valid commands: ${validCommands.join(', ')}`);
82
+ if (args.json)
83
+ reporter.flush();
84
+ process.exit(1);
85
+ }
86
+ reporter.setCommand(args.command);
87
+ // === Init ===
88
+ if (args.command === 'init') {
89
+ const ok = await runInit(args, reporter);
90
+ if (args.json)
91
+ reporter.flush();
92
+ process.exit(ok ? 0 : 1);
93
+ }
94
+ // === History (doesn't need rsync) ===
95
+ if (args.command === 'history') {
96
+ let config;
97
+ try {
98
+ config = loadConfig({ configPath: args.config ?? undefined, env: args.env ?? undefined });
99
+ }
100
+ catch {
101
+ // Show history from cwd if no config found
102
+ }
103
+ if (args.json) {
104
+ const { readFileSync: rfs, existsSync: efs } = await import('node:fs');
105
+ const { dirname: dn, join: jn } = await import('node:path');
106
+ const historyFile = config?._filePath
107
+ ? jn(dn(config._filePath), '.island-bridge-history.json')
108
+ : '.island-bridge-history.json';
109
+ let entries = [];
110
+ if (efs(historyFile)) {
111
+ try {
112
+ const parsed = JSON.parse(rfs(historyFile, 'utf-8'));
113
+ entries = Array.isArray(parsed) ? parsed : [];
114
+ }
115
+ catch { /* ignore */ }
116
+ }
117
+ reporter.historyReport(entries);
118
+ reporter.flush();
119
+ }
120
+ else {
121
+ showHistory(config?._filePath);
122
+ }
123
+ process.exit(0);
124
+ }
125
+ // === Backup ===
126
+ if (args.command === 'backup') {
127
+ let config;
128
+ try {
129
+ config = loadConfig({ configPath: args.config ?? undefined, env: args.env ?? undefined });
130
+ }
131
+ catch {
132
+ config = { backup: { enabled: true, localDir: '.island-bridge-backups', remoteDir: '~/.island-bridge-backups', maxCount: 10 } };
133
+ }
134
+ const backupConfig = config.backup;
135
+ if (args.subcommand === 'list') {
136
+ const backups = listBackups(backupConfig.localDir);
137
+ if (args.json) {
138
+ reporter.historyReport(backups.map((b) => ({ timestamp: b.name, date: b.date.toISOString() })));
139
+ reporter.flush();
140
+ }
141
+ else {
142
+ if (backups.length === 0) {
143
+ console.log('No backups found.');
144
+ }
145
+ else {
146
+ console.log('\n--- Backups ---\n');
147
+ for (const b of backups) {
148
+ console.log(` ${b.name} (${b.date.toLocaleString()})`);
149
+ }
150
+ console.log(`\n${backups.length} backup(s) found.`);
151
+ }
152
+ }
153
+ process.exit(0);
154
+ }
155
+ if (args.subcommand === 'restore') {
156
+ if (!args.backupTimestamp) {
157
+ reporter.error('backup restore requires a timestamp', 'Run "island-bridge backup list" to see available backups');
158
+ if (args.json)
159
+ reporter.flush();
160
+ process.exit(1);
161
+ }
162
+ try {
163
+ const results = restoreBackup(backupConfig.localDir, args.backupTimestamp);
164
+ if (args.json) {
165
+ for (const r of results) {
166
+ reporter.syncEnd(r.folder, r);
167
+ }
168
+ reporter.flush();
169
+ }
170
+ else {
171
+ for (const r of results) {
172
+ if (r.success) {
173
+ console.log(` \x1b[32m\u2713\x1b[0m ${r.folder} restored`);
174
+ }
175
+ else {
176
+ console.log(` \x1b[31m\u2717\x1b[0m ${r.folder} \u2014 ${r.error}`);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ catch (err) {
182
+ reporter.error(err.message, 'Run "island-bridge backup list" to see available backups');
183
+ if (args.json)
184
+ reporter.flush();
185
+ process.exit(1);
186
+ }
187
+ process.exit(0);
188
+ }
189
+ if (args.subcommand === 'clean') {
190
+ const keep = args.keep || backupConfig.maxCount || 10;
191
+ const removed = cleanBackups(backupConfig.localDir, keep);
192
+ if (args.json) {
193
+ reporter.info(`Removed ${removed.length} backup(s), keeping ${keep}`);
194
+ reporter.flush();
195
+ }
196
+ else {
197
+ if (removed.length === 0) {
198
+ console.log(`No backups to remove (keeping ${keep}).`);
199
+ }
200
+ else {
201
+ for (const name of removed) {
202
+ console.log(` Removed: ${name}`);
203
+ }
204
+ console.log(`\nRemoved ${removed.length} backup(s), keeping ${keep}.`);
205
+ }
206
+ }
207
+ process.exit(0);
208
+ }
209
+ reporter.error('Unknown backup subcommand', 'Usage: island-bridge backup <list|restore|clean>');
210
+ if (args.json)
211
+ reporter.flush();
212
+ process.exit(1);
213
+ }
214
+ // === Pre-flight: check rsync ===
215
+ const rsyncOk = await checkRsync();
216
+ if (!rsyncOk) {
217
+ reporter.error('rsync is required but not found in PATH', getErrorHint('rsync-not-found'));
218
+ if (args.json)
219
+ reporter.flush();
220
+ process.exit(1);
221
+ }
222
+ // === Load config ===
223
+ let config;
224
+ try {
225
+ config = loadConfig({ configPath: args.config ?? undefined, env: args.env ?? undefined });
226
+ }
227
+ catch (err) {
228
+ const hint = err.message.includes('Cannot find')
229
+ ? getErrorHint('config-not-found')
230
+ : null;
231
+ reporter.error(err.message, hint);
232
+ if (args.json)
233
+ reporter.flush();
234
+ process.exit(1);
235
+ }
236
+ if (args.env) {
237
+ reporter.info(`Using profile: ${args.env}`);
238
+ }
239
+ if (config._filePath) {
240
+ reporter.info(`Config: ${config._filePath}`);
241
+ }
242
+ // === Status ===
243
+ if (args.command === 'status') {
244
+ await runStatus(config, reporter);
245
+ if (args.json)
246
+ reporter.flush();
247
+ process.exit(0);
248
+ }
249
+ // === --path filter ===
250
+ if (args.path.length > 0) {
251
+ const allNames = config.remote.paths.map((p) => extractFolderName(p));
252
+ const invalid = args.path.filter((name) => !allNames.includes(name));
253
+ if (invalid.length > 0) {
254
+ reporter.error(`Unknown folder name(s): ${invalid.join(', ')}`, `Available folders: ${allNames.join(', ')}`);
255
+ if (args.json)
256
+ reporter.flush();
257
+ process.exit(1);
258
+ }
259
+ config.remote.paths = config.remote.paths.filter((p) => args.path.includes(extractFolderName(p)));
260
+ }
261
+ // === Interactive selection ===
262
+ if (args.select && !args.json && ['pull', 'push', 'diff'].includes(args.command)) {
263
+ config.remote.paths = await selectPaths(config.remote.paths, reporter);
264
+ }
265
+ // === Watch mode ===
266
+ if (args.command === 'watch') {
267
+ startWatch(config, {
268
+ dryRun: args.dryRun,
269
+ verbose: args.verbose,
270
+ quiet: args.quiet,
271
+ bwlimit: args.bwlimit,
272
+ noBackup: args.noBackup,
273
+ }, reporter);
274
+ return;
275
+ }
276
+ // === Diff preview ===
277
+ if (args.command === 'diff') {
278
+ const diffs = await diffPreview(config, 'pull', {
279
+ bwlimit: args.bwlimit,
280
+ exclude: config.exclude,
281
+ });
282
+ reporter.diffReport(diffs);
283
+ if (args.json)
284
+ reporter.flush();
285
+ process.exit(0);
286
+ }
287
+ // === Pull or Push ===
288
+ const options = {
289
+ dryRun: args.dryRun,
290
+ verbose: args.verbose,
291
+ quiet: args.quiet,
292
+ bwlimit: args.bwlimit,
293
+ noBackup: args.noBackup,
294
+ };
295
+ // Disable hooks from configs found via upward search
296
+ if (!config._explicitConfig && config._filePath !== join(process.cwd(), 'island-bridge.json')) {
297
+ if (config.hooks.beforeSync || config.hooks.afterSync) {
298
+ reporter.warn(`hooks ignored from inherited config ${config._filePath}`);
299
+ reporter.warn(`Use --config ${config._filePath} to explicitly enable hooks.`);
300
+ config.hooks = {};
301
+ }
302
+ }
303
+ // Run beforeSync hook
304
+ if (config.hooks.beforeSync) {
305
+ runHook('beforeSync', config.hooks.beforeSync, args.quiet, reporter);
306
+ }
307
+ const results = await syncAll(config, args.command, options, reporter);
308
+ // Run afterSync hook
309
+ if (config.hooks.afterSync) {
310
+ runHook('afterSync', config.hooks.afterSync, args.quiet, reporter);
311
+ }
312
+ // Record history
313
+ recordSync(config._filePath, args.command, results);
314
+ // Print summary
315
+ if (!args.quiet || args.json) {
316
+ reporter.summary(results);
317
+ if (!args.json && args.dryRun) {
318
+ reporter.info('(dry-run mode \u2014 no changes were made)');
319
+ }
320
+ }
321
+ if (args.json)
322
+ reporter.flush();
323
+ const hasFailed = results.some((r) => !r.success);
324
+ process.exit(hasFailed ? 1 : 0);
325
+ }
326
+ main();
327
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Lightweight CLI argument parser for island-bridge.
3
+ * Zero dependencies.
4
+ */
5
+ import type { ParsedArgs } from './types.js';
6
+ export declare function parseArgs(argv?: string[]): ParsedArgs;
@@ -0,0 +1,128 @@
1
+ export function parseArgs(argv = process.argv.slice(2)) {
2
+ const args = {
3
+ command: null,
4
+ subcommand: null,
5
+ dryRun: false,
6
+ verbose: false,
7
+ quiet: false,
8
+ json: false,
9
+ config: null,
10
+ env: null,
11
+ bwlimit: null,
12
+ select: false,
13
+ noBackup: false,
14
+ path: [],
15
+ help: false,
16
+ version: false,
17
+ // init-specific
18
+ host: null,
19
+ user: null,
20
+ paths: null,
21
+ // backup-specific
22
+ backupTimestamp: null,
23
+ keep: null,
24
+ };
25
+ for (let i = 0; i < argv.length; i++) {
26
+ const arg = argv[i];
27
+ switch (arg) {
28
+ case '--dry-run':
29
+ case '-n':
30
+ args.dryRun = true;
31
+ break;
32
+ case '--verbose':
33
+ case '-v':
34
+ args.verbose = true;
35
+ break;
36
+ case '--quiet':
37
+ case '-q':
38
+ args.quiet = true;
39
+ break;
40
+ case '--json':
41
+ args.json = true;
42
+ break;
43
+ case '--config':
44
+ case '-c':
45
+ args.config = argv[++i];
46
+ if (!args.config || args.config.startsWith('-'))
47
+ throw new Error('--config requires a file path');
48
+ break;
49
+ case '--env':
50
+ args.env = argv[++i];
51
+ if (!args.env || args.env.startsWith('-'))
52
+ throw new Error('--env requires a profile name');
53
+ break;
54
+ case '--bwlimit': {
55
+ const bwRaw = argv[++i];
56
+ if (!bwRaw || isNaN(Number(bwRaw))) {
57
+ throw new Error('--bwlimit requires a numeric value (KB/s)');
58
+ }
59
+ args.bwlimit = Number(bwRaw);
60
+ break;
61
+ }
62
+ case '--select':
63
+ case '-s':
64
+ args.select = true;
65
+ break;
66
+ case '--no-backup':
67
+ args.noBackup = true;
68
+ break;
69
+ case '--path':
70
+ {
71
+ const val = argv[++i];
72
+ if (!val || val.startsWith('-'))
73
+ throw new Error('--path requires a folder name');
74
+ args.path.push(val);
75
+ }
76
+ break;
77
+ case '--host':
78
+ args.host = argv[++i];
79
+ if (!args.host)
80
+ throw new Error('--host requires a value');
81
+ break;
82
+ case '--user':
83
+ args.user = argv[++i];
84
+ if (!args.user)
85
+ throw new Error('--user requires a value');
86
+ break;
87
+ case '--paths':
88
+ args.paths = argv[++i];
89
+ if (!args.paths)
90
+ throw new Error('--paths requires a value');
91
+ break;
92
+ case '--keep': {
93
+ const keepRaw = argv[++i];
94
+ if (!keepRaw || isNaN(Number(keepRaw))) {
95
+ throw new Error('--keep requires a numeric value');
96
+ }
97
+ args.keep = Number(keepRaw);
98
+ break;
99
+ }
100
+ case '--help':
101
+ case '-h':
102
+ args.help = true;
103
+ break;
104
+ case '--version':
105
+ case '-V':
106
+ args.version = true;
107
+ break;
108
+ default:
109
+ if (!arg.startsWith('-')) {
110
+ if (!args.command) {
111
+ args.command = arg;
112
+ }
113
+ else if (args.command === 'backup' && !args.subcommand) {
114
+ args.subcommand = arg;
115
+ }
116
+ else if (args.command === 'backup' && args.subcommand === 'restore' && !args.backupTimestamp) {
117
+ args.backupTimestamp = arg;
118
+ }
119
+ }
120
+ break;
121
+ }
122
+ }
123
+ if (args.verbose && args.quiet) {
124
+ throw new Error('Cannot use --verbose and --quiet together');
125
+ }
126
+ return args;
127
+ }
128
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/lib/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { BackupEntry, RestoreResult } from './types.js';
2
+ /**
3
+ * Generate a backup directory path.
4
+ */
5
+ export declare function generateBackupDir(baseDir: string, folderName: string, date?: Date): string;
6
+ /**
7
+ * Build rsync backup args.
8
+ */
9
+ export declare function buildBackupArgs(direction: string, localDir: string | null, remoteDir: string | null, folderName: string, date?: Date): string[];
10
+ /**
11
+ * Parse a list of directory names and return valid timestamped ones, sorted ascending.
12
+ */
13
+ export declare function parseBackupDirs(dirs: string[]): BackupEntry[];
14
+ /**
15
+ * List local backups.
16
+ */
17
+ export declare function listBackups(baseDir: string): BackupEntry[];
18
+ /**
19
+ * Restore a backup by copying files back.
20
+ */
21
+ export declare function restoreBackup(baseDir: string, timestamp: string, targetDir?: string): RestoreResult[];
22
+ /**
23
+ * Clean old backups, keeping the N most recent.
24
+ * @returns removed directory names
25
+ */
26
+ export declare function cleanBackups(baseDir: string, keep: number): string[];
@@ -0,0 +1,96 @@
1
+ import { existsSync, readdirSync, rmSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { execFileSync } from 'node:child_process';
4
+ const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}$/;
5
+ /**
6
+ * Format a Date as a filesystem-safe timestamp.
7
+ */
8
+ function formatTimestamp(date) {
9
+ return date.toISOString().replace(/:/g, '-').replace(/\.\d{3}Z$/, '');
10
+ }
11
+ /**
12
+ * Generate a backup directory path.
13
+ */
14
+ export function generateBackupDir(baseDir, folderName, date = new Date()) {
15
+ const ts = formatTimestamp(date);
16
+ return `${baseDir}/${ts}/${folderName}`;
17
+ }
18
+ /**
19
+ * Build rsync backup args.
20
+ */
21
+ export function buildBackupArgs(direction, localDir, remoteDir, folderName, date = new Date()) {
22
+ if (direction === 'pull' && localDir) {
23
+ const backupDir = generateBackupDir(localDir, folderName, date);
24
+ return ['--backup', `--backup-dir=${backupDir}`];
25
+ }
26
+ if (direction === 'push' && remoteDir) {
27
+ const backupDir = generateBackupDir(remoteDir, folderName, date);
28
+ return ['--backup', `--backup-dir=${backupDir}`];
29
+ }
30
+ return [];
31
+ }
32
+ /**
33
+ * Parse a list of directory names and return valid timestamped ones, sorted ascending.
34
+ */
35
+ export function parseBackupDirs(dirs) {
36
+ return dirs
37
+ .filter(d => TIMESTAMP_RE.test(d))
38
+ .map(d => ({
39
+ name: d,
40
+ date: new Date(d.replace(/T(\d{2})-(\d{2})-(\d{2})$/, 'T$1:$2:$3Z')),
41
+ }))
42
+ .sort((a, b) => a.date.getTime() - b.date.getTime());
43
+ }
44
+ /**
45
+ * List local backups.
46
+ */
47
+ export function listBackups(baseDir) {
48
+ if (!existsSync(baseDir))
49
+ return [];
50
+ const dirs = readdirSync(baseDir).filter(d => {
51
+ const full = join(baseDir, d);
52
+ return statSync(full).isDirectory();
53
+ });
54
+ return parseBackupDirs(dirs);
55
+ }
56
+ /**
57
+ * Restore a backup by copying files back.
58
+ */
59
+ export function restoreBackup(baseDir, timestamp, targetDir = '.') {
60
+ const backupPath = join(baseDir, timestamp);
61
+ if (!existsSync(backupPath)) {
62
+ throw new Error(`Backup not found: ${timestamp}`);
63
+ }
64
+ const folders = readdirSync(backupPath).filter(d => statSync(join(backupPath, d)).isDirectory());
65
+ const results = [];
66
+ for (const folder of folders) {
67
+ const src = join(backupPath, folder) + '/';
68
+ const dst = join(targetDir, folder) + '/';
69
+ try {
70
+ execFileSync('rsync', ['-av', '--', src, dst], { stdio: 'pipe' });
71
+ results.push({ folder, success: true, error: null });
72
+ }
73
+ catch (err) {
74
+ results.push({ folder, success: false, error: err.message });
75
+ }
76
+ }
77
+ return results;
78
+ }
79
+ /**
80
+ * Clean old backups, keeping the N most recent.
81
+ * @returns removed directory names
82
+ */
83
+ export function cleanBackups(baseDir, keep) {
84
+ const backups = listBackups(baseDir);
85
+ if (backups.length <= keep)
86
+ return [];
87
+ const toRemove = backups.slice(0, backups.length - keep);
88
+ const removed = [];
89
+ for (const b of toRemove) {
90
+ const fullPath = join(baseDir, b.name);
91
+ rmSync(fullPath, { recursive: true, force: true });
92
+ removed.push(b.name);
93
+ }
94
+ return removed;
95
+ }
96
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/lib/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,YAAY,GAAG,uCAAuC,CAAC;AAE7D;;GAEG;AACH,SAAS,eAAe,CAAC,IAAU;IACjC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,UAAkB,EAAE,OAAa,IAAI,IAAI,EAAE;IAC5F,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,GAAG,OAAO,IAAI,EAAE,IAAI,UAAU,EAAE,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,QAAuB,EACvB,SAAwB,EACxB,UAAkB,EAClB,OAAa,IAAI,IAAI,EAAE;IAEvB,IAAI,SAAS,KAAK,MAAM,IAAI,QAAQ,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,iBAAiB,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAChE,OAAO,CAAC,UAAU,EAAE,gBAAgB,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QACjE,OAAO,CAAC,UAAU,EAAE,gBAAgB,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,OAAO,IAAI;SACR,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACT,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,2BAA2B,EAAE,YAAY,CAAC,CAAC;KACrE,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9B,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB,EAAE,YAAoB,GAAG;IACvF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACjD,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAC5C,CAAC;IAEF,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;QAC1C,IAAI,CAAC;YACH,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,IAAY;IACxD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { IslandBridgeConfig, BackupConfig } from './types.js';
2
+ /**
3
+ * Search for config file starting from startDir, walking up to root.
4
+ */
5
+ export declare function findConfigFile(startDir?: string): string | null;
6
+ /**
7
+ * Load and validate config.
8
+ */
9
+ export declare function loadConfig(options?: {
10
+ configPath?: string;
11
+ env?: string;
12
+ }): IslandBridgeConfig;
13
+ /**
14
+ * Return default backup config values.
15
+ */
16
+ export declare function backupDefaults(): BackupConfig;
17
+ /**
18
+ * Validate backup config section.
19
+ */
20
+ export declare function validateBackupConfig(backup: any): void;
21
+ /**
22
+ * Extract folder name (last path component) from a remote path.
23
+ * Strips trailing slashes. Rejects root "/" and empty strings.
24
+ */
25
+ export declare function extractFolderName(remotePath: string): string;