@lumy-pack/syncpoint 0.0.3 → 0.0.4
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 +120 -104
- package/dist/constants.d.ts +0 -1
- package/dist/index.cjs +11 -7
- package/dist/index.mjs +12 -8
- package/dist/utils/types.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +5 -6
package/dist/cli.mjs
CHANGED
|
@@ -193,7 +193,7 @@ function generateFilename(pattern, options) {
|
|
|
193
193
|
import { appendFile, mkdir as mkdir2 } from "fs/promises";
|
|
194
194
|
import { join as join3 } from "path";
|
|
195
195
|
import pc from "picocolors";
|
|
196
|
-
var ANSI_RE =
|
|
196
|
+
var ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
197
197
|
function stripAnsi(str) {
|
|
198
198
|
return str.replace(ANSI_RE, "");
|
|
199
199
|
}
|
|
@@ -334,7 +334,7 @@ function isValidPattern(pattern) {
|
|
|
334
334
|
|
|
335
335
|
// src/core/metadata.ts
|
|
336
336
|
import { createHash } from "crypto";
|
|
337
|
-
import { lstat, readFile } from "fs/promises";
|
|
337
|
+
import { lstat, readFile, readlink } from "fs/promises";
|
|
338
338
|
|
|
339
339
|
// src/schemas/ajv.ts
|
|
340
340
|
import Ajv from "ajv";
|
|
@@ -426,7 +426,7 @@ function validateMetadata(data) {
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// src/version.ts
|
|
429
|
-
var VERSION = "0.0.
|
|
429
|
+
var VERSION = "0.0.4";
|
|
430
430
|
|
|
431
431
|
// src/core/metadata.ts
|
|
432
432
|
var METADATA_VERSION = "1.0.0";
|
|
@@ -477,7 +477,8 @@ async function collectFileInfo(absolutePath, logicalPath) {
|
|
|
477
477
|
}
|
|
478
478
|
let hash;
|
|
479
479
|
if (lstats.isSymbolicLink()) {
|
|
480
|
-
|
|
480
|
+
const linkTarget = await readlink(absolutePath);
|
|
481
|
+
hash = `sha256:${createHash("sha256").update(linkTarget).digest("hex")}`;
|
|
481
482
|
} else {
|
|
482
483
|
hash = await computeFileHash(absolutePath);
|
|
483
484
|
}
|
|
@@ -538,7 +539,8 @@ async function extractArchive(archivePath, destDir) {
|
|
|
538
539
|
const normalizedPath = normalize2(path);
|
|
539
540
|
if (normalizedPath.includes("..")) return false;
|
|
540
541
|
if (normalizedPath.startsWith("/")) return false;
|
|
541
|
-
if (entry.type === "SymbolicLink" || entry.type === "Link")
|
|
542
|
+
if ("type" in entry && (entry.type === "SymbolicLink" || entry.type === "Link"))
|
|
543
|
+
return false;
|
|
542
544
|
return true;
|
|
543
545
|
}
|
|
544
546
|
});
|
|
@@ -723,7 +725,7 @@ async function createBackup(config, options = {}) {
|
|
|
723
725
|
}
|
|
724
726
|
}
|
|
725
727
|
let allFiles = [...found];
|
|
726
|
-
if (config.scripts
|
|
728
|
+
if (config.scripts?.includeInBackup) {
|
|
727
729
|
const scripts = await collectScripts();
|
|
728
730
|
allFiles = [...allFiles, ...scripts];
|
|
729
731
|
}
|
|
@@ -1081,6 +1083,7 @@ var BackupView = ({ options }) => {
|
|
|
1081
1083
|
const [error, setError] = useState(null);
|
|
1082
1084
|
useEffect(() => {
|
|
1083
1085
|
(async () => {
|
|
1086
|
+
let progressInterval;
|
|
1084
1087
|
try {
|
|
1085
1088
|
const cfg = await loadConfig();
|
|
1086
1089
|
setConfig(cfg);
|
|
@@ -1093,7 +1096,7 @@ var BackupView = ({ options }) => {
|
|
|
1093
1096
|
return;
|
|
1094
1097
|
}
|
|
1095
1098
|
setPhase("compressing");
|
|
1096
|
-
|
|
1099
|
+
progressInterval = setInterval(() => {
|
|
1097
1100
|
setProgress((prev) => {
|
|
1098
1101
|
if (prev >= 90) return prev;
|
|
1099
1102
|
return prev + 10;
|
|
@@ -1106,9 +1109,10 @@ var BackupView = ({ options }) => {
|
|
|
1106
1109
|
setPhase("done");
|
|
1107
1110
|
setTimeout(() => exit(), 100);
|
|
1108
1111
|
} catch (err) {
|
|
1112
|
+
if (progressInterval) clearInterval(progressInterval);
|
|
1109
1113
|
setError(err instanceof Error ? err.message : String(err));
|
|
1110
1114
|
setPhase("error");
|
|
1111
|
-
exit();
|
|
1115
|
+
setTimeout(() => exit(), 100);
|
|
1112
1116
|
}
|
|
1113
1117
|
})();
|
|
1114
1118
|
}, []);
|
|
@@ -1162,7 +1166,7 @@ var BackupView = ({ options }) => {
|
|
|
1162
1166
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
1163
1167
|
" ",
|
|
1164
1168
|
"File: ",
|
|
1165
|
-
result.
|
|
1169
|
+
result.archivePath.split("/").pop()
|
|
1166
1170
|
] }),
|
|
1167
1171
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
1168
1172
|
" ",
|
|
@@ -1199,8 +1203,8 @@ import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
|
1199
1203
|
import { Text as Text3, Box as Box2, useApp as useApp2 } from "ink";
|
|
1200
1204
|
import Spinner from "ink-spinner";
|
|
1201
1205
|
import { render as render2 } from "ink";
|
|
1202
|
-
import { join as
|
|
1203
|
-
import { writeFile as
|
|
1206
|
+
import { join as join8 } from "path";
|
|
1207
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
1204
1208
|
|
|
1205
1209
|
// src/schemas/template.schema.ts
|
|
1206
1210
|
var templateSchema = {
|
|
@@ -1282,9 +1286,6 @@ Begin by asking the user to describe their provisioning needs.`;
|
|
|
1282
1286
|
|
|
1283
1287
|
// src/utils/claude-code-runner.ts
|
|
1284
1288
|
import { spawn } from "child_process";
|
|
1285
|
-
import { unlink, writeFile as writeFile3 } from "fs/promises";
|
|
1286
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
1287
|
-
import { join as join8 } from "path";
|
|
1288
1289
|
async function isClaudeCodeAvailable() {
|
|
1289
1290
|
return new Promise((resolve2) => {
|
|
1290
1291
|
const child = spawn("which", ["claude"], { shell: true });
|
|
@@ -1298,58 +1299,49 @@ async function isClaudeCodeAvailable() {
|
|
|
1298
1299
|
}
|
|
1299
1300
|
async function invokeClaudeCode(prompt, options) {
|
|
1300
1301
|
const timeout = options?.timeout ?? 12e4;
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1302
|
+
return await new Promise((resolve2, reject) => {
|
|
1303
|
+
const args = ["--permission-mode", "acceptEdits", "--model", "sonnet"];
|
|
1304
|
+
if (options?.sessionId) {
|
|
1305
|
+
args.push("--session", options.sessionId);
|
|
1306
|
+
}
|
|
1307
|
+
const child = spawn("claude", args, {
|
|
1308
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1309
|
+
});
|
|
1310
|
+
let stdout = "";
|
|
1311
|
+
let stderr = "";
|
|
1312
|
+
child.stdout.on("data", (data) => {
|
|
1313
|
+
stdout += data.toString();
|
|
1314
|
+
});
|
|
1315
|
+
child.stderr.on("data", (data) => {
|
|
1316
|
+
stderr += data.toString();
|
|
1317
|
+
});
|
|
1318
|
+
child.stdin.write(prompt);
|
|
1319
|
+
child.stdin.end();
|
|
1320
|
+
const timer = setTimeout(() => {
|
|
1321
|
+
child.kill();
|
|
1322
|
+
reject(new Error(`Claude Code invocation timeout after ${timeout}ms`));
|
|
1323
|
+
}, timeout);
|
|
1324
|
+
child.on("close", (code) => {
|
|
1325
|
+
clearTimeout(timer);
|
|
1326
|
+
if (code === 0) {
|
|
1327
|
+
resolve2({
|
|
1328
|
+
success: true,
|
|
1329
|
+
output: stdout,
|
|
1330
|
+
sessionId: options?.sessionId
|
|
1331
|
+
});
|
|
1332
|
+
} else {
|
|
1333
|
+
resolve2({
|
|
1334
|
+
success: false,
|
|
1335
|
+
output: stdout,
|
|
1336
|
+
error: stderr || `Process exited with code ${code}`
|
|
1337
|
+
});
|
|
1308
1338
|
}
|
|
1309
|
-
const child = spawn("claude", args, {
|
|
1310
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1311
|
-
});
|
|
1312
|
-
let stdout = "";
|
|
1313
|
-
let stderr = "";
|
|
1314
|
-
child.stdout.on("data", (data) => {
|
|
1315
|
-
stdout += data.toString();
|
|
1316
|
-
});
|
|
1317
|
-
child.stderr.on("data", (data) => {
|
|
1318
|
-
stderr += data.toString();
|
|
1319
|
-
});
|
|
1320
|
-
child.stdin.write(prompt);
|
|
1321
|
-
child.stdin.end();
|
|
1322
|
-
const timer = setTimeout(() => {
|
|
1323
|
-
child.kill();
|
|
1324
|
-
reject(new Error(`Claude Code invocation timeout after ${timeout}ms`));
|
|
1325
|
-
}, timeout);
|
|
1326
|
-
child.on("close", (code) => {
|
|
1327
|
-
clearTimeout(timer);
|
|
1328
|
-
if (code === 0) {
|
|
1329
|
-
resolve2({
|
|
1330
|
-
success: true,
|
|
1331
|
-
output: stdout,
|
|
1332
|
-
sessionId: options?.sessionId
|
|
1333
|
-
});
|
|
1334
|
-
} else {
|
|
1335
|
-
resolve2({
|
|
1336
|
-
success: false,
|
|
1337
|
-
output: stdout,
|
|
1338
|
-
error: stderr || `Process exited with code ${code}`
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
child.on("error", (err) => {
|
|
1343
|
-
clearTimeout(timer);
|
|
1344
|
-
reject(err);
|
|
1345
|
-
});
|
|
1346
1339
|
});
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
}
|
|
1340
|
+
child.on("error", (err) => {
|
|
1341
|
+
clearTimeout(timer);
|
|
1342
|
+
reject(err);
|
|
1343
|
+
});
|
|
1344
|
+
});
|
|
1353
1345
|
}
|
|
1354
1346
|
async function resumeClaudeCodeSession(sessionId, prompt, options) {
|
|
1355
1347
|
return invokeClaudeCode(prompt, {
|
|
@@ -1533,14 +1525,14 @@ var CreateTemplateView = ({
|
|
|
1533
1525
|
setPhase("writing");
|
|
1534
1526
|
setMessage("Writing template...");
|
|
1535
1527
|
const filename = templateName ? `${templateName}.yml` : `${parsedTemplate.name.toLowerCase().replace(/\s+/g, "-")}.yml`;
|
|
1536
|
-
const templatePath =
|
|
1528
|
+
const templatePath = join8(templatesDir, filename);
|
|
1537
1529
|
if (await fileExists(templatePath)) {
|
|
1538
1530
|
throw new Error(
|
|
1539
1531
|
`Template already exists: ${filename}
|
|
1540
1532
|
Please choose a different name or delete the existing template.`
|
|
1541
1533
|
);
|
|
1542
1534
|
}
|
|
1543
|
-
await
|
|
1535
|
+
await writeFile3(templatePath, yamlContent, "utf-8");
|
|
1544
1536
|
setPhase("done");
|
|
1545
1537
|
setMessage(`\u2713 Template created: ${filename}`);
|
|
1546
1538
|
setTimeout(() => exit(), 100);
|
|
@@ -1766,7 +1758,7 @@ function registerHelpCommand(program2) {
|
|
|
1766
1758
|
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1767
1759
|
import { Text as Text5, Box as Box4, useApp as useApp3 } from "ink";
|
|
1768
1760
|
import { render as render4 } from "ink";
|
|
1769
|
-
import { join as
|
|
1761
|
+
import { join as join9 } from "path";
|
|
1770
1762
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1771
1763
|
var InitView = () => {
|
|
1772
1764
|
const { exit } = useApp3();
|
|
@@ -1777,7 +1769,7 @@ var InitView = () => {
|
|
|
1777
1769
|
(async () => {
|
|
1778
1770
|
try {
|
|
1779
1771
|
const appDir = getAppDir();
|
|
1780
|
-
if (await fileExists(
|
|
1772
|
+
if (await fileExists(join9(appDir, CONFIG_FILENAME))) {
|
|
1781
1773
|
setError(`Already initialized: ${appDir}`);
|
|
1782
1774
|
exit();
|
|
1783
1775
|
return;
|
|
@@ -1810,11 +1802,11 @@ var InitView = () => {
|
|
|
1810
1802
|
await initDefaultConfig();
|
|
1811
1803
|
completed.push({ name: `Created ${CONFIG_FILENAME} (defaults)`, done: true });
|
|
1812
1804
|
setSteps([...completed]);
|
|
1813
|
-
const exampleTemplatePath =
|
|
1805
|
+
const exampleTemplatePath = join9(getSubDir(TEMPLATES_DIR), "example.yml");
|
|
1814
1806
|
if (!await fileExists(exampleTemplatePath)) {
|
|
1815
|
-
const { writeFile:
|
|
1807
|
+
const { writeFile: writeFile5 } = await import("fs/promises");
|
|
1816
1808
|
const exampleYaml = readAsset("template.example.yml");
|
|
1817
|
-
await
|
|
1809
|
+
await writeFile5(exampleTemplatePath, exampleYaml, "utf-8");
|
|
1818
1810
|
completed.push({ name: `Created templates/example.yml`, done: true });
|
|
1819
1811
|
setSteps([...completed]);
|
|
1820
1812
|
}
|
|
@@ -1946,7 +1938,7 @@ var Table = ({
|
|
|
1946
1938
|
// src/core/provision.ts
|
|
1947
1939
|
import { exec } from "child_process";
|
|
1948
1940
|
import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
1949
|
-
import { join as
|
|
1941
|
+
import { join as join10 } from "path";
|
|
1950
1942
|
import YAML3 from "yaml";
|
|
1951
1943
|
var REMOTE_SCRIPT_PATTERNS = [
|
|
1952
1944
|
/curl\s.*\|\s*(ba)?sh/,
|
|
@@ -1986,7 +1978,7 @@ async function listTemplates() {
|
|
|
1986
1978
|
if (!entry.isFile() || !entry.name.endsWith(".yml") && !entry.name.endsWith(".yaml")) {
|
|
1987
1979
|
continue;
|
|
1988
1980
|
}
|
|
1989
|
-
const fullPath =
|
|
1981
|
+
const fullPath = join10(templatesDir, entry.name);
|
|
1990
1982
|
try {
|
|
1991
1983
|
const config = await loadTemplate(fullPath);
|
|
1992
1984
|
templates.push({
|
|
@@ -2063,8 +2055,10 @@ async function executeStep(step) {
|
|
|
2063
2055
|
output: output || void 0
|
|
2064
2056
|
};
|
|
2065
2057
|
} catch (err) {
|
|
2066
|
-
const error = err;
|
|
2067
|
-
const
|
|
2058
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
2059
|
+
const stdout = err?.stdout ?? "";
|
|
2060
|
+
const stderr = err?.stderr ?? "";
|
|
2061
|
+
const errorOutput = [stdout, stderr, error.message].filter(Boolean).join("\n").trim();
|
|
2068
2062
|
return {
|
|
2069
2063
|
name: step.name,
|
|
2070
2064
|
status: "failed",
|
|
@@ -2103,7 +2097,7 @@ async function* runProvision(templatePath, options = {}) {
|
|
|
2103
2097
|
|
|
2104
2098
|
// src/core/restore.ts
|
|
2105
2099
|
import { copyFile, lstat as lstat2, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
2106
|
-
import { dirname as dirname2, join as
|
|
2100
|
+
import { dirname as dirname2, join as join11 } from "path";
|
|
2107
2101
|
async function getBackupList(config) {
|
|
2108
2102
|
const backupDir = config?.backup.destination ? resolveTargetPath(config.backup.destination) : getSubDir(BACKUPS_DIR);
|
|
2109
2103
|
const exists = await fileExists(backupDir);
|
|
@@ -2112,7 +2106,7 @@ async function getBackupList(config) {
|
|
|
2112
2106
|
const backups = [];
|
|
2113
2107
|
for (const entry of entries) {
|
|
2114
2108
|
if (!entry.isFile() || !entry.name.endsWith(".tar.gz")) continue;
|
|
2115
|
-
const fullPath =
|
|
2109
|
+
const fullPath = join11(backupDir, entry.name);
|
|
2116
2110
|
const fileStat = await stat2(fullPath);
|
|
2117
2111
|
let hostname;
|
|
2118
2112
|
let fileCount;
|
|
@@ -2187,7 +2181,7 @@ async function createSafetyBackup(filePaths) {
|
|
|
2187
2181
|
const filename = `_pre-restore_${formatDatetime(now)}.tar.gz`;
|
|
2188
2182
|
const backupDir = getSubDir(BACKUPS_DIR);
|
|
2189
2183
|
await ensureDir(backupDir);
|
|
2190
|
-
const archivePath =
|
|
2184
|
+
const archivePath = join11(backupDir, filename);
|
|
2191
2185
|
const files = [];
|
|
2192
2186
|
for (const fp of filePaths) {
|
|
2193
2187
|
const absPath = resolveTargetPath(fp);
|
|
@@ -2221,8 +2215,8 @@ async function restoreBackup(archivePath, options = {}) {
|
|
|
2221
2215
|
};
|
|
2222
2216
|
}
|
|
2223
2217
|
const { mkdtemp: mkdtemp2, rm: rm2 } = await import("fs/promises");
|
|
2224
|
-
const { tmpdir:
|
|
2225
|
-
const tmpDir = await mkdtemp2(
|
|
2218
|
+
const { tmpdir: tmpdir2 } = await import("os");
|
|
2219
|
+
const tmpDir = await mkdtemp2(join11(tmpdir2(), "syncpoint-restore-"));
|
|
2226
2220
|
try {
|
|
2227
2221
|
await extractArchive(archivePath, tmpDir);
|
|
2228
2222
|
for (const action of plan.actions) {
|
|
@@ -2231,7 +2225,7 @@ async function restoreBackup(archivePath, options = {}) {
|
|
|
2231
2225
|
continue;
|
|
2232
2226
|
}
|
|
2233
2227
|
const archiveName = action.path.startsWith("~/") ? action.path.slice(2) : action.path;
|
|
2234
|
-
const extractedPath =
|
|
2228
|
+
const extractedPath = join11(tmpDir, archiveName);
|
|
2235
2229
|
const destPath = resolveTargetPath(action.path);
|
|
2236
2230
|
const extractedExists = await fileExists(extractedPath);
|
|
2237
2231
|
if (!extractedExists) {
|
|
@@ -2626,6 +2620,10 @@ var ListView = ({ type, deleteIndex }) => {
|
|
|
2626
2620
|
function registerListCommand(program2) {
|
|
2627
2621
|
program2.command("list [type]").description("List backups and templates").option("--delete <n>", "Delete item #n").action(async (type, opts) => {
|
|
2628
2622
|
const deleteIndex = opts.delete ? parseInt(opts.delete, 10) : void 0;
|
|
2623
|
+
if (deleteIndex !== void 0 && isNaN(deleteIndex)) {
|
|
2624
|
+
console.error(`Invalid delete index: ${opts.delete}`);
|
|
2625
|
+
process.exit(1);
|
|
2626
|
+
}
|
|
2629
2627
|
const { waitUntilExit } = render5(
|
|
2630
2628
|
/* @__PURE__ */ jsx8(ListView, { type, deleteIndex })
|
|
2631
2629
|
);
|
|
@@ -2779,6 +2777,18 @@ var ProvisionView = ({
|
|
|
2779
2777
|
stepIdx++;
|
|
2780
2778
|
}
|
|
2781
2779
|
}
|
|
2780
|
+
if (template.backup && !options.skipRestore) {
|
|
2781
|
+
try {
|
|
2782
|
+
const backups = await getBackupList();
|
|
2783
|
+
const match = backups.find(
|
|
2784
|
+
(b) => b.filename.includes(template.backup)
|
|
2785
|
+
);
|
|
2786
|
+
if (match) {
|
|
2787
|
+
await restoreBackup(match.path);
|
|
2788
|
+
}
|
|
2789
|
+
} catch {
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2782
2792
|
setPhase("done");
|
|
2783
2793
|
setTimeout(() => exit(), 100);
|
|
2784
2794
|
} catch (err) {
|
|
@@ -2951,7 +2961,8 @@ var RestoreView = ({ filename, options }) => {
|
|
|
2951
2961
|
useEffect6(() => {
|
|
2952
2962
|
(async () => {
|
|
2953
2963
|
try {
|
|
2954
|
-
const
|
|
2964
|
+
const config = await loadConfig();
|
|
2965
|
+
const list2 = await getBackupList(config);
|
|
2955
2966
|
setBackups(list2);
|
|
2956
2967
|
if (list2.length === 0) {
|
|
2957
2968
|
setError("No backups available.");
|
|
@@ -3164,7 +3175,7 @@ function registerRestoreCommand(program2) {
|
|
|
3164
3175
|
|
|
3165
3176
|
// src/commands/Status.tsx
|
|
3166
3177
|
import { readdirSync, statSync, unlinkSync as unlinkSync2 } from "fs";
|
|
3167
|
-
import { join as
|
|
3178
|
+
import { join as join12 } from "path";
|
|
3168
3179
|
import { Box as Box10, Text as Text12, useApp as useApp7, useInput as useInput3 } from "ink";
|
|
3169
3180
|
import { render as render8 } from "ink";
|
|
3170
3181
|
import SelectInput3 from "ink-select-input";
|
|
@@ -3177,7 +3188,7 @@ function getDirStats(dirPath) {
|
|
|
3177
3188
|
let count = 0;
|
|
3178
3189
|
for (const entry of entries) {
|
|
3179
3190
|
try {
|
|
3180
|
-
const stat4 = statSync(
|
|
3191
|
+
const stat4 = statSync(join12(dirPath, entry));
|
|
3181
3192
|
if (stat4.isFile()) {
|
|
3182
3193
|
totalSize += stat4.size;
|
|
3183
3194
|
count++;
|
|
@@ -3323,9 +3334,14 @@ var StatusView = ({ cleanup }) => {
|
|
|
3323
3334
|
try {
|
|
3324
3335
|
const entries = readdirSync(logsDir);
|
|
3325
3336
|
for (const entry of entries) {
|
|
3326
|
-
const logPath =
|
|
3337
|
+
const logPath = join12(logsDir, entry);
|
|
3327
3338
|
if (!isInsideDir(logPath, logsDir)) throw new Error(`Refusing to delete file outside logs directory: ${logPath}`);
|
|
3328
|
-
|
|
3339
|
+
try {
|
|
3340
|
+
if (statSync(logPath).isFile()) {
|
|
3341
|
+
unlinkSync2(logPath);
|
|
3342
|
+
}
|
|
3343
|
+
} catch {
|
|
3344
|
+
}
|
|
3329
3345
|
}
|
|
3330
3346
|
} catch {
|
|
3331
3347
|
}
|
|
@@ -3558,8 +3574,8 @@ function registerStatusCommand(program2) {
|
|
|
3558
3574
|
}
|
|
3559
3575
|
|
|
3560
3576
|
// src/commands/Wizard.tsx
|
|
3561
|
-
import { copyFile as copyFile2, readFile as readFile5, rename, unlink
|
|
3562
|
-
import { join as
|
|
3577
|
+
import { copyFile as copyFile2, readFile as readFile5, rename, unlink, writeFile as writeFile4 } from "fs/promises";
|
|
3578
|
+
import { join as join14 } from "path";
|
|
3563
3579
|
import { Box as Box11, Text as Text13, useApp as useApp8 } from "ink";
|
|
3564
3580
|
import { render as render9 } from "ink";
|
|
3565
3581
|
import Spinner3 from "ink-spinner";
|
|
@@ -3605,7 +3621,7 @@ ${variables.defaultConfig}
|
|
|
3605
3621
|
|
|
3606
3622
|
// src/utils/file-scanner.ts
|
|
3607
3623
|
import { stat as stat3 } from "fs/promises";
|
|
3608
|
-
import { join as
|
|
3624
|
+
import { join as join13 } from "path";
|
|
3609
3625
|
import glob from "fast-glob";
|
|
3610
3626
|
var FILE_CATEGORIES = {
|
|
3611
3627
|
shell: {
|
|
@@ -3684,7 +3700,7 @@ async function scanHomeDirectory(options) {
|
|
|
3684
3700
|
const validFiles = [];
|
|
3685
3701
|
for (const file of files) {
|
|
3686
3702
|
try {
|
|
3687
|
-
const fullPath =
|
|
3703
|
+
const fullPath = join13(homeDir, file);
|
|
3688
3704
|
await stat3(fullPath);
|
|
3689
3705
|
validFiles.push(file);
|
|
3690
3706
|
categorizedFiles.add(file);
|
|
@@ -3719,7 +3735,7 @@ async function scanHomeDirectory(options) {
|
|
|
3719
3735
|
if (categorizedFiles.has(file)) continue;
|
|
3720
3736
|
if (totalFiles >= maxFiles) break;
|
|
3721
3737
|
try {
|
|
3722
|
-
const fullPath =
|
|
3738
|
+
const fullPath = join13(homeDir, file);
|
|
3723
3739
|
await stat3(fullPath);
|
|
3724
3740
|
uncategorizedFiles.push(file);
|
|
3725
3741
|
totalFiles++;
|
|
@@ -3730,7 +3746,7 @@ async function scanHomeDirectory(options) {
|
|
|
3730
3746
|
if (uncategorizedFiles.length > 0) {
|
|
3731
3747
|
categories.push({
|
|
3732
3748
|
category: "Other Files",
|
|
3733
|
-
files: uncategorizedFiles.sort()
|
|
3749
|
+
files: uncategorizedFiles.sort()
|
|
3734
3750
|
});
|
|
3735
3751
|
}
|
|
3736
3752
|
} catch {
|
|
@@ -3804,14 +3820,12 @@ var WizardView = ({ printMode }) => {
|
|
|
3804
3820
|
useEffect8(() => {
|
|
3805
3821
|
(async () => {
|
|
3806
3822
|
try {
|
|
3807
|
-
const configPath =
|
|
3823
|
+
const configPath = join14(getAppDir(), CONFIG_FILENAME);
|
|
3808
3824
|
if (await fileExists(configPath)) {
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
);
|
|
3813
|
-
await rename(configPath, `${configPath}.bak`);
|
|
3814
|
-
setMessage(`Backed up existing config to config.yml.bak`);
|
|
3825
|
+
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3826
|
+
const bakPath = `${configPath}.${timestamp2}.bak`;
|
|
3827
|
+
await rename(configPath, bakPath);
|
|
3828
|
+
setMessage(`Backed up existing config to ${bakPath}`);
|
|
3815
3829
|
}
|
|
3816
3830
|
setPhase("scanning");
|
|
3817
3831
|
setMessage("Scanning home directory for backup targets...");
|
|
@@ -3873,12 +3887,12 @@ Would you like to backup and overwrite? (Backup will be saved as config.yml.bak)
|
|
|
3873
3887
|
setPhase("writing");
|
|
3874
3888
|
setMessage("Writing config.yml...");
|
|
3875
3889
|
const tmpPath = `${configPath}.tmp`;
|
|
3876
|
-
await
|
|
3890
|
+
await writeFile4(tmpPath, yamlContent, "utf-8");
|
|
3877
3891
|
const verification = validateConfig(parseYAML(yamlContent));
|
|
3878
3892
|
if (verification.valid) {
|
|
3879
3893
|
await rename(tmpPath, configPath);
|
|
3880
3894
|
} else {
|
|
3881
|
-
await
|
|
3895
|
+
await unlink(tmpPath);
|
|
3882
3896
|
throw new Error("Final validation failed");
|
|
3883
3897
|
}
|
|
3884
3898
|
setPhase("done");
|
|
@@ -3967,11 +3981,13 @@ function registerWizardCommand(program2) {
|
|
|
3967
3981
|
await waitUntilExit();
|
|
3968
3982
|
return;
|
|
3969
3983
|
}
|
|
3970
|
-
const configPath =
|
|
3984
|
+
const configPath = join14(getAppDir(), CONFIG_FILENAME);
|
|
3971
3985
|
try {
|
|
3972
3986
|
if (await fileExists(configPath)) {
|
|
3973
|
-
|
|
3974
|
-
|
|
3987
|
+
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3988
|
+
const bakPath = `${configPath}.${timestamp2}.bak`;
|
|
3989
|
+
console.log(`\u{1F4CB} Backing up existing config to ${bakPath}`);
|
|
3990
|
+
await rename(configPath, bakPath);
|
|
3975
3991
|
}
|
|
3976
3992
|
if (!await isClaudeCodeAvailable()) {
|
|
3977
3993
|
throw new Error(
|
package/dist/constants.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ export declare const METADATA_FILENAME = "_metadata.json";
|
|
|
5
5
|
export declare const LARGE_FILE_THRESHOLD: number;
|
|
6
6
|
export declare const MAX_RETRY = 3;
|
|
7
7
|
export declare const SENSITIVE_PATTERNS: string[];
|
|
8
|
-
export declare const REMOTE_SCRIPT_PATTERN: RegExp;
|
|
9
8
|
export declare const BACKUPS_DIR = "backups";
|
|
10
9
|
export declare const TEMPLATES_DIR = "templates";
|
|
11
10
|
export declare const SCRIPTS_DIR = "scripts";
|
package/dist/index.cjs
CHANGED
|
@@ -429,7 +429,7 @@ function generateFilename(pattern, options) {
|
|
|
429
429
|
var import_promises3 = require("fs/promises");
|
|
430
430
|
var import_node_path5 = require("path");
|
|
431
431
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
432
|
-
var ANSI_RE =
|
|
432
|
+
var ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
433
433
|
function stripAnsi(str) {
|
|
434
434
|
return str.replace(ANSI_RE, "");
|
|
435
435
|
}
|
|
@@ -560,7 +560,7 @@ function validateMetadata(data) {
|
|
|
560
560
|
}
|
|
561
561
|
|
|
562
562
|
// src/version.ts
|
|
563
|
-
var VERSION = "0.0.
|
|
563
|
+
var VERSION = "0.0.4";
|
|
564
564
|
|
|
565
565
|
// src/core/metadata.ts
|
|
566
566
|
var METADATA_VERSION = "1.0.0";
|
|
@@ -611,7 +611,8 @@ async function collectFileInfo(absolutePath, logicalPath) {
|
|
|
611
611
|
}
|
|
612
612
|
let hash;
|
|
613
613
|
if (lstats.isSymbolicLink()) {
|
|
614
|
-
|
|
614
|
+
const linkTarget = await (0, import_promises4.readlink)(absolutePath);
|
|
615
|
+
hash = `sha256:${(0, import_node_crypto.createHash)("sha256").update(linkTarget).digest("hex")}`;
|
|
615
616
|
} else {
|
|
616
617
|
hash = await computeFileHash(absolutePath);
|
|
617
618
|
}
|
|
@@ -672,7 +673,8 @@ async function extractArchive(archivePath, destDir) {
|
|
|
672
673
|
const normalizedPath = (0, import_node_path6.normalize)(path);
|
|
673
674
|
if (normalizedPath.includes("..")) return false;
|
|
674
675
|
if (normalizedPath.startsWith("/")) return false;
|
|
675
|
-
if (entry.type === "SymbolicLink" || entry.type === "Link")
|
|
676
|
+
if ("type" in entry && (entry.type === "SymbolicLink" || entry.type === "Link"))
|
|
677
|
+
return false;
|
|
676
678
|
return true;
|
|
677
679
|
}
|
|
678
680
|
});
|
|
@@ -857,7 +859,7 @@ async function createBackup(config, options = {}) {
|
|
|
857
859
|
}
|
|
858
860
|
}
|
|
859
861
|
let allFiles = [...found];
|
|
860
|
-
if (config.scripts
|
|
862
|
+
if (config.scripts?.includeInBackup) {
|
|
861
863
|
const scripts = await collectScripts();
|
|
862
864
|
allFiles = [...allFiles, ...scripts];
|
|
863
865
|
}
|
|
@@ -1209,8 +1211,10 @@ async function executeStep(step) {
|
|
|
1209
1211
|
output: output || void 0
|
|
1210
1212
|
};
|
|
1211
1213
|
} catch (err) {
|
|
1212
|
-
const error = err;
|
|
1213
|
-
const
|
|
1214
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1215
|
+
const stdout = err?.stdout ?? "";
|
|
1216
|
+
const stderr = err?.stderr ?? "";
|
|
1217
|
+
const errorOutput = [stdout, stderr, error.message].filter(Boolean).join("\n").trim();
|
|
1214
1218
|
return {
|
|
1215
1219
|
name: step.name,
|
|
1216
1220
|
status: "failed",
|
package/dist/index.mjs
CHANGED
|
@@ -379,7 +379,7 @@ function generateFilename(pattern, options) {
|
|
|
379
379
|
import { appendFile, mkdir as mkdir2 } from "fs/promises";
|
|
380
380
|
import { join as join5 } from "path";
|
|
381
381
|
import pc from "picocolors";
|
|
382
|
-
var ANSI_RE =
|
|
382
|
+
var ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
383
383
|
function stripAnsi(str) {
|
|
384
384
|
return str.replace(ANSI_RE, "");
|
|
385
385
|
}
|
|
@@ -433,7 +433,7 @@ var logger = {
|
|
|
433
433
|
|
|
434
434
|
// src/core/metadata.ts
|
|
435
435
|
import { createHash } from "crypto";
|
|
436
|
-
import { lstat, readFile as readFile2 } from "fs/promises";
|
|
436
|
+
import { lstat, readFile as readFile2, readlink } from "fs/promises";
|
|
437
437
|
|
|
438
438
|
// src/schemas/metadata.schema.ts
|
|
439
439
|
var metadataSchema = {
|
|
@@ -510,7 +510,7 @@ function validateMetadata(data) {
|
|
|
510
510
|
}
|
|
511
511
|
|
|
512
512
|
// src/version.ts
|
|
513
|
-
var VERSION = "0.0.
|
|
513
|
+
var VERSION = "0.0.4";
|
|
514
514
|
|
|
515
515
|
// src/core/metadata.ts
|
|
516
516
|
var METADATA_VERSION = "1.0.0";
|
|
@@ -561,7 +561,8 @@ async function collectFileInfo(absolutePath, logicalPath) {
|
|
|
561
561
|
}
|
|
562
562
|
let hash;
|
|
563
563
|
if (lstats.isSymbolicLink()) {
|
|
564
|
-
|
|
564
|
+
const linkTarget = await readlink(absolutePath);
|
|
565
|
+
hash = `sha256:${createHash("sha256").update(linkTarget).digest("hex")}`;
|
|
565
566
|
} else {
|
|
566
567
|
hash = await computeFileHash(absolutePath);
|
|
567
568
|
}
|
|
@@ -622,7 +623,8 @@ async function extractArchive(archivePath, destDir) {
|
|
|
622
623
|
const normalizedPath = normalize2(path);
|
|
623
624
|
if (normalizedPath.includes("..")) return false;
|
|
624
625
|
if (normalizedPath.startsWith("/")) return false;
|
|
625
|
-
if (entry.type === "SymbolicLink" || entry.type === "Link")
|
|
626
|
+
if ("type" in entry && (entry.type === "SymbolicLink" || entry.type === "Link"))
|
|
627
|
+
return false;
|
|
626
628
|
return true;
|
|
627
629
|
}
|
|
628
630
|
});
|
|
@@ -807,7 +809,7 @@ async function createBackup(config, options = {}) {
|
|
|
807
809
|
}
|
|
808
810
|
}
|
|
809
811
|
let allFiles = [...found];
|
|
810
|
-
if (config.scripts
|
|
812
|
+
if (config.scripts?.includeInBackup) {
|
|
811
813
|
const scripts = await collectScripts();
|
|
812
814
|
allFiles = [...allFiles, ...scripts];
|
|
813
815
|
}
|
|
@@ -1159,8 +1161,10 @@ async function executeStep(step) {
|
|
|
1159
1161
|
output: output || void 0
|
|
1160
1162
|
};
|
|
1161
1163
|
} catch (err) {
|
|
1162
|
-
const error = err;
|
|
1163
|
-
const
|
|
1164
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1165
|
+
const stdout = err?.stdout ?? "";
|
|
1166
|
+
const stderr = err?.stderr ?? "";
|
|
1167
|
+
const errorOutput = [stdout, stderr, error.message].filter(Boolean).join("\n").trim();
|
|
1164
1168
|
return {
|
|
1165
1169
|
name: step.name,
|
|
1166
1170
|
status: "failed",
|
package/dist/utils/types.d.ts
CHANGED
package/dist/version.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumy-pack/syncpoint",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "CLI tool for project synchronization and scaffolding",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -55,23 +55,22 @@
|
|
|
55
55
|
"@types/micromatch": "^4.0.9",
|
|
56
56
|
"@types/node": "^20.11.0",
|
|
57
57
|
"@types/react": "^18.0.0",
|
|
58
|
-
"@types/tar": "^6.1.13",
|
|
59
58
|
"@vitest/coverage-v8": "^3.2.4",
|
|
60
59
|
"ink-testing-library": "^4.0.0"
|
|
61
60
|
},
|
|
62
61
|
"scripts": {
|
|
63
62
|
"build": "node scripts/inject-version.js && tsup && pnpm build:types",
|
|
64
|
-
"build:types": "tsc -p ./tsconfig.declarations.json",
|
|
65
63
|
"build:publish:npm": "pnpm build && pnpm publish:npm",
|
|
64
|
+
"build:types": "tsc -p ./tsconfig.declarations.json",
|
|
66
65
|
"dev": "node scripts/inject-version.js && tsx src/cli.ts",
|
|
67
66
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
68
67
|
"lint": "eslint \"src/**/*.ts\"",
|
|
69
68
|
"publish:npm": "pnpm publish --access public --no-git-checks",
|
|
70
69
|
"test": "vitest",
|
|
71
|
-
"test:run": "vitest run",
|
|
72
|
-
"test:e2e": "vitest run --config vitest.e2e.config.ts src/__tests__/e2e/",
|
|
73
|
-
"test:docker": "vitest run --config vitest.e2e.config.ts src/__tests__/docker/",
|
|
74
70
|
"test:all": "vitest run && pnpm test:e2e && pnpm test:docker",
|
|
71
|
+
"test:docker": "vitest run --config vitest.e2e.config.ts src/__tests__/docker/",
|
|
72
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts src/__tests__/e2e/",
|
|
73
|
+
"test:run": "vitest run",
|
|
75
74
|
"version:major": "pnpm version major",
|
|
76
75
|
"version:minor": "pnpm version minor",
|
|
77
76
|
"version:patch": "pnpm version patch"
|