@lumy-pack/syncpoint 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/config.default.yml +15 -7
- package/assets/schemas/config.schema.json +68 -0
- package/assets/schemas/metadata.schema.json +121 -0
- package/assets/schemas/template.schema.json +61 -0
- package/assets/template.example.yml +6 -5
- package/dist/cli.mjs +634 -242
- package/dist/commands/Migrate.d.ts +2 -0
- package/dist/core/migrate.d.ts +11 -0
- package/dist/index.cjs +215 -74
- package/dist/index.mjs +215 -74
- package/dist/utils/types.d.ts +8 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -336,23 +336,11 @@ function isValidPattern(pattern) {
|
|
|
336
336
|
import { createHash } from "crypto";
|
|
337
337
|
import { lstat, readFile, readlink } from "fs/promises";
|
|
338
338
|
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
ajv.addKeyword({
|
|
345
|
-
keyword: "validPattern",
|
|
346
|
-
type: "string",
|
|
347
|
-
validate: function validate(schema, data) {
|
|
348
|
-
if (!schema) return true;
|
|
349
|
-
return isValidPattern(data);
|
|
350
|
-
},
|
|
351
|
-
errors: true
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
// src/schemas/metadata.schema.ts
|
|
355
|
-
var metadataSchema = {
|
|
339
|
+
// assets/schemas/metadata.schema.json
|
|
340
|
+
var metadata_schema_default = {
|
|
341
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
342
|
+
title: "Syncpoint Backup Metadata",
|
|
343
|
+
description: "Metadata stored inside backup archives as _metadata.json",
|
|
356
344
|
type: "object",
|
|
357
345
|
required: [
|
|
358
346
|
"version",
|
|
@@ -365,57 +353,129 @@ var metadataSchema = {
|
|
|
365
353
|
"summary"
|
|
366
354
|
],
|
|
367
355
|
properties: {
|
|
368
|
-
version: {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
356
|
+
version: {
|
|
357
|
+
type: "string",
|
|
358
|
+
description: "Metadata schema version."
|
|
359
|
+
},
|
|
360
|
+
toolVersion: {
|
|
361
|
+
type: "string",
|
|
362
|
+
description: "Syncpoint tool version used to create the backup."
|
|
363
|
+
},
|
|
364
|
+
createdAt: {
|
|
365
|
+
type: "string",
|
|
366
|
+
description: "ISO 8601 timestamp of backup creation."
|
|
367
|
+
},
|
|
368
|
+
hostname: {
|
|
369
|
+
type: "string",
|
|
370
|
+
description: "Hostname of the machine where the backup was created."
|
|
371
|
+
},
|
|
372
372
|
system: {
|
|
373
373
|
type: "object",
|
|
374
|
+
description: "System information at backup time.",
|
|
374
375
|
required: ["platform", "release", "arch"],
|
|
375
376
|
properties: {
|
|
376
|
-
platform: {
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
platform: {
|
|
378
|
+
type: "string",
|
|
379
|
+
description: "Operating system platform (e.g. darwin, linux)."
|
|
380
|
+
},
|
|
381
|
+
release: {
|
|
382
|
+
type: "string",
|
|
383
|
+
description: "OS kernel release version."
|
|
384
|
+
},
|
|
385
|
+
arch: {
|
|
386
|
+
type: "string",
|
|
387
|
+
description: "CPU architecture (e.g. arm64, x64)."
|
|
388
|
+
}
|
|
379
389
|
},
|
|
380
390
|
additionalProperties: false
|
|
381
391
|
},
|
|
382
392
|
config: {
|
|
383
393
|
type: "object",
|
|
394
|
+
description: "Backup configuration snapshot.",
|
|
384
395
|
required: ["filename"],
|
|
385
396
|
properties: {
|
|
386
|
-
filename: {
|
|
387
|
-
|
|
397
|
+
filename: {
|
|
398
|
+
type: "string",
|
|
399
|
+
description: "Filename pattern used for the backup."
|
|
400
|
+
},
|
|
401
|
+
destination: {
|
|
402
|
+
type: "string",
|
|
403
|
+
description: "Custom destination path, if configured."
|
|
404
|
+
}
|
|
388
405
|
},
|
|
389
406
|
additionalProperties: false
|
|
390
407
|
},
|
|
391
408
|
files: {
|
|
392
409
|
type: "array",
|
|
410
|
+
description: "List of files included in the backup.",
|
|
393
411
|
items: {
|
|
394
412
|
type: "object",
|
|
395
413
|
required: ["path", "absolutePath", "size", "hash"],
|
|
396
414
|
properties: {
|
|
397
|
-
path: {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
415
|
+
path: {
|
|
416
|
+
type: "string",
|
|
417
|
+
description: "Display path (e.g. ~/.zshrc)."
|
|
418
|
+
},
|
|
419
|
+
absolutePath: {
|
|
420
|
+
type: "string",
|
|
421
|
+
description: "Full filesystem path."
|
|
422
|
+
},
|
|
423
|
+
size: {
|
|
424
|
+
type: "number",
|
|
425
|
+
description: "File size in bytes.",
|
|
426
|
+
minimum: 0
|
|
427
|
+
},
|
|
428
|
+
hash: {
|
|
429
|
+
type: "string",
|
|
430
|
+
description: "SHA-256 hash of file contents."
|
|
431
|
+
},
|
|
432
|
+
type: {
|
|
433
|
+
type: "string",
|
|
434
|
+
description: "File type (e.g. symlink, directory)."
|
|
435
|
+
}
|
|
402
436
|
},
|
|
403
437
|
additionalProperties: false
|
|
404
438
|
}
|
|
405
439
|
},
|
|
406
440
|
summary: {
|
|
407
441
|
type: "object",
|
|
442
|
+
description: "Backup summary statistics.",
|
|
408
443
|
required: ["fileCount", "totalSize"],
|
|
409
444
|
properties: {
|
|
410
|
-
fileCount: {
|
|
411
|
-
|
|
445
|
+
fileCount: {
|
|
446
|
+
type: "integer",
|
|
447
|
+
description: "Total number of files in the backup.",
|
|
448
|
+
minimum: 0
|
|
449
|
+
},
|
|
450
|
+
totalSize: {
|
|
451
|
+
type: "number",
|
|
452
|
+
description: "Total size of all files in bytes.",
|
|
453
|
+
minimum: 0
|
|
454
|
+
}
|
|
412
455
|
},
|
|
413
456
|
additionalProperties: false
|
|
414
457
|
}
|
|
415
458
|
},
|
|
416
459
|
additionalProperties: false
|
|
417
460
|
};
|
|
418
|
-
|
|
461
|
+
|
|
462
|
+
// src/schemas/ajv.ts
|
|
463
|
+
import Ajv from "ajv";
|
|
464
|
+
import addFormats from "ajv-formats";
|
|
465
|
+
var ajv = new Ajv({ allErrors: true });
|
|
466
|
+
addFormats(ajv);
|
|
467
|
+
ajv.addKeyword({
|
|
468
|
+
keyword: "validPattern",
|
|
469
|
+
type: "string",
|
|
470
|
+
validate: function validate(schema, data) {
|
|
471
|
+
if (!schema) return true;
|
|
472
|
+
return isValidPattern(data);
|
|
473
|
+
},
|
|
474
|
+
errors: true
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// src/schemas/metadata.schema.ts
|
|
478
|
+
var validate2 = ajv.compile(metadata_schema_default);
|
|
419
479
|
function validateMetadata(data) {
|
|
420
480
|
const valid = validate2(data);
|
|
421
481
|
if (valid) return { valid: true };
|
|
@@ -426,7 +486,7 @@ function validateMetadata(data) {
|
|
|
426
486
|
}
|
|
427
487
|
|
|
428
488
|
// src/version.ts
|
|
429
|
-
var VERSION = "0.0.
|
|
489
|
+
var VERSION = "0.0.6";
|
|
430
490
|
|
|
431
491
|
// src/core/metadata.ts
|
|
432
492
|
var METADATA_VERSION = "1.0.0";
|
|
@@ -611,6 +671,10 @@ async function scanTargets(config) {
|
|
|
611
671
|
});
|
|
612
672
|
for (const match of allFiles) {
|
|
613
673
|
if (regex.test(match) && !isExcluded(match)) {
|
|
674
|
+
if (!config.backup.includeSensitiveFiles && isSensitiveFile(match)) {
|
|
675
|
+
logger.warn(`Sensitive file excluded: ${match}`);
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
614
678
|
const entry = await collectFileInfo(match, match);
|
|
615
679
|
found.push(entry);
|
|
616
680
|
}
|
|
@@ -639,6 +703,10 @@ async function scanTargets(config) {
|
|
|
639
703
|
});
|
|
640
704
|
for (const match of matches) {
|
|
641
705
|
if (!isExcluded(match)) {
|
|
706
|
+
if (!config.backup.includeSensitiveFiles && isSensitiveFile(match)) {
|
|
707
|
+
logger.warn(`Sensitive file excluded: ${match}`);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
642
710
|
const entry = await collectFileInfo(match, match);
|
|
643
711
|
found.push(entry);
|
|
644
712
|
}
|
|
@@ -672,6 +740,10 @@ async function scanTargets(config) {
|
|
|
672
740
|
});
|
|
673
741
|
for (const match of matches) {
|
|
674
742
|
if (!isExcluded(match)) {
|
|
743
|
+
if (!config.backup.includeSensitiveFiles && isSensitiveFile(match)) {
|
|
744
|
+
logger.warn(`Sensitive file excluded: ${match}`);
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
675
747
|
const entry = await collectFileInfo(match, match);
|
|
676
748
|
found.push(entry);
|
|
677
749
|
}
|
|
@@ -683,15 +755,16 @@ async function scanTargets(config) {
|
|
|
683
755
|
if (isExcluded(absPath)) {
|
|
684
756
|
continue;
|
|
685
757
|
}
|
|
758
|
+
if (!config.backup.includeSensitiveFiles && isSensitiveFile(absPath)) {
|
|
759
|
+
logger.warn(`Sensitive file excluded: ${target}`);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
686
762
|
const entry = await collectFileInfo(absPath, absPath);
|
|
687
763
|
if (entry.size > LARGE_FILE_THRESHOLD) {
|
|
688
764
|
logger.warn(
|
|
689
765
|
`Large file (>${Math.round(LARGE_FILE_THRESHOLD / 1024 / 1024)}MB): ${target}`
|
|
690
766
|
);
|
|
691
767
|
}
|
|
692
|
-
if (isSensitiveFile(absPath)) {
|
|
693
|
-
logger.warn(`Sensitive file detected: ${target}`);
|
|
694
|
-
}
|
|
695
768
|
found.push(entry);
|
|
696
769
|
}
|
|
697
770
|
}
|
|
@@ -764,46 +837,78 @@ import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
|
764
837
|
import { join as join7 } from "path";
|
|
765
838
|
import YAML from "yaml";
|
|
766
839
|
|
|
767
|
-
//
|
|
768
|
-
var
|
|
840
|
+
// assets/schemas/config.schema.json
|
|
841
|
+
var config_schema_default = {
|
|
842
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
843
|
+
title: "Syncpoint Config",
|
|
844
|
+
description: "Configuration for syncpoint backup tool",
|
|
769
845
|
type: "object",
|
|
770
|
-
required: [
|
|
846
|
+
required: [
|
|
847
|
+
"backup"
|
|
848
|
+
],
|
|
771
849
|
properties: {
|
|
772
850
|
backup: {
|
|
773
851
|
type: "object",
|
|
774
|
-
|
|
852
|
+
description: "Backup configuration",
|
|
853
|
+
required: [
|
|
854
|
+
"targets",
|
|
855
|
+
"exclude",
|
|
856
|
+
"filename"
|
|
857
|
+
],
|
|
775
858
|
properties: {
|
|
776
859
|
targets: {
|
|
777
860
|
type: "array",
|
|
778
|
-
|
|
861
|
+
description: "List of files/directories to backup. Supports literal paths (e.g. ~/.zshrc), glob patterns (e.g. ~/.config/*.conf), and regex patterns (e.g. /\\.conf$/).",
|
|
862
|
+
items: {
|
|
863
|
+
type: "string",
|
|
864
|
+
validPattern: true
|
|
865
|
+
}
|
|
779
866
|
},
|
|
780
867
|
exclude: {
|
|
781
868
|
type: "array",
|
|
782
|
-
|
|
869
|
+
description: "List of patterns to exclude from backup. Supports glob (e.g. **/*.swp) and regex (e.g. /\\.bak$/) patterns.",
|
|
870
|
+
items: {
|
|
871
|
+
type: "string",
|
|
872
|
+
validPattern: true
|
|
873
|
+
}
|
|
783
874
|
},
|
|
784
875
|
filename: {
|
|
785
876
|
type: "string",
|
|
877
|
+
description: "Backup archive filename pattern. Available variables: {hostname}, {datetime}.",
|
|
786
878
|
minLength: 1
|
|
787
879
|
},
|
|
788
880
|
destination: {
|
|
789
|
-
type: "string"
|
|
881
|
+
type: "string",
|
|
882
|
+
description: "Backup archive destination path. Default: ~/.syncpoint/backups/"
|
|
883
|
+
},
|
|
884
|
+
includeSensitiveFiles: {
|
|
885
|
+
type: "boolean",
|
|
886
|
+
description: "Include sensitive files (SSH keys, certificates, etc.) in backup. When false (default), files matching sensitive patterns (id_rsa, id_ed25519, *.pem, *.key) are automatically excluded."
|
|
790
887
|
}
|
|
791
888
|
},
|
|
792
889
|
additionalProperties: false
|
|
793
890
|
},
|
|
794
891
|
scripts: {
|
|
795
892
|
type: "object",
|
|
893
|
+
description: "Scripts configuration",
|
|
796
894
|
properties: {
|
|
797
895
|
includeInBackup: {
|
|
798
|
-
type: "boolean"
|
|
896
|
+
type: "boolean",
|
|
897
|
+
description: "Whether to include scripts/ directory in backup. Default: true."
|
|
799
898
|
}
|
|
800
899
|
},
|
|
801
900
|
additionalProperties: false
|
|
901
|
+
},
|
|
902
|
+
"yaml-language-server": {
|
|
903
|
+
type: "string",
|
|
904
|
+
description: "Editor directive for schema association; ignored at runtime."
|
|
802
905
|
}
|
|
803
906
|
},
|
|
804
907
|
additionalProperties: false
|
|
805
908
|
};
|
|
806
|
-
|
|
909
|
+
|
|
910
|
+
// src/schemas/config.schema.ts
|
|
911
|
+
var validate3 = ajv.compile(config_schema_default);
|
|
807
912
|
function validateConfig(data) {
|
|
808
913
|
const valid = validate3(data);
|
|
809
914
|
if (valid) return { valid: true };
|
|
@@ -1051,6 +1156,21 @@ var COMMANDS = {
|
|
|
1051
1156
|
"npx @lumy-pack/syncpoint status --cleanup"
|
|
1052
1157
|
]
|
|
1053
1158
|
},
|
|
1159
|
+
migrate: {
|
|
1160
|
+
name: "migrate",
|
|
1161
|
+
description: "Migrate config.yml to match the current schema",
|
|
1162
|
+
usage: "npx @lumy-pack/syncpoint migrate [options]",
|
|
1163
|
+
options: [
|
|
1164
|
+
{
|
|
1165
|
+
flag: "--dry-run",
|
|
1166
|
+
description: "Preview changes without writing"
|
|
1167
|
+
}
|
|
1168
|
+
],
|
|
1169
|
+
examples: [
|
|
1170
|
+
"npx @lumy-pack/syncpoint migrate",
|
|
1171
|
+
"npx @lumy-pack/syncpoint migrate --dry-run"
|
|
1172
|
+
]
|
|
1173
|
+
},
|
|
1054
1174
|
help: {
|
|
1055
1175
|
name: "help",
|
|
1056
1176
|
description: "Display help information",
|
|
@@ -1206,27 +1326,61 @@ import { render as render2 } from "ink";
|
|
|
1206
1326
|
import { join as join8 } from "path";
|
|
1207
1327
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
1208
1328
|
|
|
1209
|
-
//
|
|
1210
|
-
var
|
|
1329
|
+
// assets/schemas/template.schema.json
|
|
1330
|
+
var template_schema_default = {
|
|
1331
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
1332
|
+
title: "Syncpoint Template",
|
|
1333
|
+
description: "Provisioning template for syncpoint",
|
|
1211
1334
|
type: "object",
|
|
1212
1335
|
required: ["name", "steps"],
|
|
1213
1336
|
properties: {
|
|
1214
|
-
name: {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1337
|
+
name: {
|
|
1338
|
+
type: "string",
|
|
1339
|
+
description: "Template name.",
|
|
1340
|
+
minLength: 1
|
|
1341
|
+
},
|
|
1342
|
+
description: {
|
|
1343
|
+
type: "string",
|
|
1344
|
+
description: "Template description."
|
|
1345
|
+
},
|
|
1346
|
+
backup: {
|
|
1347
|
+
type: "string",
|
|
1348
|
+
description: "Backup name to restore automatically after provisioning."
|
|
1349
|
+
},
|
|
1350
|
+
sudo: {
|
|
1351
|
+
type: "boolean",
|
|
1352
|
+
description: "Whether sudo privilege is required. If true, requests sudo authentication before execution."
|
|
1353
|
+
},
|
|
1218
1354
|
steps: {
|
|
1219
1355
|
type: "array",
|
|
1356
|
+
description: "List of provisioning steps. At least 1 step required.",
|
|
1220
1357
|
minItems: 1,
|
|
1221
1358
|
items: {
|
|
1222
1359
|
type: "object",
|
|
1223
1360
|
required: ["name", "command"],
|
|
1224
1361
|
properties: {
|
|
1225
|
-
name: {
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1362
|
+
name: {
|
|
1363
|
+
type: "string",
|
|
1364
|
+
description: "Step name.",
|
|
1365
|
+
minLength: 1
|
|
1366
|
+
},
|
|
1367
|
+
description: {
|
|
1368
|
+
type: "string",
|
|
1369
|
+
description: "Step description."
|
|
1370
|
+
},
|
|
1371
|
+
command: {
|
|
1372
|
+
type: "string",
|
|
1373
|
+
description: "Shell command to execute.",
|
|
1374
|
+
minLength: 1
|
|
1375
|
+
},
|
|
1376
|
+
skip_if: {
|
|
1377
|
+
type: "string",
|
|
1378
|
+
description: "Skip this step if this command exits with code 0."
|
|
1379
|
+
},
|
|
1380
|
+
continue_on_error: {
|
|
1381
|
+
type: "boolean",
|
|
1382
|
+
description: "Continue to next step even if this fails. Default: false."
|
|
1383
|
+
}
|
|
1230
1384
|
},
|
|
1231
1385
|
additionalProperties: false
|
|
1232
1386
|
}
|
|
@@ -1234,7 +1388,9 @@ var templateSchema = {
|
|
|
1234
1388
|
},
|
|
1235
1389
|
additionalProperties: false
|
|
1236
1390
|
};
|
|
1237
|
-
|
|
1391
|
+
|
|
1392
|
+
// src/schemas/template.schema.ts
|
|
1393
|
+
var validate4 = ajv.compile(template_schema_default);
|
|
1238
1394
|
function validateTemplate(data) {
|
|
1239
1395
|
const valid = validate4(data);
|
|
1240
1396
|
if (valid) return { valid: true };
|
|
@@ -1804,9 +1960,9 @@ var InitView = () => {
|
|
|
1804
1960
|
setSteps([...completed]);
|
|
1805
1961
|
const exampleTemplatePath = join9(getSubDir(TEMPLATES_DIR), "example.yml");
|
|
1806
1962
|
if (!await fileExists(exampleTemplatePath)) {
|
|
1807
|
-
const { writeFile:
|
|
1963
|
+
const { writeFile: writeFile6 } = await import("fs/promises");
|
|
1808
1964
|
const exampleYaml = readAsset("template.example.yml");
|
|
1809
|
-
await
|
|
1965
|
+
await writeFile6(exampleTemplatePath, exampleYaml, "utf-8");
|
|
1810
1966
|
completed.push({ name: `Created templates/example.yml`, done: true });
|
|
1811
1967
|
setSteps([...completed]);
|
|
1812
1968
|
}
|
|
@@ -2631,11 +2787,246 @@ function registerListCommand(program2) {
|
|
|
2631
2787
|
});
|
|
2632
2788
|
}
|
|
2633
2789
|
|
|
2634
|
-
// src/commands/
|
|
2790
|
+
// src/commands/Migrate.tsx
|
|
2635
2791
|
import { useState as useState6, useEffect as useEffect5 } from "react";
|
|
2636
|
-
import { Text as
|
|
2792
|
+
import { Text as Text9, Box as Box7, useApp as useApp5 } from "ink";
|
|
2637
2793
|
import { render as render6 } from "ink";
|
|
2638
2794
|
|
|
2795
|
+
// src/core/migrate.ts
|
|
2796
|
+
import { copyFile as copyFile2, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
2797
|
+
import YAML4 from "yaml";
|
|
2798
|
+
function extractSchemaPaths(schema, prefix = []) {
|
|
2799
|
+
const paths = [];
|
|
2800
|
+
const properties = schema.properties;
|
|
2801
|
+
if (!properties) return paths;
|
|
2802
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
2803
|
+
const currentPath = [...prefix, key];
|
|
2804
|
+
if (value.type === "object" && value.properties) {
|
|
2805
|
+
paths.push(
|
|
2806
|
+
...extractSchemaPaths(value, currentPath)
|
|
2807
|
+
);
|
|
2808
|
+
} else {
|
|
2809
|
+
paths.push(currentPath);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
return paths;
|
|
2813
|
+
}
|
|
2814
|
+
function extractDataPaths(data, prefix = []) {
|
|
2815
|
+
const paths = [];
|
|
2816
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return paths;
|
|
2817
|
+
for (const [key, value] of Object.entries(data)) {
|
|
2818
|
+
const currentPath = [...prefix, key];
|
|
2819
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2820
|
+
paths.push(...extractDataPaths(value, currentPath));
|
|
2821
|
+
} else {
|
|
2822
|
+
paths.push(currentPath);
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
return paths;
|
|
2826
|
+
}
|
|
2827
|
+
function pathKey(path) {
|
|
2828
|
+
return path.join(".");
|
|
2829
|
+
}
|
|
2830
|
+
function getNestedValue(obj, path) {
|
|
2831
|
+
let current = obj;
|
|
2832
|
+
for (const key of path) {
|
|
2833
|
+
if (!current || typeof current !== "object") return void 0;
|
|
2834
|
+
current = current[key];
|
|
2835
|
+
}
|
|
2836
|
+
return current;
|
|
2837
|
+
}
|
|
2838
|
+
function diffConfigFields(userData) {
|
|
2839
|
+
const schemaPaths = extractSchemaPaths(
|
|
2840
|
+
config_schema_default
|
|
2841
|
+
);
|
|
2842
|
+
const templateData = YAML4.parse(readAsset("config.default.yml"));
|
|
2843
|
+
const templatePaths = extractDataPaths(templateData);
|
|
2844
|
+
const userPaths = extractDataPaths(userData);
|
|
2845
|
+
const schemaKeys = new Set(schemaPaths.map(pathKey));
|
|
2846
|
+
const userKeys = new Set(userPaths.map(pathKey));
|
|
2847
|
+
const isEditorDirective = (p) => p.length === 1 && p[0] === "yaml-language-server";
|
|
2848
|
+
return {
|
|
2849
|
+
// Fields present in template with defaults AND valid in schema, but missing from user
|
|
2850
|
+
added: templatePaths.filter((p) => {
|
|
2851
|
+
if (isEditorDirective(p)) return false;
|
|
2852
|
+
const key = pathKey(p);
|
|
2853
|
+
return schemaKeys.has(key) && !userKeys.has(key);
|
|
2854
|
+
}),
|
|
2855
|
+
// Fields in user but not in schema (truly deprecated)
|
|
2856
|
+
removed: userPaths.filter(
|
|
2857
|
+
(p) => !isEditorDirective(p) && !schemaKeys.has(pathKey(p))
|
|
2858
|
+
),
|
|
2859
|
+
// Fields in user AND in schema (preserve user values)
|
|
2860
|
+
existing: userPaths.filter((p) => schemaKeys.has(pathKey(p)))
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
function buildMigratedDocument(templateText, userData, diff) {
|
|
2864
|
+
const doc = YAML4.parseDocument(templateText);
|
|
2865
|
+
for (const path of diff.existing) {
|
|
2866
|
+
const userValue = getNestedValue(userData, path);
|
|
2867
|
+
if (userValue === void 0) continue;
|
|
2868
|
+
const node = doc.getIn(path, true);
|
|
2869
|
+
if (YAML4.isScalar(node)) {
|
|
2870
|
+
node.value = userValue;
|
|
2871
|
+
} else {
|
|
2872
|
+
doc.setIn(path, userValue);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
let output = doc.toString();
|
|
2876
|
+
if (diff.removed.length > 0) {
|
|
2877
|
+
const lines = [
|
|
2878
|
+
"",
|
|
2879
|
+
"# [deprecated] The following fields are no longer in the current schema.",
|
|
2880
|
+
"# They have been preserved as comments for reference."
|
|
2881
|
+
];
|
|
2882
|
+
for (const path of diff.removed) {
|
|
2883
|
+
const value = getNestedValue(userData, path);
|
|
2884
|
+
const valueStr = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
2885
|
+
lines.push(`# [deprecated] ${path.join(".")}: ${valueStr}`);
|
|
2886
|
+
}
|
|
2887
|
+
output += lines.join("\n") + "\n";
|
|
2888
|
+
}
|
|
2889
|
+
return output;
|
|
2890
|
+
}
|
|
2891
|
+
async function migrateConfig(options) {
|
|
2892
|
+
const configPath = getConfigPath();
|
|
2893
|
+
if (!await fileExists(configPath)) {
|
|
2894
|
+
throw new Error(
|
|
2895
|
+
`Config file not found: ${configPath}
|
|
2896
|
+
Run "syncpoint init" first.`
|
|
2897
|
+
);
|
|
2898
|
+
}
|
|
2899
|
+
const userText = await readFile5(configPath, "utf-8");
|
|
2900
|
+
const userData = YAML4.parse(userText);
|
|
2901
|
+
const templateText = readAsset("config.default.yml");
|
|
2902
|
+
const diff = diffConfigFields(userData);
|
|
2903
|
+
if (diff.added.length === 0 && diff.removed.length === 0) {
|
|
2904
|
+
return {
|
|
2905
|
+
added: [],
|
|
2906
|
+
deprecated: [],
|
|
2907
|
+
preserved: diff.existing.map(pathKey),
|
|
2908
|
+
backupPath: "",
|
|
2909
|
+
migrated: false
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
const result = {
|
|
2913
|
+
added: diff.added.map(pathKey),
|
|
2914
|
+
deprecated: diff.removed.map(pathKey),
|
|
2915
|
+
preserved: diff.existing.map(pathKey),
|
|
2916
|
+
backupPath: "",
|
|
2917
|
+
migrated: false
|
|
2918
|
+
};
|
|
2919
|
+
if (!options?.dryRun) {
|
|
2920
|
+
const migratedText = buildMigratedDocument(templateText, userData, diff);
|
|
2921
|
+
const backupPath = configPath + ".bak";
|
|
2922
|
+
await copyFile2(configPath, backupPath);
|
|
2923
|
+
result.backupPath = backupPath;
|
|
2924
|
+
await writeFile4(configPath, migratedText, "utf-8");
|
|
2925
|
+
const migrated = YAML4.parse(migratedText);
|
|
2926
|
+
const validation = validateConfig(migrated);
|
|
2927
|
+
if (!validation.valid) {
|
|
2928
|
+
await copyFile2(backupPath, configPath);
|
|
2929
|
+
throw new Error(
|
|
2930
|
+
`Migration produced invalid config (restored from backup):
|
|
2931
|
+
${(validation.errors ?? []).join("\n")}`
|
|
2932
|
+
);
|
|
2933
|
+
}
|
|
2934
|
+
result.migrated = true;
|
|
2935
|
+
}
|
|
2936
|
+
return result;
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
// src/commands/Migrate.tsx
|
|
2940
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2941
|
+
var MigrateView = ({ dryRun }) => {
|
|
2942
|
+
const { exit } = useApp5();
|
|
2943
|
+
const [result, setResult] = useState6(null);
|
|
2944
|
+
const [error, setError] = useState6(null);
|
|
2945
|
+
const [loading, setLoading] = useState6(true);
|
|
2946
|
+
useEffect5(() => {
|
|
2947
|
+
(async () => {
|
|
2948
|
+
try {
|
|
2949
|
+
const res = await migrateConfig({ dryRun });
|
|
2950
|
+
setResult(res);
|
|
2951
|
+
setLoading(false);
|
|
2952
|
+
setTimeout(() => exit(), 100);
|
|
2953
|
+
} catch (err) {
|
|
2954
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
2955
|
+
setLoading(false);
|
|
2956
|
+
exit();
|
|
2957
|
+
}
|
|
2958
|
+
})();
|
|
2959
|
+
}, []);
|
|
2960
|
+
if (error) {
|
|
2961
|
+
return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
|
|
2962
|
+
"\u2717 ",
|
|
2963
|
+
error
|
|
2964
|
+
] }) });
|
|
2965
|
+
}
|
|
2966
|
+
if (loading) {
|
|
2967
|
+
return /* @__PURE__ */ jsx9(Text9, { children: "Analyzing config..." });
|
|
2968
|
+
}
|
|
2969
|
+
if (!result) return null;
|
|
2970
|
+
if (!result.migrated && result.added.length === 0 && result.deprecated.length === 0) {
|
|
2971
|
+
return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsx9(Text9, { color: "green", children: "\u2713 Config is already up to date." }) });
|
|
2972
|
+
}
|
|
2973
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
2974
|
+
dryRun && /* @__PURE__ */ jsx9(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "yellow", bold: true, children: "[dry-run] Preview only \u2014 no changes written." }) }),
|
|
2975
|
+
result.added.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
2976
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "New fields (added with defaults):" }),
|
|
2977
|
+
result.added.map((field, i) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
2978
|
+
" ",
|
|
2979
|
+
/* @__PURE__ */ jsx9(Text9, { color: "green", children: "+" }),
|
|
2980
|
+
" ",
|
|
2981
|
+
field
|
|
2982
|
+
] }, i))
|
|
2983
|
+
] }),
|
|
2984
|
+
result.deprecated.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: result.added.length > 0 ? 1 : 0, children: [
|
|
2985
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "Deprecated fields (commented out):" }),
|
|
2986
|
+
result.deprecated.map((field, i) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
2987
|
+
" ",
|
|
2988
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "~" }),
|
|
2989
|
+
" ",
|
|
2990
|
+
field
|
|
2991
|
+
] }, i))
|
|
2992
|
+
] }),
|
|
2993
|
+
result.preserved.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
2994
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
|
|
2995
|
+
"Preserved fields (",
|
|
2996
|
+
result.preserved.length,
|
|
2997
|
+
"):"
|
|
2998
|
+
] }),
|
|
2999
|
+
result.preserved.map((field, i) => /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3000
|
+
" ",
|
|
3001
|
+
/* @__PURE__ */ jsx9(Text9, { color: "blue", children: "\u2022" }),
|
|
3002
|
+
" ",
|
|
3003
|
+
field
|
|
3004
|
+
] }, i))
|
|
3005
|
+
] }),
|
|
3006
|
+
result.migrated && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
3007
|
+
/* @__PURE__ */ jsx9(Text9, { color: "green", children: "\u2713 Migration complete." }),
|
|
3008
|
+
result.backupPath && /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
3009
|
+
" ",
|
|
3010
|
+
"Backup saved to: ",
|
|
3011
|
+
result.backupPath
|
|
3012
|
+
] })
|
|
3013
|
+
] })
|
|
3014
|
+
] });
|
|
3015
|
+
};
|
|
3016
|
+
function registerMigrateCommand(program2) {
|
|
3017
|
+
program2.command("migrate").description("Migrate config.yml to match the current schema").option("--dry-run", "Preview changes without writing").action(async (opts) => {
|
|
3018
|
+
const { waitUntilExit } = render6(
|
|
3019
|
+
/* @__PURE__ */ jsx9(MigrateView, { dryRun: opts.dryRun ?? false })
|
|
3020
|
+
);
|
|
3021
|
+
await waitUntilExit();
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
// src/commands/Provision.tsx
|
|
3026
|
+
import { useState as useState7, useEffect as useEffect6 } from "react";
|
|
3027
|
+
import { Text as Text11, Box as Box9, useApp as useApp6 } from "ink";
|
|
3028
|
+
import { render as render7 } from "ink";
|
|
3029
|
+
|
|
2639
3030
|
// src/utils/sudo.ts
|
|
2640
3031
|
import { execSync } from "child_process";
|
|
2641
3032
|
import pc2 from "picocolors";
|
|
@@ -2669,37 +3060,37 @@ ${pc2.red("\u2717")} Sudo authentication failed or was cancelled. Aborting.`
|
|
|
2669
3060
|
}
|
|
2670
3061
|
|
|
2671
3062
|
// src/components/StepRunner.tsx
|
|
2672
|
-
import { Text as
|
|
3063
|
+
import { Text as Text10, Box as Box8 } from "ink";
|
|
2673
3064
|
import Spinner2 from "ink-spinner";
|
|
2674
|
-
import { jsx as
|
|
3065
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2675
3066
|
var StepIcon = ({ status }) => {
|
|
2676
3067
|
switch (status) {
|
|
2677
3068
|
case "success":
|
|
2678
|
-
return /* @__PURE__ */
|
|
3069
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "green", children: "\u2713" });
|
|
2679
3070
|
case "running":
|
|
2680
|
-
return /* @__PURE__ */
|
|
3071
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: /* @__PURE__ */ jsx10(Spinner2, { type: "dots" }) });
|
|
2681
3072
|
case "skipped":
|
|
2682
|
-
return /* @__PURE__ */
|
|
3073
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "blue", children: "\u23ED" });
|
|
2683
3074
|
case "failed":
|
|
2684
|
-
return /* @__PURE__ */
|
|
3075
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "red", children: "\u2717" });
|
|
2685
3076
|
case "pending":
|
|
2686
3077
|
default:
|
|
2687
|
-
return /* @__PURE__ */
|
|
3078
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "gray", children: "\u25CB" });
|
|
2688
3079
|
}
|
|
2689
3080
|
};
|
|
2690
3081
|
var StepStatusText = ({ step }) => {
|
|
2691
3082
|
switch (step.status) {
|
|
2692
3083
|
case "success":
|
|
2693
|
-
return /* @__PURE__ */
|
|
3084
|
+
return /* @__PURE__ */ jsxs10(Text10, { color: "green", children: [
|
|
2694
3085
|
"Done",
|
|
2695
3086
|
step.duration != null ? ` (${Math.round(step.duration / 1e3)}s)` : ""
|
|
2696
3087
|
] });
|
|
2697
3088
|
case "running":
|
|
2698
|
-
return /* @__PURE__ */
|
|
3089
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: "Running..." });
|
|
2699
3090
|
case "skipped":
|
|
2700
|
-
return /* @__PURE__ */
|
|
3091
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "blue", children: "Skipped (already installed)" });
|
|
2701
3092
|
case "failed":
|
|
2702
|
-
return /* @__PURE__ */
|
|
3093
|
+
return /* @__PURE__ */ jsxs10(Text10, { color: "red", children: [
|
|
2703
3094
|
"Failed",
|
|
2704
3095
|
step.error ? `: ${step.error}` : ""
|
|
2705
3096
|
] });
|
|
@@ -2712,13 +3103,13 @@ var StepRunner = ({
|
|
|
2712
3103
|
steps,
|
|
2713
3104
|
total
|
|
2714
3105
|
}) => {
|
|
2715
|
-
return /* @__PURE__ */
|
|
2716
|
-
/* @__PURE__ */
|
|
3106
|
+
return /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: steps.map((step, idx) => /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginBottom: idx < steps.length - 1 ? 1 : 0, children: [
|
|
3107
|
+
/* @__PURE__ */ jsxs10(Text10, { children: [
|
|
2717
3108
|
" ",
|
|
2718
|
-
/* @__PURE__ */
|
|
2719
|
-
/* @__PURE__ */
|
|
3109
|
+
/* @__PURE__ */ jsx10(StepIcon, { status: step.status }),
|
|
3110
|
+
/* @__PURE__ */ jsxs10(Text10, { children: [
|
|
2720
3111
|
" ",
|
|
2721
|
-
/* @__PURE__ */
|
|
3112
|
+
/* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
|
|
2722
3113
|
"Step ",
|
|
2723
3114
|
idx + 1,
|
|
2724
3115
|
"/",
|
|
@@ -2728,36 +3119,36 @@ var StepRunner = ({
|
|
|
2728
3119
|
step.name
|
|
2729
3120
|
] })
|
|
2730
3121
|
] }),
|
|
2731
|
-
step.output && step.status !== "pending" && /* @__PURE__ */
|
|
3122
|
+
step.output && step.status !== "pending" && /* @__PURE__ */ jsxs10(Text10, { color: "gray", children: [
|
|
2732
3123
|
" ",
|
|
2733
3124
|
step.output
|
|
2734
3125
|
] }),
|
|
2735
|
-
/* @__PURE__ */
|
|
3126
|
+
/* @__PURE__ */ jsxs10(Text10, { children: [
|
|
2736
3127
|
" ",
|
|
2737
|
-
/* @__PURE__ */
|
|
3128
|
+
/* @__PURE__ */ jsx10(StepStatusText, { step })
|
|
2738
3129
|
] })
|
|
2739
3130
|
] }, idx)) });
|
|
2740
3131
|
};
|
|
2741
3132
|
|
|
2742
3133
|
// src/commands/Provision.tsx
|
|
2743
|
-
import { jsx as
|
|
3134
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2744
3135
|
var ProvisionView = ({
|
|
2745
3136
|
template,
|
|
2746
3137
|
templatePath,
|
|
2747
3138
|
options
|
|
2748
3139
|
}) => {
|
|
2749
|
-
const { exit } =
|
|
2750
|
-
const [phase, setPhase] =
|
|
2751
|
-
const [steps, setSteps] =
|
|
3140
|
+
const { exit } = useApp6();
|
|
3141
|
+
const [phase, setPhase] = useState7(options.dryRun ? "done" : "running");
|
|
3142
|
+
const [steps, setSteps] = useState7(
|
|
2752
3143
|
template.steps.map((s) => ({
|
|
2753
3144
|
name: s.name,
|
|
2754
3145
|
status: "pending",
|
|
2755
3146
|
output: s.description
|
|
2756
3147
|
}))
|
|
2757
3148
|
);
|
|
2758
|
-
const [currentStep, setCurrentStep] =
|
|
2759
|
-
const [error, setError] =
|
|
2760
|
-
|
|
3149
|
+
const [currentStep, setCurrentStep] = useState7(0);
|
|
3150
|
+
const [error, setError] = useState7(null);
|
|
3151
|
+
useEffect6(() => {
|
|
2761
3152
|
if (options.dryRun) {
|
|
2762
3153
|
setTimeout(() => exit(), 100);
|
|
2763
3154
|
return;
|
|
@@ -2799,7 +3190,7 @@ var ProvisionView = ({
|
|
|
2799
3190
|
})();
|
|
2800
3191
|
}, []);
|
|
2801
3192
|
if (phase === "error" || error) {
|
|
2802
|
-
return /* @__PURE__ */
|
|
3193
|
+
return /* @__PURE__ */ jsx11(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
2803
3194
|
"\u2717 ",
|
|
2804
3195
|
error
|
|
2805
3196
|
] }) });
|
|
@@ -2807,27 +3198,27 @@ var ProvisionView = ({
|
|
|
2807
3198
|
const successCount = steps.filter((s) => s.status === "success").length;
|
|
2808
3199
|
const skippedCount = steps.filter((s) => s.status === "skipped").length;
|
|
2809
3200
|
const failedCount = steps.filter((s) => s.status === "failed").length;
|
|
2810
|
-
return /* @__PURE__ */
|
|
2811
|
-
/* @__PURE__ */
|
|
2812
|
-
/* @__PURE__ */
|
|
3201
|
+
return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
|
|
3202
|
+
/* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
3203
|
+
/* @__PURE__ */ jsxs11(Text11, { bold: true, children: [
|
|
2813
3204
|
"\u25B8 ",
|
|
2814
3205
|
template.name
|
|
2815
3206
|
] }),
|
|
2816
|
-
template.description && /* @__PURE__ */
|
|
3207
|
+
template.description && /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
2817
3208
|
" ",
|
|
2818
3209
|
template.description
|
|
2819
3210
|
] })
|
|
2820
3211
|
] }),
|
|
2821
|
-
options.dryRun && phase === "done" && /* @__PURE__ */
|
|
2822
|
-
/* @__PURE__ */
|
|
2823
|
-
template.sudo && /* @__PURE__ */
|
|
3212
|
+
options.dryRun && phase === "done" && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
|
|
3213
|
+
/* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "(dry-run) Showing execution plan only" }),
|
|
3214
|
+
template.sudo && /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
|
|
2824
3215
|
" ",
|
|
2825
3216
|
"\u26A0 This template requires sudo privileges (will prompt on actual run)"
|
|
2826
3217
|
] }),
|
|
2827
|
-
/* @__PURE__ */
|
|
2828
|
-
/* @__PURE__ */
|
|
3218
|
+
/* @__PURE__ */ jsx11(Box9, { flexDirection: "column", marginTop: 1, children: template.steps.map((step, idx) => /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginBottom: 1, children: [
|
|
3219
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
2829
3220
|
" ",
|
|
2830
|
-
/* @__PURE__ */
|
|
3221
|
+
/* @__PURE__ */ jsxs11(Text11, { bold: true, children: [
|
|
2831
3222
|
"Step ",
|
|
2832
3223
|
idx + 1,
|
|
2833
3224
|
"/",
|
|
@@ -2836,18 +3227,18 @@ var ProvisionView = ({
|
|
|
2836
3227
|
" ",
|
|
2837
3228
|
step.name
|
|
2838
3229
|
] }),
|
|
2839
|
-
step.description && /* @__PURE__ */
|
|
3230
|
+
step.description && /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
2840
3231
|
" ",
|
|
2841
3232
|
step.description
|
|
2842
3233
|
] }),
|
|
2843
|
-
step.skip_if && /* @__PURE__ */
|
|
3234
|
+
step.skip_if && /* @__PURE__ */ jsxs11(Text11, { color: "blue", children: [
|
|
2844
3235
|
" ",
|
|
2845
3236
|
"Skip condition: ",
|
|
2846
3237
|
step.skip_if
|
|
2847
3238
|
] })
|
|
2848
3239
|
] }, idx)) })
|
|
2849
3240
|
] }),
|
|
2850
|
-
(phase === "running" || phase === "done" && !options.dryRun) && /* @__PURE__ */
|
|
3241
|
+
(phase === "running" || phase === "done" && !options.dryRun) && /* @__PURE__ */ jsx11(
|
|
2851
3242
|
StepRunner,
|
|
2852
3243
|
{
|
|
2853
3244
|
steps,
|
|
@@ -2855,34 +3246,34 @@ var ProvisionView = ({
|
|
|
2855
3246
|
total: template.steps.length
|
|
2856
3247
|
}
|
|
2857
3248
|
),
|
|
2858
|
-
phase === "done" && !options.dryRun && /* @__PURE__ */
|
|
2859
|
-
/* @__PURE__ */
|
|
3249
|
+
phase === "done" && !options.dryRun && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: 1, children: [
|
|
3250
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
2860
3251
|
" ",
|
|
2861
3252
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
2862
3253
|
] }),
|
|
2863
|
-
/* @__PURE__ */
|
|
3254
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
2864
3255
|
" ",
|
|
2865
3256
|
"Result: ",
|
|
2866
|
-
/* @__PURE__ */
|
|
3257
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "green", children: [
|
|
2867
3258
|
successCount,
|
|
2868
3259
|
" succeeded"
|
|
2869
3260
|
] }),
|
|
2870
3261
|
" \xB7",
|
|
2871
3262
|
" ",
|
|
2872
|
-
/* @__PURE__ */
|
|
3263
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "blue", children: [
|
|
2873
3264
|
skippedCount,
|
|
2874
3265
|
" skipped"
|
|
2875
3266
|
] }),
|
|
2876
3267
|
" \xB7",
|
|
2877
3268
|
" ",
|
|
2878
|
-
/* @__PURE__ */
|
|
3269
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
2879
3270
|
failedCount,
|
|
2880
3271
|
" failed"
|
|
2881
3272
|
] })
|
|
2882
3273
|
] }),
|
|
2883
|
-
template.backup && !options.skipRestore && /* @__PURE__ */
|
|
2884
|
-
/* @__PURE__ */
|
|
2885
|
-
/* @__PURE__ */
|
|
3274
|
+
template.backup && !options.skipRestore && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: 1, children: [
|
|
3275
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, children: "\u25B8 Proceeding with config file restore..." }),
|
|
3276
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
2886
3277
|
" ",
|
|
2887
3278
|
"Backup link: ",
|
|
2888
3279
|
template.backup
|
|
@@ -2925,8 +3316,8 @@ function registerProvisionCommand(program2) {
|
|
|
2925
3316
|
if (tmpl.sudo && !opts.dryRun) {
|
|
2926
3317
|
ensureSudo(tmpl.name);
|
|
2927
3318
|
}
|
|
2928
|
-
const { waitUntilExit } =
|
|
2929
|
-
/* @__PURE__ */
|
|
3319
|
+
const { waitUntilExit } = render7(
|
|
3320
|
+
/* @__PURE__ */ jsx11(
|
|
2930
3321
|
ProvisionView,
|
|
2931
3322
|
{
|
|
2932
3323
|
template: tmpl,
|
|
@@ -2944,21 +3335,21 @@ function registerProvisionCommand(program2) {
|
|
|
2944
3335
|
}
|
|
2945
3336
|
|
|
2946
3337
|
// src/commands/Restore.tsx
|
|
2947
|
-
import { useState as
|
|
2948
|
-
import { Text as
|
|
3338
|
+
import { useState as useState8, useEffect as useEffect7 } from "react";
|
|
3339
|
+
import { Text as Text12, Box as Box10, useApp as useApp7 } from "ink";
|
|
2949
3340
|
import SelectInput2 from "ink-select-input";
|
|
2950
|
-
import { render as
|
|
2951
|
-
import { jsx as
|
|
3341
|
+
import { render as render8 } from "ink";
|
|
3342
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2952
3343
|
var RestoreView = ({ filename, options }) => {
|
|
2953
|
-
const { exit } =
|
|
2954
|
-
const [phase, setPhase] =
|
|
2955
|
-
const [backups, setBackups] =
|
|
2956
|
-
const [selectedPath, setSelectedPath] =
|
|
2957
|
-
const [plan, setPlan] =
|
|
2958
|
-
const [result, setResult] =
|
|
2959
|
-
const [safetyDone, setSafetyDone] =
|
|
2960
|
-
const [error, setError] =
|
|
2961
|
-
|
|
3344
|
+
const { exit } = useApp7();
|
|
3345
|
+
const [phase, setPhase] = useState8("loading");
|
|
3346
|
+
const [backups, setBackups] = useState8([]);
|
|
3347
|
+
const [selectedPath, setSelectedPath] = useState8(null);
|
|
3348
|
+
const [plan, setPlan] = useState8(null);
|
|
3349
|
+
const [result, setResult] = useState8(null);
|
|
3350
|
+
const [safetyDone, setSafetyDone] = useState8(false);
|
|
3351
|
+
const [error, setError] = useState8(null);
|
|
3352
|
+
useEffect7(() => {
|
|
2962
3353
|
(async () => {
|
|
2963
3354
|
try {
|
|
2964
3355
|
const config = await loadConfig();
|
|
@@ -2992,7 +3383,7 @@ var RestoreView = ({ filename, options }) => {
|
|
|
2992
3383
|
}
|
|
2993
3384
|
})();
|
|
2994
3385
|
}, []);
|
|
2995
|
-
|
|
3386
|
+
useEffect7(() => {
|
|
2996
3387
|
if (phase !== "planning" || !selectedPath) return;
|
|
2997
3388
|
(async () => {
|
|
2998
3389
|
try {
|
|
@@ -3040,7 +3431,7 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3040
3431
|
}
|
|
3041
3432
|
};
|
|
3042
3433
|
if (phase === "error" || error) {
|
|
3043
|
-
return /* @__PURE__ */
|
|
3434
|
+
return /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
|
|
3044
3435
|
"\u2717 ",
|
|
3045
3436
|
error
|
|
3046
3437
|
] }) });
|
|
@@ -3051,30 +3442,30 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3051
3442
|
}));
|
|
3052
3443
|
const currentHostname = getHostname();
|
|
3053
3444
|
const isRemoteBackup = plan?.metadata.hostname && plan.metadata.hostname !== currentHostname;
|
|
3054
|
-
return /* @__PURE__ */
|
|
3055
|
-
phase === "loading" && /* @__PURE__ */
|
|
3056
|
-
phase === "selecting" && /* @__PURE__ */
|
|
3057
|
-
/* @__PURE__ */
|
|
3058
|
-
/* @__PURE__ */
|
|
3445
|
+
return /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
|
|
3446
|
+
phase === "loading" && /* @__PURE__ */ jsx12(Text12, { children: "\u25B8 Loading backup list..." }),
|
|
3447
|
+
phase === "selecting" && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
|
|
3448
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, children: "\u25B8 Select backup" }),
|
|
3449
|
+
/* @__PURE__ */ jsx12(SelectInput2, { items: selectItems, onSelect: handleSelect })
|
|
3059
3450
|
] }),
|
|
3060
|
-
(phase === "planning" || phase === "confirming" || phase === "restoring" || phase === "done" && plan) && plan && /* @__PURE__ */
|
|
3061
|
-
/* @__PURE__ */
|
|
3062
|
-
/* @__PURE__ */
|
|
3451
|
+
(phase === "planning" || phase === "confirming" || phase === "restoring" || phase === "done" && plan) && plan && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
|
|
3452
|
+
/* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
3453
|
+
/* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
|
|
3063
3454
|
"\u25B8 Metadata (",
|
|
3064
3455
|
plan.metadata.config.filename ?? "",
|
|
3065
3456
|
")"
|
|
3066
3457
|
] }),
|
|
3067
|
-
/* @__PURE__ */
|
|
3458
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3068
3459
|
" ",
|
|
3069
3460
|
"Host: ",
|
|
3070
3461
|
plan.metadata.hostname
|
|
3071
3462
|
] }),
|
|
3072
|
-
/* @__PURE__ */
|
|
3463
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3073
3464
|
" ",
|
|
3074
3465
|
"Created: ",
|
|
3075
3466
|
plan.metadata.createdAt
|
|
3076
3467
|
] }),
|
|
3077
|
-
/* @__PURE__ */
|
|
3468
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3078
3469
|
" ",
|
|
3079
3470
|
"Files: ",
|
|
3080
3471
|
plan.metadata.summary.fileCount,
|
|
@@ -3082,15 +3473,15 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3082
3473
|
formatBytes(plan.metadata.summary.totalSize),
|
|
3083
3474
|
")"
|
|
3084
3475
|
] }),
|
|
3085
|
-
isRemoteBackup && /* @__PURE__ */
|
|
3476
|
+
isRemoteBackup && /* @__PURE__ */ jsxs12(Text12, { color: "yellow", children: [
|
|
3086
3477
|
" ",
|
|
3087
3478
|
"\u26A0 This backup was created on a different machine (",
|
|
3088
3479
|
plan.metadata.hostname,
|
|
3089
3480
|
")"
|
|
3090
3481
|
] })
|
|
3091
3482
|
] }),
|
|
3092
|
-
/* @__PURE__ */
|
|
3093
|
-
/* @__PURE__ */
|
|
3483
|
+
/* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
3484
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, children: "\u25B8 Restore plan:" }),
|
|
3094
3485
|
plan.actions.map((action, idx) => {
|
|
3095
3486
|
let icon;
|
|
3096
3487
|
let color;
|
|
@@ -3112,45 +3503,45 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3112
3503
|
label = "(not present)";
|
|
3113
3504
|
break;
|
|
3114
3505
|
}
|
|
3115
|
-
return /* @__PURE__ */
|
|
3506
|
+
return /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3116
3507
|
" ",
|
|
3117
|
-
/* @__PURE__ */
|
|
3508
|
+
/* @__PURE__ */ jsx12(Text12, { color, children: icon.padEnd(8) }),
|
|
3118
3509
|
" ",
|
|
3119
3510
|
contractTilde(action.path),
|
|
3120
3511
|
" ",
|
|
3121
|
-
/* @__PURE__ */
|
|
3512
|
+
/* @__PURE__ */ jsx12(Text12, { color: "gray", children: label })
|
|
3122
3513
|
] }, idx);
|
|
3123
3514
|
})
|
|
3124
3515
|
] }),
|
|
3125
|
-
options.dryRun && phase === "done" && /* @__PURE__ */
|
|
3516
|
+
options.dryRun && phase === "done" && /* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "(dry-run) No actual restore was performed" })
|
|
3126
3517
|
] }),
|
|
3127
|
-
phase === "confirming" && /* @__PURE__ */
|
|
3128
|
-
phase === "restoring" && /* @__PURE__ */
|
|
3129
|
-
safetyDone && /* @__PURE__ */
|
|
3130
|
-
/* @__PURE__ */
|
|
3518
|
+
phase === "confirming" && /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsx12(Confirm, { message: "Proceed with restore?", onConfirm: handleConfirm }) }),
|
|
3519
|
+
phase === "restoring" && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
|
|
3520
|
+
safetyDone && /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3521
|
+
/* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
|
|
3131
3522
|
" Safety backup of current files complete"
|
|
3132
3523
|
] }),
|
|
3133
|
-
/* @__PURE__ */
|
|
3524
|
+
/* @__PURE__ */ jsx12(Text12, { children: "\u25B8 Restoring..." })
|
|
3134
3525
|
] }),
|
|
3135
|
-
phase === "done" && result && !options.dryRun && /* @__PURE__ */
|
|
3136
|
-
safetyDone && /* @__PURE__ */
|
|
3137
|
-
/* @__PURE__ */
|
|
3526
|
+
phase === "done" && result && !options.dryRun && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
3527
|
+
safetyDone && /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3528
|
+
/* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
|
|
3138
3529
|
" Safety backup of current files complete"
|
|
3139
3530
|
] }),
|
|
3140
|
-
/* @__PURE__ */
|
|
3141
|
-
/* @__PURE__ */
|
|
3531
|
+
/* @__PURE__ */ jsx12(Text12, { color: "green", bold: true, children: "\u2713 Restore complete" }),
|
|
3532
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3142
3533
|
" ",
|
|
3143
3534
|
"Restored: ",
|
|
3144
3535
|
result.restoredFiles.length,
|
|
3145
3536
|
" files"
|
|
3146
3537
|
] }),
|
|
3147
|
-
/* @__PURE__ */
|
|
3538
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3148
3539
|
" ",
|
|
3149
3540
|
"Skipped: ",
|
|
3150
3541
|
result.skippedFiles.length,
|
|
3151
3542
|
" files"
|
|
3152
3543
|
] }),
|
|
3153
|
-
result.safetyBackupPath && /* @__PURE__ */
|
|
3544
|
+
result.safetyBackupPath && /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3154
3545
|
" ",
|
|
3155
3546
|
"Safety backup: ",
|
|
3156
3547
|
contractTilde(result.safetyBackupPath)
|
|
@@ -3160,8 +3551,8 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3160
3551
|
};
|
|
3161
3552
|
function registerRestoreCommand(program2) {
|
|
3162
3553
|
program2.command("restore [filename]").description("Restore config files from a backup").option("--dry-run", "Show planned changes without actual restore", false).action(async (filename, opts) => {
|
|
3163
|
-
const { waitUntilExit } =
|
|
3164
|
-
/* @__PURE__ */
|
|
3554
|
+
const { waitUntilExit } = render8(
|
|
3555
|
+
/* @__PURE__ */ jsx12(
|
|
3165
3556
|
RestoreView,
|
|
3166
3557
|
{
|
|
3167
3558
|
filename,
|
|
@@ -3176,11 +3567,11 @@ function registerRestoreCommand(program2) {
|
|
|
3176
3567
|
// src/commands/Status.tsx
|
|
3177
3568
|
import { readdirSync, statSync, unlinkSync as unlinkSync2 } from "fs";
|
|
3178
3569
|
import { join as join12 } from "path";
|
|
3179
|
-
import { Box as
|
|
3180
|
-
import { render as
|
|
3570
|
+
import { Box as Box11, Text as Text13, useApp as useApp8, useInput as useInput3 } from "ink";
|
|
3571
|
+
import { render as render9 } from "ink";
|
|
3181
3572
|
import SelectInput3 from "ink-select-input";
|
|
3182
|
-
import { useEffect as
|
|
3183
|
-
import { jsx as
|
|
3573
|
+
import { useEffect as useEffect8, useState as useState9 } from "react";
|
|
3574
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3184
3575
|
function getDirStats(dirPath) {
|
|
3185
3576
|
try {
|
|
3186
3577
|
const entries = readdirSync(dirPath);
|
|
@@ -3202,34 +3593,34 @@ function getDirStats(dirPath) {
|
|
|
3202
3593
|
}
|
|
3203
3594
|
}
|
|
3204
3595
|
var DisplayActionItem = ({ isSelected = false, label }) => {
|
|
3205
|
-
return /* @__PURE__ */
|
|
3596
|
+
return /* @__PURE__ */ jsx13(Text13, { bold: isSelected, children: label });
|
|
3206
3597
|
};
|
|
3207
3598
|
var CleanupActionItem = ({ isSelected = false, label }) => {
|
|
3208
3599
|
if (label === "Cancel" || label === "Select specific backups to delete") {
|
|
3209
|
-
return /* @__PURE__ */
|
|
3600
|
+
return /* @__PURE__ */ jsx13(Text13, { bold: isSelected, children: label });
|
|
3210
3601
|
}
|
|
3211
3602
|
const parts = label.split(/\s{2,}/);
|
|
3212
3603
|
if (parts.length === 2) {
|
|
3213
|
-
return /* @__PURE__ */
|
|
3604
|
+
return /* @__PURE__ */ jsxs13(Text13, { bold: isSelected, children: [
|
|
3214
3605
|
parts[0],
|
|
3215
3606
|
" ",
|
|
3216
|
-
/* @__PURE__ */
|
|
3607
|
+
/* @__PURE__ */ jsx13(Text13, { dimColor: true, children: parts[1] })
|
|
3217
3608
|
] });
|
|
3218
3609
|
}
|
|
3219
|
-
return /* @__PURE__ */
|
|
3610
|
+
return /* @__PURE__ */ jsx13(Text13, { bold: isSelected, children: label });
|
|
3220
3611
|
};
|
|
3221
3612
|
var StatusView = ({ cleanup }) => {
|
|
3222
|
-
const { exit } =
|
|
3223
|
-
const [phase, setPhase] =
|
|
3224
|
-
const [status, setStatus] =
|
|
3225
|
-
const [backups, setBackups] =
|
|
3226
|
-
const [cleanupAction, setCleanupAction] =
|
|
3227
|
-
const [cleanupMessage, setCleanupMessage] =
|
|
3228
|
-
const [error, setError] =
|
|
3229
|
-
const [selectedForDeletion, setSelectedForDeletion] =
|
|
3613
|
+
const { exit } = useApp8();
|
|
3614
|
+
const [phase, setPhase] = useState9("loading");
|
|
3615
|
+
const [status, setStatus] = useState9(null);
|
|
3616
|
+
const [backups, setBackups] = useState9([]);
|
|
3617
|
+
const [cleanupAction, setCleanupAction] = useState9(null);
|
|
3618
|
+
const [cleanupMessage, setCleanupMessage] = useState9("");
|
|
3619
|
+
const [error, setError] = useState9(null);
|
|
3620
|
+
const [selectedForDeletion, setSelectedForDeletion] = useState9(
|
|
3230
3621
|
[]
|
|
3231
3622
|
);
|
|
3232
|
-
const [backupDir, setBackupDir] =
|
|
3623
|
+
const [backupDir, setBackupDir] = useState9(getSubDir("backups"));
|
|
3233
3624
|
useInput3((_input, key) => {
|
|
3234
3625
|
if (!key.escape) return;
|
|
3235
3626
|
if (phase === "display") {
|
|
@@ -3241,7 +3632,7 @@ var StatusView = ({ cleanup }) => {
|
|
|
3241
3632
|
setPhase("cleanup");
|
|
3242
3633
|
}
|
|
3243
3634
|
});
|
|
3244
|
-
|
|
3635
|
+
useEffect8(() => {
|
|
3245
3636
|
(async () => {
|
|
3246
3637
|
try {
|
|
3247
3638
|
const config = await loadConfig();
|
|
@@ -3378,26 +3769,26 @@ var StatusView = ({ cleanup }) => {
|
|
|
3378
3769
|
}
|
|
3379
3770
|
};
|
|
3380
3771
|
if (phase === "error" || error) {
|
|
3381
|
-
return /* @__PURE__ */
|
|
3772
|
+
return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
|
|
3382
3773
|
"\u2717 ",
|
|
3383
3774
|
error
|
|
3384
3775
|
] }) });
|
|
3385
3776
|
}
|
|
3386
3777
|
if (phase === "loading") {
|
|
3387
|
-
return /* @__PURE__ */
|
|
3778
|
+
return /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "Loading..." });
|
|
3388
3779
|
}
|
|
3389
3780
|
if (!status) return null;
|
|
3390
3781
|
const totalCount = status.backups.count + status.templates.count + status.scripts.count + status.logs.count;
|
|
3391
3782
|
const totalSize = status.backups.totalSize + status.templates.totalSize + status.scripts.totalSize + status.logs.totalSize;
|
|
3392
|
-
const statusDisplay = /* @__PURE__ */
|
|
3393
|
-
/* @__PURE__ */
|
|
3783
|
+
const statusDisplay = /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
3784
|
+
/* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
|
|
3394
3785
|
"\u25B8 ",
|
|
3395
3786
|
APP_NAME,
|
|
3396
3787
|
" status \u2014 ~/.",
|
|
3397
3788
|
APP_NAME,
|
|
3398
3789
|
"/"
|
|
3399
3790
|
] }),
|
|
3400
|
-
/* @__PURE__ */
|
|
3791
|
+
/* @__PURE__ */ jsx13(Box11, { marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsx13(
|
|
3401
3792
|
Table,
|
|
3402
3793
|
{
|
|
3403
3794
|
headers: ["Directory", "Count", "Size"],
|
|
@@ -3426,15 +3817,15 @@ var StatusView = ({ cleanup }) => {
|
|
|
3426
3817
|
]
|
|
3427
3818
|
}
|
|
3428
3819
|
) }),
|
|
3429
|
-
status.lastBackup && /* @__PURE__ */
|
|
3430
|
-
/* @__PURE__ */
|
|
3820
|
+
status.lastBackup && /* @__PURE__ */ jsxs13(Box11, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
|
|
3821
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3431
3822
|
"Latest backup: ",
|
|
3432
3823
|
formatDate(status.lastBackup),
|
|
3433
3824
|
" (",
|
|
3434
3825
|
formatRelativeTime(status.lastBackup),
|
|
3435
3826
|
")"
|
|
3436
3827
|
] }),
|
|
3437
|
-
status.oldestBackup && /* @__PURE__ */
|
|
3828
|
+
status.oldestBackup && /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3438
3829
|
"Oldest backup: ",
|
|
3439
3830
|
formatDate(status.oldestBackup),
|
|
3440
3831
|
" (",
|
|
@@ -3443,18 +3834,18 @@ var StatusView = ({ cleanup }) => {
|
|
|
3443
3834
|
] })
|
|
3444
3835
|
] })
|
|
3445
3836
|
] });
|
|
3446
|
-
const escHint = (action) => /* @__PURE__ */
|
|
3837
|
+
const escHint = (action) => /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
3447
3838
|
"Press ",
|
|
3448
|
-
/* @__PURE__ */
|
|
3839
|
+
/* @__PURE__ */ jsx13(Text13, { bold: true, children: "ESC" }),
|
|
3449
3840
|
" to ",
|
|
3450
3841
|
action
|
|
3451
3842
|
] }) });
|
|
3452
3843
|
if (phase === "display") {
|
|
3453
|
-
return /* @__PURE__ */
|
|
3844
|
+
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
3454
3845
|
statusDisplay,
|
|
3455
|
-
/* @__PURE__ */
|
|
3456
|
-
/* @__PURE__ */
|
|
3457
|
-
/* @__PURE__ */
|
|
3846
|
+
/* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
3847
|
+
/* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Actions" }),
|
|
3848
|
+
/* @__PURE__ */ jsx13(
|
|
3458
3849
|
SelectInput3,
|
|
3459
3850
|
{
|
|
3460
3851
|
items: [
|
|
@@ -3502,11 +3893,11 @@ var StatusView = ({ cleanup }) => {
|
|
|
3502
3893
|
value: "cancel"
|
|
3503
3894
|
}
|
|
3504
3895
|
];
|
|
3505
|
-
return /* @__PURE__ */
|
|
3896
|
+
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
3506
3897
|
statusDisplay,
|
|
3507
|
-
/* @__PURE__ */
|
|
3508
|
-
/* @__PURE__ */
|
|
3509
|
-
/* @__PURE__ */
|
|
3898
|
+
/* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
3899
|
+
/* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Cleanup options" }),
|
|
3900
|
+
/* @__PURE__ */ jsx13(
|
|
3510
3901
|
SelectInput3,
|
|
3511
3902
|
{
|
|
3512
3903
|
items: cleanupItems,
|
|
@@ -3532,26 +3923,26 @@ var StatusView = ({ cleanup }) => {
|
|
|
3532
3923
|
value: "done"
|
|
3533
3924
|
}
|
|
3534
3925
|
];
|
|
3535
|
-
return /* @__PURE__ */
|
|
3926
|
+
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
3536
3927
|
statusDisplay,
|
|
3537
|
-
/* @__PURE__ */
|
|
3538
|
-
/* @__PURE__ */
|
|
3539
|
-
selectedForDeletion.length > 0 && /* @__PURE__ */
|
|
3928
|
+
/* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
3929
|
+
/* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Select backups to delete" }),
|
|
3930
|
+
selectedForDeletion.length > 0 && /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
3540
3931
|
" ",
|
|
3541
3932
|
selectedForDeletion.length,
|
|
3542
3933
|
" backup(s) selected (",
|
|
3543
3934
|
formatBytes(selectedForDeletion.reduce((s, b) => s + b.size, 0)),
|
|
3544
3935
|
")"
|
|
3545
3936
|
] }),
|
|
3546
|
-
/* @__PURE__ */
|
|
3937
|
+
/* @__PURE__ */ jsx13(SelectInput3, { items: selectItems, onSelect: handleSelectBackup })
|
|
3547
3938
|
] }),
|
|
3548
3939
|
escHint("go back")
|
|
3549
3940
|
] });
|
|
3550
3941
|
}
|
|
3551
3942
|
if (phase === "confirming") {
|
|
3552
|
-
return /* @__PURE__ */
|
|
3553
|
-
/* @__PURE__ */
|
|
3554
|
-
/* @__PURE__ */
|
|
3943
|
+
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
3944
|
+
/* @__PURE__ */ jsx13(Text13, { children: cleanupMessage }),
|
|
3945
|
+
/* @__PURE__ */ jsx13(
|
|
3555
3946
|
Confirm,
|
|
3556
3947
|
{
|
|
3557
3948
|
message: "Proceed?",
|
|
@@ -3562,24 +3953,24 @@ var StatusView = ({ cleanup }) => {
|
|
|
3562
3953
|
] });
|
|
3563
3954
|
}
|
|
3564
3955
|
if (phase === "done") {
|
|
3565
|
-
return /* @__PURE__ */
|
|
3956
|
+
return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713 Cleanup complete" }) });
|
|
3566
3957
|
}
|
|
3567
3958
|
return null;
|
|
3568
3959
|
};
|
|
3569
3960
|
function registerStatusCommand(program2) {
|
|
3570
3961
|
program2.command("status").description(`Show ~/.${APP_NAME}/ status summary`).option("--cleanup", "Interactive cleanup mode", false).action(async (opts) => {
|
|
3571
|
-
const { waitUntilExit } =
|
|
3962
|
+
const { waitUntilExit } = render9(/* @__PURE__ */ jsx13(StatusView, { cleanup: opts.cleanup }));
|
|
3572
3963
|
await waitUntilExit();
|
|
3573
3964
|
});
|
|
3574
3965
|
}
|
|
3575
3966
|
|
|
3576
3967
|
// src/commands/Wizard.tsx
|
|
3577
|
-
import { copyFile as
|
|
3968
|
+
import { copyFile as copyFile3, readFile as readFile6, rename, unlink, writeFile as writeFile5 } from "fs/promises";
|
|
3578
3969
|
import { join as join14 } from "path";
|
|
3579
|
-
import { Box as
|
|
3580
|
-
import { render as
|
|
3970
|
+
import { Box as Box12, Text as Text14, useApp as useApp9 } from "ink";
|
|
3971
|
+
import { render as render10 } from "ink";
|
|
3581
3972
|
import Spinner3 from "ink-spinner";
|
|
3582
|
-
import { useEffect as
|
|
3973
|
+
import { useEffect as useEffect9, useState as useState10 } from "react";
|
|
3583
3974
|
|
|
3584
3975
|
// src/prompts/wizard-config.ts
|
|
3585
3976
|
function generateConfigWizardPrompt(variables) {
|
|
@@ -3760,12 +4151,12 @@ async function scanHomeDirectory(options) {
|
|
|
3760
4151
|
}
|
|
3761
4152
|
|
|
3762
4153
|
// src/commands/Wizard.tsx
|
|
3763
|
-
import { jsx as
|
|
4154
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3764
4155
|
var MAX_RETRIES2 = 3;
|
|
3765
4156
|
async function restoreBackup2(configPath) {
|
|
3766
4157
|
const bakPath = `${configPath}.bak`;
|
|
3767
4158
|
if (await fileExists(bakPath)) {
|
|
3768
|
-
await
|
|
4159
|
+
await copyFile3(bakPath, configPath);
|
|
3769
4160
|
}
|
|
3770
4161
|
}
|
|
3771
4162
|
async function runScanPhase() {
|
|
@@ -3791,7 +4182,7 @@ async function runValidationPhase(configPath) {
|
|
|
3791
4182
|
console.log("\u26A0\uFE0F Config file was not created. Restored backup.");
|
|
3792
4183
|
return;
|
|
3793
4184
|
}
|
|
3794
|
-
const content = await
|
|
4185
|
+
const content = await readFile6(configPath, "utf-8");
|
|
3795
4186
|
const parsed = parseYAML(content);
|
|
3796
4187
|
const validation = validateConfig(parsed);
|
|
3797
4188
|
if (!validation.valid) {
|
|
@@ -3810,14 +4201,14 @@ ${formatValidationErrors(validation.errors || [])}`
|
|
|
3810
4201
|
}
|
|
3811
4202
|
}
|
|
3812
4203
|
var WizardView = ({ printMode }) => {
|
|
3813
|
-
const { exit } =
|
|
3814
|
-
const [phase, setPhase] =
|
|
3815
|
-
const [message, setMessage] =
|
|
3816
|
-
const [error, setError] =
|
|
3817
|
-
const [prompt, setPrompt] =
|
|
3818
|
-
const [sessionId, setSessionId] =
|
|
3819
|
-
const [attemptNumber, setAttemptNumber] =
|
|
3820
|
-
|
|
4204
|
+
const { exit } = useApp9();
|
|
4205
|
+
const [phase, setPhase] = useState10("init");
|
|
4206
|
+
const [message, setMessage] = useState10("");
|
|
4207
|
+
const [error, setError] = useState10(null);
|
|
4208
|
+
const [prompt, setPrompt] = useState10("");
|
|
4209
|
+
const [sessionId, setSessionId] = useState10(void 0);
|
|
4210
|
+
const [attemptNumber, setAttemptNumber] = useState10(1);
|
|
4211
|
+
useEffect9(() => {
|
|
3821
4212
|
(async () => {
|
|
3822
4213
|
try {
|
|
3823
4214
|
const configPath = join14(getAppDir(), CONFIG_FILENAME);
|
|
@@ -3887,7 +4278,7 @@ var WizardView = ({ printMode }) => {
|
|
|
3887
4278
|
setPhase("writing");
|
|
3888
4279
|
setMessage("Writing config.yml...");
|
|
3889
4280
|
const tmpPath = `${configPath}.tmp`;
|
|
3890
|
-
await
|
|
4281
|
+
await writeFile5(tmpPath, yamlContent, "utf-8");
|
|
3891
4282
|
const verification = validateConfig(parseYAML(yamlContent));
|
|
3892
4283
|
if (verification.valid) {
|
|
3893
4284
|
await rename(tmpPath, configPath);
|
|
@@ -3931,37 +4322,37 @@ ${formatValidationErrors(validation.errors || [])}`
|
|
|
3931
4322
|
}
|
|
3932
4323
|
}
|
|
3933
4324
|
if (error) {
|
|
3934
|
-
return /* @__PURE__ */
|
|
4325
|
+
return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs14(Text14, { color: "red", children: [
|
|
3935
4326
|
"\u2717 ",
|
|
3936
4327
|
error
|
|
3937
4328
|
] }) });
|
|
3938
4329
|
}
|
|
3939
4330
|
if (printMode && phase === "done") {
|
|
3940
|
-
return /* @__PURE__ */
|
|
3941
|
-
/* @__PURE__ */
|
|
3942
|
-
/* @__PURE__ */
|
|
3943
|
-
/* @__PURE__ */
|
|
3944
|
-
/* @__PURE__ */
|
|
3945
|
-
/* @__PURE__ */
|
|
4331
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4332
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Config Wizard Prompt (Copy and paste to your LLM):" }),
|
|
4333
|
+
/* @__PURE__ */ jsx14(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u2500".repeat(60) }) }),
|
|
4334
|
+
/* @__PURE__ */ jsx14(Text14, { children: prompt }),
|
|
4335
|
+
/* @__PURE__ */ jsx14(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u2500".repeat(60) }) }),
|
|
4336
|
+
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "After getting the YAML response, save it to ~/.syncpoint/config.yml" })
|
|
3946
4337
|
] });
|
|
3947
4338
|
}
|
|
3948
4339
|
if (phase === "done") {
|
|
3949
|
-
return /* @__PURE__ */
|
|
3950
|
-
/* @__PURE__ */
|
|
3951
|
-
/* @__PURE__ */
|
|
3952
|
-
/* @__PURE__ */
|
|
3953
|
-
/* @__PURE__ */
|
|
3954
|
-
/* @__PURE__ */
|
|
4340
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4341
|
+
/* @__PURE__ */ jsx14(Text14, { color: "green", children: message }),
|
|
4342
|
+
/* @__PURE__ */ jsxs14(Box12, { marginTop: 1, children: [
|
|
4343
|
+
/* @__PURE__ */ jsx14(Text14, { children: "Next steps:" }),
|
|
4344
|
+
/* @__PURE__ */ jsx14(Text14, { children: " 1. Review your config: ~/.syncpoint/config.yml" }),
|
|
4345
|
+
/* @__PURE__ */ jsx14(Text14, { children: " 2. Run: syncpoint backup" })
|
|
3955
4346
|
] })
|
|
3956
4347
|
] });
|
|
3957
4348
|
}
|
|
3958
|
-
return /* @__PURE__ */
|
|
3959
|
-
/* @__PURE__ */
|
|
3960
|
-
/* @__PURE__ */
|
|
4349
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4350
|
+
/* @__PURE__ */ jsxs14(Text14, { children: [
|
|
4351
|
+
/* @__PURE__ */ jsx14(Text14, { color: "cyan", children: /* @__PURE__ */ jsx14(Spinner3, { type: "dots" }) }),
|
|
3961
4352
|
" ",
|
|
3962
4353
|
message
|
|
3963
4354
|
] }),
|
|
3964
|
-
attemptNumber > 1 && /* @__PURE__ */
|
|
4355
|
+
attemptNumber > 1 && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
3965
4356
|
"Attempt ",
|
|
3966
4357
|
attemptNumber,
|
|
3967
4358
|
"/",
|
|
@@ -3977,7 +4368,7 @@ function registerWizardCommand(program2) {
|
|
|
3977
4368
|
});
|
|
3978
4369
|
cmd.action(async (opts) => {
|
|
3979
4370
|
if (opts.print) {
|
|
3980
|
-
const { waitUntilExit } =
|
|
4371
|
+
const { waitUntilExit } = render10(/* @__PURE__ */ jsx14(WizardView, { printMode: true }));
|
|
3981
4372
|
await waitUntilExit();
|
|
3982
4373
|
return;
|
|
3983
4374
|
}
|
|
@@ -4020,6 +4411,7 @@ registerRestoreCommand(program);
|
|
|
4020
4411
|
registerProvisionCommand(program);
|
|
4021
4412
|
registerCreateTemplateCommand(program);
|
|
4022
4413
|
registerListCommand(program);
|
|
4414
|
+
registerMigrateCommand(program);
|
|
4023
4415
|
registerStatusCommand(program);
|
|
4024
4416
|
registerHelpCommand(program);
|
|
4025
4417
|
program.parseAsync(process.argv).catch((error) => {
|