@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
|
@@ -1,41 +1,21 @@
|
|
|
1
|
-
// setup-wizard.js -
|
|
2
|
-
//
|
|
3
|
-
// Individual commands (auth, init) still exist for power users
|
|
1
|
+
// setup-wizard.js - Interactive setup wizard
|
|
2
|
+
// Streamlined flow: choose lane, create config, optional Studio launch
|
|
4
3
|
|
|
5
4
|
const inquirer = require("inquirer");
|
|
6
5
|
const chalk = require("chalk");
|
|
7
6
|
const fs = require("fs-extra");
|
|
8
7
|
const path = require("path");
|
|
9
8
|
const config = require("../lib/config");
|
|
10
|
-
const {
|
|
11
|
-
|
|
12
|
-
getStorageSetupHelp,
|
|
13
|
-
} = require("../lib/storage-providers");
|
|
9
|
+
const { normalizeConfigContract } = require("../lib/target-contract");
|
|
10
|
+
const { detectCI } = require("../lib/ci-detect");
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
|
-
*
|
|
13
|
+
* Whether the current process can prompt the user interactively.
|
|
14
|
+
* False in CI or when stdin is not a TTY (piped / redirected), where an
|
|
15
|
+
* inquirer prompt would throw `ERR_USE_AFTER_CLOSE: readline`.
|
|
17
16
|
*/
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
"docs",
|
|
21
|
-
"documentation",
|
|
22
|
-
"content",
|
|
23
|
-
"doc",
|
|
24
|
-
"guides",
|
|
25
|
-
".vitepress/content",
|
|
26
|
-
".docusaurus/docs",
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
for (const dir of commonPaths) {
|
|
30
|
-
const fullPath = path.join(process.cwd(), dir);
|
|
31
|
-
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
32
|
-
const files = fs.readdirSync(fullPath);
|
|
33
|
-
if (files.some((f) => f.endsWith(".md") || f.endsWith(".mdx"))) {
|
|
34
|
-
return dir;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
17
|
+
function isInteractive() {
|
|
18
|
+
return !!process.stdin.isTTY && !detectCI().isCI;
|
|
39
19
|
}
|
|
40
20
|
|
|
41
21
|
/**
|
|
@@ -91,184 +71,110 @@ function detectPlaywright() {
|
|
|
91
71
|
return { hasPlaywright: false, configFile: null };
|
|
92
72
|
}
|
|
93
73
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
include: ["**/*.md", "**/*.mdx"],
|
|
101
|
-
exclude: ["**/_*.mdx", "**/node_modules/**", "**/.next/**"],
|
|
102
|
-
strategy,
|
|
103
|
-
assetFormat: "markdown",
|
|
104
|
-
mappings: {},
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Scaffold a basic documentation structure
|
|
110
|
-
*/
|
|
111
|
-
async function scaffoldDocumentation(template = "basic") {
|
|
112
|
-
const docsDir = path.join(process.cwd(), "docs");
|
|
113
|
-
fs.ensureDirSync(docsDir);
|
|
114
|
-
|
|
115
|
-
const readmeContent = `# Documentation
|
|
116
|
-
|
|
117
|
-
This directory contains documentation for your project.
|
|
118
|
-
|
|
119
|
-
## Linking Docs to Visual Journeys
|
|
120
|
-
|
|
121
|
-
Add \`reshot_journey\` frontmatter to your markdown files to link them to Playwright test captures:
|
|
122
|
-
|
|
123
|
-
\`\`\`markdown
|
|
124
|
-
---
|
|
125
|
-
title: "Feature Name"
|
|
126
|
-
reshot_journey: "feature/workflow-name"
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
# Feature Documentation
|
|
130
|
-
|
|
131
|
-
Your content here...
|
|
132
|
-
\`\`\`
|
|
133
|
-
|
|
134
|
-
The journey key should match your Playwright test structure. For example:
|
|
135
|
-
- Test file: \`tests/01-auth-flows.spec.ts\` with "Login Flow" describe block
|
|
136
|
-
- Journey key: \`auth-flows/login-flow\`
|
|
137
|
-
|
|
138
|
-
## Commands
|
|
139
|
-
|
|
140
|
-
- \`reshot import-tests\` - Import existing Playwright tests
|
|
141
|
-
- \`reshot sync\` - Sync visuals and documentation
|
|
142
|
-
- \`reshot status\` - View sync status and drifts
|
|
143
|
-
- \`reshot validate\` - Validate configuration
|
|
144
|
-
- \`reshot studio\` - Open visual management UI
|
|
145
|
-
|
|
146
|
-
For more information, visit: https://docs.reshot.dev
|
|
147
|
-
`;
|
|
148
|
-
|
|
149
|
-
fs.writeFileSync(path.join(docsDir, "README.md"), readmeContent);
|
|
150
|
-
|
|
151
|
-
const exampleContent = `---
|
|
152
|
-
title: "Getting Started"
|
|
153
|
-
reshot_journey: "onboarding/first-steps"
|
|
154
|
-
description: "Quick start guide for new users"
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
# Getting Started
|
|
158
|
-
|
|
159
|
-
Welcome! This is an example documentation file linked to the \`onboarding/first-steps\` journey.
|
|
160
|
-
|
|
161
|
-
## Overview
|
|
162
|
-
|
|
163
|
-
When Reshot detects UI changes in your Playwright tests, it will automatically propose updates to this document with new screenshots and descriptions.
|
|
74
|
+
function detectSetupMode(projectConfig, useCloud) {
|
|
75
|
+
const normalizedConfig =
|
|
76
|
+
projectConfig && typeof projectConfig === "object"
|
|
77
|
+
? normalizeConfigContract(projectConfig)
|
|
78
|
+
: null;
|
|
79
|
+
const targetTier = normalizedConfig?.target?.tier || null;
|
|
164
80
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<!-- Reshot will inject screenshots here when visual drift is detected -->
|
|
170
|
-
|
|
171
|
-
2. **Step 2**: Description of second step
|
|
172
|
-
|
|
173
|
-
3. **Step 3**: Description of third step
|
|
174
|
-
|
|
175
|
-
## Next Steps
|
|
81
|
+
if (targetTier === "certified") {
|
|
82
|
+
return "certified-target";
|
|
83
|
+
}
|
|
176
84
|
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
|
|
180
|
-
`;
|
|
85
|
+
if (targetTier === "candidate") {
|
|
86
|
+
return "candidate-target";
|
|
87
|
+
}
|
|
181
88
|
|
|
182
|
-
|
|
183
|
-
return "docs";
|
|
89
|
+
return useCloud ? "cloud-connected" : "local-only";
|
|
184
90
|
}
|
|
185
91
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
{
|
|
192
|
-
name: "Astro/Starlight",
|
|
193
|
-
files: ["astro.config.mjs", "astro.config.ts"],
|
|
194
|
-
deps: ["@astrojs/starlight"],
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: "Docusaurus",
|
|
198
|
-
files: ["docusaurus.config.js", "docusaurus.config.ts"],
|
|
199
|
-
deps: ["@docusaurus/core"],
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
name: "VitePress",
|
|
203
|
-
files: [".vitepress/config.js", ".vitepress/config.ts"],
|
|
204
|
-
deps: ["vitepress"],
|
|
205
|
-
},
|
|
206
|
-
{ name: "Mintlify", files: ["mint.json"], deps: [] },
|
|
207
|
-
{ name: "GitBook", files: [".gitbook.yaml"], deps: [] },
|
|
208
|
-
];
|
|
92
|
+
function getRecommendedLocalServerCommand(normalizedConfig) {
|
|
93
|
+
const explicitCommand = normalizedConfig?.target?.supportedLocalCommand;
|
|
94
|
+
if (explicitCommand) {
|
|
95
|
+
return explicitCommand;
|
|
96
|
+
}
|
|
209
97
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
for (const file of fw.files) {
|
|
213
|
-
if (fs.existsSync(path.join(process.cwd(), file))) {
|
|
214
|
-
return { framework: fw.name, detected: true };
|
|
215
|
-
}
|
|
216
|
-
}
|
|
98
|
+
if (normalizedConfig?.target?.tier === "certified") {
|
|
99
|
+
return "npm run build && npm run start";
|
|
217
100
|
}
|
|
218
101
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const pkg = fs.readJsonSync(path.join(process.cwd(), "package.json"));
|
|
222
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
223
104
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return { framework: fw.name, detected: true };
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
// No package.json
|
|
105
|
+
function getNextRecommendedCommand(mode, useCloud) {
|
|
106
|
+
if (mode === "certified-target" || mode === "candidate-target") {
|
|
107
|
+
return "reshot doctor target";
|
|
233
108
|
}
|
|
234
109
|
|
|
235
|
-
return
|
|
110
|
+
return useCloud ? "reshot publish" : "reshot run";
|
|
236
111
|
}
|
|
237
112
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
113
|
+
function writeSetupReport({
|
|
114
|
+
mode,
|
|
115
|
+
useCloud,
|
|
116
|
+
projectId,
|
|
117
|
+
projectName,
|
|
118
|
+
configCreated,
|
|
119
|
+
playwrightDetected,
|
|
120
|
+
supportedLocalCommand,
|
|
121
|
+
}) {
|
|
122
|
+
const reportPath = path.join(
|
|
123
|
+
process.cwd(),
|
|
124
|
+
".reshot",
|
|
125
|
+
"reports",
|
|
126
|
+
"self-serve-setup.json",
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
fs.ensureDirSync(path.dirname(reportPath));
|
|
130
|
+
fs.writeJSONSync(
|
|
131
|
+
reportPath,
|
|
132
|
+
{
|
|
133
|
+
generatedAt: new Date().toISOString(),
|
|
134
|
+
mode,
|
|
135
|
+
nextRecommendedCommand: getNextRecommendedCommand(mode, useCloud),
|
|
136
|
+
useCloud,
|
|
137
|
+
projectId: projectId || null,
|
|
138
|
+
projectName: projectName || null,
|
|
139
|
+
configCreated,
|
|
140
|
+
playwrightDetected,
|
|
141
|
+
supportedEnvironment: {
|
|
142
|
+
launchSupported: "production-like localhost",
|
|
143
|
+
launchUnsupported: ["next dev"],
|
|
144
|
+
supportedLocalCommand: supportedLocalCommand || null,
|
|
145
|
+
},
|
|
146
|
+
blockingIssues: [],
|
|
147
|
+
advisories: [
|
|
148
|
+
"Run your target app with a production-like local server for launch-grade captures.",
|
|
149
|
+
],
|
|
150
|
+
nextMilestones: [
|
|
151
|
+
"reshot setup",
|
|
152
|
+
"reshot run",
|
|
153
|
+
...(useCloud ? ["reshot publish"] : []),
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
{ spaces: 2 },
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
return reportPath;
|
|
257
160
|
}
|
|
258
161
|
|
|
259
162
|
/**
|
|
260
163
|
* Main setup wizard
|
|
261
164
|
*/
|
|
262
165
|
async function setupWizard(options = {}) {
|
|
263
|
-
const {
|
|
166
|
+
const {
|
|
167
|
+
offline = false,
|
|
168
|
+
force = false,
|
|
169
|
+
noStudio = false,
|
|
170
|
+
project: linkedProjectId,
|
|
171
|
+
token: linkedToken,
|
|
172
|
+
} = options;
|
|
264
173
|
|
|
265
|
-
console.log(chalk.cyan.bold("\n🚀 Reshot Setup
|
|
266
|
-
console.log(chalk.gray("Let's configure Reshot for your project.\n"));
|
|
174
|
+
console.log(chalk.cyan.bold("\n🚀 Reshot Setup\n"));
|
|
267
175
|
|
|
268
176
|
// Detect project context
|
|
269
|
-
const gitInfo = detectGitInfo();
|
|
270
177
|
const playwrightInfo = detectPlaywright();
|
|
271
|
-
const existingDocsRoot = detectDocumentationRoot();
|
|
272
178
|
|
|
273
179
|
// Check existing setup
|
|
274
180
|
let existingSettings = null;
|
|
@@ -292,8 +198,55 @@ async function setupWizard(options = {}) {
|
|
|
292
198
|
// No existing config
|
|
293
199
|
}
|
|
294
200
|
|
|
295
|
-
|
|
296
|
-
if (
|
|
201
|
+
let didLinkFromOptions = false;
|
|
202
|
+
if (linkedProjectId && linkedToken && !offline) {
|
|
203
|
+
const authCommand = require("./auth");
|
|
204
|
+
const authResult = await authCommand({
|
|
205
|
+
projectId: linkedProjectId,
|
|
206
|
+
apiKey: linkedToken,
|
|
207
|
+
});
|
|
208
|
+
existingSettings = config.readSettings();
|
|
209
|
+
isAlreadyAuthed = !!(
|
|
210
|
+
existingSettings?.apiKey && existingSettings?.projectId
|
|
211
|
+
);
|
|
212
|
+
didLinkFromOptions = true;
|
|
213
|
+
console.log(
|
|
214
|
+
chalk.green("\n✔ Connected to"),
|
|
215
|
+
chalk.cyan(existingSettings.projectName || existingSettings.projectId),
|
|
216
|
+
);
|
|
217
|
+
if (authResult?.mode) {
|
|
218
|
+
console.log(chalk.gray(` Mode: ${authResult.mode}`));
|
|
219
|
+
}
|
|
220
|
+
} else if (linkedProjectId && !linkedToken && !offline) {
|
|
221
|
+
console.log(
|
|
222
|
+
chalk.gray(
|
|
223
|
+
`Setup will target project ${linkedProjectId}. Complete browser authentication when prompted.`,
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// The interactive wizard below needs a TTY. In CI / piped-stdin contexts
|
|
229
|
+
// (and when no `--project/--token` were supplied to drive it non-
|
|
230
|
+
// interactively), bail out with clear guidance instead of letting inquirer
|
|
231
|
+
// throw an unhandled `ERR_USE_AFTER_CLOSE: readline` (audit run-11 F4).
|
|
232
|
+
if (!isInteractive() && !didLinkFromOptions) {
|
|
233
|
+
console.log(
|
|
234
|
+
chalk.yellow("\n⚠ Non-interactive environment detected (no TTY)."),
|
|
235
|
+
);
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.gray(" Run setup with your project credentials instead:"),
|
|
238
|
+
);
|
|
239
|
+
console.log(
|
|
240
|
+
chalk.cyan(" npx reshot setup --project <projectId> --token <token>"),
|
|
241
|
+
);
|
|
242
|
+
console.log(
|
|
243
|
+
chalk.gray(" or set RESHOT_PROJECT_ID and RESHOT_API_KEY, then re-run.\n"),
|
|
244
|
+
);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// If already set up and not forcing, show status and offer options
|
|
249
|
+
if ((isAlreadyAuthed || existingConfig) && !force && !didLinkFromOptions) {
|
|
297
250
|
console.log(
|
|
298
251
|
chalk.yellow("⚠ Reshot is already configured in this project.\n"),
|
|
299
252
|
);
|
|
@@ -307,20 +260,39 @@ async function setupWizard(options = {}) {
|
|
|
307
260
|
if (existingConfig) {
|
|
308
261
|
console.log(
|
|
309
262
|
chalk.green(" ✔ Config found:"),
|
|
310
|
-
chalk.cyan("
|
|
263
|
+
chalk.cyan("reshot.config.json"),
|
|
311
264
|
);
|
|
312
265
|
}
|
|
313
266
|
|
|
314
|
-
const
|
|
267
|
+
const choices = [];
|
|
268
|
+
|
|
269
|
+
if (isAlreadyAuthed) {
|
|
270
|
+
choices.push({
|
|
271
|
+
name: "Re-authenticate (connect to a different project)",
|
|
272
|
+
value: "reauth",
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
choices.push({
|
|
277
|
+
name: "Reconfigure (regenerate reshot.config.json)",
|
|
278
|
+
value: "reconfig",
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
choices.push({
|
|
282
|
+
name: "Exit",
|
|
283
|
+
value: "exit",
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const { action } = await inquirer.prompt([
|
|
315
287
|
{
|
|
316
|
-
type: "
|
|
317
|
-
name: "
|
|
318
|
-
message: "
|
|
319
|
-
|
|
288
|
+
type: "list",
|
|
289
|
+
name: "action",
|
|
290
|
+
message: "What would you like to do?",
|
|
291
|
+
choices,
|
|
320
292
|
},
|
|
321
293
|
]);
|
|
322
294
|
|
|
323
|
-
if (
|
|
295
|
+
if (action === "exit") {
|
|
324
296
|
console.log(
|
|
325
297
|
chalk.gray("\nRun"),
|
|
326
298
|
chalk.cyan("reshot studio"),
|
|
@@ -328,19 +300,50 @@ async function setupWizard(options = {}) {
|
|
|
328
300
|
);
|
|
329
301
|
return;
|
|
330
302
|
}
|
|
303
|
+
|
|
304
|
+
if (action === "reauth") {
|
|
305
|
+
console.log(chalk.gray("\nOpening browser for authentication...\n"));
|
|
306
|
+
const authCommand = require("./auth");
|
|
307
|
+
const authResult = await authCommand();
|
|
308
|
+
|
|
309
|
+
// Re-read settings after auth
|
|
310
|
+
try {
|
|
311
|
+
existingSettings = config.readSettings();
|
|
312
|
+
isAlreadyAuthed = !!(
|
|
313
|
+
existingSettings?.apiKey && existingSettings?.projectId
|
|
314
|
+
);
|
|
315
|
+
} catch {
|
|
316
|
+
throw new Error("Authentication failed. Please try again.");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!isAlreadyAuthed) {
|
|
320
|
+
throw new Error("Authentication was not completed.");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
console.log(
|
|
324
|
+
chalk.green("\n✔ Connected to"),
|
|
325
|
+
chalk.cyan(existingSettings.projectName || existingSettings.projectId),
|
|
326
|
+
);
|
|
327
|
+
if (authResult?.mode) {
|
|
328
|
+
console.log(chalk.gray(` Mode: ${authResult.mode}`));
|
|
329
|
+
}
|
|
330
|
+
console.log(
|
|
331
|
+
chalk.gray("\nRun"),
|
|
332
|
+
chalk.cyan("reshot studio"),
|
|
333
|
+
chalk.gray("to manage your visuals.\n"),
|
|
334
|
+
);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// action === "reconfig" — fall through to config generation below
|
|
331
339
|
}
|
|
332
340
|
|
|
333
341
|
// ========================================
|
|
334
342
|
// STEP 1: Platform Connection
|
|
335
343
|
// ========================================
|
|
336
|
-
|
|
344
|
+
if (!isAlreadyAuthed && !offline) {
|
|
345
|
+
console.log(chalk.cyan("\n━━━ Step 1: Choose Your Lane ━━━\n"));
|
|
337
346
|
|
|
338
|
-
let useCloud = false;
|
|
339
|
-
let projectId = existingSettings?.projectId;
|
|
340
|
-
let apiKey = existingSettings?.apiKey;
|
|
341
|
-
let projectName = existingSettings?.projectName;
|
|
342
|
-
|
|
343
|
-
if (!offline) {
|
|
344
347
|
const { connectToCloud } = await inquirer.prompt([
|
|
345
348
|
{
|
|
346
349
|
type: "list",
|
|
@@ -348,33 +351,29 @@ async function setupWizard(options = {}) {
|
|
|
348
351
|
message: "How would you like to use Reshot?",
|
|
349
352
|
choices: [
|
|
350
353
|
{
|
|
351
|
-
name: `${chalk.cyan("
|
|
354
|
+
name: `${chalk.cyan("Set up hosted pipeline")} - Connect to Reshot Cloud for hosted assets, review workflows, and team collaboration`,
|
|
352
355
|
value: true,
|
|
353
356
|
},
|
|
354
357
|
{
|
|
355
|
-
name: `${chalk.gray("
|
|
358
|
+
name: `${chalk.gray("Start locally")} - Get a first capture working on your machine before you add hosted delivery`,
|
|
356
359
|
value: false,
|
|
357
360
|
},
|
|
358
361
|
],
|
|
359
|
-
default: isAlreadyAuthed ? 0 : 1,
|
|
360
362
|
},
|
|
361
363
|
]);
|
|
362
364
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (useCloud && !isAlreadyAuthed) {
|
|
365
|
+
if (connectToCloud) {
|
|
366
366
|
console.log(chalk.gray("\nOpening browser for authentication...\n"));
|
|
367
367
|
|
|
368
368
|
const authCommand = require("./auth");
|
|
369
|
-
await authCommand();
|
|
369
|
+
const authResult = await authCommand();
|
|
370
370
|
|
|
371
371
|
// Re-read settings after auth
|
|
372
372
|
try {
|
|
373
373
|
existingSettings = config.readSettings();
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
isAlreadyAuthed = !!(apiKey && projectId);
|
|
374
|
+
isAlreadyAuthed = !!(
|
|
375
|
+
existingSettings?.apiKey && existingSettings?.projectId
|
|
376
|
+
);
|
|
378
377
|
} catch {
|
|
379
378
|
throw new Error("Authentication failed. Please try again.");
|
|
380
379
|
}
|
|
@@ -385,105 +384,46 @@ async function setupWizard(options = {}) {
|
|
|
385
384
|
|
|
386
385
|
console.log(
|
|
387
386
|
chalk.green("\n✔ Connected to"),
|
|
388
|
-
chalk.cyan(projectName || projectId),
|
|
387
|
+
chalk.cyan(existingSettings.projectName || existingSettings.projectId),
|
|
389
388
|
);
|
|
390
|
-
|
|
389
|
+
if (authResult?.mode) {
|
|
390
|
+
console.log(chalk.gray(` Mode: ${authResult.mode}`));
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
391
393
|
console.log(
|
|
392
|
-
chalk.
|
|
393
|
-
|
|
394
|
+
chalk.gray(
|
|
395
|
+
"Running in local-only mode — hosted publish and pull stay unavailable until you run `reshot auth`.",
|
|
396
|
+
),
|
|
394
397
|
);
|
|
395
398
|
}
|
|
396
|
-
} else {
|
|
397
|
-
console.log(
|
|
399
|
+
} else if (offline) {
|
|
400
|
+
console.log(
|
|
401
|
+
chalk.gray(
|
|
402
|
+
"Running in local-only mode — hosted publish and pull stay unavailable until you run `reshot auth`.",
|
|
403
|
+
),
|
|
404
|
+
);
|
|
398
405
|
}
|
|
399
406
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
type: "checkbox",
|
|
408
|
-
name: "features",
|
|
409
|
-
message: "Which features would you like to enable?",
|
|
410
|
-
choices: [
|
|
411
|
-
{
|
|
412
|
-
name: "Visual capture from Playwright tests",
|
|
413
|
-
value: "visuals",
|
|
414
|
-
checked: playwrightInfo.hasPlaywright,
|
|
415
|
-
},
|
|
416
|
-
{
|
|
417
|
-
name: "Documentation sync (detect drift, auto-update docs)",
|
|
418
|
-
value: "docsync",
|
|
419
|
-
checked: !!existingDocsRoot,
|
|
420
|
-
},
|
|
421
|
-
],
|
|
422
|
-
validate: (answers) => {
|
|
423
|
-
if (answers.length === 0) {
|
|
424
|
-
return "Please select at least one feature";
|
|
425
|
-
}
|
|
426
|
-
return true;
|
|
427
|
-
},
|
|
428
|
-
},
|
|
429
|
-
]);
|
|
430
|
-
|
|
431
|
-
const enableVisuals = features.includes("visuals");
|
|
432
|
-
const enableDocSync = features.includes("docsync");
|
|
407
|
+
const useCloud = isAlreadyAuthed;
|
|
408
|
+
const projectId = existingSettings?.projectId;
|
|
409
|
+
const normalizedSetupConfig = normalizeConfigContract(
|
|
410
|
+
existingConfig && typeof existingConfig === "object" ? existingConfig : {},
|
|
411
|
+
);
|
|
412
|
+
const supportedLocalCommand =
|
|
413
|
+
getRecommendedLocalServerCommand(normalizedSetupConfig);
|
|
433
414
|
|
|
434
415
|
// ========================================
|
|
435
|
-
// STEP
|
|
416
|
+
// STEP 2: Project Defaults
|
|
436
417
|
// ========================================
|
|
437
|
-
|
|
438
|
-
let journeyMappings = {};
|
|
439
|
-
|
|
440
|
-
if (enableVisuals) {
|
|
441
|
-
console.log(chalk.cyan("\n━━━ Step 3: Visual Capture ━━━\n"));
|
|
418
|
+
console.log(chalk.cyan("\n━━━ Step 2: Project Defaults ━━━\n"));
|
|
442
419
|
|
|
443
|
-
|
|
444
|
-
console.log(chalk.green("✔ Playwright detected"));
|
|
445
|
-
if (playwrightInfo.configFile) {
|
|
446
|
-
console.log(chalk.gray(` Config: ${playwrightInfo.configFile}`));
|
|
447
|
-
}
|
|
420
|
+
let traceDir = "./test-results";
|
|
448
421
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
name: "importTests",
|
|
454
|
-
message:
|
|
455
|
-
"Import existing Playwright tests to create journey mappings?",
|
|
456
|
-
default: true,
|
|
457
|
-
},
|
|
458
|
-
]);
|
|
459
|
-
|
|
460
|
-
if (importTests) {
|
|
461
|
-
console.log(chalk.gray("\nScanning Playwright tests..."));
|
|
462
|
-
try {
|
|
463
|
-
const importTestsCommand = require("./import-tests");
|
|
464
|
-
const result = await importTestsCommand({
|
|
465
|
-
interactive: false,
|
|
466
|
-
dryRun: false,
|
|
467
|
-
});
|
|
468
|
-
if (result?.journeyMappings) {
|
|
469
|
-
journeyMappings = result.journeyMappings;
|
|
470
|
-
console.log(
|
|
471
|
-
chalk.green(
|
|
472
|
-
`✔ Imported ${Object.keys(journeyMappings).length} journey mapping(s)`,
|
|
473
|
-
),
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
} catch (err) {
|
|
477
|
-
console.log(chalk.yellow(`⚠ Could not import tests: ${err.message}`));
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
} else {
|
|
481
|
-
console.log(chalk.yellow("⚠ Playwright not detected"));
|
|
482
|
-
console.log(
|
|
483
|
-
chalk.gray(" Install with: npm install -D @playwright/test"),
|
|
484
|
-
);
|
|
422
|
+
if (playwrightInfo.hasPlaywright) {
|
|
423
|
+
console.log(chalk.green("✔ Playwright detected"));
|
|
424
|
+
if (playwrightInfo.configFile) {
|
|
425
|
+
console.log(chalk.gray(` Config: ${playwrightInfo.configFile}`));
|
|
485
426
|
}
|
|
486
|
-
|
|
487
427
|
const { customTraceDir } = await inquirer.prompt([
|
|
488
428
|
{
|
|
489
429
|
type: "input",
|
|
@@ -494,253 +434,165 @@ async function setupWizard(options = {}) {
|
|
|
494
434
|
]);
|
|
495
435
|
|
|
496
436
|
traceDir = customTraceDir;
|
|
437
|
+
} else {
|
|
438
|
+
console.log(chalk.green("✔ No Playwright setup detected"));
|
|
439
|
+
console.log(
|
|
440
|
+
chalk.gray(
|
|
441
|
+
" That is okay for the local-first workflow. You can define or record scenarios and run them directly.",
|
|
442
|
+
),
|
|
443
|
+
);
|
|
497
444
|
}
|
|
498
445
|
|
|
499
446
|
// ========================================
|
|
500
|
-
//
|
|
501
|
-
// ========================================
|
|
502
|
-
let docConfig = null;
|
|
503
|
-
|
|
504
|
-
if (enableDocSync) {
|
|
505
|
-
console.log(chalk.cyan("\n━━━ Step 4: Documentation Sync ━━━\n"));
|
|
506
|
-
|
|
507
|
-
// Check for existing documentation framework
|
|
508
|
-
const docFramework = detectDocumentationFramework();
|
|
509
|
-
let docsRoot = existingDocsRoot;
|
|
510
|
-
|
|
511
|
-
if (docFramework.detected) {
|
|
512
|
-
console.log(chalk.green(`✔ ${docFramework.framework} detected`));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (docsRoot) {
|
|
516
|
-
const docCount = countDocFiles(path.join(process.cwd(), docsRoot));
|
|
517
|
-
console.log(
|
|
518
|
-
chalk.green(
|
|
519
|
-
`✔ Found documentation at: ${docsRoot}/ (${docCount} files)`,
|
|
520
|
-
),
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
const { useDetected } = await inquirer.prompt([
|
|
524
|
-
{
|
|
525
|
-
type: "confirm",
|
|
526
|
-
name: "useDetected",
|
|
527
|
-
message: `Use ${docsRoot}/ for documentation sync?`,
|
|
528
|
-
default: true,
|
|
529
|
-
},
|
|
530
|
-
]);
|
|
531
|
-
|
|
532
|
-
if (!useDetected) {
|
|
533
|
-
docsRoot = null;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (!docsRoot) {
|
|
538
|
-
const choices = [
|
|
539
|
-
{ name: "Create docs/ directory with examples", value: "scaffold" },
|
|
540
|
-
{ name: "Specify existing directory path", value: "specify" },
|
|
541
|
-
];
|
|
542
|
-
|
|
543
|
-
// Add Astro Starlight option if not already a docs framework
|
|
544
|
-
if (!docFramework.detected) {
|
|
545
|
-
choices.push({
|
|
546
|
-
name:
|
|
547
|
-
chalk.cyan("Create Astro Starlight project") +
|
|
548
|
-
chalk.gray(" (recommended for new docs)"),
|
|
549
|
-
value: "starlight",
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const { docAction } = await inquirer.prompt([
|
|
554
|
-
{
|
|
555
|
-
type: "list",
|
|
556
|
-
name: "docAction",
|
|
557
|
-
message: "Documentation setup:",
|
|
558
|
-
choices,
|
|
559
|
-
},
|
|
560
|
-
]);
|
|
561
|
-
|
|
562
|
-
if (docAction === "scaffold") {
|
|
563
|
-
console.log(chalk.gray("\nCreating docs/ directory..."));
|
|
564
|
-
docsRoot = await scaffoldDocumentation();
|
|
565
|
-
console.log(chalk.green("✔ Created docs/ with example files"));
|
|
566
|
-
} else if (docAction === "starlight") {
|
|
567
|
-
console.log(chalk.cyan("\nTo create an Astro Starlight project:\n"));
|
|
568
|
-
console.log(
|
|
569
|
-
chalk.white(" npm create astro@latest -- --template starlight"),
|
|
570
|
-
);
|
|
571
|
-
console.log(
|
|
572
|
-
chalk.gray(
|
|
573
|
-
"\nRun that command, then re-run reshot setup to link it.\n",
|
|
574
|
-
),
|
|
575
|
-
);
|
|
576
|
-
|
|
577
|
-
// Still create a basic docs folder as fallback
|
|
578
|
-
const { createBasic } = await inquirer.prompt([
|
|
579
|
-
{
|
|
580
|
-
type: "confirm",
|
|
581
|
-
name: "createBasic",
|
|
582
|
-
message: "Create a basic docs/ folder for now?",
|
|
583
|
-
default: true,
|
|
584
|
-
},
|
|
585
|
-
]);
|
|
586
|
-
|
|
587
|
-
if (createBasic) {
|
|
588
|
-
docsRoot = await scaffoldDocumentation();
|
|
589
|
-
console.log(chalk.green("✔ Created docs/ with example files"));
|
|
590
|
-
}
|
|
591
|
-
} else {
|
|
592
|
-
const { customPath } = await inquirer.prompt([
|
|
593
|
-
{
|
|
594
|
-
type: "input",
|
|
595
|
-
name: "customPath",
|
|
596
|
-
message: "Documentation directory path:",
|
|
597
|
-
default: "./docs",
|
|
598
|
-
validate: (input) => {
|
|
599
|
-
const fullPath = path.join(process.cwd(), input);
|
|
600
|
-
if (!fs.existsSync(fullPath)) {
|
|
601
|
-
return `Directory ${input} does not exist. Create it first or use scaffolding.`;
|
|
602
|
-
}
|
|
603
|
-
return true;
|
|
604
|
-
},
|
|
605
|
-
},
|
|
606
|
-
]);
|
|
607
|
-
docsRoot = customPath;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Determine strategy
|
|
612
|
-
let strategy = "git_pr";
|
|
613
|
-
|
|
614
|
-
if (useCloud && docsRoot) {
|
|
615
|
-
if (gitInfo.isGitHub) {
|
|
616
|
-
console.log(chalk.green("\n✔ GitHub repository detected"));
|
|
617
|
-
const { useGitPR } = await inquirer.prompt([
|
|
618
|
-
{
|
|
619
|
-
type: "confirm",
|
|
620
|
-
name: "useGitPR",
|
|
621
|
-
message: "Auto-create Pull Requests for documentation updates?",
|
|
622
|
-
default: true,
|
|
623
|
-
},
|
|
624
|
-
]);
|
|
625
|
-
strategy = useGitPR ? "git_pr" : "external_host";
|
|
626
|
-
} else {
|
|
627
|
-
const { strategyChoice } = await inquirer.prompt([
|
|
628
|
-
{
|
|
629
|
-
type: "list",
|
|
630
|
-
name: "strategyChoice",
|
|
631
|
-
message: "How should documentation updates be delivered?",
|
|
632
|
-
choices: [
|
|
633
|
-
{ name: "Git Pull Requests", value: "git_pr" },
|
|
634
|
-
{
|
|
635
|
-
name: "Notifications only (external CMS)",
|
|
636
|
-
value: "external_host",
|
|
637
|
-
},
|
|
638
|
-
],
|
|
639
|
-
},
|
|
640
|
-
]);
|
|
641
|
-
strategy = strategyChoice;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
docConfig = generateDocumentationConfig(docsRoot, strategy);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// ========================================
|
|
649
|
-
// STEP 5: Generate Configuration
|
|
447
|
+
// Generate Configuration
|
|
650
448
|
// ========================================
|
|
651
449
|
console.log(chalk.cyan("\n━━━ Generating Configuration ━━━\n"));
|
|
652
450
|
|
|
653
451
|
const newConfig = {
|
|
654
|
-
$schema: "https://reshot.dev/schemas/
|
|
452
|
+
$schema: "https://reshot.dev/schemas/reshot-config.json",
|
|
655
453
|
version: "2.0",
|
|
656
454
|
baseUrl: existingConfig?.baseUrl || "http://localhost:3000",
|
|
657
455
|
viewport: existingConfig?.viewport || { width: 1280, height: 720 },
|
|
658
|
-
_metadata: {
|
|
659
|
-
features: {
|
|
660
|
-
visuals: enableVisuals,
|
|
661
|
-
docsync: enableDocSync,
|
|
662
|
-
},
|
|
663
|
-
},
|
|
664
456
|
};
|
|
665
457
|
|
|
666
458
|
if (useCloud && projectId) {
|
|
667
459
|
newConfig.projectId = projectId;
|
|
668
460
|
}
|
|
669
461
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
journeyMappings:
|
|
674
|
-
Object.keys(journeyMappings).length > 0 ? journeyMappings : undefined,
|
|
675
|
-
};
|
|
676
|
-
|
|
677
|
-
if (!useCloud) {
|
|
678
|
-
const { customAssetDir } = await inquirer.prompt([
|
|
679
|
-
{
|
|
680
|
-
type: "input",
|
|
681
|
-
name: "customAssetDir",
|
|
682
|
-
message: "Where should screenshots be saved?",
|
|
683
|
-
default: ".reshot/output",
|
|
684
|
-
},
|
|
685
|
-
]);
|
|
686
|
-
newConfig.assetDir = customAssetDir;
|
|
687
|
-
} else {
|
|
688
|
-
newConfig.assetDir = ".reshot/output";
|
|
689
|
-
}
|
|
462
|
+
newConfig.visuals = {
|
|
463
|
+
traceDir,
|
|
464
|
+
};
|
|
690
465
|
|
|
691
|
-
|
|
466
|
+
if (!useCloud) {
|
|
467
|
+
const { customAssetDir } = await inquirer.prompt([
|
|
468
|
+
{
|
|
469
|
+
type: "input",
|
|
470
|
+
name: "customAssetDir",
|
|
471
|
+
message: "Where should screenshots be saved?",
|
|
472
|
+
default: ".reshot/output",
|
|
473
|
+
},
|
|
474
|
+
]);
|
|
475
|
+
newConfig.assetDir = customAssetDir;
|
|
476
|
+
} else {
|
|
477
|
+
newConfig.assetDir = ".reshot/output";
|
|
692
478
|
}
|
|
693
479
|
|
|
694
|
-
|
|
695
|
-
newConfig.documentation = docConfig;
|
|
696
|
-
}
|
|
480
|
+
newConfig.scenarios = existingConfig?.scenarios || [];
|
|
697
481
|
|
|
698
482
|
// Write configuration
|
|
699
483
|
config.writeConfig(newConfig);
|
|
700
|
-
console.log(chalk.green("✔ Created
|
|
484
|
+
console.log(chalk.green("✔ Created reshot.config.json"));
|
|
485
|
+
|
|
486
|
+
const combinedConfig =
|
|
487
|
+
existingConfig && typeof existingConfig === "object"
|
|
488
|
+
? { ...existingConfig, ...newConfig }
|
|
489
|
+
: newConfig;
|
|
490
|
+
const normalizedCombinedConfig = normalizeConfigContract(combinedConfig);
|
|
491
|
+
const setupMode = detectSetupMode(combinedConfig, useCloud);
|
|
492
|
+
const finalSupportedLocalCommand =
|
|
493
|
+
getRecommendedLocalServerCommand(normalizedCombinedConfig) ||
|
|
494
|
+
supportedLocalCommand;
|
|
495
|
+
const reportPath = writeSetupReport({
|
|
496
|
+
mode: setupMode,
|
|
497
|
+
useCloud,
|
|
498
|
+
projectId,
|
|
499
|
+
projectName: existingSettings?.projectName,
|
|
500
|
+
configCreated: true,
|
|
501
|
+
playwrightDetected: playwrightInfo.hasPlaywright,
|
|
502
|
+
supportedLocalCommand: finalSupportedLocalCommand,
|
|
503
|
+
});
|
|
701
504
|
|
|
702
505
|
// ========================================
|
|
703
|
-
//
|
|
506
|
+
// Success & Next Steps
|
|
704
507
|
// ========================================
|
|
705
508
|
console.log(chalk.green("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
706
509
|
console.log(chalk.green.bold("✔ Reshot setup complete!"));
|
|
707
510
|
console.log(chalk.green("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"));
|
|
708
511
|
|
|
709
|
-
console.log(chalk.cyan("
|
|
512
|
+
console.log(chalk.cyan("Supported launch path:\n"));
|
|
513
|
+
console.log(
|
|
514
|
+
` ${chalk.gray("Run your target app with a production-like local server before capture.")}`,
|
|
515
|
+
);
|
|
516
|
+
console.log(
|
|
517
|
+
` ${chalk.cyan(finalSupportedLocalCommand || "npm run build && npm run start")}`,
|
|
518
|
+
);
|
|
519
|
+
console.log(
|
|
520
|
+
` ${chalk.gray("Unsupported for launch reliability:")} ${chalk.yellow("next dev")}\n`,
|
|
521
|
+
);
|
|
710
522
|
|
|
523
|
+
console.log(chalk.cyan("Setup mode:\n"));
|
|
524
|
+
console.log(` ${chalk.green(setupMode)}\n`);
|
|
525
|
+
|
|
526
|
+
console.log(chalk.cyan("Next steps:\n"));
|
|
527
|
+
console.log(
|
|
528
|
+
` 1. ${chalk.gray("Review")} ${chalk.cyan("reshot.config.json")} ${chalk.gray("and add your first scenario or recording")}`,
|
|
529
|
+
);
|
|
711
530
|
console.log(
|
|
712
|
-
`
|
|
531
|
+
` 2. ${chalk.gray("Start your app in the supported environment:")}`,
|
|
713
532
|
);
|
|
533
|
+
console.log(
|
|
534
|
+
` ${chalk.cyan(finalSupportedLocalCommand || "npm run build && npm run start")}`,
|
|
535
|
+
);
|
|
536
|
+
console.log(` 3. ${chalk.gray("Generate your first local capture:")}`);
|
|
537
|
+
console.log(` ${chalk.cyan("reshot run")}`);
|
|
714
538
|
|
|
715
|
-
if (
|
|
539
|
+
if (useCloud) {
|
|
716
540
|
console.log(
|
|
717
|
-
|
|
541
|
+
`\n 4. ${chalk.gray("Upgrade to hosted assets when you are ready:")}`,
|
|
718
542
|
);
|
|
719
|
-
console.log(` ${chalk.cyan("
|
|
543
|
+
console.log(` ${chalk.cyan("reshot publish")}`);
|
|
544
|
+
} else {
|
|
545
|
+
console.log(
|
|
546
|
+
`\n 4. ${chalk.gray("Connect hosted delivery later when you are ready:")}`,
|
|
547
|
+
);
|
|
548
|
+
console.log(` ${chalk.cyan("reshot auth")}`);
|
|
720
549
|
}
|
|
721
550
|
|
|
722
|
-
if (
|
|
551
|
+
if (setupMode === "certified-target" || setupMode === "candidate-target") {
|
|
723
552
|
console.log(
|
|
724
|
-
|
|
553
|
+
`\n 5. ${chalk.gray("Use advanced target checks when you need them:")}`,
|
|
725
554
|
);
|
|
555
|
+
console.log(` ${chalk.cyan("reshot doctor target")}`);
|
|
556
|
+
console.log(` ${chalk.cyan("reshot verify publish")}`);
|
|
557
|
+
if (setupMode === "certified-target") {
|
|
558
|
+
console.log(` ${chalk.cyan("reshot certify")}`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
console.log(`\n ${chalk.gray("Open Studio to inspect output locally:")}`);
|
|
563
|
+
console.log(` ${chalk.cyan("reshot studio")}`);
|
|
564
|
+
console.log(
|
|
565
|
+
`\n ${chalk.gray("Supported environments guide:")} ${chalk.cyan("https://reshot.dev/docs/cli/getting-started/supported-environments")}`,
|
|
566
|
+
);
|
|
567
|
+
console.log(
|
|
568
|
+
`\n ${chalk.gray("Setup report written to:")} ${chalk.cyan(path.relative(process.cwd(), reportPath))}\n`,
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
if (noStudio) {
|
|
726
572
|
console.log(
|
|
727
|
-
|
|
573
|
+
chalk.gray("Studio launch skipped. Run `reshot studio` when you want the local UI.\n"),
|
|
728
574
|
);
|
|
575
|
+
return;
|
|
729
576
|
}
|
|
730
577
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
578
|
+
// Offer to launch studio. In non-interactive environments (CI, piped stdin)
|
|
579
|
+
// an inquirer prompt throws `ERR_USE_AFTER_CLOSE: readline`, so skip the
|
|
580
|
+
// prompt and auto-answer the safe default (do NOT launch Studio).
|
|
581
|
+
if (!isInteractive()) {
|
|
582
|
+
console.log(
|
|
583
|
+
chalk.gray(
|
|
584
|
+
"Non-interactive environment detected — skipping Studio launch. Run `reshot studio` when you want the local UI.\n",
|
|
585
|
+
),
|
|
586
|
+
);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
736
589
|
|
|
737
|
-
// Offer to launch studio
|
|
738
590
|
const { launchStudio } = await inquirer.prompt([
|
|
739
591
|
{
|
|
740
592
|
type: "confirm",
|
|
741
593
|
name: "launchStudio",
|
|
742
594
|
message: "Launch Reshot Studio now?",
|
|
743
|
-
default:
|
|
595
|
+
default: false,
|
|
744
596
|
},
|
|
745
597
|
]);
|
|
746
598
|
|