@reshotdev/screenshot 0.0.1-beta.0
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 +190 -0
- package/README.md +388 -0
- package/package.json +64 -0
- package/src/commands/auth.js +259 -0
- package/src/commands/chrome.js +140 -0
- package/src/commands/ci-run.js +123 -0
- package/src/commands/ci-setup.js +288 -0
- package/src/commands/drifts.js +423 -0
- package/src/commands/import-tests.js +309 -0
- package/src/commands/ingest.js +458 -0
- package/src/commands/init.js +633 -0
- package/src/commands/publish.js +1721 -0
- package/src/commands/pull.js +303 -0
- package/src/commands/record.js +94 -0
- package/src/commands/run.js +476 -0
- package/src/commands/setup-wizard.js +740 -0
- package/src/commands/setup.js +137 -0
- package/src/commands/status.js +275 -0
- package/src/commands/sync.js +621 -0
- package/src/commands/ui.js +248 -0
- package/src/commands/validate-docs.js +529 -0
- package/src/index.js +462 -0
- package/src/lib/api-client.js +815 -0
- package/src/lib/capture-engine.js +1623 -0
- package/src/lib/capture-script-runner.js +3120 -0
- package/src/lib/ci-detect.js +137 -0
- package/src/lib/config.js +1240 -0
- package/src/lib/diff-engine.js +642 -0
- package/src/lib/hash.js +74 -0
- package/src/lib/image-crop.js +396 -0
- package/src/lib/matrix.js +89 -0
- package/src/lib/output-path-template.js +318 -0
- package/src/lib/playwright-runner.js +252 -0
- package/src/lib/polished-clip.js +553 -0
- package/src/lib/privacy-engine.js +408 -0
- package/src/lib/progress-tracker.js +142 -0
- package/src/lib/record-browser-injection.js +654 -0
- package/src/lib/record-cdp.js +612 -0
- package/src/lib/record-clip.js +343 -0
- package/src/lib/record-config.js +623 -0
- package/src/lib/record-screenshot.js +360 -0
- package/src/lib/record-terminal.js +123 -0
- package/src/lib/recorder-service.js +781 -0
- package/src/lib/secrets.js +51 -0
- package/src/lib/selector-strategies.js +859 -0
- package/src/lib/standalone-mode.js +400 -0
- package/src/lib/storage-providers.js +569 -0
- package/src/lib/style-engine.js +684 -0
- package/src/lib/ui-api.js +4677 -0
- package/src/lib/ui-assets.js +373 -0
- package/src/lib/ui-executor.js +587 -0
- package/src/lib/variant-injector.js +591 -0
- package/src/lib/viewport-presets.js +454 -0
- package/src/lib/worker-pool.js +118 -0
- package/web/cropper/index.html +436 -0
- package/web/manager/dist/assets/index--ZgioErz.js +507 -0
- package/web/manager/dist/assets/index-n468W0Wr.css +1 -0
- package/web/manager/dist/index.html +27 -0
- package/web/subtitle-editor/index.html +295 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// setup.js - Consolidated command: auth + init + studio launch
|
|
2
|
+
const chalk = require("chalk");
|
|
3
|
+
const oraModule = require("ora");
|
|
4
|
+
const ora = oraModule.default || oraModule;
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
writeSettings,
|
|
8
|
+
readSettings,
|
|
9
|
+
configExists,
|
|
10
|
+
writeConfig,
|
|
11
|
+
SETTINGS_DIR,
|
|
12
|
+
} = require("../lib/config");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create a default docsync.config.json for platform mode
|
|
16
|
+
* @param {Object} settings - Project settings from auth
|
|
17
|
+
* @returns {Object} The created config
|
|
18
|
+
*/
|
|
19
|
+
function createDefaultConfig(settings) {
|
|
20
|
+
const defaultConfig = {
|
|
21
|
+
$schema: "https://reshot.dev/schemas/docsync-config.json",
|
|
22
|
+
version: "2.0",
|
|
23
|
+
projectId: settings.projectId,
|
|
24
|
+
baseUrl: "http://localhost:3000",
|
|
25
|
+
assetDir: ".reshot/output",
|
|
26
|
+
viewport: { width: 1280, height: 720 },
|
|
27
|
+
timeout: 30000,
|
|
28
|
+
headless: true,
|
|
29
|
+
scenarios: [],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return defaultConfig;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Main setup command - consolidates auth, init, and launches studio
|
|
37
|
+
* One command to rule them all!
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} options - Command options
|
|
40
|
+
* @param {boolean} options.noStudio - Skip launching studio after setup
|
|
41
|
+
* @param {boolean} options.force - Force re-initialization even if already set up
|
|
42
|
+
*/
|
|
43
|
+
async function setupCommand(options = {}) {
|
|
44
|
+
const { noStudio = false, force = false } = options;
|
|
45
|
+
|
|
46
|
+
console.log(chalk.cyan("\nš Reshot Setup\n"));
|
|
47
|
+
console.log(chalk.gray("Setting up your project in one step...\n"));
|
|
48
|
+
|
|
49
|
+
// Step 1: Check current authentication state
|
|
50
|
+
let existingSettings = null;
|
|
51
|
+
let isAlreadyAuthed = false;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
existingSettings = readSettings();
|
|
55
|
+
isAlreadyAuthed = !!(
|
|
56
|
+
existingSettings?.apiKey && existingSettings?.projectId
|
|
57
|
+
);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// No existing settings - that's fine
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Step 2: Handle authentication
|
|
63
|
+
if (isAlreadyAuthed && !force) {
|
|
64
|
+
console.log(
|
|
65
|
+
chalk.green("ā Already authenticated as:"),
|
|
66
|
+
chalk.cyan(existingSettings.projectName || existingSettings.projectId)
|
|
67
|
+
);
|
|
68
|
+
console.log(
|
|
69
|
+
chalk.gray(
|
|
70
|
+
" Use --force to re-authenticate with a different project.\n"
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
// Run browser-based authentication
|
|
75
|
+
console.log(chalk.gray("Opening browser for authentication...\n"));
|
|
76
|
+
|
|
77
|
+
const authCommand = require("./auth");
|
|
78
|
+
await authCommand();
|
|
79
|
+
|
|
80
|
+
// Re-read settings after auth
|
|
81
|
+
try {
|
|
82
|
+
existingSettings = readSettings();
|
|
83
|
+
isAlreadyAuthed = !!(
|
|
84
|
+
existingSettings?.apiKey && existingSettings?.projectId
|
|
85
|
+
);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
throw new Error("Authentication failed. Please try again.");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!isAlreadyAuthed) {
|
|
91
|
+
throw new Error("Authentication was not completed.");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(); // Add spacing after auth
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Step 3: Handle initialization (create docsync.config.json if missing)
|
|
98
|
+
const hasConfig = configExists();
|
|
99
|
+
|
|
100
|
+
if (hasConfig && !force) {
|
|
101
|
+
console.log(chalk.green("ā Configuration found:"), chalk.cyan("docsync.config.json"));
|
|
102
|
+
} else if (hasConfig && force) {
|
|
103
|
+
console.log(chalk.yellow("ā Overwriting existing docsync.config.json..."));
|
|
104
|
+
const newConfig = createDefaultConfig(existingSettings);
|
|
105
|
+
writeConfig(newConfig);
|
|
106
|
+
console.log(chalk.green("ā Configuration updated"));
|
|
107
|
+
} else {
|
|
108
|
+
// No config - create it
|
|
109
|
+
console.log(chalk.gray("Creating docsync.config.json..."));
|
|
110
|
+
const newConfig = createDefaultConfig(existingSettings);
|
|
111
|
+
writeConfig(newConfig);
|
|
112
|
+
console.log(chalk.green("ā Configuration created:"), chalk.cyan("docsync.config.json"));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Step 4: Success summary
|
|
116
|
+
console.log(chalk.green("\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
|
|
117
|
+
console.log(chalk.green.bold("ā Project initialized successfully!"));
|
|
118
|
+
console.log(chalk.green("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n"));
|
|
119
|
+
|
|
120
|
+
// Step 5: Launch Studio (unless --no-studio)
|
|
121
|
+
if (!noStudio) {
|
|
122
|
+
console.log(chalk.cyan("š¬ Launching Reshot Studio...\n"));
|
|
123
|
+
|
|
124
|
+
// Small delay to let the user read the success message
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
126
|
+
|
|
127
|
+
const uiCommand = require("./ui");
|
|
128
|
+
await uiCommand({ open: true });
|
|
129
|
+
} else {
|
|
130
|
+
console.log(chalk.gray("Next steps:"));
|
|
131
|
+
console.log(chalk.gray(" ⢠Run"), chalk.cyan("reshot studio"), chalk.gray("to open the management UI"));
|
|
132
|
+
console.log(chalk.gray(" ⢠Run"), chalk.cyan("reshot record"), chalk.gray("to capture your first visual"));
|
|
133
|
+
console.log(chalk.gray(" ⢠Run"), chalk.cyan("reshot run"), chalk.gray("to execute scenarios\n"));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = setupCommand;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* status - Check DocSync status, sync jobs, and drift queue
|
|
4
|
+
*
|
|
5
|
+
* Displays:
|
|
6
|
+
* - Current project configuration
|
|
7
|
+
* - Recent sync jobs with status
|
|
8
|
+
* - Drift queue summary
|
|
9
|
+
* - Pending actions
|
|
10
|
+
*
|
|
11
|
+
* Usage: reshot status [--jobs] [--drifts] [--json]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const chalk = require("chalk");
|
|
15
|
+
const config = require("../lib/config");
|
|
16
|
+
const apiClient = require("../lib/api-client");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Format timestamp for display
|
|
20
|
+
*/
|
|
21
|
+
function formatTime(dateString) {
|
|
22
|
+
const date = new Date(dateString);
|
|
23
|
+
const now = new Date();
|
|
24
|
+
const diffMs = now - date;
|
|
25
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
26
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
27
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
28
|
+
|
|
29
|
+
if (diffMins < 1) return "just now";
|
|
30
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
31
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
32
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
33
|
+
return date.toLocaleDateString();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format job status with color
|
|
38
|
+
*/
|
|
39
|
+
function formatJobStatus(status) {
|
|
40
|
+
switch (status) {
|
|
41
|
+
case "COMPLETED":
|
|
42
|
+
return chalk.green("ā Completed");
|
|
43
|
+
case "PROCESSING":
|
|
44
|
+
return chalk.blue("ā³ Processing");
|
|
45
|
+
case "QUEUED":
|
|
46
|
+
return chalk.yellow("ā· Queued");
|
|
47
|
+
case "FAILED":
|
|
48
|
+
return chalk.red("ā Failed");
|
|
49
|
+
default:
|
|
50
|
+
return chalk.gray(status);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format drift status with color
|
|
56
|
+
*/
|
|
57
|
+
function formatDriftStatus(status) {
|
|
58
|
+
switch (status) {
|
|
59
|
+
case "PENDING":
|
|
60
|
+
return chalk.yellow("ā Pending");
|
|
61
|
+
case "APPROVED":
|
|
62
|
+
return chalk.green("ā Approved");
|
|
63
|
+
case "REJECTED":
|
|
64
|
+
return chalk.red("ā Rejected");
|
|
65
|
+
case "IGNORED":
|
|
66
|
+
return chalk.gray("ā Ignored");
|
|
67
|
+
default:
|
|
68
|
+
return chalk.gray(status);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format drift type with color
|
|
74
|
+
*/
|
|
75
|
+
function formatDriftType(type) {
|
|
76
|
+
switch (type) {
|
|
77
|
+
case "VISUAL":
|
|
78
|
+
return chalk.cyan("š¼ Visual");
|
|
79
|
+
case "SEMANTIC":
|
|
80
|
+
return chalk.magenta("š Text");
|
|
81
|
+
case "BOTH":
|
|
82
|
+
return chalk.yellow("ā” Both");
|
|
83
|
+
default:
|
|
84
|
+
return chalk.gray(type);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get and display project configuration
|
|
90
|
+
*/
|
|
91
|
+
async function displayConfig(docSyncConfig) {
|
|
92
|
+
console.log(chalk.bold("\nš Project Configuration\n"));
|
|
93
|
+
|
|
94
|
+
const docConfig = docSyncConfig.documentation;
|
|
95
|
+
if (!docConfig) {
|
|
96
|
+
console.log(chalk.yellow(" No documentation block configured."));
|
|
97
|
+
console.log(chalk.gray(" Add a 'documentation' block to docsync.config.json"));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(chalk.gray(" Root: ") + chalk.white(docConfig.root || "./docs"));
|
|
102
|
+
console.log(chalk.gray(" Strategy: ") + chalk.white(docConfig.strategy));
|
|
103
|
+
console.log(chalk.gray(" Format: ") + chalk.white(docConfig.assetFormat || "markdown"));
|
|
104
|
+
|
|
105
|
+
if (docConfig.include?.length) {
|
|
106
|
+
console.log(chalk.gray(" Include: ") + chalk.white(docConfig.include.join(", ")));
|
|
107
|
+
}
|
|
108
|
+
if (docConfig.exclude?.length) {
|
|
109
|
+
console.log(chalk.gray(" Exclude: ") + chalk.white(docConfig.exclude.join(", ")));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const mappingCount = Object.keys(docConfig.mappings || {}).length;
|
|
113
|
+
if (mappingCount > 0) {
|
|
114
|
+
console.log(chalk.gray(" Mappings: ") + chalk.white(`${mappingCount} explicit binding(s)`));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get and display sync job history
|
|
120
|
+
*/
|
|
121
|
+
async function displayJobs(apiKey, projectId, limit = 5) {
|
|
122
|
+
console.log(chalk.bold("\nš¦ Recent Sync Jobs\n"));
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Fetch jobs via API
|
|
126
|
+
const response = await apiClient.getSyncJobs(apiKey, projectId, { limit });
|
|
127
|
+
|
|
128
|
+
const jobs = response.jobs || response.data?.jobs || [];
|
|
129
|
+
|
|
130
|
+
if (jobs.length === 0) {
|
|
131
|
+
console.log(chalk.gray(" No sync jobs found."));
|
|
132
|
+
console.log(chalk.gray(" Run `reshot ingest` to upload traces and docs."));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const job of jobs) {
|
|
137
|
+
console.log(
|
|
138
|
+
` ${formatJobStatus(job.status)} ` +
|
|
139
|
+
chalk.gray(`${job.id.slice(0, 8)} `) +
|
|
140
|
+
chalk.white(`${job.branch || "unknown"}@${(job.commitHash || "").slice(0, 7)} `) +
|
|
141
|
+
chalk.gray(formatTime(job.createdAt))
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (job.driftCount > 0) {
|
|
145
|
+
console.log(chalk.gray(` āā ${job.driftCount} drift(s) detected`));
|
|
146
|
+
}
|
|
147
|
+
if (job.errorMessage) {
|
|
148
|
+
console.log(chalk.red(` āā Error: ${job.errorMessage}`));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.log(chalk.yellow(" Could not fetch sync jobs: " + error.message));
|
|
153
|
+
console.log(chalk.gray(" This endpoint may not be available yet."));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get and display drift queue
|
|
159
|
+
*/
|
|
160
|
+
async function displayDrifts(apiKey, projectId, limit = 10) {
|
|
161
|
+
console.log(chalk.bold("\nš Drift Queue\n"));
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const response = await apiClient.getDrifts(apiKey, projectId, { status: "PENDING" });
|
|
165
|
+
const drifts = response.drifts || [];
|
|
166
|
+
const stats = response.stats || {};
|
|
167
|
+
|
|
168
|
+
// Show stats summary
|
|
169
|
+
if (stats.total > 0) {
|
|
170
|
+
console.log(
|
|
171
|
+
chalk.gray(" Summary: ") +
|
|
172
|
+
chalk.yellow(`${stats.pending || 0} pending`) + ", " +
|
|
173
|
+
chalk.green(`${stats.approved || 0} approved`) + ", " +
|
|
174
|
+
chalk.red(`${stats.rejected || 0} rejected`) + ", " +
|
|
175
|
+
chalk.gray(`${stats.ignored || 0} ignored`)
|
|
176
|
+
);
|
|
177
|
+
console.log();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (drifts.length === 0) {
|
|
181
|
+
console.log(chalk.green(" ā No pending drifts!"));
|
|
182
|
+
console.log(chalk.gray(" Your documentation is in sync with the application."));
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Show pending drifts
|
|
187
|
+
const displayed = drifts.slice(0, limit);
|
|
188
|
+
for (const drift of displayed) {
|
|
189
|
+
console.log(
|
|
190
|
+
` ${formatDriftType(drift.driftType)} ` +
|
|
191
|
+
chalk.white(drift.journeyKey || drift.docPath) + " " +
|
|
192
|
+
chalk.gray(`(${Math.round(drift.confidenceScore * 100)}% confidence)`)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (drift.docPath) {
|
|
196
|
+
console.log(chalk.gray(` āā ${drift.docPath}`));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (drifts.length > limit) {
|
|
201
|
+
console.log(chalk.gray(`\n ... and ${drifts.length - limit} more.`));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log();
|
|
205
|
+
console.log(chalk.gray(" View in dashboard: ") + chalk.blue(`https://reshot.dev/app/projects/${projectId}/docsync`));
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.log(chalk.yellow(" Could not fetch drifts: " + error.message));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Main status command
|
|
213
|
+
*/
|
|
214
|
+
async function statusCommand(options = {}) {
|
|
215
|
+
console.log(chalk.blue("\nš Reshot DocSync Status\n"));
|
|
216
|
+
|
|
217
|
+
// Read configuration
|
|
218
|
+
let docSyncConfig;
|
|
219
|
+
try {
|
|
220
|
+
docSyncConfig = config.readDocSyncConfig();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error(chalk.red("Error:"), "docsync.config.json not found. Run `reshot init` first.");
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Get API key and project ID
|
|
227
|
+
const settings = config.readSettings();
|
|
228
|
+
const apiKey = process.env.RESHOT_API_KEY || settings?.apiKey;
|
|
229
|
+
const projectId =
|
|
230
|
+
process.env.RESHOT_PROJECT_ID ||
|
|
231
|
+
settings?.projectId ||
|
|
232
|
+
docSyncConfig._metadata?.projectId;
|
|
233
|
+
|
|
234
|
+
if (!apiKey) {
|
|
235
|
+
console.error(chalk.red("Error:"), "API key not found. Set RESHOT_API_KEY or run `reshot auth`.");
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!projectId) {
|
|
240
|
+
console.error(chalk.red("Error:"), "Project ID not found. Set RESHOT_PROJECT_ID or run `reshot init`.");
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
console.log(chalk.gray(` Project: ${projectId}`));
|
|
245
|
+
|
|
246
|
+
// Display configuration
|
|
247
|
+
if (options.config || (!options.jobs && !options.drifts)) {
|
|
248
|
+
await displayConfig(docSyncConfig);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Display jobs
|
|
252
|
+
if (options.jobs || (!options.config && !options.drifts)) {
|
|
253
|
+
await displayJobs(apiKey, projectId, options.limit || 5);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Display drifts
|
|
257
|
+
if (options.drifts || (!options.config && !options.jobs)) {
|
|
258
|
+
await displayDrifts(apiKey, projectId, options.limit || 10);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// JSON output mode
|
|
262
|
+
if (options.json) {
|
|
263
|
+
const response = await apiClient.getDrifts(apiKey, projectId).catch(() => ({}));
|
|
264
|
+
console.log(JSON.stringify({
|
|
265
|
+
projectId,
|
|
266
|
+
config: docSyncConfig.documentation,
|
|
267
|
+
drifts: response.drifts || [],
|
|
268
|
+
stats: response.stats || {},
|
|
269
|
+
}, null, 2));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
console.log();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
module.exports = statusCommand;
|