@lumy-pack/syncpoint 0.0.1 → 0.0.2
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/README.md +921 -0
- package/assets/config.default.yml +12 -2
- package/dist/cli.mjs +2750 -1440
- package/dist/commands/Backup.d.ts +1 -1
- package/dist/commands/CreateTemplate.d.ts +2 -0
- package/dist/commands/Help.d.ts +2 -0
- package/dist/commands/Wizard.d.ts +2 -0
- package/dist/constants.d.ts +1 -2
- package/dist/core/backup.d.ts +8 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/metadata.d.ts +1 -1
- package/dist/core/provision.d.ts +1 -1
- package/dist/core/restore.d.ts +1 -1
- package/dist/index.cjs +215 -46
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +219 -50
- package/dist/prompts/wizard-config.d.ts +9 -0
- package/dist/prompts/wizard-template.d.ts +7 -0
- package/dist/schemas/ajv.d.ts +1 -1
- package/dist/utils/claude-code-runner.d.ts +29 -0
- package/dist/utils/command-registry.d.ts +23 -0
- package/dist/utils/error-formatter.d.ts +16 -0
- package/dist/utils/file-scanner.d.ts +25 -0
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/pattern.d.ts +46 -0
- package/dist/utils/types.d.ts +4 -3
- package/dist/utils/yaml-parser.d.ts +19 -0
- package/dist/version.d.ts +5 -0
- package/package.json +5 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Command } from
|
|
1
|
+
import { Command } from 'commander';
|
|
2
2
|
export declare function registerBackupCommand(program: Command): void;
|
package/dist/constants.d.ts
CHANGED
|
@@ -11,6 +11,5 @@ export declare const TEMPLATES_DIR = "templates";
|
|
|
11
11
|
export declare const SCRIPTS_DIR = "scripts";
|
|
12
12
|
export declare const LOGS_DIR = "logs";
|
|
13
13
|
export declare function getAppDir(): string;
|
|
14
|
-
export
|
|
15
|
-
export type SubDirName = "backups" | "templates" | "scripts" | "logs";
|
|
14
|
+
export type SubDirName = 'backups' | 'templates' | 'scripts' | 'logs';
|
|
16
15
|
export declare function getSubDir(sub: SubDirName): string;
|
package/dist/core/backup.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import type { BackupOptions, BackupResult, FileEntry, SyncpointConfig } from
|
|
1
|
+
import type { BackupOptions, BackupResult, FileEntry, SyncpointConfig } from '../utils/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Scan config targets, resolve globs, filter excludes.
|
|
4
4
|
* Returns found FileEntry[] and missing path strings.
|
|
5
|
+
*
|
|
6
|
+
* Supports three pattern types:
|
|
7
|
+
* - Regex: /pattern/ format (e.g., /\.conf$/)
|
|
8
|
+
* - Glob: *.ext, **\/pattern (e.g., ~/.config/*.yml)
|
|
9
|
+
* - Literal: Direct file paths (e.g., ~/.zshrc)
|
|
10
|
+
*
|
|
11
|
+
* Exclude patterns are applied consistently across all target types.
|
|
5
12
|
*/
|
|
6
13
|
export declare function scanTargets(config: SyncpointConfig): Promise<{
|
|
7
14
|
found: FileEntry[];
|
package/dist/core/config.d.ts
CHANGED
package/dist/core/metadata.d.ts
CHANGED
package/dist/core/provision.d.ts
CHANGED
package/dist/core/restore.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BackupInfo, RestoreOptions, RestorePlan, RestoreResult, SyncpointConfig } from
|
|
1
|
+
import type { BackupInfo, RestoreOptions, RestorePlan, RestoreResult, SyncpointConfig } from '../utils/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* List all backup archives in the backup directory, sorted by date desc.
|
|
4
4
|
*/
|
package/dist/index.cjs
CHANGED
|
@@ -99,6 +99,14 @@ async function fileExists(filePath) {
|
|
|
99
99
|
return false;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
+
async function isDirectory(filePath) {
|
|
103
|
+
try {
|
|
104
|
+
const stats = await (0, import_promises.stat)(filePath);
|
|
105
|
+
return stats.isDirectory();
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
102
110
|
|
|
103
111
|
// src/constants.ts
|
|
104
112
|
var APP_NAME = "syncpoint";
|
|
@@ -114,7 +122,6 @@ var LOGS_DIR = "logs";
|
|
|
114
122
|
function getAppDir() {
|
|
115
123
|
return (0, import_node_path2.join)(getHomeDir(), APP_DIR);
|
|
116
124
|
}
|
|
117
|
-
var APP_VERSION = "0.0.1";
|
|
118
125
|
function getSubDir(sub) {
|
|
119
126
|
return (0, import_node_path2.join)(getAppDir(), sub);
|
|
120
127
|
}
|
|
@@ -122,8 +129,106 @@ function getSubDir(sub) {
|
|
|
122
129
|
// src/schemas/ajv.ts
|
|
123
130
|
var import_ajv = __toESM(require("ajv"), 1);
|
|
124
131
|
var import_ajv_formats = __toESM(require("ajv-formats"), 1);
|
|
132
|
+
|
|
133
|
+
// src/utils/pattern.ts
|
|
134
|
+
var import_micromatch = __toESM(require("micromatch"), 1);
|
|
135
|
+
function detectPatternType(pattern) {
|
|
136
|
+
if (pattern.startsWith("/") && pattern.endsWith("/") && pattern.length > 2) {
|
|
137
|
+
const inner = pattern.slice(1, -1);
|
|
138
|
+
const hasUnescapedSlash = /(?<!\\)\//.test(inner);
|
|
139
|
+
if (!hasUnescapedSlash) {
|
|
140
|
+
return "regex";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("{")) {
|
|
144
|
+
return "glob";
|
|
145
|
+
}
|
|
146
|
+
return "literal";
|
|
147
|
+
}
|
|
148
|
+
function parseRegexPattern(pattern) {
|
|
149
|
+
if (!pattern.startsWith("/") || !pattern.endsWith("/")) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Invalid regex pattern format: ${pattern}. Must be enclosed in slashes like /pattern/`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
const regexBody = pattern.slice(1, -1);
|
|
155
|
+
try {
|
|
156
|
+
return new RegExp(regexBody);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Invalid regex pattern: ${pattern}. ${error instanceof Error ? error.message : String(error)}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function createExcludeMatcher(excludePatterns) {
|
|
164
|
+
if (excludePatterns.length === 0) {
|
|
165
|
+
return () => false;
|
|
166
|
+
}
|
|
167
|
+
const regexPatterns = [];
|
|
168
|
+
const globPatterns = [];
|
|
169
|
+
const literalPatterns = /* @__PURE__ */ new Set();
|
|
170
|
+
for (const pattern of excludePatterns) {
|
|
171
|
+
const type = detectPatternType(pattern);
|
|
172
|
+
if (type === "regex") {
|
|
173
|
+
try {
|
|
174
|
+
regexPatterns.push(parseRegexPattern(pattern));
|
|
175
|
+
} catch {
|
|
176
|
+
console.warn(`Skipping invalid regex pattern: ${pattern}`);
|
|
177
|
+
}
|
|
178
|
+
} else if (type === "glob") {
|
|
179
|
+
globPatterns.push(pattern);
|
|
180
|
+
} else {
|
|
181
|
+
literalPatterns.add(pattern);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return (filePath) => {
|
|
185
|
+
if (literalPatterns.has(filePath)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
if (globPatterns.length > 0 && import_micromatch.default.isMatch(filePath, globPatterns, {
|
|
189
|
+
dot: true,
|
|
190
|
+
// Match dotfiles
|
|
191
|
+
matchBase: false
|
|
192
|
+
// Don't use basename matching, match full path
|
|
193
|
+
})) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
for (const regex of regexPatterns) {
|
|
197
|
+
if (regex.test(filePath)) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function isValidPattern(pattern) {
|
|
205
|
+
if (typeof pattern !== "string" || pattern.length === 0) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
const type = detectPatternType(pattern);
|
|
209
|
+
if (type === "regex") {
|
|
210
|
+
try {
|
|
211
|
+
parseRegexPattern(pattern);
|
|
212
|
+
return true;
|
|
213
|
+
} catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/schemas/ajv.ts
|
|
125
221
|
var ajv = new import_ajv.default({ allErrors: true });
|
|
126
222
|
(0, import_ajv_formats.default)(ajv);
|
|
223
|
+
ajv.addKeyword({
|
|
224
|
+
keyword: "validPattern",
|
|
225
|
+
type: "string",
|
|
226
|
+
validate: function validate(schema, data) {
|
|
227
|
+
if (!schema) return true;
|
|
228
|
+
return isValidPattern(data);
|
|
229
|
+
},
|
|
230
|
+
errors: true
|
|
231
|
+
});
|
|
127
232
|
|
|
128
233
|
// src/schemas/config.schema.ts
|
|
129
234
|
var configSchema = {
|
|
@@ -136,11 +241,11 @@ var configSchema = {
|
|
|
136
241
|
properties: {
|
|
137
242
|
targets: {
|
|
138
243
|
type: "array",
|
|
139
|
-
items: { type: "string" }
|
|
244
|
+
items: { type: "string", validPattern: true }
|
|
140
245
|
},
|
|
141
246
|
exclude: {
|
|
142
247
|
type: "array",
|
|
143
|
-
items: { type: "string" }
|
|
248
|
+
items: { type: "string", validPattern: true }
|
|
144
249
|
},
|
|
145
250
|
filename: {
|
|
146
251
|
type: "string",
|
|
@@ -164,11 +269,11 @@ var configSchema = {
|
|
|
164
269
|
},
|
|
165
270
|
additionalProperties: false
|
|
166
271
|
};
|
|
167
|
-
var
|
|
272
|
+
var validate2 = ajv.compile(configSchema);
|
|
168
273
|
function validateConfig(data) {
|
|
169
|
-
const valid =
|
|
274
|
+
const valid = validate2(data);
|
|
170
275
|
if (valid) return { valid: true };
|
|
171
|
-
const errors =
|
|
276
|
+
const errors = validate2.errors?.map(
|
|
172
277
|
(e) => `${e.instancePath || "/"} ${e.message ?? "unknown error"}`
|
|
173
278
|
);
|
|
174
279
|
return { valid: false, errors };
|
|
@@ -220,20 +325,16 @@ Run "syncpoint init" first.`
|
|
|
220
325
|
const data = stripDangerousKeys(import_yaml.default.parse(raw));
|
|
221
326
|
const result = validateConfig(data);
|
|
222
327
|
if (!result.valid) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
|
|
225
|
-
${(result.errors ?? []).join("\n")}`
|
|
226
|
-
);
|
|
328
|
+
throw new Error(`Invalid config:
|
|
329
|
+
${(result.errors ?? []).join("\n")}`);
|
|
227
330
|
}
|
|
228
331
|
return data;
|
|
229
332
|
}
|
|
230
333
|
async function saveConfig(config) {
|
|
231
334
|
const result = validateConfig(config);
|
|
232
335
|
if (!result.valid) {
|
|
233
|
-
throw new Error(
|
|
234
|
-
|
|
235
|
-
${(result.errors ?? []).join("\n")}`
|
|
236
|
-
);
|
|
336
|
+
throw new Error(`Invalid config:
|
|
337
|
+
${(result.errors ?? []).join("\n")}`);
|
|
237
338
|
}
|
|
238
339
|
const configPath = getConfigPath();
|
|
239
340
|
await ensureDir(getAppDir());
|
|
@@ -290,7 +391,7 @@ function getSystemInfo() {
|
|
|
290
391
|
}
|
|
291
392
|
function formatHostname(name) {
|
|
292
393
|
const raw = name ?? getHostname();
|
|
293
|
-
return raw.replace(/\s+/g, "-").replace(/\./g, "-").replace(/[^a-zA-Z0-9
|
|
394
|
+
return raw.replace(/\s+/g, "-").replace(/\./g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
294
395
|
}
|
|
295
396
|
|
|
296
397
|
// src/utils/format.ts
|
|
@@ -448,23 +549,26 @@ var metadataSchema = {
|
|
|
448
549
|
},
|
|
449
550
|
additionalProperties: false
|
|
450
551
|
};
|
|
451
|
-
var
|
|
552
|
+
var validate3 = ajv.compile(metadataSchema);
|
|
452
553
|
function validateMetadata(data) {
|
|
453
|
-
const valid =
|
|
554
|
+
const valid = validate3(data);
|
|
454
555
|
if (valid) return { valid: true };
|
|
455
|
-
const errors =
|
|
556
|
+
const errors = validate3.errors?.map(
|
|
456
557
|
(e) => `${e.instancePath || "/"} ${e.message ?? "unknown error"}`
|
|
457
558
|
);
|
|
458
559
|
return { valid: false, errors };
|
|
459
560
|
}
|
|
460
561
|
|
|
562
|
+
// src/version.ts
|
|
563
|
+
var VERSION = "0.0.2";
|
|
564
|
+
|
|
461
565
|
// src/core/metadata.ts
|
|
462
566
|
var METADATA_VERSION = "1.0.0";
|
|
463
567
|
function createMetadata(files, config) {
|
|
464
568
|
const totalSize = files.reduce((sum, f) => sum + f.size, 0);
|
|
465
569
|
return {
|
|
466
570
|
version: METADATA_VERSION,
|
|
467
|
-
toolVersion:
|
|
571
|
+
toolVersion: VERSION,
|
|
468
572
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
469
573
|
hostname: getHostname(),
|
|
470
574
|
system: getSystemInfo(),
|
|
@@ -484,10 +588,8 @@ function parseMetadata(data) {
|
|
|
484
588
|
const parsed = JSON.parse(str);
|
|
485
589
|
const result = validateMetadata(parsed);
|
|
486
590
|
if (!result.valid) {
|
|
487
|
-
throw new Error(
|
|
488
|
-
|
|
489
|
-
${(result.errors ?? []).join("\n")}`
|
|
490
|
-
);
|
|
591
|
+
throw new Error(`Invalid metadata:
|
|
592
|
+
${(result.errors ?? []).join("\n")}`);
|
|
491
593
|
}
|
|
492
594
|
return parsed;
|
|
493
595
|
}
|
|
@@ -503,6 +605,9 @@ async function collectFileInfo(absolutePath, logicalPath) {
|
|
|
503
605
|
type = "symlink";
|
|
504
606
|
} else if (lstats.isDirectory()) {
|
|
505
607
|
type = "directory";
|
|
608
|
+
throw new Error(
|
|
609
|
+
`Cannot collect file info for directory: ${logicalPath}. Directories should be converted to glob patterns before calling collectFileInfo().`
|
|
610
|
+
);
|
|
506
611
|
}
|
|
507
612
|
let hash;
|
|
508
613
|
if (lstats.isSymbolicLink()) {
|
|
@@ -530,7 +635,10 @@ async function createArchive(files, outputPath) {
|
|
|
530
635
|
const fileNames = [];
|
|
531
636
|
for (const file of files) {
|
|
532
637
|
const targetPath = (0, import_node_path6.join)(tmpDir, file.name);
|
|
533
|
-
const parentDir = (0, import_node_path6.join)(
|
|
638
|
+
const parentDir = (0, import_node_path6.join)(
|
|
639
|
+
tmpDir,
|
|
640
|
+
file.name.split("/").slice(0, -1).join("/")
|
|
641
|
+
);
|
|
534
642
|
if (parentDir !== tmpDir) {
|
|
535
643
|
await (0, import_promises5.mkdir)(parentDir, { recursive: true });
|
|
536
644
|
}
|
|
@@ -607,18 +715,48 @@ function isSensitiveFile(filePath) {
|
|
|
607
715
|
async function scanTargets(config) {
|
|
608
716
|
const found = [];
|
|
609
717
|
const missing = [];
|
|
718
|
+
const isExcluded = createExcludeMatcher(config.backup.exclude);
|
|
610
719
|
for (const target of config.backup.targets) {
|
|
611
720
|
const expanded = expandTilde(target);
|
|
612
|
-
|
|
721
|
+
const patternType = detectPatternType(expanded);
|
|
722
|
+
if (patternType === "regex") {
|
|
723
|
+
try {
|
|
724
|
+
const regex = parseRegexPattern(expanded);
|
|
725
|
+
const homeDir = expandTilde("~/");
|
|
726
|
+
const homeDirNormalized = homeDir.endsWith("/") ? homeDir : `${homeDir}/`;
|
|
727
|
+
const allFiles = await (0, import_fast_glob.default)(`${homeDirNormalized}**`, {
|
|
728
|
+
dot: true,
|
|
729
|
+
absolute: true,
|
|
730
|
+
onlyFiles: true,
|
|
731
|
+
deep: 5
|
|
732
|
+
// Limit depth for performance
|
|
733
|
+
});
|
|
734
|
+
for (const match of allFiles) {
|
|
735
|
+
if (regex.test(match) && !isExcluded(match)) {
|
|
736
|
+
const entry = await collectFileInfo(match, match);
|
|
737
|
+
found.push(entry);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
} catch (error) {
|
|
741
|
+
logger.warn(
|
|
742
|
+
`Invalid regex pattern "${target}": ${error instanceof Error ? error.message : String(error)}`
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
} else if (patternType === "glob") {
|
|
746
|
+
const globExcludes = config.backup.exclude.filter(
|
|
747
|
+
(p) => detectPatternType(p) === "glob"
|
|
748
|
+
);
|
|
613
749
|
const matches = await (0, import_fast_glob.default)(expanded, {
|
|
614
750
|
dot: true,
|
|
615
751
|
absolute: true,
|
|
616
|
-
ignore:
|
|
752
|
+
ignore: globExcludes,
|
|
617
753
|
onlyFiles: true
|
|
618
754
|
});
|
|
619
755
|
for (const match of matches) {
|
|
620
|
-
|
|
621
|
-
|
|
756
|
+
if (!isExcluded(match)) {
|
|
757
|
+
const entry = await collectFileInfo(match, match);
|
|
758
|
+
found.push(entry);
|
|
759
|
+
}
|
|
622
760
|
}
|
|
623
761
|
} else {
|
|
624
762
|
const absPath = resolveTargetPath(target);
|
|
@@ -627,16 +765,43 @@ async function scanTargets(config) {
|
|
|
627
765
|
missing.push(target);
|
|
628
766
|
continue;
|
|
629
767
|
}
|
|
630
|
-
const
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
|
|
768
|
+
const isDir = await isDirectory(absPath);
|
|
769
|
+
if (isDir) {
|
|
770
|
+
const dirGlob = `${expanded}/**/*`;
|
|
771
|
+
const globExcludes = config.backup.exclude.filter(
|
|
772
|
+
(p) => detectPatternType(p) === "glob"
|
|
634
773
|
);
|
|
774
|
+
const matches = await (0, import_fast_glob.default)(dirGlob, {
|
|
775
|
+
dot: true,
|
|
776
|
+
absolute: true,
|
|
777
|
+
ignore: globExcludes,
|
|
778
|
+
onlyFiles: true
|
|
779
|
+
// Only include files, not subdirectories
|
|
780
|
+
});
|
|
781
|
+
for (const match of matches) {
|
|
782
|
+
if (!isExcluded(match)) {
|
|
783
|
+
const entry = await collectFileInfo(match, match);
|
|
784
|
+
found.push(entry);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (matches.length === 0) {
|
|
788
|
+
logger.warn(`Directory is empty or fully excluded: ${target}`);
|
|
789
|
+
}
|
|
790
|
+
} else {
|
|
791
|
+
if (isExcluded(absPath)) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
const entry = await collectFileInfo(absPath, absPath);
|
|
795
|
+
if (entry.size > LARGE_FILE_THRESHOLD) {
|
|
796
|
+
logger.warn(
|
|
797
|
+
`Large file (>${Math.round(LARGE_FILE_THRESHOLD / 1024 / 1024)}MB): ${target}`
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
if (isSensitiveFile(absPath)) {
|
|
801
|
+
logger.warn(`Sensitive file detected: ${target}`);
|
|
802
|
+
}
|
|
803
|
+
found.push(entry);
|
|
635
804
|
}
|
|
636
|
-
if (isSensitiveFile(absPath)) {
|
|
637
|
-
logger.warn(`Sensitive file detected: ${target}`);
|
|
638
|
-
}
|
|
639
|
-
found.push(entry);
|
|
640
805
|
}
|
|
641
806
|
}
|
|
642
807
|
return { found, missing };
|
|
@@ -662,8 +827,10 @@ async function collectScripts() {
|
|
|
662
827
|
}
|
|
663
828
|
async function createBackup(config, options = {}) {
|
|
664
829
|
const { found, missing } = await scanTargets(config);
|
|
665
|
-
|
|
666
|
-
|
|
830
|
+
if (options.verbose && missing.length > 0) {
|
|
831
|
+
for (const m of missing) {
|
|
832
|
+
logger.warn(`File not found, skipping: ${m}`);
|
|
833
|
+
}
|
|
667
834
|
}
|
|
668
835
|
let allFiles = [...found];
|
|
669
836
|
if (config.scripts.includeInBackup) {
|
|
@@ -892,11 +1059,11 @@ var templateSchema = {
|
|
|
892
1059
|
},
|
|
893
1060
|
additionalProperties: false
|
|
894
1061
|
};
|
|
895
|
-
var
|
|
1062
|
+
var validate4 = ajv.compile(templateSchema);
|
|
896
1063
|
function validateTemplate(data) {
|
|
897
|
-
const valid =
|
|
1064
|
+
const valid = validate4(data);
|
|
898
1065
|
if (valid) return { valid: true };
|
|
899
|
-
const errors =
|
|
1066
|
+
const errors = validate4.errors?.map(
|
|
900
1067
|
(e) => `${e.instancePath || "/"} ${e.message ?? "unknown error"}`
|
|
901
1068
|
);
|
|
902
1069
|
return { valid: false, errors };
|
|
@@ -980,7 +1147,9 @@ function execAsync(command) {
|
|
|
980
1147
|
}
|
|
981
1148
|
async function evaluateSkipIf(command, stepName) {
|
|
982
1149
|
if (containsRemoteScriptPattern(command)) {
|
|
983
|
-
throw new Error(
|
|
1150
|
+
throw new Error(
|
|
1151
|
+
`Blocked dangerous remote script pattern in skip_if: ${stepName}`
|
|
1152
|
+
);
|
|
984
1153
|
}
|
|
985
1154
|
try {
|
|
986
1155
|
await execAsync(command);
|
|
@@ -992,7 +1161,9 @@ async function evaluateSkipIf(command, stepName) {
|
|
|
992
1161
|
async function executeStep(step) {
|
|
993
1162
|
const startTime = Date.now();
|
|
994
1163
|
if (containsRemoteScriptPattern(step.command)) {
|
|
995
|
-
throw new Error(
|
|
1164
|
+
throw new Error(
|
|
1165
|
+
`Blocked dangerous remote script pattern in command: ${step.name}`
|
|
1166
|
+
);
|
|
996
1167
|
}
|
|
997
1168
|
if (step.skip_if) {
|
|
998
1169
|
const shouldSkip = await evaluateSkipIf(step.skip_if, step.name);
|
|
@@ -1046,9 +1217,7 @@ async function* runProvision(templatePath, options = {}) {
|
|
|
1046
1217
|
const result = await executeStep(step);
|
|
1047
1218
|
yield result;
|
|
1048
1219
|
if (result.status === "failed" && !step.continue_on_error) {
|
|
1049
|
-
logger.error(
|
|
1050
|
-
`Step "${step.name}" failed. Stopping provisioning.`
|
|
1051
|
-
);
|
|
1220
|
+
logger.error(`Step "${step.name}" failed. Stopping provisioning.`);
|
|
1052
1221
|
return;
|
|
1053
1222
|
}
|
|
1054
1223
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { SyncpointConfig, BackupMetadata, FileEntry, TemplateConfig, TemplateStep, BackupResult, RestoreResult, RestorePlan, RestoreAction, StepResult, BackupOptions, RestoreOptions, ProvisionOptions, BackupInfo, StatusInfo, } from
|
|
2
|
-
export { loadConfig, saveConfig, initDefaultConfig } from
|
|
3
|
-
export { createBackup, scanTargets } from
|
|
4
|
-
export { restoreBackup, getBackupList, getRestorePlan } from
|
|
5
|
-
export { runProvision, loadTemplate, listTemplates } from
|
|
1
|
+
export type { SyncpointConfig, BackupMetadata, FileEntry, TemplateConfig, TemplateStep, BackupResult, RestoreResult, RestorePlan, RestoreAction, StepResult, BackupOptions, RestoreOptions, ProvisionOptions, BackupInfo, StatusInfo, } from './utils/types.js';
|
|
2
|
+
export { loadConfig, saveConfig, initDefaultConfig } from './core/config.js';
|
|
3
|
+
export { createBackup, scanTargets } from './core/backup.js';
|
|
4
|
+
export { restoreBackup, getBackupList, getRestorePlan, } from './core/restore.js';
|
|
5
|
+
export { runProvision, loadTemplate, listTemplates } from './core/provision.js';
|