@reshotdev/screenshot 0.0.1-beta.2 → 0.0.1-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +9 -2
- package/src/commands/auth.js +1 -3
- package/src/commands/ci-setup.js +2 -2
- package/src/commands/drifts.js +5 -70
- package/src/commands/import-tests.js +13 -13
- package/src/commands/ingest.js +10 -10
- package/src/commands/init.js +16 -277
- package/src/commands/publish.js +3 -204
- package/src/commands/pull.js +2 -2
- package/src/commands/setup-wizard.js +123 -523
- package/src/commands/setup.js +7 -7
- package/src/commands/status.js +26 -43
- package/src/commands/sync.js +28 -236
- package/src/commands/ui.js +1 -1
- package/src/index.js +9 -90
- package/src/lib/api-client.js +8 -8
- package/src/lib/capture-engine.js +3 -3
- package/src/lib/capture-script-runner.js +27 -37
- package/src/lib/config.js +8 -72
- package/src/lib/record-config.js +1 -1
- package/src/lib/standalone-mode.js +1 -1
- package/src/lib/storage-providers.js +4 -4
- package/src/lib/ui-api.js +3 -3
- package/web/manager/dist/assets/{index--ZgioErz.js → index-8H7P9ANi.js} +1 -1
- package/web/manager/dist/index.html +1 -1
- package/src/commands/validate-docs.js +0 -529
package/src/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// index.js - Reshot CLI entry point
|
|
4
|
-
// Consolidated command structure for ease of use while maintaining power features
|
|
5
4
|
|
|
6
5
|
require("dotenv").config({
|
|
7
6
|
path: require("path").join(__dirname, "..", ".env"),
|
|
@@ -15,7 +14,7 @@ const program = new Command();
|
|
|
15
14
|
|
|
16
15
|
program
|
|
17
16
|
.name("reshot")
|
|
18
|
-
.description("Visual
|
|
17
|
+
.description("Visual capture, publishing, and governance for your UI")
|
|
19
18
|
.version(pkg.version);
|
|
20
19
|
|
|
21
20
|
// ============================================================================
|
|
@@ -25,7 +24,7 @@ program
|
|
|
25
24
|
// Setup: Interactive wizard for initial configuration
|
|
26
25
|
program
|
|
27
26
|
.command("setup")
|
|
28
|
-
.description("Interactive setup wizard (auth, config
|
|
27
|
+
.description("Interactive setup wizard (auth, config)")
|
|
29
28
|
.option("--offline", "Skip authentication (local-only mode)")
|
|
30
29
|
.option("--force", "Force re-initialization even if already set up")
|
|
31
30
|
.action(async (options) => {
|
|
@@ -38,13 +37,11 @@ program
|
|
|
38
37
|
}
|
|
39
38
|
});
|
|
40
39
|
|
|
41
|
-
// Sync: Upload traces
|
|
40
|
+
// Sync: Upload Playwright traces to platform
|
|
42
41
|
program
|
|
43
42
|
.command("sync")
|
|
44
|
-
.description("Upload Playwright traces
|
|
43
|
+
.description("Upload Playwright traces to Reshot")
|
|
45
44
|
.option("--trace-dir <path>", "Path to test-results directory")
|
|
46
|
-
.option("--traces", "Sync traces only")
|
|
47
|
-
.option("--docs", "Sync documentation only")
|
|
48
45
|
.option("--dry-run", "Preview what would be synced")
|
|
49
46
|
.option("-v, --verbose", "Show detailed output")
|
|
50
47
|
.action(async (options) => {
|
|
@@ -52,8 +49,6 @@ program
|
|
|
52
49
|
const syncCommand = require("./commands/sync");
|
|
53
50
|
await syncCommand({
|
|
54
51
|
traceDir: options.traceDir,
|
|
55
|
-
traces: options.traces,
|
|
56
|
-
docs: options.docs,
|
|
57
52
|
dryRun: options.dryRun,
|
|
58
53
|
verbose: options.verbose,
|
|
59
54
|
});
|
|
@@ -63,7 +58,7 @@ program
|
|
|
63
58
|
}
|
|
64
59
|
});
|
|
65
60
|
|
|
66
|
-
// Status: View project status
|
|
61
|
+
// Status: View project status and drift summary
|
|
67
62
|
program
|
|
68
63
|
.command("status")
|
|
69
64
|
.description("View project status, sync history, and drift summary")
|
|
@@ -99,27 +94,6 @@ program
|
|
|
99
94
|
}
|
|
100
95
|
});
|
|
101
96
|
|
|
102
|
-
// Validate: Check configuration and documentation bindings
|
|
103
|
-
program
|
|
104
|
-
.command("validate")
|
|
105
|
-
.description("Validate configuration and documentation bindings")
|
|
106
|
-
.option("--strict", "Fail on warnings (zombie links, unbound files)")
|
|
107
|
-
.option("--fix", "Attempt to auto-fix issues where possible")
|
|
108
|
-
.option("-v, --verbose", "Show detailed validation output")
|
|
109
|
-
.action(async (options) => {
|
|
110
|
-
try {
|
|
111
|
-
const validateCommand = require("./commands/validate-docs");
|
|
112
|
-
if (typeof validateCommand.validateDocSync === "function") {
|
|
113
|
-
await validateCommand.validateDocSync(options);
|
|
114
|
-
} else {
|
|
115
|
-
await validateCommand.validateDocs(options);
|
|
116
|
-
}
|
|
117
|
-
} catch (error) {
|
|
118
|
-
console.error(chalk.red("Error:"), error.message);
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
97
|
// ============================================================================
|
|
124
98
|
// VISUAL CAPTURE COMMANDS
|
|
125
99
|
// ============================================================================
|
|
@@ -304,7 +278,7 @@ ciCommand
|
|
|
304
278
|
ciCommand
|
|
305
279
|
.command("run")
|
|
306
280
|
.description("Run capture + publish in one step (CI-optimized)")
|
|
307
|
-
.option("-c, --config <path>", "Path to
|
|
281
|
+
.option("-c, --config <path>", "Path to reshot.config.json")
|
|
308
282
|
.option("--tag <tag>", "Version tag for publish")
|
|
309
283
|
.option("-m, --message <message>", "Commit message for publish")
|
|
310
284
|
.option("--dry-run", "Preview without uploading")
|
|
@@ -323,10 +297,10 @@ ciCommand
|
|
|
323
297
|
// DRIFT MANAGEMENT COMMANDS
|
|
324
298
|
// ============================================================================
|
|
325
299
|
|
|
326
|
-
// Drifts: View and manage
|
|
300
|
+
// Drifts: View and manage visual drifts
|
|
327
301
|
program
|
|
328
302
|
.command("drifts [action] [id]")
|
|
329
|
-
.description("View and manage
|
|
303
|
+
.description("View and manage visual drifts")
|
|
330
304
|
.addHelpText(
|
|
331
305
|
"after",
|
|
332
306
|
`
|
|
@@ -339,7 +313,6 @@ Actions:
|
|
|
339
313
|
sync <id> Mark as manually synced (external_host)
|
|
340
314
|
approve-all Approve all pending drifts
|
|
341
315
|
reject-all Reject all pending drifts
|
|
342
|
-
validate Validate journey bindings
|
|
343
316
|
`,
|
|
344
317
|
)
|
|
345
318
|
.option("--status <status>", "Filter: PENDING, APPROVED, REJECTED, IGNORED")
|
|
@@ -348,7 +321,6 @@ Actions:
|
|
|
348
321
|
.action(async (action, id, options) => {
|
|
349
322
|
try {
|
|
350
323
|
const driftsCommand = require("./commands/drifts");
|
|
351
|
-
// Map action/id to the expected subcommand/args format
|
|
352
324
|
const subcommand = action || "list";
|
|
353
325
|
const args = id ? [id] : [];
|
|
354
326
|
await driftsCommand(subcommand, args, options);
|
|
@@ -364,7 +336,7 @@ Actions:
|
|
|
364
336
|
|
|
365
337
|
// Auth: Standalone authentication (for re-auth scenarios)
|
|
366
338
|
program
|
|
367
|
-
.command("auth"
|
|
339
|
+
.command("auth")
|
|
368
340
|
.description("Authenticate with Reshot Cloud")
|
|
369
341
|
.action(async () => {
|
|
370
342
|
try {
|
|
@@ -406,57 +378,4 @@ program
|
|
|
406
378
|
}
|
|
407
379
|
});
|
|
408
380
|
|
|
409
|
-
// Ingest: Alias for sync (backward compatibility)
|
|
410
|
-
program
|
|
411
|
-
.command("ingest", { hidden: true })
|
|
412
|
-
.description("Upload traces and docs (alias for sync)")
|
|
413
|
-
.option("--trace-dir <path>", "Path to test-results directory")
|
|
414
|
-
.option("--dry-run", "Preview what would be synced")
|
|
415
|
-
.action(async (options) => {
|
|
416
|
-
console.log(
|
|
417
|
-
chalk.yellow("Note: 'ingest' is now 'sync'. Running sync...\n"),
|
|
418
|
-
);
|
|
419
|
-
try {
|
|
420
|
-
const syncCommand = require("./commands/sync");
|
|
421
|
-
await syncCommand({
|
|
422
|
-
traceDir: options.traceDir,
|
|
423
|
-
dryRun: options.dryRun,
|
|
424
|
-
});
|
|
425
|
-
} catch (error) {
|
|
426
|
-
console.error(chalk.red("Error:"), error.message);
|
|
427
|
-
process.exit(1);
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// UI: Alias for studio (backward compatibility)
|
|
432
|
-
program
|
|
433
|
-
.command("ui", { hidden: true })
|
|
434
|
-
.description("Launch Reshot Studio (alias for studio)")
|
|
435
|
-
.option("--port <port>", "Port for web UI", "4300")
|
|
436
|
-
.option("--host <host>", "Host for web UI", "127.0.0.1")
|
|
437
|
-
.option("--no-open", "Do not automatically open browser")
|
|
438
|
-
.action(async (options) => {
|
|
439
|
-
try {
|
|
440
|
-
const uiCommand = require("./commands/ui");
|
|
441
|
-
await uiCommand(options);
|
|
442
|
-
} catch (error) {
|
|
443
|
-
console.error(chalk.red("Error:"), error.message);
|
|
444
|
-
process.exit(1);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
// Init-docs: Alias hidden (merged into setup wizard)
|
|
449
|
-
program
|
|
450
|
-
.command("init-docs", { hidden: true })
|
|
451
|
-
.description("Initialize documentation sync (use setup instead)")
|
|
452
|
-
.action(async () => {
|
|
453
|
-
try {
|
|
454
|
-
const initDocsCommand = require("./commands/init-docs");
|
|
455
|
-
await initDocsCommand();
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error(chalk.red("Error:"), error.message);
|
|
458
|
-
process.exit(1);
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
|
|
462
381
|
program.parse(process.argv);
|
package/src/lib/api-client.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require("fs");
|
|
|
5
5
|
|
|
6
6
|
const baseUrl =
|
|
7
7
|
process.env.RESHOT_API_BASE_URL ||
|
|
8
|
-
process.env.
|
|
8
|
+
process.env.RESHOT_API_BASE_URL ||
|
|
9
9
|
"http://localhost:3000/api";
|
|
10
10
|
|
|
11
11
|
function getApiBaseUrl() {
|
|
@@ -648,7 +648,7 @@ async function getBaselines(projectId, apiKey) {
|
|
|
648
648
|
*/
|
|
649
649
|
async function exportVisuals(projectId, options = {}) {
|
|
650
650
|
const { format = "json", status = "approved" } = options;
|
|
651
|
-
const settings = require("./config").
|
|
651
|
+
const settings = require("./config").readSettings();
|
|
652
652
|
const apiKey = settings?.apiKey;
|
|
653
653
|
|
|
654
654
|
if (!apiKey) {
|
|
@@ -688,7 +688,7 @@ async function post(endpoint, data, options = {}) {
|
|
|
688
688
|
}
|
|
689
689
|
|
|
690
690
|
/**
|
|
691
|
-
*
|
|
691
|
+
* Initialize ingestion job with manifest
|
|
692
692
|
*/
|
|
693
693
|
async function initIngest(apiKey, projectId, manifest) {
|
|
694
694
|
return withRetry(async () => {
|
|
@@ -705,7 +705,7 @@ async function initIngest(apiKey, projectId, manifest) {
|
|
|
705
705
|
}
|
|
706
706
|
|
|
707
707
|
/**
|
|
708
|
-
*
|
|
708
|
+
* Commit ingestion job after uploads complete
|
|
709
709
|
*/
|
|
710
710
|
async function commitIngest(apiKey, projectId, uploadResults, git, cli) {
|
|
711
711
|
return withRetry(async () => {
|
|
@@ -722,7 +722,7 @@ async function commitIngest(apiKey, projectId, uploadResults, git, cli) {
|
|
|
722
722
|
}
|
|
723
723
|
|
|
724
724
|
/**
|
|
725
|
-
*
|
|
725
|
+
* Get drift records for a project
|
|
726
726
|
*/
|
|
727
727
|
async function getDrifts(apiKey, projectId, options = {}) {
|
|
728
728
|
return withRetry(async () => {
|
|
@@ -742,7 +742,7 @@ async function getDrifts(apiKey, projectId, options = {}) {
|
|
|
742
742
|
}
|
|
743
743
|
|
|
744
744
|
/**
|
|
745
|
-
*
|
|
745
|
+
* Get sync jobs for a project
|
|
746
746
|
*/
|
|
747
747
|
async function getSyncJobs(apiKey, projectId, options = {}) {
|
|
748
748
|
return withRetry(async () => {
|
|
@@ -762,7 +762,7 @@ async function getSyncJobs(apiKey, projectId, options = {}) {
|
|
|
762
762
|
}
|
|
763
763
|
|
|
764
764
|
/**
|
|
765
|
-
*
|
|
765
|
+
* Perform action on a drift record
|
|
766
766
|
*/
|
|
767
767
|
async function driftAction(apiKey, projectId, driftId, action, options = {}) {
|
|
768
768
|
return withRetry(async () => {
|
|
@@ -805,7 +805,7 @@ module.exports = {
|
|
|
805
805
|
getBaselines,
|
|
806
806
|
// Export support
|
|
807
807
|
exportVisuals,
|
|
808
|
-
//
|
|
808
|
+
// Reshot
|
|
809
809
|
post,
|
|
810
810
|
initIngest,
|
|
811
811
|
commitIngest,
|
|
@@ -412,11 +412,11 @@ class CaptureEngine {
|
|
|
412
412
|
projectId = settings.urlVariables?.PROJECT_ID;
|
|
413
413
|
// 2. Check settings projectId
|
|
414
414
|
if (!projectId) projectId = settings.projectId;
|
|
415
|
-
// 3. Check
|
|
415
|
+
// 3. Check reshot.config.json urlVariables
|
|
416
416
|
if (!projectId) {
|
|
417
417
|
try {
|
|
418
|
-
const
|
|
419
|
-
projectId =
|
|
418
|
+
const reshotConfig = config.readConfig() || {};
|
|
419
|
+
projectId = reshotConfig.urlVariables?.PROJECT_ID;
|
|
420
420
|
} catch (_e) {
|
|
421
421
|
// Config may not exist
|
|
422
422
|
}
|
|
@@ -1931,12 +1931,17 @@ async function runScenarioWithVideoCapture(scenario, options = {}) {
|
|
|
1931
1931
|
|
|
1932
1932
|
await fs.writeFile(sentinelPath, buffer);
|
|
1933
1933
|
sentinelPaths.push({ index: sentinelIndex, label, path: sentinelPath });
|
|
1934
|
+
if (firstSentinelTimestamp === null) {
|
|
1935
|
+
firstSentinelTimestamp = (Date.now() - startTime) / 1000;
|
|
1936
|
+
debug(`First sentinel captured at ${firstSentinelTimestamp.toFixed(2)}s`);
|
|
1937
|
+
}
|
|
1934
1938
|
sentinelIndex++;
|
|
1935
1939
|
return sentinelPath;
|
|
1936
1940
|
}
|
|
1937
1941
|
|
|
1938
1942
|
// Capture initial state BEFORE first navigation (placeholder - actual capture after goto)
|
|
1939
1943
|
let hasNavigated = false;
|
|
1944
|
+
let firstSentinelTimestamp = null;
|
|
1940
1945
|
|
|
1941
1946
|
// Execute all steps and capture timeline
|
|
1942
1947
|
for (let stepIndex = 0; stepIndex < script.length; stepIndex++) {
|
|
@@ -2292,20 +2297,32 @@ async function runScenarioWithVideoCapture(scenario, options = {}) {
|
|
|
2292
2297
|
)
|
|
2293
2298
|
);
|
|
2294
2299
|
|
|
2295
|
-
// Convert to MP4 with ffmpeg, trimming
|
|
2296
|
-
//
|
|
2297
|
-
const
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2300
|
+
// Convert to MP4 with ffmpeg, trimming blank loading frames from start
|
|
2301
|
+
// and excess frames from end
|
|
2302
|
+
const startOffset = Math.max(0, (firstSentinelTimestamp || 0) - 0.3);
|
|
2303
|
+
const endTimestamp = finalTimestamp + 0.5;
|
|
2304
|
+
const contentDuration = endTimestamp - startOffset;
|
|
2305
|
+
if (startOffset > 0) {
|
|
2306
|
+
console.log(
|
|
2307
|
+
chalk.cyan(
|
|
2308
|
+
` 📹 Converting to MP4 (${startOffset.toFixed(1)}s–${endTimestamp.toFixed(1)}s, ${contentDuration.toFixed(1)}s content)...`
|
|
2309
|
+
)
|
|
2310
|
+
);
|
|
2311
|
+
} else {
|
|
2312
|
+
console.log(
|
|
2313
|
+
chalk.cyan(
|
|
2314
|
+
` 📹 Converting to MP4 (trimmed to ${contentDuration.toFixed(1)}s)...`
|
|
2315
|
+
)
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2318
|
+
debug(`Running ffmpeg: start=${startOffset}s, duration=${contentDuration}s`);
|
|
2304
2319
|
await runFFmpegConvert([
|
|
2320
|
+
"-ss",
|
|
2321
|
+
startOffset.toFixed(2),
|
|
2305
2322
|
"-i",
|
|
2306
2323
|
recordedVideoPath,
|
|
2307
2324
|
"-t",
|
|
2308
|
-
|
|
2325
|
+
contentDuration.toFixed(2),
|
|
2309
2326
|
"-c:v",
|
|
2310
2327
|
"libx264",
|
|
2311
2328
|
"-preset",
|
|
@@ -2330,33 +2347,6 @@ async function runScenarioWithVideoCapture(scenario, options = {}) {
|
|
|
2330
2347
|
)
|
|
2331
2348
|
);
|
|
2332
2349
|
|
|
2333
|
-
// Extract poster frame from the video (first frame at 0.5s for non-blank content)
|
|
2334
|
-
const posterPath = finalVideoPath.replace(/\.mp4$/, "-poster.png");
|
|
2335
|
-
try {
|
|
2336
|
-
await runFFmpegConvert([
|
|
2337
|
-
"-i",
|
|
2338
|
-
finalVideoPath,
|
|
2339
|
-
"-ss",
|
|
2340
|
-
"0.5",
|
|
2341
|
-
"-frames:v",
|
|
2342
|
-
"1",
|
|
2343
|
-
"-q:v",
|
|
2344
|
-
"2",
|
|
2345
|
-
"-y",
|
|
2346
|
-
posterPath,
|
|
2347
|
-
]);
|
|
2348
|
-
if (fs.existsSync(posterPath)) {
|
|
2349
|
-
const posterSize = fs.statSync(posterPath).size;
|
|
2350
|
-
console.log(
|
|
2351
|
-
chalk.green(
|
|
2352
|
-
` ✔ Poster frame: ${posterPath} (${(posterSize / 1024).toFixed(1)} KB)`
|
|
2353
|
-
)
|
|
2354
|
-
);
|
|
2355
|
-
}
|
|
2356
|
-
} catch (e) {
|
|
2357
|
-
debug(`Poster frame extraction failed: ${e.message}`);
|
|
2358
|
-
}
|
|
2359
|
-
|
|
2360
2350
|
// Save timeline for reference
|
|
2361
2351
|
const timelinePath = path.join(
|
|
2362
2352
|
outputDir || path.join(".reshot/output", scenario.key, "default"),
|
package/src/lib/config.js
CHANGED
|
@@ -64,7 +64,7 @@ function createAuthErrorResponse(message) {
|
|
|
64
64
|
code: "AUTH_REQUIRED",
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
const CONFIG_PATH = path.join(process.cwd(), "
|
|
67
|
+
const CONFIG_PATH = path.join(process.cwd(), "reshot.config.json");
|
|
68
68
|
const WORKSPACE_PATH = path.join(process.cwd(), SETTINGS_DIR, "workspace.json");
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -470,84 +470,22 @@ function readConfig() {
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
// Validate optional docs block
|
|
474
|
-
if (config.docs !== undefined) {
|
|
475
|
-
if (
|
|
476
|
-
typeof config.docs !== "object" ||
|
|
477
|
-
config.docs === null ||
|
|
478
|
-
Array.isArray(config.docs)
|
|
479
|
-
) {
|
|
480
|
-
throw new Error("docs must be an object");
|
|
481
|
-
}
|
|
482
|
-
if (
|
|
483
|
-
config.docs.root !== undefined &&
|
|
484
|
-
typeof config.docs.root !== "string"
|
|
485
|
-
) {
|
|
486
|
-
throw new Error("docs.root must be a string");
|
|
487
|
-
}
|
|
488
|
-
if (
|
|
489
|
-
config.docs.include !== undefined &&
|
|
490
|
-
!Array.isArray(config.docs.include)
|
|
491
|
-
) {
|
|
492
|
-
throw new Error("docs.include must be an array of strings");
|
|
493
|
-
}
|
|
494
|
-
if (
|
|
495
|
-
config.docs.exclude !== undefined &&
|
|
496
|
-
!Array.isArray(config.docs.exclude)
|
|
497
|
-
) {
|
|
498
|
-
throw new Error("docs.exclude must be an array of strings");
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
473
|
return config;
|
|
503
474
|
}
|
|
504
475
|
|
|
505
476
|
/**
|
|
506
|
-
* Read
|
|
507
|
-
*
|
|
508
|
-
* @returns {Object}
|
|
477
|
+
* Read reshot.config.json without requiring scenarios array
|
|
478
|
+
* Used by sync and other commands that don't need scenario validation
|
|
479
|
+
* @returns {Object} Configuration
|
|
509
480
|
*/
|
|
510
|
-
function
|
|
481
|
+
function readConfigLenient() {
|
|
511
482
|
if (!fs.existsSync(CONFIG_PATH)) {
|
|
512
483
|
throw new Error(
|
|
513
484
|
`Config file not found at ${CONFIG_PATH}. Run \`reshot init\` to create one.`
|
|
514
485
|
);
|
|
515
486
|
}
|
|
516
487
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
// Validate documentation block if present
|
|
520
|
-
if (config.documentation) {
|
|
521
|
-
const doc = config.documentation;
|
|
522
|
-
|
|
523
|
-
// Validate required fields
|
|
524
|
-
if (!doc.strategy) {
|
|
525
|
-
throw new Error('documentation.strategy is required (git_pr or external_host)');
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (!['git_pr', 'external_host'].includes(doc.strategy)) {
|
|
529
|
-
throw new Error('documentation.strategy must be "git_pr" or "external_host"');
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// Validate optional fields
|
|
533
|
-
if (doc.assetFormat && !['cdn_link', 'markdown'].includes(doc.assetFormat)) {
|
|
534
|
-
throw new Error('documentation.assetFormat must be "cdn_link" or "markdown"');
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (doc.include && !Array.isArray(doc.include)) {
|
|
538
|
-
throw new Error('documentation.include must be an array of glob patterns');
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (doc.exclude && !Array.isArray(doc.exclude)) {
|
|
542
|
-
throw new Error('documentation.exclude must be an array of glob patterns');
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (doc.mappings && typeof doc.mappings !== 'object') {
|
|
546
|
-
throw new Error('documentation.mappings must be an object');
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return config;
|
|
488
|
+
return fs.readJSONSync(CONFIG_PATH);
|
|
551
489
|
}
|
|
552
490
|
|
|
553
491
|
/**
|
|
@@ -851,8 +789,6 @@ async function initializeProject(projectId, apiKey, options = {}) {
|
|
|
851
789
|
contextCount: 1,
|
|
852
790
|
features: {
|
|
853
791
|
visuals: true,
|
|
854
|
-
docs: false,
|
|
855
|
-
changelog: true,
|
|
856
792
|
},
|
|
857
793
|
},
|
|
858
794
|
};
|
|
@@ -1230,8 +1166,8 @@ module.exports = {
|
|
|
1230
1166
|
// Mode & validation
|
|
1231
1167
|
getModeInfo,
|
|
1232
1168
|
validateConfig,
|
|
1233
|
-
//
|
|
1234
|
-
|
|
1169
|
+
// Lenient config read (no scenario validation)
|
|
1170
|
+
readConfigLenient,
|
|
1235
1171
|
// Paths
|
|
1236
1172
|
SETTINGS_PATH,
|
|
1237
1173
|
SETTINGS_DIR,
|
package/src/lib/record-config.js
CHANGED
|
@@ -600,7 +600,7 @@ async function finalizeScenarioAndWriteConfig(
|
|
|
600
600
|
|
|
601
601
|
console.log(
|
|
602
602
|
chalk.green(
|
|
603
|
-
"\n✔
|
|
603
|
+
"\n✔ reshot.config.json has been updated. Please review and commit the changes to your repository.\n"
|
|
604
604
|
)
|
|
605
605
|
);
|
|
606
606
|
console.log(chalk.gray(`Scenario: ${result.scenarioName}`));
|
|
@@ -103,7 +103,7 @@ const DEFAULT_STANDALONE_SETTINGS = {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
const SETTINGS_DIR = ".reshot";
|
|
106
|
-
const CONFIG_PATH = "
|
|
106
|
+
const CONFIG_PATH = "reshot.config.json";
|
|
107
107
|
const SETTINGS_PATH = path.join(SETTINGS_DIR, "settings.json");
|
|
108
108
|
|
|
109
109
|
/**
|
|
@@ -114,7 +114,7 @@ ${chalk.cyan('AWS S3 Setup:')}
|
|
|
114
114
|
${chalk.gray('export AWS_SECRET_ACCESS_KEY="your-secret-access-key"')}
|
|
115
115
|
${chalk.gray('export AWS_REGION="us-east-1" # optional, defaults to us-east-1')}
|
|
116
116
|
|
|
117
|
-
3. ${chalk.yellow('Update
|
|
117
|
+
3. ${chalk.yellow('Update reshot.config.json:')}
|
|
118
118
|
${chalk.gray(JSON.stringify({
|
|
119
119
|
storage: {
|
|
120
120
|
type: 's3',
|
|
@@ -143,7 +143,7 @@ ${chalk.cyan('Cloudflare R2 Setup:')}
|
|
|
143
143
|
${chalk.gray('export R2_ACCESS_KEY_ID="your-r2-access-key"')}
|
|
144
144
|
${chalk.gray('export R2_SECRET_ACCESS_KEY="your-r2-secret-key"')}
|
|
145
145
|
|
|
146
|
-
3. ${chalk.yellow('Update
|
|
146
|
+
3. ${chalk.yellow('Update reshot.config.json:')}
|
|
147
147
|
${chalk.gray(JSON.stringify({
|
|
148
148
|
storage: {
|
|
149
149
|
type: 'r2',
|
|
@@ -165,7 +165,7 @@ ${chalk.cyan('Local Storage Setup:')}
|
|
|
165
165
|
|
|
166
166
|
For local testing or self-hosted scenarios:
|
|
167
167
|
|
|
168
|
-
1. ${chalk.yellow('Update
|
|
168
|
+
1. ${chalk.yellow('Update reshot.config.json:')}
|
|
169
169
|
${chalk.gray(JSON.stringify({
|
|
170
170
|
storage: {
|
|
171
171
|
type: 'local',
|
|
@@ -536,7 +536,7 @@ function createStorageProvider(config) {
|
|
|
536
536
|
|
|
537
537
|
/**
|
|
538
538
|
* Determine storage mode from config
|
|
539
|
-
* @param {object} docSyncConfig - The
|
|
539
|
+
* @param {object} docSyncConfig - The reshot.config.json content
|
|
540
540
|
* @returns {'platform'|'byos'}
|
|
541
541
|
*/
|
|
542
542
|
function getStorageMode(docSyncConfig) {
|
package/src/lib/ui-api.js
CHANGED
|
@@ -48,7 +48,7 @@ function getPlatformUrl(settings) {
|
|
|
48
48
|
return settings.platformUrl;
|
|
49
49
|
}
|
|
50
50
|
const envUrl =
|
|
51
|
-
process.env.RESHOT_API_BASE_URL || process.env.
|
|
51
|
+
process.env.RESHOT_API_BASE_URL || process.env.RESHOT_API_BASE_URL;
|
|
52
52
|
if (envUrl) {
|
|
53
53
|
// Remove /api suffix if present to get platform URL
|
|
54
54
|
return envUrl.replace(/\/api\/?$/, "");
|
|
@@ -431,7 +431,7 @@ function attachApiRoutes(app, context) {
|
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
433
|
* PUT /api/privacy
|
|
434
|
-
* Update privacy configuration in
|
|
434
|
+
* Update privacy configuration in reshot.config.json
|
|
435
435
|
*/
|
|
436
436
|
app.put("/api/privacy", async (req, res, next) => {
|
|
437
437
|
try {
|
|
@@ -472,7 +472,7 @@ function attachApiRoutes(app, context) {
|
|
|
472
472
|
|
|
473
473
|
/**
|
|
474
474
|
* PUT /api/style
|
|
475
|
-
* Update style configuration in
|
|
475
|
+
* Update style configuration in reshot.config.json
|
|
476
476
|
*/
|
|
477
477
|
app.put("/api/style", async (req, res, next) => {
|
|
478
478
|
try {
|