@lumy-pack/syncpoint 0.0.10 → 0.0.11
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/dist/cli.mjs +791 -339
- package/dist/commands/Link.d.ts +9 -0
- package/dist/commands/Unlink.d.ts +2 -0
- package/dist/core/link.d.ts +19 -0
- package/dist/errors.d.ts +2 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/utils/types.d.ts +15 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -87,9 +87,6 @@ var init_wizard_template = __esm({
|
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
// src/cli.ts
|
|
91
|
-
import { Command } from "commander";
|
|
92
|
-
|
|
93
90
|
// ../shared/src/respond.ts
|
|
94
91
|
function respond(command, data, startTime, version) {
|
|
95
92
|
const response = {
|
|
@@ -119,6 +116,9 @@ function respondError(command, code, message, startTime, version, details) {
|
|
|
119
116
|
process.exitCode = 1;
|
|
120
117
|
}
|
|
121
118
|
|
|
119
|
+
// src/cli.ts
|
|
120
|
+
import { Command } from "commander";
|
|
121
|
+
|
|
122
122
|
// src/commands/Backup.tsx
|
|
123
123
|
import { Box, Static, Text as Text2, useApp } from "ink";
|
|
124
124
|
import { render } from "ink";
|
|
@@ -602,7 +602,7 @@ function validateMetadata(data) {
|
|
|
602
602
|
}
|
|
603
603
|
|
|
604
604
|
// src/version.ts
|
|
605
|
-
var VERSION = "0.0.
|
|
605
|
+
var VERSION = "0.0.11";
|
|
606
606
|
|
|
607
607
|
// src/core/metadata.ts
|
|
608
608
|
var METADATA_VERSION = "1.0.0";
|
|
@@ -1102,6 +1102,8 @@ var SyncpointErrorCode = {
|
|
|
1102
1102
|
PROVISION_FAILED: "PROVISION_FAILED",
|
|
1103
1103
|
MISSING_ARGUMENT: "MISSING_ARGUMENT",
|
|
1104
1104
|
INVALID_ARGUMENT: "INVALID_ARGUMENT",
|
|
1105
|
+
LINK_FAILED: "LINK_FAILED",
|
|
1106
|
+
UNLINK_FAILED: "UNLINK_FAILED",
|
|
1105
1107
|
UNKNOWN: "UNKNOWN"
|
|
1106
1108
|
};
|
|
1107
1109
|
function classifyError(err) {
|
|
@@ -1118,6 +1120,12 @@ function classifyError(err) {
|
|
|
1118
1120
|
if (msg.includes("Template file not found")) {
|
|
1119
1121
|
return SyncpointErrorCode.TEMPLATE_NOT_FOUND;
|
|
1120
1122
|
}
|
|
1123
|
+
if (msg.includes("not a symlink") || msg.includes('"syncpoint link"')) {
|
|
1124
|
+
return SyncpointErrorCode.UNLINK_FAILED;
|
|
1125
|
+
}
|
|
1126
|
+
if (msg.includes("destination is not set") || msg.includes("cross-device") || msg.includes("EXDEV")) {
|
|
1127
|
+
return SyncpointErrorCode.LINK_FAILED;
|
|
1128
|
+
}
|
|
1121
1129
|
return SyncpointErrorCode.UNKNOWN;
|
|
1122
1130
|
}
|
|
1123
1131
|
|
|
@@ -1267,7 +1275,13 @@ var COMMANDS = {
|
|
|
1267
1275
|
required: false
|
|
1268
1276
|
}
|
|
1269
1277
|
],
|
|
1270
|
-
options: [
|
|
1278
|
+
options: [
|
|
1279
|
+
{
|
|
1280
|
+
flag: "--delete <filename>",
|
|
1281
|
+
description: "Delete item by filename",
|
|
1282
|
+
type: "string"
|
|
1283
|
+
}
|
|
1284
|
+
],
|
|
1271
1285
|
examples: [
|
|
1272
1286
|
"npx @lumy-pack/syncpoint list",
|
|
1273
1287
|
"npx @lumy-pack/syncpoint list backups",
|
|
@@ -1279,7 +1293,11 @@ var COMMANDS = {
|
|
|
1279
1293
|
description: "Show ~/.syncpoint/ status summary and manage cleanup",
|
|
1280
1294
|
usage: "npx @lumy-pack/syncpoint status [options]",
|
|
1281
1295
|
options: [
|
|
1282
|
-
{
|
|
1296
|
+
{
|
|
1297
|
+
flag: "--cleanup",
|
|
1298
|
+
description: "Enter interactive cleanup mode",
|
|
1299
|
+
type: "boolean"
|
|
1300
|
+
}
|
|
1283
1301
|
],
|
|
1284
1302
|
examples: [
|
|
1285
1303
|
"npx @lumy-pack/syncpoint status",
|
|
@@ -1302,6 +1320,38 @@ var COMMANDS = {
|
|
|
1302
1320
|
"npx @lumy-pack/syncpoint migrate --dry-run"
|
|
1303
1321
|
]
|
|
1304
1322
|
},
|
|
1323
|
+
link: {
|
|
1324
|
+
name: "link",
|
|
1325
|
+
description: "Move ~/.syncpoint to backup destination and create a symlink",
|
|
1326
|
+
usage: "npx @lumy-pack/syncpoint link [options]",
|
|
1327
|
+
options: [
|
|
1328
|
+
{
|
|
1329
|
+
flag: "-r, --ref <path>",
|
|
1330
|
+
description: "Adopt <path>/.syncpoint as ~/.syncpoint via symlink",
|
|
1331
|
+
type: "string"
|
|
1332
|
+
}
|
|
1333
|
+
],
|
|
1334
|
+
examples: [
|
|
1335
|
+
"npx @lumy-pack/syncpoint link",
|
|
1336
|
+
"npx @lumy-pack/syncpoint link --ref ~/Dropbox"
|
|
1337
|
+
]
|
|
1338
|
+
},
|
|
1339
|
+
unlink: {
|
|
1340
|
+
name: "unlink",
|
|
1341
|
+
description: "Remove the symlink and restore ~/.syncpoint from the destination copy",
|
|
1342
|
+
usage: "npx @lumy-pack/syncpoint unlink [options]",
|
|
1343
|
+
options: [
|
|
1344
|
+
{
|
|
1345
|
+
flag: "--clean",
|
|
1346
|
+
description: "Also remove the destination copy after restoring",
|
|
1347
|
+
type: "boolean"
|
|
1348
|
+
}
|
|
1349
|
+
],
|
|
1350
|
+
examples: [
|
|
1351
|
+
"npx @lumy-pack/syncpoint unlink",
|
|
1352
|
+
"npx @lumy-pack/syncpoint unlink --clean"
|
|
1353
|
+
]
|
|
1354
|
+
},
|
|
1305
1355
|
help: {
|
|
1306
1356
|
name: "help",
|
|
1307
1357
|
description: "Display help information",
|
|
@@ -1604,19 +1654,19 @@ init_assets();
|
|
|
1604
1654
|
// src/utils/claude-code-runner.ts
|
|
1605
1655
|
import { spawn } from "child_process";
|
|
1606
1656
|
async function isClaudeCodeAvailable() {
|
|
1607
|
-
return new Promise((
|
|
1657
|
+
return new Promise((resolve3) => {
|
|
1608
1658
|
const child = spawn("which", ["claude"], { shell: true });
|
|
1609
1659
|
child.on("close", (code) => {
|
|
1610
|
-
|
|
1660
|
+
resolve3(code === 0);
|
|
1611
1661
|
});
|
|
1612
1662
|
child.on("error", () => {
|
|
1613
|
-
|
|
1663
|
+
resolve3(false);
|
|
1614
1664
|
});
|
|
1615
1665
|
});
|
|
1616
1666
|
}
|
|
1617
1667
|
async function invokeClaudeCode(prompt, options) {
|
|
1618
1668
|
const timeout = options?.timeout ?? 12e4;
|
|
1619
|
-
return await new Promise((
|
|
1669
|
+
return await new Promise((resolve3, reject) => {
|
|
1620
1670
|
const args = ["--permission-mode", "acceptEdits", "--model", "sonnet"];
|
|
1621
1671
|
if (options?.sessionId) {
|
|
1622
1672
|
args.push("--session", options.sessionId);
|
|
@@ -1641,13 +1691,13 @@ async function invokeClaudeCode(prompt, options) {
|
|
|
1641
1691
|
child.on("close", (code) => {
|
|
1642
1692
|
clearTimeout(timer);
|
|
1643
1693
|
if (code === 0) {
|
|
1644
|
-
|
|
1694
|
+
resolve3({
|
|
1645
1695
|
success: true,
|
|
1646
1696
|
output: stdout,
|
|
1647
1697
|
sessionId: options?.sessionId
|
|
1648
1698
|
});
|
|
1649
1699
|
} else {
|
|
1650
|
-
|
|
1700
|
+
resolve3({
|
|
1651
1701
|
success: false,
|
|
1652
1702
|
output: stdout,
|
|
1653
1703
|
error: stderr || `Process exited with code ${code}`
|
|
@@ -1667,7 +1717,7 @@ async function resumeClaudeCodeSession(sessionId, prompt, options) {
|
|
|
1667
1717
|
});
|
|
1668
1718
|
}
|
|
1669
1719
|
async function invokeClaudeCodeInteractive(prompt) {
|
|
1670
|
-
return await new Promise((
|
|
1720
|
+
return await new Promise((resolve3, reject) => {
|
|
1671
1721
|
const initialMessage = `${prompt}
|
|
1672
1722
|
|
|
1673
1723
|
IMPORTANT INSTRUCTIONS:
|
|
@@ -1691,7 +1741,7 @@ Start by asking the user about their backup priorities for the home directory st
|
|
|
1691
1741
|
// Share stdin/stdout/stderr with parent process
|
|
1692
1742
|
});
|
|
1693
1743
|
child.on("close", (code) => {
|
|
1694
|
-
|
|
1744
|
+
resolve3({
|
|
1695
1745
|
success: code === 0,
|
|
1696
1746
|
output: ""
|
|
1697
1747
|
// No captured output in interactive mode
|
|
@@ -2247,11 +2297,10 @@ function registerInitCommand(program2) {
|
|
|
2247
2297
|
});
|
|
2248
2298
|
}
|
|
2249
2299
|
|
|
2250
|
-
// src/commands/
|
|
2251
|
-
import {
|
|
2252
|
-
import { Box as
|
|
2300
|
+
// src/commands/Link.tsx
|
|
2301
|
+
import { lstat as lstat3 } from "fs/promises";
|
|
2302
|
+
import { Box as Box5, Text as Text7, useApp as useApp4 } from "ink";
|
|
2253
2303
|
import { render as render5 } from "ink";
|
|
2254
|
-
import SelectInput from "ink-select-input";
|
|
2255
2304
|
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
2256
2305
|
|
|
2257
2306
|
// src/components/Confirm.tsx
|
|
@@ -2291,9 +2340,298 @@ var Confirm = ({
|
|
|
2291
2340
|
] });
|
|
2292
2341
|
};
|
|
2293
2342
|
|
|
2343
|
+
// src/core/link.ts
|
|
2344
|
+
import {
|
|
2345
|
+
cp,
|
|
2346
|
+
lstat as lstat2,
|
|
2347
|
+
readlink as readlink2,
|
|
2348
|
+
rename,
|
|
2349
|
+
rm as rm2,
|
|
2350
|
+
stat as stat2,
|
|
2351
|
+
symlink,
|
|
2352
|
+
unlink
|
|
2353
|
+
} from "fs/promises";
|
|
2354
|
+
import { basename as basename2, join as join10, resolve as resolve2 } from "path";
|
|
2355
|
+
async function linkSyncpoint(destination, _options = {}) {
|
|
2356
|
+
const appDir = getAppDir();
|
|
2357
|
+
const expandedDest = expandTilde(destination);
|
|
2358
|
+
const targetDir = join10(expandedDest, ".syncpoint");
|
|
2359
|
+
let wasAlreadyLinked = false;
|
|
2360
|
+
let lstats;
|
|
2361
|
+
try {
|
|
2362
|
+
lstats = await lstat2(appDir);
|
|
2363
|
+
} catch {
|
|
2364
|
+
throw new Error(`${appDir} does not exist. Run "syncpoint init" first.`);
|
|
2365
|
+
}
|
|
2366
|
+
if (lstats.isSymbolicLink()) {
|
|
2367
|
+
wasAlreadyLinked = true;
|
|
2368
|
+
const existingTarget = await readlink2(appDir);
|
|
2369
|
+
await unlink(appDir);
|
|
2370
|
+
if (existingTarget !== targetDir) {
|
|
2371
|
+
await rename(existingTarget, appDir);
|
|
2372
|
+
} else {
|
|
2373
|
+
await symlink(targetDir, appDir);
|
|
2374
|
+
return { appDir, targetDir, wasAlreadyLinked };
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
try {
|
|
2378
|
+
await rename(appDir, targetDir);
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
const isExdev = err instanceof Error && (err.message.includes("EXDEV") || err.message.includes("cross-device"));
|
|
2381
|
+
if (isExdev) {
|
|
2382
|
+
throw new Error(
|
|
2383
|
+
`Cannot move ${appDir} to ${targetDir}: source and destination are on different filesystems. Use a destination on the same filesystem, or manually copy the directory.`
|
|
2384
|
+
);
|
|
2385
|
+
}
|
|
2386
|
+
throw err;
|
|
2387
|
+
}
|
|
2388
|
+
await symlink(targetDir, appDir);
|
|
2389
|
+
return { appDir, targetDir, wasAlreadyLinked };
|
|
2390
|
+
}
|
|
2391
|
+
async function linkSyncpointByRef(refPath) {
|
|
2392
|
+
const appDir = getAppDir();
|
|
2393
|
+
const absoluteRef = resolve2(expandTilde(refPath));
|
|
2394
|
+
const targetDir = basename2(absoluteRef) === ".syncpoint" ? absoluteRef : join10(absoluteRef, ".syncpoint");
|
|
2395
|
+
let refStats;
|
|
2396
|
+
try {
|
|
2397
|
+
refStats = await stat2(targetDir);
|
|
2398
|
+
} catch {
|
|
2399
|
+
throw new Error(`Reference syncpoint path does not exist: ${targetDir}`);
|
|
2400
|
+
}
|
|
2401
|
+
if (!refStats.isDirectory()) {
|
|
2402
|
+
throw new Error(
|
|
2403
|
+
`Reference syncpoint path is not a directory: ${targetDir}`
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
try {
|
|
2407
|
+
const existing = await lstat2(appDir);
|
|
2408
|
+
if (existing.isSymbolicLink()) {
|
|
2409
|
+
await unlink(appDir);
|
|
2410
|
+
} else {
|
|
2411
|
+
await rm2(appDir, { recursive: true, force: true });
|
|
2412
|
+
}
|
|
2413
|
+
} catch {
|
|
2414
|
+
}
|
|
2415
|
+
await symlink(targetDir, appDir);
|
|
2416
|
+
return { appDir, targetDir, wasAlreadyLinked: false };
|
|
2417
|
+
}
|
|
2418
|
+
async function unlinkSyncpoint(options = {}) {
|
|
2419
|
+
const appDir = getAppDir();
|
|
2420
|
+
let lstats;
|
|
2421
|
+
try {
|
|
2422
|
+
lstats = await lstat2(appDir);
|
|
2423
|
+
} catch {
|
|
2424
|
+
throw new Error(`${appDir} does not exist.`);
|
|
2425
|
+
}
|
|
2426
|
+
if (!lstats.isSymbolicLink()) {
|
|
2427
|
+
throw new Error(`${appDir} is not a symlink. Run "syncpoint link" first.`);
|
|
2428
|
+
}
|
|
2429
|
+
const targetDir = await readlink2(appDir);
|
|
2430
|
+
await unlink(appDir);
|
|
2431
|
+
await cp(targetDir, appDir, { recursive: true });
|
|
2432
|
+
if (options.clean) {
|
|
2433
|
+
await rm2(targetDir, { recursive: true, force: true });
|
|
2434
|
+
}
|
|
2435
|
+
return { appDir, targetDir, cleaned: options.clean ?? false };
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// src/commands/Link.tsx
|
|
2439
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2440
|
+
var LinkView = ({ refPath, yes }) => {
|
|
2441
|
+
const { exit } = useApp4();
|
|
2442
|
+
const [phase, setPhase] = useState5("checking");
|
|
2443
|
+
const [result, setResult] = useState5(null);
|
|
2444
|
+
const [error, setError] = useState5(null);
|
|
2445
|
+
const [existingType, setExistingType] = useState5("directory");
|
|
2446
|
+
useEffect4(() => {
|
|
2447
|
+
(async () => {
|
|
2448
|
+
try {
|
|
2449
|
+
if (!refPath) {
|
|
2450
|
+
setPhase("linking");
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
const appDir = getAppDir();
|
|
2454
|
+
try {
|
|
2455
|
+
const stats = await lstat3(appDir);
|
|
2456
|
+
setExistingType(stats.isSymbolicLink() ? "symlink" : "directory");
|
|
2457
|
+
if (yes) {
|
|
2458
|
+
setPhase("linking");
|
|
2459
|
+
} else {
|
|
2460
|
+
setPhase("confirming");
|
|
2461
|
+
}
|
|
2462
|
+
} catch {
|
|
2463
|
+
setPhase("linking");
|
|
2464
|
+
}
|
|
2465
|
+
} catch (err) {
|
|
2466
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
2467
|
+
setPhase("error");
|
|
2468
|
+
setTimeout(() => exit(), 100);
|
|
2469
|
+
}
|
|
2470
|
+
})();
|
|
2471
|
+
}, []);
|
|
2472
|
+
useEffect4(() => {
|
|
2473
|
+
if (phase !== "linking") return;
|
|
2474
|
+
(async () => {
|
|
2475
|
+
try {
|
|
2476
|
+
let linkResult;
|
|
2477
|
+
if (refPath) {
|
|
2478
|
+
linkResult = await linkSyncpointByRef(refPath);
|
|
2479
|
+
} else {
|
|
2480
|
+
const config = await loadConfig();
|
|
2481
|
+
const destination = config.backup.destination;
|
|
2482
|
+
if (!destination) {
|
|
2483
|
+
throw new Error(
|
|
2484
|
+
'backup.destination is not set in config.yml. Set it before running "syncpoint link".'
|
|
2485
|
+
);
|
|
2486
|
+
}
|
|
2487
|
+
linkResult = await linkSyncpoint(destination);
|
|
2488
|
+
}
|
|
2489
|
+
setResult(linkResult);
|
|
2490
|
+
setPhase("done");
|
|
2491
|
+
setTimeout(() => exit(), 100);
|
|
2492
|
+
} catch (err) {
|
|
2493
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
2494
|
+
setPhase("error");
|
|
2495
|
+
setTimeout(() => exit(), 100);
|
|
2496
|
+
}
|
|
2497
|
+
})();
|
|
2498
|
+
}, [phase]);
|
|
2499
|
+
if (phase === "error" || error) {
|
|
2500
|
+
return /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text7, { color: "red", children: [
|
|
2501
|
+
"\u2717 Link failed: ",
|
|
2502
|
+
error
|
|
2503
|
+
] }) });
|
|
2504
|
+
}
|
|
2505
|
+
if (phase === "confirming") {
|
|
2506
|
+
return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
|
|
2507
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
2508
|
+
"\u26A0 ~/.syncpoint already exists as a ",
|
|
2509
|
+
existingType,
|
|
2510
|
+
"."
|
|
2511
|
+
] }),
|
|
2512
|
+
/* @__PURE__ */ jsx7(
|
|
2513
|
+
Confirm,
|
|
2514
|
+
{
|
|
2515
|
+
message: "Overwrite with symlink?",
|
|
2516
|
+
defaultYes: false,
|
|
2517
|
+
onConfirm: (confirmed) => {
|
|
2518
|
+
if (confirmed) {
|
|
2519
|
+
setPhase("linking");
|
|
2520
|
+
} else {
|
|
2521
|
+
setError("Aborted.");
|
|
2522
|
+
setPhase("error");
|
|
2523
|
+
setTimeout(() => exit(), 100);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
)
|
|
2528
|
+
] });
|
|
2529
|
+
}
|
|
2530
|
+
if (phase === "checking" || phase === "linking") {
|
|
2531
|
+
return /* @__PURE__ */ jsx7(Box5, { children: /* @__PURE__ */ jsx7(Text7, { children: "\u25B8 Linking ~/.syncpoint to destination..." }) });
|
|
2532
|
+
}
|
|
2533
|
+
return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
|
|
2534
|
+
/* @__PURE__ */ jsx7(Text7, { color: "green", bold: true, children: "\u2713 Link complete" }),
|
|
2535
|
+
result && /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
2536
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
2537
|
+
" ",
|
|
2538
|
+
"From: ",
|
|
2539
|
+
contractTilde(result.appDir)
|
|
2540
|
+
] }),
|
|
2541
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
2542
|
+
" ",
|
|
2543
|
+
"To: ",
|
|
2544
|
+
contractTilde(result.targetDir)
|
|
2545
|
+
] }),
|
|
2546
|
+
result.wasAlreadyLinked && /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
2547
|
+
" ",
|
|
2548
|
+
"(previous link was removed and re-linked)"
|
|
2549
|
+
] })
|
|
2550
|
+
] })
|
|
2551
|
+
] });
|
|
2552
|
+
};
|
|
2553
|
+
function registerLinkCommand(program2) {
|
|
2554
|
+
const cmdInfo = COMMANDS.link;
|
|
2555
|
+
const cmd = program2.command("link").description(cmdInfo.description);
|
|
2556
|
+
cmd.option(
|
|
2557
|
+
"-r, --ref <path>",
|
|
2558
|
+
"Adopt <path>/.syncpoint as ~/.syncpoint via symlink"
|
|
2559
|
+
);
|
|
2560
|
+
cmd.action(async (opts) => {
|
|
2561
|
+
const globalOpts = program2.opts();
|
|
2562
|
+
const startTime = Date.now();
|
|
2563
|
+
if (globalOpts.json) {
|
|
2564
|
+
try {
|
|
2565
|
+
if (opts.ref) {
|
|
2566
|
+
const appDir = getAppDir();
|
|
2567
|
+
let appDirExists = false;
|
|
2568
|
+
try {
|
|
2569
|
+
await lstat3(appDir);
|
|
2570
|
+
appDirExists = true;
|
|
2571
|
+
} catch {
|
|
2572
|
+
}
|
|
2573
|
+
if (appDirExists && !globalOpts.yes) {
|
|
2574
|
+
respondError(
|
|
2575
|
+
"link",
|
|
2576
|
+
SyncpointErrorCode.LINK_FAILED,
|
|
2577
|
+
"~/.syncpoint already exists. Use --yes to overwrite.",
|
|
2578
|
+
startTime,
|
|
2579
|
+
VERSION
|
|
2580
|
+
);
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
const linkResult = await linkSyncpointByRef(opts.ref);
|
|
2584
|
+
respond(
|
|
2585
|
+
"link",
|
|
2586
|
+
{
|
|
2587
|
+
appDir: linkResult.appDir,
|
|
2588
|
+
targetDir: linkResult.targetDir,
|
|
2589
|
+
wasAlreadyLinked: linkResult.wasAlreadyLinked
|
|
2590
|
+
},
|
|
2591
|
+
startTime,
|
|
2592
|
+
VERSION
|
|
2593
|
+
);
|
|
2594
|
+
} else {
|
|
2595
|
+
const config = await loadConfig();
|
|
2596
|
+
const destination = config.backup.destination;
|
|
2597
|
+
if (!destination) {
|
|
2598
|
+
throw new Error("backup.destination is not set in config.yml.");
|
|
2599
|
+
}
|
|
2600
|
+
const linkResult = await linkSyncpoint(destination);
|
|
2601
|
+
respond(
|
|
2602
|
+
"link",
|
|
2603
|
+
{
|
|
2604
|
+
appDir: linkResult.appDir,
|
|
2605
|
+
targetDir: linkResult.targetDir,
|
|
2606
|
+
wasAlreadyLinked: linkResult.wasAlreadyLinked
|
|
2607
|
+
},
|
|
2608
|
+
startTime,
|
|
2609
|
+
VERSION
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
} catch (error) {
|
|
2613
|
+
const code = classifyError(error);
|
|
2614
|
+
respondError("link", code, error.message, startTime, VERSION);
|
|
2615
|
+
}
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
const { waitUntilExit } = render5(
|
|
2619
|
+
/* @__PURE__ */ jsx7(LinkView, { refPath: opts.ref, yes: globalOpts.yes ?? false })
|
|
2620
|
+
);
|
|
2621
|
+
await waitUntilExit();
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
// src/commands/List.tsx
|
|
2626
|
+
import { unlinkSync } from "fs";
|
|
2627
|
+
import { Box as Box7, Text as Text9, useApp as useApp5, useInput as useInput2 } from "ink";
|
|
2628
|
+
import { render as render6 } from "ink";
|
|
2629
|
+
import SelectInput from "ink-select-input";
|
|
2630
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
2631
|
+
|
|
2294
2632
|
// src/components/Table.tsx
|
|
2295
|
-
import { Box as
|
|
2296
|
-
import { jsx as
|
|
2633
|
+
import { Box as Box6, Text as Text8 } from "ink";
|
|
2634
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2297
2635
|
var Table = ({
|
|
2298
2636
|
headers,
|
|
2299
2637
|
rows,
|
|
@@ -2310,13 +2648,13 @@ var Table = ({
|
|
|
2310
2648
|
return text.padEnd(width);
|
|
2311
2649
|
};
|
|
2312
2650
|
const separator = widths.map((w) => "\u2500".repeat(w)).join(" ");
|
|
2313
|
-
return /* @__PURE__ */
|
|
2314
|
-
/* @__PURE__ */
|
|
2651
|
+
return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
|
|
2652
|
+
/* @__PURE__ */ jsx8(Text8, { children: headers.map((h, i) => /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
|
|
2315
2653
|
padCell(h, widths[i]),
|
|
2316
2654
|
i < headers.length - 1 ? " " : ""
|
|
2317
2655
|
] }, i)) }),
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
rows.map((row, rowIdx) => /* @__PURE__ */
|
|
2656
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: separator }),
|
|
2657
|
+
rows.map((row, rowIdx) => /* @__PURE__ */ jsx8(Text8, { children: row.map((cell, colIdx) => /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2320
2658
|
padCell(cell, widths[colIdx]),
|
|
2321
2659
|
colIdx < row.length - 1 ? " " : ""
|
|
2322
2660
|
] }, colIdx)) }, rowIdx))
|
|
@@ -2326,7 +2664,7 @@ var Table = ({
|
|
|
2326
2664
|
// src/core/provision.ts
|
|
2327
2665
|
import { exec } from "child_process";
|
|
2328
2666
|
import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
2329
|
-
import { join as
|
|
2667
|
+
import { join as join11 } from "path";
|
|
2330
2668
|
import YAML3 from "yaml";
|
|
2331
2669
|
var REMOTE_SCRIPT_PATTERNS = [
|
|
2332
2670
|
/curl\s.*\|\s*(ba)?sh/,
|
|
@@ -2366,7 +2704,7 @@ async function listTemplates() {
|
|
|
2366
2704
|
if (!entry.isFile() || !entry.name.endsWith(".yml") && !entry.name.endsWith(".yaml")) {
|
|
2367
2705
|
continue;
|
|
2368
2706
|
}
|
|
2369
|
-
const fullPath =
|
|
2707
|
+
const fullPath = join11(templatesDir, entry.name);
|
|
2370
2708
|
try {
|
|
2371
2709
|
const config = await loadTemplate(fullPath);
|
|
2372
2710
|
templates.push({
|
|
@@ -2381,7 +2719,7 @@ async function listTemplates() {
|
|
|
2381
2719
|
return templates;
|
|
2382
2720
|
}
|
|
2383
2721
|
function execAsync(command) {
|
|
2384
|
-
return new Promise((
|
|
2722
|
+
return new Promise((resolve3, reject) => {
|
|
2385
2723
|
exec(
|
|
2386
2724
|
command,
|
|
2387
2725
|
{ shell: "/bin/bash", timeout: 3e5 },
|
|
@@ -2394,7 +2732,7 @@ function execAsync(command) {
|
|
|
2394
2732
|
})
|
|
2395
2733
|
);
|
|
2396
2734
|
} else {
|
|
2397
|
-
|
|
2735
|
+
resolve3({
|
|
2398
2736
|
stdout: stdout?.toString() ?? "",
|
|
2399
2737
|
stderr: stderr?.toString() ?? ""
|
|
2400
2738
|
});
|
|
@@ -2484,8 +2822,8 @@ async function* runProvision(templatePath, options = {}) {
|
|
|
2484
2822
|
}
|
|
2485
2823
|
|
|
2486
2824
|
// src/core/restore.ts
|
|
2487
|
-
import { copyFile, lstat as
|
|
2488
|
-
import { dirname as dirname2, join as
|
|
2825
|
+
import { copyFile, lstat as lstat4, readdir as readdir3, stat as stat3 } from "fs/promises";
|
|
2826
|
+
import { dirname as dirname2, join as join12 } from "path";
|
|
2489
2827
|
async function getBackupList(config) {
|
|
2490
2828
|
const backupDir = config?.backup.destination ? resolveTargetPath(config.backup.destination) : getSubDir(BACKUPS_DIR);
|
|
2491
2829
|
const exists = await fileExists(backupDir);
|
|
@@ -2494,8 +2832,8 @@ async function getBackupList(config) {
|
|
|
2494
2832
|
const backups = [];
|
|
2495
2833
|
for (const entry of entries) {
|
|
2496
2834
|
if (!entry.isFile() || !entry.name.endsWith(".tar.gz")) continue;
|
|
2497
|
-
const fullPath =
|
|
2498
|
-
const fileStat = await
|
|
2835
|
+
const fullPath = join12(backupDir, entry.name);
|
|
2836
|
+
const fileStat = await stat3(fullPath);
|
|
2499
2837
|
let hostname;
|
|
2500
2838
|
let fileCount;
|
|
2501
2839
|
try {
|
|
@@ -2543,7 +2881,7 @@ This may not be a valid syncpoint backup.`
|
|
|
2543
2881
|
continue;
|
|
2544
2882
|
}
|
|
2545
2883
|
const currentHash = await computeFileHash(absPath);
|
|
2546
|
-
const currentStat = await
|
|
2884
|
+
const currentStat = await stat3(absPath);
|
|
2547
2885
|
if (currentHash === file.hash) {
|
|
2548
2886
|
actions.push({
|
|
2549
2887
|
path: file.path,
|
|
@@ -2569,7 +2907,7 @@ async function createSafetyBackup(filePaths) {
|
|
|
2569
2907
|
const filename = `_pre-restore_${formatDatetime(now)}.tar.gz`;
|
|
2570
2908
|
const backupDir = getSubDir(BACKUPS_DIR);
|
|
2571
2909
|
await ensureDir(backupDir);
|
|
2572
|
-
const archivePath =
|
|
2910
|
+
const archivePath = join12(backupDir, filename);
|
|
2573
2911
|
const files = [];
|
|
2574
2912
|
for (const fp of filePaths) {
|
|
2575
2913
|
const absPath = resolveTargetPath(fp);
|
|
@@ -2602,9 +2940,9 @@ async function restoreBackup(archivePath, options = {}) {
|
|
|
2602
2940
|
safetyBackupPath
|
|
2603
2941
|
};
|
|
2604
2942
|
}
|
|
2605
|
-
const { mkdtemp: mkdtemp2, rm:
|
|
2943
|
+
const { mkdtemp: mkdtemp2, rm: rm3 } = await import("fs/promises");
|
|
2606
2944
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
2607
|
-
const tmpDir = await mkdtemp2(
|
|
2945
|
+
const tmpDir = await mkdtemp2(join12(tmpdir2(), "syncpoint-restore-"));
|
|
2608
2946
|
try {
|
|
2609
2947
|
await extractArchive(archivePath, tmpDir);
|
|
2610
2948
|
for (const action of plan.actions) {
|
|
@@ -2613,7 +2951,7 @@ async function restoreBackup(archivePath, options = {}) {
|
|
|
2613
2951
|
continue;
|
|
2614
2952
|
}
|
|
2615
2953
|
const archiveName = action.path.startsWith("~/") ? action.path.slice(2) : action.path;
|
|
2616
|
-
const extractedPath =
|
|
2954
|
+
const extractedPath = join12(tmpDir, archiveName);
|
|
2617
2955
|
const destPath = resolveTargetPath(action.path);
|
|
2618
2956
|
const extractedExists = await fileExists(extractedPath);
|
|
2619
2957
|
if (!extractedExists) {
|
|
@@ -2623,7 +2961,7 @@ async function restoreBackup(archivePath, options = {}) {
|
|
|
2623
2961
|
}
|
|
2624
2962
|
await ensureDir(dirname2(destPath));
|
|
2625
2963
|
try {
|
|
2626
|
-
const destStat = await
|
|
2964
|
+
const destStat = await lstat4(destPath);
|
|
2627
2965
|
if (destStat.isSymbolicLink()) {
|
|
2628
2966
|
logger.warn(`Skipping symlink target: ${action.path}`);
|
|
2629
2967
|
skippedFiles.push(action.path);
|
|
@@ -2636,45 +2974,45 @@ async function restoreBackup(archivePath, options = {}) {
|
|
|
2636
2974
|
restoredFiles.push(action.path);
|
|
2637
2975
|
}
|
|
2638
2976
|
} finally {
|
|
2639
|
-
await
|
|
2977
|
+
await rm3(tmpDir, { recursive: true, force: true });
|
|
2640
2978
|
}
|
|
2641
2979
|
return { restoredFiles, skippedFiles, safetyBackupPath };
|
|
2642
2980
|
}
|
|
2643
2981
|
|
|
2644
2982
|
// src/commands/List.tsx
|
|
2645
|
-
import { jsx as
|
|
2983
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2646
2984
|
var MenuItem = ({ isSelected = false, label }) => {
|
|
2647
2985
|
if (label === "Delete") {
|
|
2648
|
-
return /* @__PURE__ */
|
|
2986
|
+
return /* @__PURE__ */ jsx9(Text9, { bold: isSelected, color: "red", children: label });
|
|
2649
2987
|
}
|
|
2650
2988
|
const match = label.match(/^(.+?)\s+\((\d+)\)$/);
|
|
2651
2989
|
if (match) {
|
|
2652
2990
|
const [, name, count] = match;
|
|
2653
|
-
return /* @__PURE__ */
|
|
2654
|
-
/* @__PURE__ */
|
|
2991
|
+
return /* @__PURE__ */ jsxs9(Text9, { children: [
|
|
2992
|
+
/* @__PURE__ */ jsx9(Text9, { bold: isSelected, children: name }),
|
|
2655
2993
|
" ",
|
|
2656
|
-
/* @__PURE__ */
|
|
2994
|
+
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2657
2995
|
"(",
|
|
2658
2996
|
count,
|
|
2659
2997
|
")"
|
|
2660
2998
|
] })
|
|
2661
2999
|
] });
|
|
2662
3000
|
}
|
|
2663
|
-
return /* @__PURE__ */
|
|
3001
|
+
return /* @__PURE__ */ jsx9(Text9, { bold: isSelected, children: label });
|
|
2664
3002
|
};
|
|
2665
3003
|
var ListView = ({ type, deleteIndex }) => {
|
|
2666
|
-
const { exit } =
|
|
2667
|
-
const [phase, setPhase] =
|
|
2668
|
-
const [backups, setBackups] =
|
|
2669
|
-
const [templates, setTemplates] =
|
|
2670
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
3004
|
+
const { exit } = useApp5();
|
|
3005
|
+
const [phase, setPhase] = useState6("loading");
|
|
3006
|
+
const [backups, setBackups] = useState6([]);
|
|
3007
|
+
const [templates, setTemplates] = useState6([]);
|
|
3008
|
+
const [selectedTemplate, setSelectedTemplate] = useState6(
|
|
2671
3009
|
null
|
|
2672
3010
|
);
|
|
2673
|
-
const [selectedBackup, setSelectedBackup] =
|
|
2674
|
-
const [deleteTarget, setDeleteTarget] =
|
|
2675
|
-
const [error, setError] =
|
|
2676
|
-
const [backupDir, setBackupDir] =
|
|
2677
|
-
const [successMessage, setSuccessMessage] =
|
|
3011
|
+
const [selectedBackup, setSelectedBackup] = useState6(null);
|
|
3012
|
+
const [deleteTarget, setDeleteTarget] = useState6(null);
|
|
3013
|
+
const [error, setError] = useState6(null);
|
|
3014
|
+
const [backupDir, setBackupDir] = useState6(getSubDir("backups"));
|
|
3015
|
+
const [successMessage, setSuccessMessage] = useState6(null);
|
|
2678
3016
|
useInput2((_input, key) => {
|
|
2679
3017
|
if (!key.escape) return;
|
|
2680
3018
|
switch (phase) {
|
|
@@ -2698,7 +3036,7 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2698
3036
|
break;
|
|
2699
3037
|
}
|
|
2700
3038
|
});
|
|
2701
|
-
|
|
3039
|
+
useEffect5(() => {
|
|
2702
3040
|
(async () => {
|
|
2703
3041
|
try {
|
|
2704
3042
|
const config = await loadConfig();
|
|
@@ -2790,16 +3128,16 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2790
3128
|
}
|
|
2791
3129
|
};
|
|
2792
3130
|
if (phase === "error" || error) {
|
|
2793
|
-
return /* @__PURE__ */
|
|
3131
|
+
return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
|
|
2794
3132
|
"\u2717 ",
|
|
2795
3133
|
error
|
|
2796
3134
|
] }) });
|
|
2797
3135
|
}
|
|
2798
3136
|
if (phase === "loading") {
|
|
2799
|
-
return /* @__PURE__ */
|
|
3137
|
+
return /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "Loading..." });
|
|
2800
3138
|
}
|
|
2801
3139
|
if (phase === "deleting" && deleteTarget) {
|
|
2802
|
-
return /* @__PURE__ */
|
|
3140
|
+
return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsx9(
|
|
2803
3141
|
Confirm,
|
|
2804
3142
|
{
|
|
2805
3143
|
message: `Delete ${deleteTarget.name}?`,
|
|
@@ -2809,7 +3147,7 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2809
3147
|
) });
|
|
2810
3148
|
}
|
|
2811
3149
|
if (phase === "done" && deleteTarget) {
|
|
2812
|
-
return /* @__PURE__ */
|
|
3150
|
+
return /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
|
|
2813
3151
|
"\u2713 ",
|
|
2814
3152
|
deleteTarget.name,
|
|
2815
3153
|
" deleted"
|
|
@@ -2841,8 +3179,8 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2841
3179
|
setPhase("template-list");
|
|
2842
3180
|
}
|
|
2843
3181
|
};
|
|
2844
|
-
return /* @__PURE__ */
|
|
2845
|
-
/* @__PURE__ */
|
|
3182
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
3183
|
+
/* @__PURE__ */ jsx9(
|
|
2846
3184
|
SelectInput,
|
|
2847
3185
|
{
|
|
2848
3186
|
items: menuItems,
|
|
@@ -2850,9 +3188,9 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2850
3188
|
itemComponent: MenuItem
|
|
2851
3189
|
}
|
|
2852
3190
|
),
|
|
2853
|
-
/* @__PURE__ */
|
|
3191
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2854
3192
|
"Press ",
|
|
2855
|
-
/* @__PURE__ */
|
|
3193
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
|
|
2856
3194
|
" to exit"
|
|
2857
3195
|
] }) })
|
|
2858
3196
|
] });
|
|
@@ -2869,10 +3207,10 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2869
3207
|
setPhase("backup-detail");
|
|
2870
3208
|
}
|
|
2871
3209
|
};
|
|
2872
|
-
return /* @__PURE__ */
|
|
2873
|
-
successMessage && /* @__PURE__ */
|
|
2874
|
-
/* @__PURE__ */
|
|
2875
|
-
/* @__PURE__ */
|
|
3210
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
3211
|
+
successMessage && /* @__PURE__ */ jsx9(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "green", children: successMessage }) }),
|
|
3212
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "\u25B8 Backups" }),
|
|
3213
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: backups.length === 0 ? /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " No backups found." }) : /* @__PURE__ */ jsx9(
|
|
2876
3214
|
SelectInput,
|
|
2877
3215
|
{
|
|
2878
3216
|
items,
|
|
@@ -2880,9 +3218,9 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2880
3218
|
itemComponent: MenuItem
|
|
2881
3219
|
}
|
|
2882
3220
|
) }),
|
|
2883
|
-
/* @__PURE__ */
|
|
3221
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2884
3222
|
"Press ",
|
|
2885
|
-
/* @__PURE__ */
|
|
3223
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
|
|
2886
3224
|
" to go back"
|
|
2887
3225
|
] }) })
|
|
2888
3226
|
] });
|
|
@@ -2899,9 +3237,9 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2899
3237
|
setPhase("template-detail");
|
|
2900
3238
|
}
|
|
2901
3239
|
};
|
|
2902
|
-
return /* @__PURE__ */
|
|
2903
|
-
/* @__PURE__ */
|
|
2904
|
-
/* @__PURE__ */
|
|
3240
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
3241
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "\u25B8 Templates" }),
|
|
3242
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: templates.length === 0 ? /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " No templates found." }) : /* @__PURE__ */ jsx9(
|
|
2905
3243
|
SelectInput,
|
|
2906
3244
|
{
|
|
2907
3245
|
items,
|
|
@@ -2909,9 +3247,9 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2909
3247
|
itemComponent: MenuItem
|
|
2910
3248
|
}
|
|
2911
3249
|
) }),
|
|
2912
|
-
/* @__PURE__ */
|
|
3250
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2913
3251
|
"Press ",
|
|
2914
|
-
/* @__PURE__ */
|
|
3252
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
|
|
2915
3253
|
" to go back"
|
|
2916
3254
|
] }) })
|
|
2917
3255
|
] });
|
|
@@ -2939,20 +3277,20 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2939
3277
|
goBackToBackupList();
|
|
2940
3278
|
}
|
|
2941
3279
|
};
|
|
2942
|
-
return /* @__PURE__ */
|
|
2943
|
-
/* @__PURE__ */
|
|
3280
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
3281
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
|
|
2944
3282
|
"\u25B8 ",
|
|
2945
3283
|
selectedBackup.filename.replace(".tar.gz", "")
|
|
2946
3284
|
] }),
|
|
2947
|
-
/* @__PURE__ */
|
|
3285
|
+
/* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: sections.map((section, idx) => {
|
|
2948
3286
|
const labelWidth = Math.max(...sections.map((s) => s.label.length)) + 1;
|
|
2949
|
-
return /* @__PURE__ */
|
|
2950
|
-
/* @__PURE__ */
|
|
2951
|
-
/* @__PURE__ */
|
|
2952
|
-
/* @__PURE__ */
|
|
3287
|
+
return /* @__PURE__ */ jsxs9(Box7, { children: [
|
|
3288
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: section.label.padEnd(labelWidth) }),
|
|
3289
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
3290
|
+
/* @__PURE__ */ jsx9(Text9, { children: section.value })
|
|
2953
3291
|
] }, idx);
|
|
2954
3292
|
}) }),
|
|
2955
|
-
/* @__PURE__ */
|
|
3293
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(
|
|
2956
3294
|
SelectInput,
|
|
2957
3295
|
{
|
|
2958
3296
|
items: actionItems,
|
|
@@ -2960,9 +3298,9 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2960
3298
|
itemComponent: MenuItem
|
|
2961
3299
|
}
|
|
2962
3300
|
) }),
|
|
2963
|
-
/* @__PURE__ */
|
|
3301
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
2964
3302
|
"Press ",
|
|
2965
|
-
/* @__PURE__ */
|
|
3303
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
|
|
2966
3304
|
" to go back"
|
|
2967
3305
|
] }) })
|
|
2968
3306
|
] });
|
|
@@ -2982,19 +3320,19 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2982
3320
|
}
|
|
2983
3321
|
};
|
|
2984
3322
|
const labelWidth = Math.max(...sections.map((s) => s.label.length)) + 1;
|
|
2985
|
-
return /* @__PURE__ */
|
|
2986
|
-
/* @__PURE__ */
|
|
3323
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
|
|
3324
|
+
/* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
|
|
2987
3325
|
"\u25B8 ",
|
|
2988
3326
|
selectedTemplate.name
|
|
2989
3327
|
] }),
|
|
2990
|
-
/* @__PURE__ */
|
|
2991
|
-
/* @__PURE__ */
|
|
2992
|
-
/* @__PURE__ */
|
|
2993
|
-
/* @__PURE__ */
|
|
3328
|
+
/* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: sections.map((section, idx) => /* @__PURE__ */ jsxs9(Box7, { children: [
|
|
3329
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: section.label.padEnd(labelWidth) }),
|
|
3330
|
+
/* @__PURE__ */ jsx9(Text9, { children: " " }),
|
|
3331
|
+
/* @__PURE__ */ jsx9(Text9, { children: section.value })
|
|
2994
3332
|
] }, idx)) }),
|
|
2995
|
-
t.steps.length > 0 && /* @__PURE__ */
|
|
2996
|
-
/* @__PURE__ */
|
|
2997
|
-
/* @__PURE__ */
|
|
3333
|
+
t.steps.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
3334
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " Provisioning Steps" }),
|
|
3335
|
+
/* @__PURE__ */ jsx9(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx9(
|
|
2998
3336
|
Table,
|
|
2999
3337
|
{
|
|
3000
3338
|
headers: ["#", "Step", "Description"],
|
|
@@ -3006,7 +3344,7 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
3006
3344
|
}
|
|
3007
3345
|
) })
|
|
3008
3346
|
] }),
|
|
3009
|
-
/* @__PURE__ */
|
|
3347
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(
|
|
3010
3348
|
SelectInput,
|
|
3011
3349
|
{
|
|
3012
3350
|
items: actionItems,
|
|
@@ -3014,9 +3352,9 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
3014
3352
|
itemComponent: MenuItem
|
|
3015
3353
|
}
|
|
3016
3354
|
) }),
|
|
3017
|
-
/* @__PURE__ */
|
|
3355
|
+
/* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
3018
3356
|
"Press ",
|
|
3019
|
-
/* @__PURE__ */
|
|
3357
|
+
/* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
|
|
3020
3358
|
" to go back"
|
|
3021
3359
|
] }) })
|
|
3022
3360
|
] });
|
|
@@ -3085,23 +3423,23 @@ function registerListCommand(program2) {
|
|
|
3085
3423
|
}
|
|
3086
3424
|
const deleteIndex = opts.delete ? parseInt(opts.delete, 10) : void 0;
|
|
3087
3425
|
if (deleteIndex !== void 0 && isNaN(deleteIndex)) {
|
|
3088
|
-
const { waitUntilExit: waitUntilExit2 } =
|
|
3089
|
-
/* @__PURE__ */
|
|
3426
|
+
const { waitUntilExit: waitUntilExit2 } = render6(
|
|
3427
|
+
/* @__PURE__ */ jsx9(ListView, { type, deleteIndex: void 0 })
|
|
3090
3428
|
);
|
|
3091
3429
|
await waitUntilExit2();
|
|
3092
3430
|
return;
|
|
3093
3431
|
}
|
|
3094
|
-
const { waitUntilExit } =
|
|
3095
|
-
/* @__PURE__ */
|
|
3432
|
+
const { waitUntilExit } = render6(
|
|
3433
|
+
/* @__PURE__ */ jsx9(ListView, { type, deleteIndex })
|
|
3096
3434
|
);
|
|
3097
3435
|
await waitUntilExit();
|
|
3098
3436
|
});
|
|
3099
3437
|
}
|
|
3100
3438
|
|
|
3101
3439
|
// src/commands/Migrate.tsx
|
|
3102
|
-
import { Box as
|
|
3103
|
-
import { render as
|
|
3104
|
-
import { useEffect as
|
|
3440
|
+
import { Box as Box8, Text as Text10, useApp as useApp6 } from "ink";
|
|
3441
|
+
import { render as render7 } from "ink";
|
|
3442
|
+
import { useEffect as useEffect6, useState as useState7 } from "react";
|
|
3105
3443
|
|
|
3106
3444
|
// src/core/migrate.ts
|
|
3107
3445
|
import { copyFile as copyFile2, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
@@ -3249,13 +3587,13 @@ ${(validation.errors ?? []).join("\n")}`
|
|
|
3249
3587
|
}
|
|
3250
3588
|
|
|
3251
3589
|
// src/commands/Migrate.tsx
|
|
3252
|
-
import { jsx as
|
|
3590
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3253
3591
|
var MigrateView = ({ dryRun }) => {
|
|
3254
|
-
const { exit } =
|
|
3255
|
-
const [result, setResult] =
|
|
3256
|
-
const [error, setError] =
|
|
3257
|
-
const [loading, setLoading] =
|
|
3258
|
-
|
|
3592
|
+
const { exit } = useApp6();
|
|
3593
|
+
const [result, setResult] = useState7(null);
|
|
3594
|
+
const [error, setError] = useState7(null);
|
|
3595
|
+
const [loading, setLoading] = useState7(true);
|
|
3596
|
+
useEffect6(() => {
|
|
3259
3597
|
(async () => {
|
|
3260
3598
|
try {
|
|
3261
3599
|
const res = await migrateConfig({ dryRun });
|
|
@@ -3270,54 +3608,54 @@ var MigrateView = ({ dryRun }) => {
|
|
|
3270
3608
|
})();
|
|
3271
3609
|
}, []);
|
|
3272
3610
|
if (error) {
|
|
3273
|
-
return /* @__PURE__ */
|
|
3611
|
+
return /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text10, { color: "red", children: [
|
|
3274
3612
|
"\u2717 ",
|
|
3275
3613
|
error
|
|
3276
3614
|
] }) });
|
|
3277
3615
|
}
|
|
3278
3616
|
if (loading) {
|
|
3279
|
-
return /* @__PURE__ */
|
|
3617
|
+
return /* @__PURE__ */ jsx10(Text10, { children: "Analyzing config..." });
|
|
3280
3618
|
}
|
|
3281
3619
|
if (!result) return null;
|
|
3282
3620
|
if (!result.migrated && result.added.length === 0 && result.deprecated.length === 0) {
|
|
3283
|
-
return /* @__PURE__ */
|
|
3621
|
+
return /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsx10(Text10, { color: "green", children: "\u2713 Config is already up to date." }) });
|
|
3284
3622
|
}
|
|
3285
|
-
return /* @__PURE__ */
|
|
3286
|
-
dryRun && /* @__PURE__ */
|
|
3287
|
-
result.added.length > 0 && /* @__PURE__ */
|
|
3288
|
-
/* @__PURE__ */
|
|
3289
|
-
result.added.map((field, i) => /* @__PURE__ */
|
|
3623
|
+
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
|
|
3624
|
+
dryRun && /* @__PURE__ */ jsx10(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "yellow", bold: true, children: "[dry-run] Preview only \u2014 no changes written." }) }),
|
|
3625
|
+
result.added.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
|
|
3626
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "New fields (added with defaults):" }),
|
|
3627
|
+
result.added.map((field, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
3290
3628
|
" ",
|
|
3291
|
-
/* @__PURE__ */
|
|
3629
|
+
/* @__PURE__ */ jsx10(Text10, { color: "green", children: "+" }),
|
|
3292
3630
|
" ",
|
|
3293
3631
|
field
|
|
3294
3632
|
] }, i))
|
|
3295
3633
|
] }),
|
|
3296
|
-
result.deprecated.length > 0 && /* @__PURE__ */
|
|
3297
|
-
/* @__PURE__ */
|
|
3298
|
-
result.deprecated.map((field, i) => /* @__PURE__ */
|
|
3634
|
+
result.deprecated.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: result.added.length > 0 ? 1 : 0, children: [
|
|
3635
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "Deprecated fields (commented out):" }),
|
|
3636
|
+
result.deprecated.map((field, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
3299
3637
|
" ",
|
|
3300
|
-
/* @__PURE__ */
|
|
3638
|
+
/* @__PURE__ */ jsx10(Text10, { color: "yellow", children: "~" }),
|
|
3301
3639
|
" ",
|
|
3302
3640
|
field
|
|
3303
3641
|
] }, i))
|
|
3304
3642
|
] }),
|
|
3305
|
-
result.preserved.length > 0 && /* @__PURE__ */
|
|
3306
|
-
/* @__PURE__ */
|
|
3643
|
+
result.preserved.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
3644
|
+
/* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
|
|
3307
3645
|
"Preserved fields (",
|
|
3308
3646
|
result.preserved.length,
|
|
3309
3647
|
"):"
|
|
3310
3648
|
] }),
|
|
3311
|
-
result.preserved.map((field, i) => /* @__PURE__ */
|
|
3649
|
+
result.preserved.map((field, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
3312
3650
|
" ",
|
|
3313
|
-
/* @__PURE__ */
|
|
3651
|
+
/* @__PURE__ */ jsx10(Text10, { color: "blue", children: "\u2022" }),
|
|
3314
3652
|
" ",
|
|
3315
3653
|
field
|
|
3316
3654
|
] }, i))
|
|
3317
3655
|
] }),
|
|
3318
|
-
result.migrated && /* @__PURE__ */
|
|
3319
|
-
/* @__PURE__ */
|
|
3320
|
-
result.backupPath && /* @__PURE__ */
|
|
3656
|
+
result.migrated && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
3657
|
+
/* @__PURE__ */ jsx10(Text10, { color: "green", children: "\u2713 Migration complete." }),
|
|
3658
|
+
result.backupPath && /* @__PURE__ */ jsxs10(Text10, { children: [
|
|
3321
3659
|
" ",
|
|
3322
3660
|
"Backup saved to: ",
|
|
3323
3661
|
result.backupPath
|
|
@@ -3339,50 +3677,50 @@ function registerMigrateCommand(program2) {
|
|
|
3339
3677
|
}
|
|
3340
3678
|
return;
|
|
3341
3679
|
}
|
|
3342
|
-
const { waitUntilExit } =
|
|
3343
|
-
/* @__PURE__ */
|
|
3680
|
+
const { waitUntilExit } = render7(
|
|
3681
|
+
/* @__PURE__ */ jsx10(MigrateView, { dryRun: opts.dryRun ?? false })
|
|
3344
3682
|
);
|
|
3345
3683
|
await waitUntilExit();
|
|
3346
3684
|
});
|
|
3347
3685
|
}
|
|
3348
3686
|
|
|
3349
3687
|
// src/commands/Provision.tsx
|
|
3350
|
-
import { Box as
|
|
3351
|
-
import { render as
|
|
3352
|
-
import { useEffect as
|
|
3688
|
+
import { Box as Box10, Text as Text12, useApp as useApp7 } from "ink";
|
|
3689
|
+
import { render as render8 } from "ink";
|
|
3690
|
+
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
3353
3691
|
|
|
3354
3692
|
// src/components/StepRunner.tsx
|
|
3355
|
-
import { Box as
|
|
3693
|
+
import { Box as Box9, Static as Static2, Text as Text11 } from "ink";
|
|
3356
3694
|
import Spinner2 from "ink-spinner";
|
|
3357
|
-
import { jsx as
|
|
3695
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3358
3696
|
var StepIcon = ({ status }) => {
|
|
3359
3697
|
switch (status) {
|
|
3360
3698
|
case "success":
|
|
3361
|
-
return /* @__PURE__ */
|
|
3699
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713" });
|
|
3362
3700
|
case "running":
|
|
3363
|
-
return /* @__PURE__ */
|
|
3701
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }) });
|
|
3364
3702
|
case "skipped":
|
|
3365
|
-
return /* @__PURE__ */
|
|
3703
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "blue", children: "\u23ED" });
|
|
3366
3704
|
case "failed":
|
|
3367
|
-
return /* @__PURE__ */
|
|
3705
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "red", children: "\u2717" });
|
|
3368
3706
|
case "pending":
|
|
3369
3707
|
default:
|
|
3370
|
-
return /* @__PURE__ */
|
|
3708
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "gray", children: "\u25CB" });
|
|
3371
3709
|
}
|
|
3372
3710
|
};
|
|
3373
3711
|
var StepStatusText = ({ step }) => {
|
|
3374
3712
|
switch (step.status) {
|
|
3375
3713
|
case "success":
|
|
3376
|
-
return /* @__PURE__ */
|
|
3714
|
+
return /* @__PURE__ */ jsxs11(Text11, { color: "green", children: [
|
|
3377
3715
|
"Done",
|
|
3378
3716
|
step.duration != null ? ` (${Math.round(step.duration / 1e3)}s)` : ""
|
|
3379
3717
|
] });
|
|
3380
3718
|
case "running":
|
|
3381
|
-
return /* @__PURE__ */
|
|
3719
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "Running..." });
|
|
3382
3720
|
case "skipped":
|
|
3383
|
-
return /* @__PURE__ */
|
|
3721
|
+
return /* @__PURE__ */ jsx11(Text11, { color: "blue", children: "Skipped (already installed)" });
|
|
3384
3722
|
case "failed":
|
|
3385
|
-
return /* @__PURE__ */
|
|
3723
|
+
return /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
|
|
3386
3724
|
"Failed",
|
|
3387
3725
|
step.error ? `: ${step.error}` : ""
|
|
3388
3726
|
] });
|
|
@@ -3396,13 +3734,13 @@ var StepItemView = ({
|
|
|
3396
3734
|
index,
|
|
3397
3735
|
total,
|
|
3398
3736
|
isLast
|
|
3399
|
-
}) => /* @__PURE__ */
|
|
3400
|
-
/* @__PURE__ */
|
|
3737
|
+
}) => /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginBottom: isLast ? 0 : 1, children: [
|
|
3738
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
3401
3739
|
" ",
|
|
3402
|
-
/* @__PURE__ */
|
|
3403
|
-
/* @__PURE__ */
|
|
3740
|
+
/* @__PURE__ */ jsx11(StepIcon, { status: step.status }),
|
|
3741
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
3404
3742
|
" ",
|
|
3405
|
-
/* @__PURE__ */
|
|
3743
|
+
/* @__PURE__ */ jsxs11(Text11, { bold: true, children: [
|
|
3406
3744
|
"Step ",
|
|
3407
3745
|
index + 1,
|
|
3408
3746
|
"/",
|
|
@@ -3412,13 +3750,13 @@ var StepItemView = ({
|
|
|
3412
3750
|
step.name
|
|
3413
3751
|
] })
|
|
3414
3752
|
] }),
|
|
3415
|
-
step.output && step.status !== "pending" && /* @__PURE__ */
|
|
3753
|
+
step.output && step.status !== "pending" && /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
3416
3754
|
" ",
|
|
3417
3755
|
step.output
|
|
3418
3756
|
] }),
|
|
3419
|
-
/* @__PURE__ */
|
|
3757
|
+
/* @__PURE__ */ jsxs11(Text11, { children: [
|
|
3420
3758
|
" ",
|
|
3421
|
-
/* @__PURE__ */
|
|
3759
|
+
/* @__PURE__ */ jsx11(StepStatusText, { step })
|
|
3422
3760
|
] })
|
|
3423
3761
|
] });
|
|
3424
3762
|
var StepRunner = ({ steps, total }) => {
|
|
@@ -3433,8 +3771,8 @@ var StepRunner = ({ steps, total }) => {
|
|
|
3433
3771
|
}
|
|
3434
3772
|
});
|
|
3435
3773
|
const lastIdx = steps.length - 1;
|
|
3436
|
-
return /* @__PURE__ */
|
|
3437
|
-
/* @__PURE__ */
|
|
3774
|
+
return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
|
|
3775
|
+
/* @__PURE__ */ jsx11(Static2, { items: completedSteps, children: (item) => /* @__PURE__ */ jsx11(
|
|
3438
3776
|
StepItemView,
|
|
3439
3777
|
{
|
|
3440
3778
|
step: item,
|
|
@@ -3444,7 +3782,7 @@ var StepRunner = ({ steps, total }) => {
|
|
|
3444
3782
|
},
|
|
3445
3783
|
item.idx
|
|
3446
3784
|
) }),
|
|
3447
|
-
activeSteps.map((item) => /* @__PURE__ */
|
|
3785
|
+
activeSteps.map((item) => /* @__PURE__ */ jsx11(
|
|
3448
3786
|
StepItemView,
|
|
3449
3787
|
{
|
|
3450
3788
|
step: item,
|
|
@@ -3490,26 +3828,26 @@ ${pc2.red("\u2717")} Sudo authentication failed or was cancelled. Aborting.`
|
|
|
3490
3828
|
}
|
|
3491
3829
|
|
|
3492
3830
|
// src/commands/Provision.tsx
|
|
3493
|
-
import { jsx as
|
|
3831
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3494
3832
|
var ProvisionView = ({
|
|
3495
3833
|
template,
|
|
3496
3834
|
templatePath,
|
|
3497
3835
|
options
|
|
3498
3836
|
}) => {
|
|
3499
|
-
const { exit } =
|
|
3500
|
-
const [phase, setPhase] =
|
|
3837
|
+
const { exit } = useApp7();
|
|
3838
|
+
const [phase, setPhase] = useState8(
|
|
3501
3839
|
options.dryRun ? "done" : "running"
|
|
3502
3840
|
);
|
|
3503
|
-
const [steps, setSteps] =
|
|
3841
|
+
const [steps, setSteps] = useState8(
|
|
3504
3842
|
template.steps.map((s) => ({
|
|
3505
3843
|
name: s.name,
|
|
3506
3844
|
status: "pending",
|
|
3507
3845
|
output: s.description
|
|
3508
3846
|
}))
|
|
3509
3847
|
);
|
|
3510
|
-
const [currentStep, setCurrentStep] =
|
|
3511
|
-
const [error, setError] =
|
|
3512
|
-
|
|
3848
|
+
const [currentStep, setCurrentStep] = useState8(0);
|
|
3849
|
+
const [error, setError] = useState8(null);
|
|
3850
|
+
useEffect7(() => {
|
|
3513
3851
|
if (options.dryRun) {
|
|
3514
3852
|
setTimeout(() => exit(), 100);
|
|
3515
3853
|
return;
|
|
@@ -3551,7 +3889,7 @@ var ProvisionView = ({
|
|
|
3551
3889
|
})();
|
|
3552
3890
|
}, []);
|
|
3553
3891
|
if (phase === "error" || error) {
|
|
3554
|
-
return /* @__PURE__ */
|
|
3892
|
+
return /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
|
|
3555
3893
|
"\u2717 ",
|
|
3556
3894
|
error
|
|
3557
3895
|
] }) });
|
|
@@ -3559,27 +3897,27 @@ var ProvisionView = ({
|
|
|
3559
3897
|
const successCount = steps.filter((s) => s.status === "success").length;
|
|
3560
3898
|
const skippedCount = steps.filter((s) => s.status === "skipped").length;
|
|
3561
3899
|
const failedCount = steps.filter((s) => s.status === "failed").length;
|
|
3562
|
-
return /* @__PURE__ */
|
|
3563
|
-
/* @__PURE__ */
|
|
3564
|
-
/* @__PURE__ */
|
|
3900
|
+
return /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
|
|
3901
|
+
/* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
3902
|
+
/* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
|
|
3565
3903
|
"\u25B8 ",
|
|
3566
3904
|
template.name
|
|
3567
3905
|
] }),
|
|
3568
|
-
template.description && /* @__PURE__ */
|
|
3906
|
+
template.description && /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
|
|
3569
3907
|
" ",
|
|
3570
3908
|
template.description
|
|
3571
3909
|
] })
|
|
3572
3910
|
] }),
|
|
3573
|
-
options.dryRun && phase === "done" && /* @__PURE__ */
|
|
3574
|
-
/* @__PURE__ */
|
|
3575
|
-
template.sudo && /* @__PURE__ */
|
|
3911
|
+
options.dryRun && phase === "done" && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
|
|
3912
|
+
/* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "(dry-run) Showing execution plan only" }),
|
|
3913
|
+
template.sudo && /* @__PURE__ */ jsxs12(Text12, { color: "yellow", children: [
|
|
3576
3914
|
" ",
|
|
3577
3915
|
"\u26A0 This template requires sudo privileges (will prompt on actual run)"
|
|
3578
3916
|
] }),
|
|
3579
|
-
/* @__PURE__ */
|
|
3580
|
-
/* @__PURE__ */
|
|
3917
|
+
/* @__PURE__ */ jsx12(Box10, { flexDirection: "column", marginTop: 1, children: template.steps.map((step, idx) => /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
|
|
3918
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3581
3919
|
" ",
|
|
3582
|
-
/* @__PURE__ */
|
|
3920
|
+
/* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
|
|
3583
3921
|
"Step ",
|
|
3584
3922
|
idx + 1,
|
|
3585
3923
|
"/",
|
|
@@ -3588,18 +3926,18 @@ var ProvisionView = ({
|
|
|
3588
3926
|
" ",
|
|
3589
3927
|
step.name
|
|
3590
3928
|
] }),
|
|
3591
|
-
step.description && /* @__PURE__ */
|
|
3929
|
+
step.description && /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
|
|
3592
3930
|
" ",
|
|
3593
3931
|
step.description
|
|
3594
3932
|
] }),
|
|
3595
|
-
step.skip_if && /* @__PURE__ */
|
|
3933
|
+
step.skip_if && /* @__PURE__ */ jsxs12(Text12, { color: "blue", children: [
|
|
3596
3934
|
" ",
|
|
3597
3935
|
"Skip condition: ",
|
|
3598
3936
|
step.skip_if
|
|
3599
3937
|
] })
|
|
3600
3938
|
] }, idx)) })
|
|
3601
3939
|
] }),
|
|
3602
|
-
(phase === "running" || phase === "done" && !options.dryRun) && /* @__PURE__ */
|
|
3940
|
+
(phase === "running" || phase === "done" && !options.dryRun) && /* @__PURE__ */ jsx12(
|
|
3603
3941
|
StepRunner,
|
|
3604
3942
|
{
|
|
3605
3943
|
steps,
|
|
@@ -3607,34 +3945,34 @@ var ProvisionView = ({
|
|
|
3607
3945
|
total: template.steps.length
|
|
3608
3946
|
}
|
|
3609
3947
|
),
|
|
3610
|
-
phase === "done" && !options.dryRun && /* @__PURE__ */
|
|
3611
|
-
/* @__PURE__ */
|
|
3948
|
+
phase === "done" && !options.dryRun && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
3949
|
+
/* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
|
|
3612
3950
|
" ",
|
|
3613
3951
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
3614
3952
|
] }),
|
|
3615
|
-
/* @__PURE__ */
|
|
3953
|
+
/* @__PURE__ */ jsxs12(Text12, { children: [
|
|
3616
3954
|
" ",
|
|
3617
3955
|
"Result: ",
|
|
3618
|
-
/* @__PURE__ */
|
|
3956
|
+
/* @__PURE__ */ jsxs12(Text12, { color: "green", children: [
|
|
3619
3957
|
successCount,
|
|
3620
3958
|
" succeeded"
|
|
3621
3959
|
] }),
|
|
3622
3960
|
" \xB7",
|
|
3623
3961
|
" ",
|
|
3624
|
-
/* @__PURE__ */
|
|
3962
|
+
/* @__PURE__ */ jsxs12(Text12, { color: "blue", children: [
|
|
3625
3963
|
skippedCount,
|
|
3626
3964
|
" skipped"
|
|
3627
3965
|
] }),
|
|
3628
3966
|
" \xB7",
|
|
3629
3967
|
" ",
|
|
3630
|
-
/* @__PURE__ */
|
|
3968
|
+
/* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
|
|
3631
3969
|
failedCount,
|
|
3632
3970
|
" failed"
|
|
3633
3971
|
] })
|
|
3634
3972
|
] }),
|
|
3635
|
-
template.backup && !options.skipRestore && /* @__PURE__ */
|
|
3636
|
-
/* @__PURE__ */
|
|
3637
|
-
/* @__PURE__ */
|
|
3973
|
+
template.backup && !options.skipRestore && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
3974
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, children: "\u25B8 Proceeding with config file restore..." }),
|
|
3975
|
+
/* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
|
|
3638
3976
|
" ",
|
|
3639
3977
|
"Backup link: ",
|
|
3640
3978
|
template.backup
|
|
@@ -3727,8 +4065,8 @@ function registerProvisionCommand(program2) {
|
|
|
3727
4065
|
}
|
|
3728
4066
|
return;
|
|
3729
4067
|
}
|
|
3730
|
-
const { waitUntilExit } =
|
|
3731
|
-
/* @__PURE__ */
|
|
4068
|
+
const { waitUntilExit } = render8(
|
|
4069
|
+
/* @__PURE__ */ jsx12(
|
|
3732
4070
|
ProvisionView,
|
|
3733
4071
|
{
|
|
3734
4072
|
template: tmpl,
|
|
@@ -3746,21 +4084,21 @@ function registerProvisionCommand(program2) {
|
|
|
3746
4084
|
}
|
|
3747
4085
|
|
|
3748
4086
|
// src/commands/Restore.tsx
|
|
3749
|
-
import { Box as
|
|
3750
|
-
import { render as
|
|
4087
|
+
import { Box as Box11, Text as Text13, useApp as useApp8 } from "ink";
|
|
4088
|
+
import { render as render9 } from "ink";
|
|
3751
4089
|
import SelectInput2 from "ink-select-input";
|
|
3752
|
-
import { useEffect as
|
|
3753
|
-
import { jsx as
|
|
4090
|
+
import { useEffect as useEffect8, useState as useState9 } from "react";
|
|
4091
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3754
4092
|
var RestoreView = ({ filename, options }) => {
|
|
3755
|
-
const { exit } =
|
|
3756
|
-
const [phase, setPhase] =
|
|
3757
|
-
const [backups, setBackups] =
|
|
3758
|
-
const [selectedPath, setSelectedPath] =
|
|
3759
|
-
const [plan, setPlan] =
|
|
3760
|
-
const [result, setResult] =
|
|
3761
|
-
const [safetyDone, setSafetyDone] =
|
|
3762
|
-
const [error, setError] =
|
|
3763
|
-
|
|
4093
|
+
const { exit } = useApp8();
|
|
4094
|
+
const [phase, setPhase] = useState9("loading");
|
|
4095
|
+
const [backups, setBackups] = useState9([]);
|
|
4096
|
+
const [selectedPath, setSelectedPath] = useState9(null);
|
|
4097
|
+
const [plan, setPlan] = useState9(null);
|
|
4098
|
+
const [result, setResult] = useState9(null);
|
|
4099
|
+
const [safetyDone, setSafetyDone] = useState9(false);
|
|
4100
|
+
const [error, setError] = useState9(null);
|
|
4101
|
+
useEffect8(() => {
|
|
3764
4102
|
(async () => {
|
|
3765
4103
|
try {
|
|
3766
4104
|
const config = await loadConfig();
|
|
@@ -3794,7 +4132,7 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3794
4132
|
}
|
|
3795
4133
|
})();
|
|
3796
4134
|
}, []);
|
|
3797
|
-
|
|
4135
|
+
useEffect8(() => {
|
|
3798
4136
|
if (phase !== "planning" || !selectedPath) return;
|
|
3799
4137
|
(async () => {
|
|
3800
4138
|
try {
|
|
@@ -3842,7 +4180,7 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3842
4180
|
}
|
|
3843
4181
|
};
|
|
3844
4182
|
if (phase === "error" || error) {
|
|
3845
|
-
return /* @__PURE__ */
|
|
4183
|
+
return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
|
|
3846
4184
|
"\u2717 ",
|
|
3847
4185
|
error
|
|
3848
4186
|
] }) });
|
|
@@ -3853,30 +4191,30 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3853
4191
|
}));
|
|
3854
4192
|
const currentHostname = getHostname();
|
|
3855
4193
|
const isRemoteBackup = plan?.metadata.hostname && plan.metadata.hostname !== currentHostname;
|
|
3856
|
-
return /* @__PURE__ */
|
|
3857
|
-
phase === "loading" && /* @__PURE__ */
|
|
3858
|
-
phase === "selecting" && /* @__PURE__ */
|
|
3859
|
-
/* @__PURE__ */
|
|
3860
|
-
/* @__PURE__ */
|
|
4194
|
+
return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
4195
|
+
phase === "loading" && /* @__PURE__ */ jsx13(Text13, { children: "\u25B8 Loading backup list..." }),
|
|
4196
|
+
phase === "selecting" && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
4197
|
+
/* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Select backup" }),
|
|
4198
|
+
/* @__PURE__ */ jsx13(SelectInput2, { items: selectItems, onSelect: handleSelect })
|
|
3861
4199
|
] }),
|
|
3862
|
-
(phase === "planning" || phase === "confirming" || phase === "restoring" || phase === "done" && plan) && plan && /* @__PURE__ */
|
|
3863
|
-
/* @__PURE__ */
|
|
3864
|
-
/* @__PURE__ */
|
|
4200
|
+
(phase === "planning" || phase === "confirming" || phase === "restoring" || phase === "done" && plan) && plan && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
4201
|
+
/* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginBottom: 1, children: [
|
|
4202
|
+
/* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
|
|
3865
4203
|
"\u25B8 Metadata (",
|
|
3866
4204
|
plan.metadata.config.filename ?? "",
|
|
3867
4205
|
")"
|
|
3868
4206
|
] }),
|
|
3869
|
-
/* @__PURE__ */
|
|
4207
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3870
4208
|
" ",
|
|
3871
4209
|
"Host: ",
|
|
3872
4210
|
plan.metadata.hostname
|
|
3873
4211
|
] }),
|
|
3874
|
-
/* @__PURE__ */
|
|
4212
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3875
4213
|
" ",
|
|
3876
4214
|
"Created: ",
|
|
3877
4215
|
plan.metadata.createdAt
|
|
3878
4216
|
] }),
|
|
3879
|
-
/* @__PURE__ */
|
|
4217
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3880
4218
|
" ",
|
|
3881
4219
|
"Files: ",
|
|
3882
4220
|
plan.metadata.summary.fileCount,
|
|
@@ -3884,15 +4222,15 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3884
4222
|
formatBytes(plan.metadata.summary.totalSize),
|
|
3885
4223
|
")"
|
|
3886
4224
|
] }),
|
|
3887
|
-
isRemoteBackup && /* @__PURE__ */
|
|
4225
|
+
isRemoteBackup && /* @__PURE__ */ jsxs13(Text13, { color: "yellow", children: [
|
|
3888
4226
|
" ",
|
|
3889
4227
|
"\u26A0 This backup was created on a different machine (",
|
|
3890
4228
|
plan.metadata.hostname,
|
|
3891
4229
|
")"
|
|
3892
4230
|
] })
|
|
3893
4231
|
] }),
|
|
3894
|
-
/* @__PURE__ */
|
|
3895
|
-
/* @__PURE__ */
|
|
4232
|
+
/* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginBottom: 1, children: [
|
|
4233
|
+
/* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Restore plan:" }),
|
|
3896
4234
|
plan.actions.map((action, idx) => {
|
|
3897
4235
|
let icon;
|
|
3898
4236
|
let color;
|
|
@@ -3914,45 +4252,45 @@ var RestoreView = ({ filename, options }) => {
|
|
|
3914
4252
|
label = "(not present)";
|
|
3915
4253
|
break;
|
|
3916
4254
|
}
|
|
3917
|
-
return /* @__PURE__ */
|
|
4255
|
+
return /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3918
4256
|
" ",
|
|
3919
|
-
/* @__PURE__ */
|
|
4257
|
+
/* @__PURE__ */ jsx13(Text13, { color, children: icon.padEnd(8) }),
|
|
3920
4258
|
" ",
|
|
3921
4259
|
contractTilde(action.path),
|
|
3922
4260
|
" ",
|
|
3923
|
-
/* @__PURE__ */
|
|
4261
|
+
/* @__PURE__ */ jsx13(Text13, { color: "gray", children: label })
|
|
3924
4262
|
] }, idx);
|
|
3925
4263
|
})
|
|
3926
4264
|
] }),
|
|
3927
|
-
options.dryRun && phase === "done" && /* @__PURE__ */
|
|
4265
|
+
options.dryRun && phase === "done" && /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: "(dry-run) No actual restore was performed" })
|
|
3928
4266
|
] }),
|
|
3929
|
-
phase === "confirming" && /* @__PURE__ */
|
|
3930
|
-
phase === "restoring" && /* @__PURE__ */
|
|
3931
|
-
safetyDone && /* @__PURE__ */
|
|
3932
|
-
/* @__PURE__ */
|
|
4267
|
+
phase === "confirming" && /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx13(Confirm, { message: "Proceed with restore?", onConfirm: handleConfirm }) }),
|
|
4268
|
+
phase === "restoring" && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
|
|
4269
|
+
safetyDone && /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
4270
|
+
/* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
|
|
3933
4271
|
" Safety backup of current files complete"
|
|
3934
4272
|
] }),
|
|
3935
|
-
/* @__PURE__ */
|
|
4273
|
+
/* @__PURE__ */ jsx13(Text13, { children: "\u25B8 Restoring..." })
|
|
3936
4274
|
] }),
|
|
3937
|
-
phase === "done" && result && !options.dryRun && /* @__PURE__ */
|
|
3938
|
-
safetyDone && /* @__PURE__ */
|
|
3939
|
-
/* @__PURE__ */
|
|
4275
|
+
phase === "done" && result && !options.dryRun && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
|
|
4276
|
+
safetyDone && /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
4277
|
+
/* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
|
|
3940
4278
|
" Safety backup of current files complete"
|
|
3941
4279
|
] }),
|
|
3942
|
-
/* @__PURE__ */
|
|
3943
|
-
/* @__PURE__ */
|
|
4280
|
+
/* @__PURE__ */ jsx13(Text13, { color: "green", bold: true, children: "\u2713 Restore complete" }),
|
|
4281
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3944
4282
|
" ",
|
|
3945
4283
|
"Restored: ",
|
|
3946
4284
|
result.restoredFiles.length,
|
|
3947
4285
|
" files"
|
|
3948
4286
|
] }),
|
|
3949
|
-
/* @__PURE__ */
|
|
4287
|
+
/* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3950
4288
|
" ",
|
|
3951
4289
|
"Skipped: ",
|
|
3952
4290
|
result.skippedFiles.length,
|
|
3953
4291
|
" files"
|
|
3954
4292
|
] }),
|
|
3955
|
-
result.safetyBackupPath && /* @__PURE__ */
|
|
4293
|
+
result.safetyBackupPath && /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
3956
4294
|
" ",
|
|
3957
4295
|
"Safety backup: ",
|
|
3958
4296
|
contractTilde(result.safetyBackupPath)
|
|
@@ -4008,8 +4346,8 @@ function registerRestoreCommand(program2) {
|
|
|
4008
4346
|
}
|
|
4009
4347
|
return;
|
|
4010
4348
|
}
|
|
4011
|
-
const { waitUntilExit } =
|
|
4012
|
-
/* @__PURE__ */
|
|
4349
|
+
const { waitUntilExit } = render9(
|
|
4350
|
+
/* @__PURE__ */ jsx13(RestoreView, { filename, options: { dryRun: opts.dryRun } })
|
|
4013
4351
|
);
|
|
4014
4352
|
await waitUntilExit();
|
|
4015
4353
|
});
|
|
@@ -4017,12 +4355,12 @@ function registerRestoreCommand(program2) {
|
|
|
4017
4355
|
|
|
4018
4356
|
// src/commands/Status.tsx
|
|
4019
4357
|
import { readdirSync, statSync, unlinkSync as unlinkSync2 } from "fs";
|
|
4020
|
-
import { join as
|
|
4021
|
-
import { Box as
|
|
4022
|
-
import { render as
|
|
4358
|
+
import { join as join13 } from "path";
|
|
4359
|
+
import { Box as Box12, Text as Text14, useApp as useApp9, useInput as useInput3 } from "ink";
|
|
4360
|
+
import { render as render10 } from "ink";
|
|
4023
4361
|
import SelectInput3 from "ink-select-input";
|
|
4024
|
-
import { useEffect as
|
|
4025
|
-
import { jsx as
|
|
4362
|
+
import { useEffect as useEffect9, useState as useState10 } from "react";
|
|
4363
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4026
4364
|
function getDirStats(dirPath) {
|
|
4027
4365
|
try {
|
|
4028
4366
|
const entries = readdirSync(dirPath);
|
|
@@ -4030,9 +4368,9 @@ function getDirStats(dirPath) {
|
|
|
4030
4368
|
let count = 0;
|
|
4031
4369
|
for (const entry of entries) {
|
|
4032
4370
|
try {
|
|
4033
|
-
const
|
|
4034
|
-
if (
|
|
4035
|
-
totalSize +=
|
|
4371
|
+
const stat5 = statSync(join13(dirPath, entry));
|
|
4372
|
+
if (stat5.isFile()) {
|
|
4373
|
+
totalSize += stat5.size;
|
|
4036
4374
|
count++;
|
|
4037
4375
|
}
|
|
4038
4376
|
} catch {
|
|
@@ -4044,34 +4382,34 @@ function getDirStats(dirPath) {
|
|
|
4044
4382
|
}
|
|
4045
4383
|
}
|
|
4046
4384
|
var DisplayActionItem = ({ isSelected = false, label }) => {
|
|
4047
|
-
return /* @__PURE__ */
|
|
4385
|
+
return /* @__PURE__ */ jsx14(Text14, { bold: isSelected, children: label });
|
|
4048
4386
|
};
|
|
4049
4387
|
var CleanupActionItem = ({ isSelected = false, label }) => {
|
|
4050
4388
|
if (label === "Cancel" || label === "Select specific backups to delete") {
|
|
4051
|
-
return /* @__PURE__ */
|
|
4389
|
+
return /* @__PURE__ */ jsx14(Text14, { bold: isSelected, children: label });
|
|
4052
4390
|
}
|
|
4053
4391
|
const parts = label.split(/\s{2,}/);
|
|
4054
4392
|
if (parts.length === 2) {
|
|
4055
|
-
return /* @__PURE__ */
|
|
4393
|
+
return /* @__PURE__ */ jsxs14(Text14, { bold: isSelected, children: [
|
|
4056
4394
|
parts[0],
|
|
4057
4395
|
" ",
|
|
4058
|
-
/* @__PURE__ */
|
|
4396
|
+
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: parts[1] })
|
|
4059
4397
|
] });
|
|
4060
4398
|
}
|
|
4061
|
-
return /* @__PURE__ */
|
|
4399
|
+
return /* @__PURE__ */ jsx14(Text14, { bold: isSelected, children: label });
|
|
4062
4400
|
};
|
|
4063
4401
|
var StatusView = ({ cleanup }) => {
|
|
4064
|
-
const { exit } =
|
|
4065
|
-
const [phase, setPhase] =
|
|
4066
|
-
const [status, setStatus] =
|
|
4067
|
-
const [backups, setBackups] =
|
|
4068
|
-
const [cleanupAction, setCleanupAction] =
|
|
4069
|
-
const [cleanupMessage, setCleanupMessage] =
|
|
4070
|
-
const [error, setError] =
|
|
4071
|
-
const [selectedForDeletion, setSelectedForDeletion] =
|
|
4402
|
+
const { exit } = useApp9();
|
|
4403
|
+
const [phase, setPhase] = useState10("loading");
|
|
4404
|
+
const [status, setStatus] = useState10(null);
|
|
4405
|
+
const [backups, setBackups] = useState10([]);
|
|
4406
|
+
const [cleanupAction, setCleanupAction] = useState10(null);
|
|
4407
|
+
const [cleanupMessage, setCleanupMessage] = useState10("");
|
|
4408
|
+
const [error, setError] = useState10(null);
|
|
4409
|
+
const [selectedForDeletion, setSelectedForDeletion] = useState10(
|
|
4072
4410
|
[]
|
|
4073
4411
|
);
|
|
4074
|
-
const [backupDir, setBackupDir] =
|
|
4412
|
+
const [backupDir, setBackupDir] = useState10(getSubDir("backups"));
|
|
4075
4413
|
useInput3((_input, key) => {
|
|
4076
4414
|
if (!key.escape) return;
|
|
4077
4415
|
if (phase === "display") {
|
|
@@ -4083,7 +4421,7 @@ var StatusView = ({ cleanup }) => {
|
|
|
4083
4421
|
setPhase("cleanup");
|
|
4084
4422
|
}
|
|
4085
4423
|
});
|
|
4086
|
-
|
|
4424
|
+
useEffect9(() => {
|
|
4087
4425
|
(async () => {
|
|
4088
4426
|
try {
|
|
4089
4427
|
const config = await loadConfig();
|
|
@@ -4182,7 +4520,7 @@ var StatusView = ({ cleanup }) => {
|
|
|
4182
4520
|
try {
|
|
4183
4521
|
const entries = readdirSync(logsDir);
|
|
4184
4522
|
for (const entry of entries) {
|
|
4185
|
-
const logPath =
|
|
4523
|
+
const logPath = join13(logsDir, entry);
|
|
4186
4524
|
if (!isInsideDir(logPath, logsDir))
|
|
4187
4525
|
throw new Error(
|
|
4188
4526
|
`Refusing to delete file outside logs directory: ${logPath}`
|
|
@@ -4232,26 +4570,26 @@ var StatusView = ({ cleanup }) => {
|
|
|
4232
4570
|
}
|
|
4233
4571
|
};
|
|
4234
4572
|
if (phase === "error" || error) {
|
|
4235
|
-
return /* @__PURE__ */
|
|
4573
|
+
return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs14(Text14, { color: "red", children: [
|
|
4236
4574
|
"\u2717 ",
|
|
4237
4575
|
error
|
|
4238
4576
|
] }) });
|
|
4239
4577
|
}
|
|
4240
4578
|
if (phase === "loading") {
|
|
4241
|
-
return /* @__PURE__ */
|
|
4579
|
+
return /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "Loading..." });
|
|
4242
4580
|
}
|
|
4243
4581
|
if (!status) return null;
|
|
4244
4582
|
const totalCount = status.backups.count + status.templates.count + status.scripts.count + status.logs.count;
|
|
4245
4583
|
const totalSize = status.backups.totalSize + status.templates.totalSize + status.scripts.totalSize + status.logs.totalSize;
|
|
4246
|
-
const statusDisplay = /* @__PURE__ */
|
|
4247
|
-
/* @__PURE__ */
|
|
4584
|
+
const statusDisplay = /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4585
|
+
/* @__PURE__ */ jsxs14(Text14, { bold: true, children: [
|
|
4248
4586
|
"\u25B8 ",
|
|
4249
4587
|
APP_NAME,
|
|
4250
4588
|
" status \u2014 ~/.",
|
|
4251
4589
|
APP_NAME,
|
|
4252
4590
|
"/"
|
|
4253
4591
|
] }),
|
|
4254
|
-
/* @__PURE__ */
|
|
4592
|
+
/* @__PURE__ */ jsx14(Box12, { marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
4255
4593
|
Table,
|
|
4256
4594
|
{
|
|
4257
4595
|
headers: ["Directory", "Count", "Size"],
|
|
@@ -4280,15 +4618,15 @@ var StatusView = ({ cleanup }) => {
|
|
|
4280
4618
|
]
|
|
4281
4619
|
}
|
|
4282
4620
|
) }),
|
|
4283
|
-
status.lastBackup && /* @__PURE__ */
|
|
4284
|
-
/* @__PURE__ */
|
|
4621
|
+
status.lastBackup && /* @__PURE__ */ jsxs14(Box12, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
|
|
4622
|
+
/* @__PURE__ */ jsxs14(Text14, { children: [
|
|
4285
4623
|
"Latest backup: ",
|
|
4286
4624
|
formatDate(status.lastBackup),
|
|
4287
4625
|
" (",
|
|
4288
4626
|
formatRelativeTime(status.lastBackup),
|
|
4289
4627
|
")"
|
|
4290
4628
|
] }),
|
|
4291
|
-
status.oldestBackup && /* @__PURE__ */
|
|
4629
|
+
status.oldestBackup && /* @__PURE__ */ jsxs14(Text14, { children: [
|
|
4292
4630
|
"Oldest backup: ",
|
|
4293
4631
|
formatDate(status.oldestBackup),
|
|
4294
4632
|
" (",
|
|
@@ -4297,18 +4635,18 @@ var StatusView = ({ cleanup }) => {
|
|
|
4297
4635
|
] })
|
|
4298
4636
|
] })
|
|
4299
4637
|
] });
|
|
4300
|
-
const escHint = (action) => /* @__PURE__ */
|
|
4638
|
+
const escHint = (action) => /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
4301
4639
|
"Press ",
|
|
4302
|
-
/* @__PURE__ */
|
|
4640
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "ESC" }),
|
|
4303
4641
|
" to ",
|
|
4304
4642
|
action
|
|
4305
4643
|
] }) });
|
|
4306
4644
|
if (phase === "display") {
|
|
4307
|
-
return /* @__PURE__ */
|
|
4645
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4308
4646
|
statusDisplay,
|
|
4309
|
-
/* @__PURE__ */
|
|
4310
|
-
/* @__PURE__ */
|
|
4311
|
-
/* @__PURE__ */
|
|
4647
|
+
/* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
4648
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "\u25B8 Actions" }),
|
|
4649
|
+
/* @__PURE__ */ jsx14(
|
|
4312
4650
|
SelectInput3,
|
|
4313
4651
|
{
|
|
4314
4652
|
items: [
|
|
@@ -4356,11 +4694,11 @@ var StatusView = ({ cleanup }) => {
|
|
|
4356
4694
|
value: "cancel"
|
|
4357
4695
|
}
|
|
4358
4696
|
];
|
|
4359
|
-
return /* @__PURE__ */
|
|
4697
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4360
4698
|
statusDisplay,
|
|
4361
|
-
/* @__PURE__ */
|
|
4362
|
-
/* @__PURE__ */
|
|
4363
|
-
/* @__PURE__ */
|
|
4699
|
+
/* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
4700
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "\u25B8 Cleanup options" }),
|
|
4701
|
+
/* @__PURE__ */ jsx14(
|
|
4364
4702
|
SelectInput3,
|
|
4365
4703
|
{
|
|
4366
4704
|
items: cleanupItems,
|
|
@@ -4386,26 +4724,26 @@ var StatusView = ({ cleanup }) => {
|
|
|
4386
4724
|
value: "done"
|
|
4387
4725
|
}
|
|
4388
4726
|
];
|
|
4389
|
-
return /* @__PURE__ */
|
|
4727
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4390
4728
|
statusDisplay,
|
|
4391
|
-
/* @__PURE__ */
|
|
4392
|
-
/* @__PURE__ */
|
|
4393
|
-
selectedForDeletion.length > 0 && /* @__PURE__ */
|
|
4729
|
+
/* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
4730
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "\u25B8 Select backups to delete" }),
|
|
4731
|
+
selectedForDeletion.length > 0 && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
4394
4732
|
" ",
|
|
4395
4733
|
selectedForDeletion.length,
|
|
4396
4734
|
" backup(s) selected (",
|
|
4397
4735
|
formatBytes(selectedForDeletion.reduce((s, b) => s + b.size, 0)),
|
|
4398
4736
|
")"
|
|
4399
4737
|
] }),
|
|
4400
|
-
/* @__PURE__ */
|
|
4738
|
+
/* @__PURE__ */ jsx14(SelectInput3, { items: selectItems, onSelect: handleSelectBackup })
|
|
4401
4739
|
] }),
|
|
4402
4740
|
escHint("go back")
|
|
4403
4741
|
] });
|
|
4404
4742
|
}
|
|
4405
4743
|
if (phase === "confirming") {
|
|
4406
|
-
return /* @__PURE__ */
|
|
4407
|
-
/* @__PURE__ */
|
|
4408
|
-
/* @__PURE__ */
|
|
4744
|
+
return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
|
|
4745
|
+
/* @__PURE__ */ jsx14(Text14, { children: cleanupMessage }),
|
|
4746
|
+
/* @__PURE__ */ jsx14(
|
|
4409
4747
|
Confirm,
|
|
4410
4748
|
{
|
|
4411
4749
|
message: "Proceed?",
|
|
@@ -4416,7 +4754,7 @@ var StatusView = ({ cleanup }) => {
|
|
|
4416
4754
|
] });
|
|
4417
4755
|
}
|
|
4418
4756
|
if (phase === "done") {
|
|
4419
|
-
return /* @__PURE__ */
|
|
4757
|
+
return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx14(Text14, { color: "green", children: "\u2713 Cleanup complete" }) });
|
|
4420
4758
|
}
|
|
4421
4759
|
return null;
|
|
4422
4760
|
};
|
|
@@ -4450,7 +4788,93 @@ function registerStatusCommand(program2) {
|
|
|
4450
4788
|
}
|
|
4451
4789
|
return;
|
|
4452
4790
|
}
|
|
4453
|
-
const { waitUntilExit } =
|
|
4791
|
+
const { waitUntilExit } = render10(/* @__PURE__ */ jsx14(StatusView, { cleanup: opts.cleanup }));
|
|
4792
|
+
await waitUntilExit();
|
|
4793
|
+
});
|
|
4794
|
+
}
|
|
4795
|
+
|
|
4796
|
+
// src/commands/Unlink.tsx
|
|
4797
|
+
import { Box as Box13, Text as Text15, useApp as useApp10 } from "ink";
|
|
4798
|
+
import { render as render11 } from "ink";
|
|
4799
|
+
import { useEffect as useEffect10, useState as useState11 } from "react";
|
|
4800
|
+
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4801
|
+
var UnlinkView = ({ clean }) => {
|
|
4802
|
+
const { exit } = useApp10();
|
|
4803
|
+
const [phase, setPhase] = useState11("unlinking");
|
|
4804
|
+
const [result, setResult] = useState11(null);
|
|
4805
|
+
const [error, setError] = useState11(null);
|
|
4806
|
+
useEffect10(() => {
|
|
4807
|
+
(async () => {
|
|
4808
|
+
try {
|
|
4809
|
+
const unlinkResult = await unlinkSyncpoint({ clean });
|
|
4810
|
+
setResult(unlinkResult);
|
|
4811
|
+
setPhase("done");
|
|
4812
|
+
setTimeout(() => exit(), 100);
|
|
4813
|
+
} catch (err) {
|
|
4814
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
4815
|
+
setPhase("error");
|
|
4816
|
+
setTimeout(() => exit(), 100);
|
|
4817
|
+
}
|
|
4818
|
+
})();
|
|
4819
|
+
}, []);
|
|
4820
|
+
if (phase === "error" || error) {
|
|
4821
|
+
return /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text15, { color: "red", children: [
|
|
4822
|
+
"\u2717 Unlink failed: ",
|
|
4823
|
+
error
|
|
4824
|
+
] }) });
|
|
4825
|
+
}
|
|
4826
|
+
if (phase === "unlinking") {
|
|
4827
|
+
return /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { children: "\u25B8 Restoring ~/.syncpoint from destination..." }) });
|
|
4828
|
+
}
|
|
4829
|
+
return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
|
|
4830
|
+
/* @__PURE__ */ jsx15(Text15, { color: "green", bold: true, children: "\u2713 Unlink complete" }),
|
|
4831
|
+
result && /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
4832
|
+
/* @__PURE__ */ jsxs15(Text15, { children: [
|
|
4833
|
+
" ",
|
|
4834
|
+
"Restored: ",
|
|
4835
|
+
contractTilde(result.appDir)
|
|
4836
|
+
] }),
|
|
4837
|
+
/* @__PURE__ */ jsxs15(Text15, { children: [
|
|
4838
|
+
" ",
|
|
4839
|
+
"Source: ",
|
|
4840
|
+
contractTilde(result.targetDir)
|
|
4841
|
+
] }),
|
|
4842
|
+
result.cleaned && /* @__PURE__ */ jsxs15(Text15, { color: "yellow", children: [
|
|
4843
|
+
" ",
|
|
4844
|
+
"Destination copy removed (--clean)"
|
|
4845
|
+
] })
|
|
4846
|
+
] })
|
|
4847
|
+
] });
|
|
4848
|
+
};
|
|
4849
|
+
function registerUnlinkCommand(program2) {
|
|
4850
|
+
const cmdInfo = COMMANDS.unlink;
|
|
4851
|
+
const cmd = program2.command("unlink").description(cmdInfo.description);
|
|
4852
|
+
cmdInfo.options?.forEach((opt) => {
|
|
4853
|
+
cmd.option(opt.flag, opt.description);
|
|
4854
|
+
});
|
|
4855
|
+
cmd.action(async (opts) => {
|
|
4856
|
+
const globalOpts = program2.opts();
|
|
4857
|
+
const startTime = Date.now();
|
|
4858
|
+
if (globalOpts.json) {
|
|
4859
|
+
try {
|
|
4860
|
+
const unlinkResult = await unlinkSyncpoint({ clean: opts.clean });
|
|
4861
|
+
respond(
|
|
4862
|
+
"unlink",
|
|
4863
|
+
{
|
|
4864
|
+
appDir: unlinkResult.appDir,
|
|
4865
|
+
targetDir: unlinkResult.targetDir,
|
|
4866
|
+
cleaned: unlinkResult.cleaned
|
|
4867
|
+
},
|
|
4868
|
+
startTime,
|
|
4869
|
+
VERSION
|
|
4870
|
+
);
|
|
4871
|
+
} catch (error) {
|
|
4872
|
+
const code = classifyError(error);
|
|
4873
|
+
respondError("unlink", code, error.message, startTime, VERSION);
|
|
4874
|
+
}
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
const { waitUntilExit } = render11(/* @__PURE__ */ jsx15(UnlinkView, { clean: opts.clean ?? false }));
|
|
4454
4878
|
await waitUntilExit();
|
|
4455
4879
|
});
|
|
4456
4880
|
}
|
|
@@ -4459,15 +4883,15 @@ function registerStatusCommand(program2) {
|
|
|
4459
4883
|
import {
|
|
4460
4884
|
copyFile as copyFile3,
|
|
4461
4885
|
readFile as readFile6,
|
|
4462
|
-
rename,
|
|
4463
|
-
unlink,
|
|
4886
|
+
rename as rename2,
|
|
4887
|
+
unlink as unlink2,
|
|
4464
4888
|
writeFile as writeFile5
|
|
4465
4889
|
} from "fs/promises";
|
|
4466
|
-
import { join as
|
|
4467
|
-
import { Box as
|
|
4468
|
-
import { render as
|
|
4890
|
+
import { join as join15 } from "path";
|
|
4891
|
+
import { Box as Box14, Text as Text16, useApp as useApp11 } from "ink";
|
|
4892
|
+
import { render as render12 } from "ink";
|
|
4469
4893
|
import Spinner3 from "ink-spinner";
|
|
4470
|
-
import { useEffect as
|
|
4894
|
+
import { useEffect as useEffect11, useState as useState12 } from "react";
|
|
4471
4895
|
|
|
4472
4896
|
// src/prompts/wizard-config.ts
|
|
4473
4897
|
function generateConfigWizardPrompt(variables) {
|
|
@@ -4511,8 +4935,8 @@ ${variables.defaultConfig}
|
|
|
4511
4935
|
init_assets();
|
|
4512
4936
|
|
|
4513
4937
|
// src/utils/file-scanner.ts
|
|
4514
|
-
import { stat as
|
|
4515
|
-
import { join as
|
|
4938
|
+
import { stat as stat4 } from "fs/promises";
|
|
4939
|
+
import { join as join14 } from "path";
|
|
4516
4940
|
import glob from "fast-glob";
|
|
4517
4941
|
var FILE_CATEGORIES = {
|
|
4518
4942
|
shell: {
|
|
@@ -4591,8 +5015,8 @@ async function scanHomeDirectory(options) {
|
|
|
4591
5015
|
const validFiles = [];
|
|
4592
5016
|
for (const file of files) {
|
|
4593
5017
|
try {
|
|
4594
|
-
const fullPath =
|
|
4595
|
-
await
|
|
5018
|
+
const fullPath = join14(homeDir, file);
|
|
5019
|
+
await stat4(fullPath);
|
|
4596
5020
|
validFiles.push(file);
|
|
4597
5021
|
categorizedFiles.add(file);
|
|
4598
5022
|
} catch {
|
|
@@ -4626,8 +5050,8 @@ async function scanHomeDirectory(options) {
|
|
|
4626
5050
|
if (categorizedFiles.has(file)) continue;
|
|
4627
5051
|
if (totalFiles >= maxFiles) break;
|
|
4628
5052
|
try {
|
|
4629
|
-
const fullPath =
|
|
4630
|
-
await
|
|
5053
|
+
const fullPath = join14(homeDir, file);
|
|
5054
|
+
await stat4(fullPath);
|
|
4631
5055
|
uncategorizedFiles.push(file);
|
|
4632
5056
|
totalFiles++;
|
|
4633
5057
|
} catch {
|
|
@@ -4651,7 +5075,7 @@ async function scanHomeDirectory(options) {
|
|
|
4651
5075
|
}
|
|
4652
5076
|
|
|
4653
5077
|
// src/commands/Wizard.tsx
|
|
4654
|
-
import { jsx as
|
|
5078
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4655
5079
|
var MAX_RETRIES2 = 3;
|
|
4656
5080
|
async function restoreBackup2(configPath) {
|
|
4657
5081
|
const bakPath = `${configPath}.bak`;
|
|
@@ -4701,21 +5125,21 @@ ${formatValidationErrors(validation.errors || [])}`
|
|
|
4701
5125
|
}
|
|
4702
5126
|
}
|
|
4703
5127
|
var WizardView = ({ printMode }) => {
|
|
4704
|
-
const { exit } =
|
|
4705
|
-
const [phase, setPhase] =
|
|
4706
|
-
const [message, setMessage] =
|
|
4707
|
-
const [error, setError] =
|
|
4708
|
-
const [prompt, setPrompt] =
|
|
4709
|
-
const [sessionId, setSessionId] =
|
|
4710
|
-
const [attemptNumber, setAttemptNumber] =
|
|
4711
|
-
|
|
5128
|
+
const { exit } = useApp11();
|
|
5129
|
+
const [phase, setPhase] = useState12("init");
|
|
5130
|
+
const [message, setMessage] = useState12("");
|
|
5131
|
+
const [error, setError] = useState12(null);
|
|
5132
|
+
const [prompt, setPrompt] = useState12("");
|
|
5133
|
+
const [sessionId, setSessionId] = useState12(void 0);
|
|
5134
|
+
const [attemptNumber, setAttemptNumber] = useState12(1);
|
|
5135
|
+
useEffect11(() => {
|
|
4712
5136
|
(async () => {
|
|
4713
5137
|
try {
|
|
4714
|
-
const configPath =
|
|
5138
|
+
const configPath = join15(getAppDir(), CONFIG_FILENAME);
|
|
4715
5139
|
if (await fileExists(configPath)) {
|
|
4716
5140
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4717
5141
|
const bakPath = `${configPath}.${timestamp2}.bak`;
|
|
4718
|
-
await
|
|
5142
|
+
await rename2(configPath, bakPath);
|
|
4719
5143
|
setMessage(`Backed up existing config to ${bakPath}`);
|
|
4720
5144
|
}
|
|
4721
5145
|
setPhase("scanning");
|
|
@@ -4781,9 +5205,9 @@ var WizardView = ({ printMode }) => {
|
|
|
4781
5205
|
await writeFile5(tmpPath, yamlContent, "utf-8");
|
|
4782
5206
|
const verification = validateConfig(parseYAML(yamlContent));
|
|
4783
5207
|
if (verification.valid) {
|
|
4784
|
-
await
|
|
5208
|
+
await rename2(tmpPath, configPath);
|
|
4785
5209
|
} else {
|
|
4786
|
-
await
|
|
5210
|
+
await unlink2(tmpPath);
|
|
4787
5211
|
throw new Error("Final validation failed");
|
|
4788
5212
|
}
|
|
4789
5213
|
setPhase("done");
|
|
@@ -4822,37 +5246,37 @@ ${formatValidationErrors(validation.errors || [])}`
|
|
|
4822
5246
|
}
|
|
4823
5247
|
}
|
|
4824
5248
|
if (error) {
|
|
4825
|
-
return /* @__PURE__ */
|
|
5249
|
+
return /* @__PURE__ */ jsx16(Box14, { flexDirection: "column", children: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
|
|
4826
5250
|
"\u2717 ",
|
|
4827
5251
|
error
|
|
4828
5252
|
] }) });
|
|
4829
5253
|
}
|
|
4830
5254
|
if (printMode && phase === "done") {
|
|
4831
|
-
return /* @__PURE__ */
|
|
4832
|
-
/* @__PURE__ */
|
|
4833
|
-
/* @__PURE__ */
|
|
4834
|
-
/* @__PURE__ */
|
|
4835
|
-
/* @__PURE__ */
|
|
4836
|
-
/* @__PURE__ */
|
|
5255
|
+
return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
|
|
5256
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Config Wizard Prompt (Copy and paste to your LLM):" }),
|
|
5257
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u2500".repeat(60) }) }),
|
|
5258
|
+
/* @__PURE__ */ jsx16(Text16, { children: prompt }),
|
|
5259
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u2500".repeat(60) }) }),
|
|
5260
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "After getting the YAML response, save it to ~/.syncpoint/config.yml" })
|
|
4837
5261
|
] });
|
|
4838
5262
|
}
|
|
4839
5263
|
if (phase === "done") {
|
|
4840
|
-
return /* @__PURE__ */
|
|
4841
|
-
/* @__PURE__ */
|
|
4842
|
-
/* @__PURE__ */
|
|
4843
|
-
/* @__PURE__ */
|
|
4844
|
-
/* @__PURE__ */
|
|
4845
|
-
/* @__PURE__ */
|
|
5264
|
+
return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
|
|
5265
|
+
/* @__PURE__ */ jsx16(Text16, { color: "green", children: message }),
|
|
5266
|
+
/* @__PURE__ */ jsxs16(Box14, { marginTop: 1, children: [
|
|
5267
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Next steps:" }),
|
|
5268
|
+
/* @__PURE__ */ jsx16(Text16, { children: " 1. Review your config: ~/.syncpoint/config.yml" }),
|
|
5269
|
+
/* @__PURE__ */ jsx16(Text16, { children: " 2. Run: syncpoint backup" })
|
|
4846
5270
|
] })
|
|
4847
5271
|
] });
|
|
4848
5272
|
}
|
|
4849
|
-
return /* @__PURE__ */
|
|
4850
|
-
/* @__PURE__ */
|
|
4851
|
-
/* @__PURE__ */
|
|
5273
|
+
return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
|
|
5274
|
+
/* @__PURE__ */ jsxs16(Text16, { children: [
|
|
5275
|
+
/* @__PURE__ */ jsx16(Text16, { color: "cyan", children: /* @__PURE__ */ jsx16(Spinner3, { type: "dots" }) }),
|
|
4852
5276
|
" ",
|
|
4853
5277
|
message
|
|
4854
5278
|
] }),
|
|
4855
|
-
attemptNumber > 1 && /* @__PURE__ */
|
|
5279
|
+
attemptNumber > 1 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
4856
5280
|
"Attempt ",
|
|
4857
5281
|
attemptNumber,
|
|
4858
5282
|
"/",
|
|
@@ -4889,17 +5313,17 @@ function registerWizardCommand(program2) {
|
|
|
4889
5313
|
return;
|
|
4890
5314
|
}
|
|
4891
5315
|
if (opts.print) {
|
|
4892
|
-
const { waitUntilExit } =
|
|
5316
|
+
const { waitUntilExit } = render12(/* @__PURE__ */ jsx16(WizardView, { printMode: true }));
|
|
4893
5317
|
await waitUntilExit();
|
|
4894
5318
|
return;
|
|
4895
5319
|
}
|
|
4896
|
-
const configPath =
|
|
5320
|
+
const configPath = join15(getAppDir(), CONFIG_FILENAME);
|
|
4897
5321
|
try {
|
|
4898
5322
|
if (await fileExists(configPath)) {
|
|
4899
5323
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4900
5324
|
const bakPath = `${configPath}.${timestamp2}.bak`;
|
|
4901
5325
|
console.log(`\u{1F4CB} Backing up existing config to ${bakPath}`);
|
|
4902
|
-
await
|
|
5326
|
+
await rename2(configPath, bakPath);
|
|
4903
5327
|
}
|
|
4904
5328
|
if (!await isClaudeCodeAvailable()) {
|
|
4905
5329
|
throw new Error(
|
|
@@ -4934,15 +5358,37 @@ registerCreateTemplateCommand(program);
|
|
|
4934
5358
|
registerListCommand(program);
|
|
4935
5359
|
registerMigrateCommand(program);
|
|
4936
5360
|
registerStatusCommand(program);
|
|
5361
|
+
registerLinkCommand(program);
|
|
5362
|
+
registerUnlinkCommand(program);
|
|
4937
5363
|
registerHelpCommand(program);
|
|
4938
5364
|
if (process.argv.includes("--describe")) {
|
|
4939
5365
|
const startTime = Date.now();
|
|
4940
5366
|
const globalOptions = [
|
|
4941
|
-
{
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
5367
|
+
{
|
|
5368
|
+
flag: "--json",
|
|
5369
|
+
description: "Output structured JSON to stdout",
|
|
5370
|
+
type: "boolean"
|
|
5371
|
+
},
|
|
5372
|
+
{
|
|
5373
|
+
flag: "--yes",
|
|
5374
|
+
description: "Skip confirmation prompts (non-interactive mode)",
|
|
5375
|
+
type: "boolean"
|
|
5376
|
+
},
|
|
5377
|
+
{
|
|
5378
|
+
flag: "--describe",
|
|
5379
|
+
description: "Print CLI schema as JSON and exit",
|
|
5380
|
+
type: "boolean"
|
|
5381
|
+
},
|
|
5382
|
+
{
|
|
5383
|
+
flag: "-V, --version",
|
|
5384
|
+
description: "Output the version number",
|
|
5385
|
+
type: "boolean"
|
|
5386
|
+
},
|
|
5387
|
+
{
|
|
5388
|
+
flag: "-h, --help",
|
|
5389
|
+
description: "Display help for command",
|
|
5390
|
+
type: "boolean"
|
|
5391
|
+
}
|
|
4946
5392
|
];
|
|
4947
5393
|
respond(
|
|
4948
5394
|
"describe",
|
|
@@ -4960,7 +5406,13 @@ if (process.argv.includes("--describe")) {
|
|
|
4960
5406
|
}
|
|
4961
5407
|
program.parseAsync(process.argv).catch((error) => {
|
|
4962
5408
|
if (process.argv.includes("--json")) {
|
|
4963
|
-
respondError(
|
|
5409
|
+
respondError(
|
|
5410
|
+
"unknown",
|
|
5411
|
+
SyncpointErrorCode.UNKNOWN,
|
|
5412
|
+
error.message,
|
|
5413
|
+
Date.now(),
|
|
5414
|
+
VERSION
|
|
5415
|
+
);
|
|
4964
5416
|
process.exit(1);
|
|
4965
5417
|
}
|
|
4966
5418
|
console.error("Fatal error:", error.message);
|