@reshotdev/screenshot 0.0.1-beta.2 → 0.0.1-beta.20
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/LICENSE +1 -1
- package/README.md +138 -47
- package/package.json +27 -16
- package/src/commands/auth.js +159 -30
- package/src/commands/capture-dom.js +50 -0
- package/src/commands/certify.js +62 -0
- package/src/commands/compose.js +220 -0
- package/src/commands/doctor-release.js +74 -0
- package/src/commands/doctor-target.js +108 -0
- package/src/commands/drifts.js +16 -69
- package/src/commands/import-tests.js +13 -13
- package/src/commands/init.js +16 -277
- package/src/commands/publish.js +484 -257
- package/src/commands/pull.js +302 -35
- package/src/commands/refresh.js +166 -0
- package/src/commands/run.js +292 -12
- package/src/commands/setup-wizard.js +348 -496
- package/src/commands/status.js +334 -126
- package/src/commands/sync.js +28 -236
- package/src/commands/ui.js +1 -1
- package/src/commands/variation.js +194 -0
- package/src/commands/verify-publish.js +46 -0
- package/src/index.js +383 -118
- package/src/lib/api-client.js +172 -60
- package/src/lib/auto-update/refresh.js +598 -0
- package/src/lib/auto-update/scene-runtime.compose.tsx +73 -0
- package/src/lib/auto-update/spec.js +89 -0
- package/src/lib/capture-engine.js +179 -9
- package/src/lib/capture-script-runner.js +639 -214
- package/src/lib/certification.js +887 -0
- package/src/lib/compose-context.js +156 -0
- package/src/lib/compose-pack.js +42 -0
- package/src/lib/compose-runtime.js +34 -0
- package/src/lib/compose-upload.js +142 -0
- package/src/lib/config.js +186 -81
- package/src/lib/dom-capture.js +64 -0
- package/src/lib/ensure-browser.js +147 -0
- package/src/lib/output-path-template.js +3 -3
- package/src/lib/record-cdp.js +288 -16
- package/src/lib/record-clip.js +83 -3
- package/src/lib/record-config.js +1 -5
- package/src/lib/release-doctor.js +321 -0
- package/src/lib/resolve-targets.js +60 -0
- package/src/lib/run-manifest.js +148 -0
- package/src/lib/standalone-mode.js +1 -1
- package/src/lib/storage-providers.js +5 -5
- package/src/lib/style-engine.js +5 -5
- package/src/lib/target-contract.js +292 -0
- package/src/lib/ui-api-helpers.js +118 -0
- package/src/lib/ui-api.js +31 -824
- package/src/lib/ui-asset-cleanup.js +62 -0
- package/src/lib/ui-output-versions.js +165 -0
- package/src/lib/ui-recorder-routes.js +341 -0
- package/src/lib/ui-scenario-metadata.js +161 -0
- package/vendor/compose/dist/auto-update.cjs +5544 -0
- package/vendor/compose/dist/auto-update.mjs +5518 -0
- package/vendor/compose/dist/capture.cjs +1450 -0
- package/vendor/compose/dist/capture.mjs +1416 -0
- package/vendor/compose/dist/eligibility.cjs +5331 -0
- package/vendor/compose/dist/eligibility.mjs +5313 -0
- package/vendor/compose/dist/index.cjs +2046 -0
- package/vendor/compose/dist/index.mjs +1997 -0
- package/vendor/compose/dist/jsx-dev-runtime.cjs +55 -0
- package/vendor/compose/dist/jsx-dev-runtime.mjs +27 -0
- package/vendor/compose/dist/jsx-runtime.cjs +58 -0
- package/vendor/compose/dist/jsx-runtime.mjs +31 -0
- package/vendor/compose/dist/render.cjs +558 -0
- package/vendor/compose/dist/render.mjs +515 -0
- package/vendor/compose/dist/verify-cli.cjs +3806 -0
- package/vendor/compose/dist/verify-cli.mjs +3812 -0
- package/vendor/compose/dist/verify.cjs +3880 -0
- package/vendor/compose/dist/verify.mjs +3858 -0
- package/web/manager/dist/assets/index-D0S2otug.js +507 -0
- package/web/manager/dist/index.html +1 -1
- package/src/commands/ci-run.js +0 -123
- package/src/commands/ci-setup.js +0 -288
- package/src/commands/ingest.js +0 -458
- package/src/commands/setup.js +0 -137
- package/src/commands/validate-docs.js +0 -529
- package/src/lib/playwright-runner.js +0 -252
- package/web/manager/dist/assets/index--ZgioErz.js +0 -507
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"),
|
|
@@ -9,13 +8,15 @@ require("dotenv").config({
|
|
|
9
8
|
|
|
10
9
|
const { Command } = require("commander");
|
|
11
10
|
const chalk = require("chalk");
|
|
11
|
+
const fs = require("fs-extra");
|
|
12
|
+
const path = require("path");
|
|
12
13
|
const pkg = require("../package.json");
|
|
13
14
|
|
|
14
15
|
const program = new Command();
|
|
15
16
|
|
|
16
17
|
program
|
|
17
18
|
.name("reshot")
|
|
18
|
-
.description("Visual
|
|
19
|
+
.description("Visual capture, publishing, and governance for your UI")
|
|
19
20
|
.version(pkg.version);
|
|
20
21
|
|
|
21
22
|
// ============================================================================
|
|
@@ -25,26 +26,78 @@ program
|
|
|
25
26
|
// Setup: Interactive wizard for initial configuration
|
|
26
27
|
program
|
|
27
28
|
.command("setup")
|
|
28
|
-
.description("
|
|
29
|
-
.option("--offline", "
|
|
29
|
+
.description("Set up the local or hosted workflow from scratch")
|
|
30
|
+
.option("--offline", "Stay local-only and skip hosted authentication")
|
|
31
|
+
.option("--project <id>", "Connect setup to an existing Reshot project")
|
|
32
|
+
.option("--token <token>", "Publish token for non-interactive project linking")
|
|
33
|
+
.option("--no-studio", "Skip offering to launch Studio after setup")
|
|
30
34
|
.option("--force", "Force re-initialization even if already set up")
|
|
31
35
|
.action(async (options) => {
|
|
32
36
|
try {
|
|
33
37
|
const setupWizard = require("./commands/setup-wizard");
|
|
34
|
-
|
|
38
|
+
// Commander stores `--no-studio` as `options.studio === false`, not
|
|
39
|
+
// `options.noStudio`. Normalize it so the flag is honored end-to-end.
|
|
40
|
+
await setupWizard({ ...options, noStudio: options.studio === false });
|
|
35
41
|
} catch (error) {
|
|
36
42
|
console.error(chalk.red("Error:"), error.message);
|
|
37
43
|
process.exit(1);
|
|
38
44
|
}
|
|
39
45
|
});
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
program
|
|
48
|
+
.command("record-clip [target]")
|
|
49
|
+
.description("Record a scenario as a summary MP4 (alias for `reshot run --format summary-video`)")
|
|
50
|
+
.option("-s, --scenarios <keys>", "Comma-separated scenario keys")
|
|
51
|
+
.option("--out <dir>", "Copy the generated MP4 and metadata into this directory")
|
|
52
|
+
.option("--no-headless", "Run browser in visible mode")
|
|
53
|
+
.option("--debug", "Enable verbose debug logging")
|
|
54
|
+
.action(async (target, options) => {
|
|
55
|
+
if (options.debug) {
|
|
56
|
+
process.env.RESHOT_DEBUG = "1";
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const runCommand = require("./commands/run");
|
|
60
|
+
const scenarioKeys = resolveRecordClipScenarioKeys(target, options);
|
|
61
|
+
const result = await runCommand({
|
|
62
|
+
scenarioKeys,
|
|
63
|
+
headless: options.headless,
|
|
64
|
+
format: "summary-video",
|
|
65
|
+
noExit: true,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (options.out && result?.success !== false) {
|
|
69
|
+
await copyRecordClipOutputs(result, target, options.out);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (result?.success === false) {
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(chalk.red("Error:"), error.message);
|
|
77
|
+
if (options.debug && error.stack) {
|
|
78
|
+
console.error(chalk.gray(error.stack));
|
|
79
|
+
}
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
program.addHelpText(
|
|
85
|
+
"after",
|
|
86
|
+
`
|
|
87
|
+
Primary workflow:
|
|
88
|
+
reshot setup Configure the project and choose local-only or hosted mode
|
|
89
|
+
reshot run Run configured scenarios with fail-fast preflight checks
|
|
90
|
+
reshot publish Upload captured assets to the Reshot platform
|
|
91
|
+
reshot pull Pull approved CDN-backed asset references into your repo
|
|
92
|
+
reshot doctor target Audit certified-target readiness before capture
|
|
93
|
+
`,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Sync: Upload Playwright traces to platform
|
|
42
97
|
program
|
|
43
98
|
.command("sync")
|
|
44
|
-
.description("Upload Playwright traces
|
|
99
|
+
.description("Upload Playwright traces to Reshot")
|
|
45
100
|
.option("--trace-dir <path>", "Path to test-results directory")
|
|
46
|
-
.option("--traces", "Sync traces only")
|
|
47
|
-
.option("--docs", "Sync documentation only")
|
|
48
101
|
.option("--dry-run", "Preview what would be synced")
|
|
49
102
|
.option("-v, --verbose", "Show detailed output")
|
|
50
103
|
.action(async (options) => {
|
|
@@ -52,8 +105,6 @@ program
|
|
|
52
105
|
const syncCommand = require("./commands/sync");
|
|
53
106
|
await syncCommand({
|
|
54
107
|
traceDir: options.traceDir,
|
|
55
|
-
traces: options.traces,
|
|
56
|
-
docs: options.docs,
|
|
57
108
|
dryRun: options.dryRun,
|
|
58
109
|
verbose: options.verbose,
|
|
59
110
|
});
|
|
@@ -63,7 +114,7 @@ program
|
|
|
63
114
|
}
|
|
64
115
|
});
|
|
65
116
|
|
|
66
|
-
// Status: View project status
|
|
117
|
+
// Status: View project status and drift summary
|
|
67
118
|
program
|
|
68
119
|
.command("status")
|
|
69
120
|
.description("View project status, sync history, and drift summary")
|
|
@@ -99,36 +150,16 @@ program
|
|
|
99
150
|
}
|
|
100
151
|
});
|
|
101
152
|
|
|
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
153
|
// ============================================================================
|
|
124
154
|
// VISUAL CAPTURE COMMANDS
|
|
125
155
|
// ============================================================================
|
|
126
156
|
|
|
127
157
|
// Run: Execute scenarios from config (automated visual capture)
|
|
128
158
|
program
|
|
129
|
-
.command("run")
|
|
159
|
+
.command("run [target]")
|
|
130
160
|
.description("Execute visual capture scenarios from config")
|
|
131
161
|
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
162
|
+
.option("--scenario <keys>", "Alias for --scenarios")
|
|
132
163
|
.option("--no-headless", "Run browser in visible mode")
|
|
133
164
|
.option("--variant <json>", "Override variant configuration as JSON")
|
|
134
165
|
.option("--all-variants", "Run all configured variant combinations")
|
|
@@ -148,15 +179,13 @@ program
|
|
|
148
179
|
.option("--no-style", "Disable style processing")
|
|
149
180
|
.option("--cloud", "Compare against cloud baselines")
|
|
150
181
|
.option("--debug", "Enable verbose debug logging")
|
|
151
|
-
.action(async (options) => {
|
|
182
|
+
.action(async (target, options) => {
|
|
152
183
|
if (options.debug) {
|
|
153
184
|
process.env.RESHOT_DEBUG = "1";
|
|
154
185
|
}
|
|
155
186
|
try {
|
|
156
187
|
const runCommand = require("./commands/run");
|
|
157
|
-
const scenarioKeys = options
|
|
158
|
-
? options.scenarios.split(",").map((s) => s.trim())
|
|
159
|
-
: null;
|
|
188
|
+
const scenarioKeys = resolveScenarioKeysFromTarget(target, options);
|
|
160
189
|
await runCommand({
|
|
161
190
|
scenarioKeys,
|
|
162
191
|
headless: options.headless,
|
|
@@ -179,19 +208,96 @@ program
|
|
|
179
208
|
}
|
|
180
209
|
});
|
|
181
210
|
|
|
211
|
+
// Capture: Compatibility alias for older docs and snippets
|
|
212
|
+
program
|
|
213
|
+
.command("capture")
|
|
214
|
+
.description("Compatibility alias for `reshot run` (prefer `reshot run` and `reshot publish`)")
|
|
215
|
+
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
216
|
+
.option("--mode <mode>", "Compatibility mode: preview | publish", "preview")
|
|
217
|
+
.option("--config <path>", "Compatibility config path (defaults to reshot.config.json)")
|
|
218
|
+
.option("--base-url <url>", "Temporary base URL override for compatibility flows")
|
|
219
|
+
.option("--tag <tag>", "Version tag when mode=publish")
|
|
220
|
+
.option("-m, --message <message>", "Publish message when mode=publish")
|
|
221
|
+
.option("--no-headless", "Run browser in visible mode")
|
|
222
|
+
.option("-c, --concurrency <n>", "Number of parallel browser workers", parseInt)
|
|
223
|
+
.option("--debug", "Enable verbose debug logging")
|
|
224
|
+
.action(async (options) => {
|
|
225
|
+
if (options.debug) {
|
|
226
|
+
process.env.RESHOT_DEBUG = "1";
|
|
227
|
+
}
|
|
228
|
+
if (options.config && options.config !== "reshot.config.json") {
|
|
229
|
+
console.log(
|
|
230
|
+
chalk.yellow(
|
|
231
|
+
"Compatibility mode currently uses reshot.config.json from the working directory.",
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (options.baseUrl) {
|
|
236
|
+
process.env.RESHOT_BASE_URL = options.baseUrl;
|
|
237
|
+
console.log(
|
|
238
|
+
chalk.gray(
|
|
239
|
+
`Using temporary base URL override for this run: ${options.baseUrl}`,
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const runCommand = require("./commands/run");
|
|
245
|
+
const scenarioKeys = options.scenarios
|
|
246
|
+
? options.scenarios.split(",").map((s) => s.trim())
|
|
247
|
+
: null;
|
|
248
|
+
await runCommand({
|
|
249
|
+
scenarioKeys,
|
|
250
|
+
headless: options.headless,
|
|
251
|
+
concurrency: options.concurrency,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (String(options.mode || "preview").toLowerCase() === "publish") {
|
|
255
|
+
const publishCommand = require("./commands/publish");
|
|
256
|
+
await publishCommand({
|
|
257
|
+
tag: options.tag,
|
|
258
|
+
message: options.message,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error(chalk.red("Error:"), error.message);
|
|
263
|
+
if (options.debug && error.stack) {
|
|
264
|
+
console.error(chalk.gray(error.stack));
|
|
265
|
+
}
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
182
270
|
// Record: Interactive visual recording from live browser
|
|
183
271
|
program
|
|
184
272
|
.command("record [title]")
|
|
185
273
|
.description("Interactively record visuals from a live browser session")
|
|
274
|
+
.option("--name <title>", "Compatibility alias for the scenario title")
|
|
186
275
|
.option("--browser", "Launch Chrome with remote debugging before recording")
|
|
187
276
|
.option("-p, --port <port>", "Chrome debugging port (default: 9222)")
|
|
188
277
|
.option("--url <url>", "URL to open when launching browser")
|
|
278
|
+
.option("--refresh-session", "Only refresh the auth session (no recording prompts)")
|
|
189
279
|
.option("--debug", "Enable verbose debug logging")
|
|
190
280
|
.action(async (title, options) => {
|
|
191
281
|
if (options.debug) {
|
|
192
282
|
process.env.RESHOT_DEBUG = "1";
|
|
193
283
|
}
|
|
284
|
+
const resolvedTitle = title || options.name;
|
|
194
285
|
try {
|
|
286
|
+
// If --refresh-session, just sync the session and exit
|
|
287
|
+
if (options.refreshSession) {
|
|
288
|
+
const { autoSyncSessionFromCDP, getDefaultSessionPath } = require("./lib/record-cdp");
|
|
289
|
+
const sessionPath = getDefaultSessionPath();
|
|
290
|
+
console.log(chalk.gray(" Syncing session from active browser..."));
|
|
291
|
+
const result = await autoSyncSessionFromCDP(sessionPath);
|
|
292
|
+
if (result.synced) {
|
|
293
|
+
console.log(chalk.green(" ✔ Session refreshed at " + sessionPath));
|
|
294
|
+
} else {
|
|
295
|
+
console.log(chalk.yellow(" ⚠ No active CDP browser found. Launch Chrome with remote debugging first:"));
|
|
296
|
+
console.log(chalk.gray(" reshot record --browser --refresh-session"));
|
|
297
|
+
}
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
195
301
|
// If --browser flag, launch Chrome first
|
|
196
302
|
if (options.browser) {
|
|
197
303
|
const chromeCommand = require("./commands/chrome");
|
|
@@ -204,7 +310,7 @@ program
|
|
|
204
310
|
}
|
|
205
311
|
|
|
206
312
|
const recordCommand = require("./commands/record");
|
|
207
|
-
await recordCommand(
|
|
313
|
+
await recordCommand(resolvedTitle);
|
|
208
314
|
} catch (error) {
|
|
209
315
|
console.error(chalk.red("Error:"), error.message);
|
|
210
316
|
if (options.debug && error.stack) {
|
|
@@ -233,6 +339,18 @@ program
|
|
|
233
339
|
}
|
|
234
340
|
});
|
|
235
341
|
|
|
342
|
+
// Compose: Render a local JSX composition into a video pack
|
|
343
|
+
const { registerCompose } = require("./commands/compose");
|
|
344
|
+
registerCompose(program);
|
|
345
|
+
|
|
346
|
+
// Capture-DOM: Capture a self-contained DOM reconstruction artifact from a URL
|
|
347
|
+
const { registerCaptureDom } = require("./commands/capture-dom");
|
|
348
|
+
registerCaptureDom(program);
|
|
349
|
+
|
|
350
|
+
// Refresh: Phase 5 auto-update loop — recapture, drift-check, re-publish or flag
|
|
351
|
+
const { registerRefresh } = require("./commands/refresh");
|
|
352
|
+
registerRefresh(program);
|
|
353
|
+
|
|
236
354
|
// ============================================================================
|
|
237
355
|
// PUBLISHING & INTEGRATION COMMANDS
|
|
238
356
|
// ============================================================================
|
|
@@ -246,28 +364,58 @@ program
|
|
|
246
364
|
.option("--video <path>", "Explicit video file to upload with this publish")
|
|
247
365
|
.option("--dry-run", "Preview without uploading")
|
|
248
366
|
.option("-f, --force", "Skip confirmation prompts")
|
|
367
|
+
.option("--all-output", "Publish from the full .reshot/output tree instead of the latest successful run manifest")
|
|
249
368
|
.option("--output-json", "Write structured result to .reshot/output/publish-result.json")
|
|
369
|
+
.option("--auto-approve", "Automatically approve published visuals (skip review queue)")
|
|
370
|
+
.option("--skip-release-doctor", "Skip the composed release gate precheck")
|
|
250
371
|
.action(async (options) => {
|
|
251
372
|
try {
|
|
252
373
|
const publishCommand = require("./commands/publish");
|
|
253
|
-
await publishCommand({
|
|
374
|
+
const result = await publishCommand({
|
|
254
375
|
...options,
|
|
255
376
|
outputJson: options.outputJson,
|
|
377
|
+
autoApprove: options.autoApprove,
|
|
378
|
+
skipReleaseDoctor: options.skipReleaseDoctor,
|
|
256
379
|
});
|
|
380
|
+
if (result && result.success === false) {
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
257
383
|
} catch (error) {
|
|
258
384
|
console.error(chalk.red("Error:"), error.message);
|
|
259
385
|
process.exit(1);
|
|
260
386
|
}
|
|
261
387
|
});
|
|
262
388
|
|
|
263
|
-
//
|
|
389
|
+
// Variation: Render a variation from a captured DOM scene (MHTML).
|
|
390
|
+
// Beta — see docs/variation-pipeline.md.
|
|
391
|
+
program
|
|
392
|
+
.command("variation")
|
|
393
|
+
.description("Render a variation from a captured DOM scene (beta)")
|
|
394
|
+
.option("-s, --source <path>", "Path to source .mhtml (overrides --scenario/--capture)")
|
|
395
|
+
.option("--scenario <key>", "Scenario key under .reshot/output/")
|
|
396
|
+
.option("--capture <key>", "Capture key (e.g., 'observation-detail')")
|
|
397
|
+
.option("--theme <name>", "Theme variant: light | dark", "light")
|
|
398
|
+
.option("-m, --manifest <path>", "Path to variation manifest (.json)")
|
|
399
|
+
.option("-o, --output <path>", "Output PNG path")
|
|
400
|
+
.option("--no-headless", "Run browser visibly for debugging")
|
|
401
|
+
.action(async (options) => {
|
|
402
|
+
try {
|
|
403
|
+
const variationCommand = require("./commands/variation");
|
|
404
|
+
await variationCommand(options);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error(chalk.red("Error:"), error.message);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Pull: Generate asset map for local workflows
|
|
264
412
|
program
|
|
265
413
|
.command("pull")
|
|
266
|
-
.description("Pull asset map for your
|
|
414
|
+
.description("Pull asset map for your capture workflow")
|
|
267
415
|
.option("-f, --format <format>", "Output format: json, ts, csv", "json")
|
|
268
416
|
.option("-o, --output <path>", "Output file path")
|
|
269
417
|
.option("--full", "Include full metadata in TypeScript output")
|
|
270
|
-
.option("--status <status>", "Filter: approved, pending, all", "
|
|
418
|
+
.option("--status <status>", "Filter: approved, pending, all", "all")
|
|
271
419
|
.action(async (options) => {
|
|
272
420
|
try {
|
|
273
421
|
const pullCommand = require("./commands/pull");
|
|
@@ -283,50 +431,105 @@ program
|
|
|
283
431
|
}
|
|
284
432
|
});
|
|
285
433
|
|
|
286
|
-
//
|
|
287
|
-
const
|
|
288
|
-
.command("
|
|
289
|
-
.description("
|
|
434
|
+
// Doctor: Validate target contract and readiness
|
|
435
|
+
const doctor = program
|
|
436
|
+
.command("doctor")
|
|
437
|
+
.description("Validate target configuration and readiness")
|
|
438
|
+
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
439
|
+
.option("--timeout <ms>", "Per-step timeout in milliseconds (default 15000)")
|
|
440
|
+
.option("--json", "Output JSON report")
|
|
441
|
+
.action(async (options) => {
|
|
442
|
+
// Bare `reshot doctor` runs the target readiness audit by default instead
|
|
443
|
+
// of just printing help (audit run-10 F5). Use `doctor release` for the
|
|
444
|
+
// full release gate.
|
|
445
|
+
try {
|
|
446
|
+
const doctorTargetCommand = require("./commands/doctor-target");
|
|
447
|
+
await doctorTargetCommand(options || {});
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.error(chalk.red("Error:"), error.message);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
290
453
|
|
|
291
|
-
|
|
292
|
-
.command("
|
|
293
|
-
.description("
|
|
294
|
-
.
|
|
454
|
+
doctor
|
|
455
|
+
.command("target")
|
|
456
|
+
.description("Audit the certified target contract before capture")
|
|
457
|
+
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
458
|
+
.option("--timeout <ms>", "Per-step timeout in milliseconds (default 15000)")
|
|
459
|
+
.option("--json", "Output JSON report")
|
|
460
|
+
.action(async (options) => {
|
|
295
461
|
try {
|
|
296
|
-
const
|
|
297
|
-
await
|
|
462
|
+
const doctorTargetCommand = require("./commands/doctor-target");
|
|
463
|
+
await doctorTargetCommand(options);
|
|
298
464
|
} catch (error) {
|
|
299
465
|
console.error(chalk.red("Error:"), error.message);
|
|
300
466
|
process.exit(1);
|
|
301
467
|
}
|
|
302
468
|
});
|
|
303
469
|
|
|
304
|
-
|
|
305
|
-
.command("
|
|
306
|
-
.description("Run
|
|
307
|
-
.option("-
|
|
308
|
-
.option("--
|
|
309
|
-
.
|
|
310
|
-
|
|
311
|
-
|
|
470
|
+
doctor
|
|
471
|
+
.command("release")
|
|
472
|
+
.description("Run the combined release gate: preflight, target doctor, and docs assets")
|
|
473
|
+
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
474
|
+
.option("--json", "Output JSON report")
|
|
475
|
+
.action(async (options) => {
|
|
476
|
+
try {
|
|
477
|
+
const doctorReleaseCommand = require("./commands/doctor-release");
|
|
478
|
+
await doctorReleaseCommand(options);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
console.error(chalk.red("Error:"), error.message);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Verify: Publish/pull/hosted delivery verification
|
|
486
|
+
const verify = program.command("verify").description("Verify publish and delivery flows");
|
|
487
|
+
|
|
488
|
+
verify
|
|
489
|
+
.command("publish")
|
|
490
|
+
.description("Verify publish, pull, and hosted asset delivery")
|
|
491
|
+
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
492
|
+
.option("--tag <tag>", "Version tag for verification publish")
|
|
493
|
+
.option("-m, --message <message>", "Publish message override")
|
|
494
|
+
.option("--json", "Output JSON report")
|
|
495
|
+
.action(async (options) => {
|
|
496
|
+
try {
|
|
497
|
+
const verifyPublishCommand = require("./commands/verify-publish");
|
|
498
|
+
await verifyPublishCommand(options);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
console.error(chalk.red("Error:"), error.message);
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// Certify: full certified-target release gate
|
|
506
|
+
program
|
|
507
|
+
.command("certify")
|
|
508
|
+
.description("Run the full certified target pipeline")
|
|
509
|
+
.option("-s, --scenarios <keys>", "Comma-separated list of scenario keys")
|
|
510
|
+
.option("--tag <tag>", "Version tag for certification publish")
|
|
511
|
+
.option("-m, --message <message>", "Publish message override")
|
|
512
|
+
.option("--skip-release-doctor", "Skip the composed release gate precheck")
|
|
513
|
+
.option("--json", "Output JSON report")
|
|
312
514
|
.action(async (options) => {
|
|
313
515
|
try {
|
|
314
|
-
const
|
|
315
|
-
await
|
|
516
|
+
const certifyCommand = require("./commands/certify");
|
|
517
|
+
await certifyCommand(options);
|
|
316
518
|
} catch (error) {
|
|
317
519
|
console.error(chalk.red("Error:"), error.message);
|
|
318
520
|
process.exit(1);
|
|
319
521
|
}
|
|
320
522
|
});
|
|
321
523
|
|
|
524
|
+
|
|
322
525
|
// ============================================================================
|
|
323
526
|
// DRIFT MANAGEMENT COMMANDS
|
|
324
527
|
// ============================================================================
|
|
325
528
|
|
|
326
|
-
// Drifts: View and manage
|
|
529
|
+
// Drifts: View and manage visual drifts
|
|
327
530
|
program
|
|
328
531
|
.command("drifts [action] [id]")
|
|
329
|
-
.description("View and manage
|
|
532
|
+
.description("View and manage visual drifts")
|
|
330
533
|
.addHelpText(
|
|
331
534
|
"after",
|
|
332
535
|
`
|
|
@@ -339,7 +542,6 @@ Actions:
|
|
|
339
542
|
sync <id> Mark as manually synced (external_host)
|
|
340
543
|
approve-all Approve all pending drifts
|
|
341
544
|
reject-all Reject all pending drifts
|
|
342
|
-
validate Validate journey bindings
|
|
343
545
|
`,
|
|
344
546
|
)
|
|
345
547
|
.option("--status <status>", "Filter: PENDING, APPROVED, REJECTED, IGNORED")
|
|
@@ -348,7 +550,6 @@ Actions:
|
|
|
348
550
|
.action(async (action, id, options) => {
|
|
349
551
|
try {
|
|
350
552
|
const driftsCommand = require("./commands/drifts");
|
|
351
|
-
// Map action/id to the expected subcommand/args format
|
|
352
553
|
const subcommand = action || "list";
|
|
353
554
|
const args = id ? [id] : [];
|
|
354
555
|
await driftsCommand(subcommand, args, options);
|
|
@@ -363,9 +564,25 @@ Actions:
|
|
|
363
564
|
// ============================================================================
|
|
364
565
|
|
|
365
566
|
// Auth: Standalone authentication (for re-auth scenarios)
|
|
366
|
-
program
|
|
367
|
-
.command("auth"
|
|
368
|
-
.description(
|
|
567
|
+
const auth = program
|
|
568
|
+
.command("auth")
|
|
569
|
+
.description(
|
|
570
|
+
"Link this CLI to a Reshot project. Opens a browser to approve the session and stores a project API key locally.",
|
|
571
|
+
)
|
|
572
|
+
.addHelpText(
|
|
573
|
+
"after",
|
|
574
|
+
`
|
|
575
|
+
Authentication paths:
|
|
576
|
+
Interactive (default) Opens your browser, you approve the session, and the
|
|
577
|
+
CLI saves a project API key to .reshot/settings.json.
|
|
578
|
+
Non-interactive (CI) Set RESHOT_API_KEY and RESHOT_PROJECT_ID and the CLI
|
|
579
|
+
links without any browser or prompt.
|
|
580
|
+
|
|
581
|
+
Examples:
|
|
582
|
+
reshot auth Browser-based login
|
|
583
|
+
RESHOT_API_KEY=… RESHOT_PROJECT_ID=… reshot auth Headless / CI login
|
|
584
|
+
`,
|
|
585
|
+
)
|
|
369
586
|
.action(async () => {
|
|
370
587
|
try {
|
|
371
588
|
const authCommand = require("./commands/auth");
|
|
@@ -376,87 +593,135 @@ program
|
|
|
376
593
|
}
|
|
377
594
|
});
|
|
378
595
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
.
|
|
382
|
-
|
|
596
|
+
auth
|
|
597
|
+
.command("login")
|
|
598
|
+
.description(
|
|
599
|
+
"Alias for `reshot auth` — opens the browser approval flow (or uses RESHOT_API_KEY in CI).",
|
|
600
|
+
)
|
|
383
601
|
.action(async () => {
|
|
384
602
|
try {
|
|
385
|
-
const
|
|
386
|
-
await
|
|
603
|
+
const authCommand = require("./commands/auth");
|
|
604
|
+
await authCommand();
|
|
387
605
|
} catch (error) {
|
|
388
606
|
console.error(chalk.red("Error:"), error.message);
|
|
389
607
|
process.exit(1);
|
|
390
608
|
}
|
|
391
609
|
});
|
|
392
610
|
|
|
393
|
-
// Chrome: Launch Chrome with debugging (absorbed into record --browser)
|
|
394
611
|
program
|
|
395
|
-
.command("
|
|
396
|
-
.description(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
.action(async (
|
|
612
|
+
.command("login")
|
|
613
|
+
.description(
|
|
614
|
+
"Alias for `reshot auth` — link this CLI to a project via browser approval (or RESHOT_API_KEY in CI).",
|
|
615
|
+
)
|
|
616
|
+
.action(async () => {
|
|
400
617
|
try {
|
|
401
|
-
const
|
|
402
|
-
await
|
|
618
|
+
const authCommand = require("./commands/auth");
|
|
619
|
+
await authCommand();
|
|
403
620
|
} catch (error) {
|
|
404
621
|
console.error(chalk.red("Error:"), error.message);
|
|
405
622
|
process.exit(1);
|
|
406
623
|
}
|
|
407
624
|
});
|
|
408
625
|
|
|
409
|
-
//
|
|
626
|
+
// Init: Standalone initialization (for manual config)
|
|
410
627
|
program
|
|
411
|
-
.command("
|
|
412
|
-
.description("
|
|
413
|
-
.
|
|
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
|
-
);
|
|
628
|
+
.command("init", { hidden: true })
|
|
629
|
+
.description("Initialize Reshot configuration")
|
|
630
|
+
.action(async () => {
|
|
419
631
|
try {
|
|
420
|
-
const
|
|
421
|
-
await
|
|
422
|
-
traceDir: options.traceDir,
|
|
423
|
-
dryRun: options.dryRun,
|
|
424
|
-
});
|
|
632
|
+
const initCommand = require("./commands/init");
|
|
633
|
+
await initCommand();
|
|
425
634
|
} catch (error) {
|
|
426
635
|
console.error(chalk.red("Error:"), error.message);
|
|
427
636
|
process.exit(1);
|
|
428
637
|
}
|
|
429
638
|
});
|
|
430
639
|
|
|
431
|
-
//
|
|
640
|
+
// Chrome: Launch Chrome with debugging (absorbed into record --browser)
|
|
432
641
|
program
|
|
433
|
-
.command("
|
|
434
|
-
.description("Launch
|
|
435
|
-
.option("--port <port>", "
|
|
436
|
-
.option("--
|
|
437
|
-
.option("--no-open", "Do not automatically open browser")
|
|
642
|
+
.command("chrome", { hidden: true })
|
|
643
|
+
.description("Launch Chrome with remote debugging")
|
|
644
|
+
.option("-p, --port <port>", "Remote debugging port", "9222")
|
|
645
|
+
.option("--url <url>", "URL to open after launch", "about:blank")
|
|
438
646
|
.action(async (options) => {
|
|
439
647
|
try {
|
|
440
|
-
const
|
|
441
|
-
await
|
|
648
|
+
const chromeCommand = require("./commands/chrome");
|
|
649
|
+
await chromeCommand(options);
|
|
442
650
|
} catch (error) {
|
|
443
651
|
console.error(chalk.red("Error:"), error.message);
|
|
444
652
|
process.exit(1);
|
|
445
653
|
}
|
|
446
654
|
});
|
|
447
655
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
656
|
+
function resolveRecordClipScenarioKeys(target, options = {}) {
|
|
657
|
+
return resolveScenarioKeysFromTarget(target, options);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function resolveScenarioKeysFromTarget(target, options = {}) {
|
|
661
|
+
// Accept the singular `--scenario` as an alias for `--scenarios` — an easy
|
|
662
|
+
// trap that otherwise hard-fails the run (audit run-10 F2).
|
|
663
|
+
const scenarioList = options.scenarios || options.scenario;
|
|
664
|
+
if (scenarioList) {
|
|
665
|
+
return scenarioList.split(",").map((value) => value.trim()).filter(Boolean);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
if (!target) {
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const absoluteTarget = path.resolve(process.cwd(), target);
|
|
673
|
+
if (!fs.existsSync(absoluteTarget)) {
|
|
674
|
+
return [target];
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const source = fs.readFileSync(absoluteTarget, "utf8");
|
|
678
|
+
const config = require("./lib/config").readConfig();
|
|
679
|
+
const scenarios = config.scenarios || [];
|
|
680
|
+
const mentioned = scenarios.find((scenario) => source.includes(scenario.key));
|
|
681
|
+
if (mentioned) {
|
|
682
|
+
return [mentioned.key];
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const basename = path.basename(target).replace(/\.(spec\.)?[cm]?[tj]sx?$/i, "");
|
|
686
|
+
const byName = scenarios.find((scenario) => {
|
|
687
|
+
const normalizedKey = String(scenario.key || "").replace(/^dogfood-/, "");
|
|
688
|
+
return scenario.key === basename || normalizedKey === basename;
|
|
460
689
|
});
|
|
690
|
+
if (byName) {
|
|
691
|
+
return [byName.key];
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
throw new Error(
|
|
695
|
+
`Could not map ${target} to a configured scenario. Add the scenario key to the spec file or pass --scenarios <key>.`,
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
async function copyRecordClipOutputs(result, target, outDir) {
|
|
700
|
+
const firstScenario = (result.results || []).find((item) => item?.success !== false);
|
|
701
|
+
const outputDir = firstScenario?.outputDir;
|
|
702
|
+
if (!outputDir) {
|
|
703
|
+
throw new Error("No summary-video output directory was produced.");
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const slug = target
|
|
707
|
+
? path.basename(target).replace(/\.(spec\.)?[cm]?[tj]sx?$/i, "")
|
|
708
|
+
: firstScenario.key || "summary-video";
|
|
709
|
+
const destinationDir = path.resolve(process.cwd(), outDir);
|
|
710
|
+
await fs.ensureDir(destinationDir);
|
|
711
|
+
|
|
712
|
+
const copies = [
|
|
713
|
+
["summary-video.mp4", `${slug}.mp4`],
|
|
714
|
+
["summary-video.metadata.json", `${slug}.metadata.json`],
|
|
715
|
+
["sentinels.json", `${slug}.sentinels.json`],
|
|
716
|
+
];
|
|
717
|
+
|
|
718
|
+
for (const [fromName, toName] of copies) {
|
|
719
|
+
const sourcePath = path.join(outputDir, fromName);
|
|
720
|
+
if (await fs.pathExists(sourcePath)) {
|
|
721
|
+
await fs.copy(sourcePath, path.join(destinationDir, toName));
|
|
722
|
+
console.log(chalk.gray(` copied ${toName}`));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
461
726
|
|
|
462
727
|
program.parse(process.argv);
|