cssprobe-cli 0.3.0 → 0.4.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/AGENTS.md +59 -6
- package/CHANGELOG.md +21 -0
- package/README.md +111 -2
- package/dist/browser/frames.d.ts +15 -0
- package/dist/browser/frames.js +60 -0
- package/dist/browser/interactions.d.ts +49 -0
- package/dist/browser/interactions.js +96 -0
- package/dist/browser/launcher.d.ts +30 -2
- package/dist/browser/launcher.js +141 -34
- package/dist/browser/sampler.d.ts +43 -0
- package/dist/browser/sampler.js +111 -0
- package/dist/cli/commands.d.ts +3 -0
- package/dist/cli/commands.js +162 -4
- package/dist/cli/input.d.ts +6 -0
- package/dist/cli/input.js +24 -0
- package/dist/cli/program.js +171 -1
- package/dist/cli/session-ops.d.ts +10 -1
- package/dist/cli/session-ops.js +27 -56
- package/dist/cli/state-check.d.ts +17 -0
- package/dist/cli/state-check.js +43 -0
- package/dist/cli/url-guard.d.ts +2 -0
- package/dist/cli/url-guard.js +24 -0
- package/dist/daemon/daemon.js +162 -18
- package/dist/daemon/expect.d.ts +8 -0
- package/dist/daemon/expect.js +47 -0
- package/dist/daemon/flowRunner.d.ts +73 -0
- package/dist/daemon/flowRunner.js +177 -0
- package/dist/engine/evalSerializer.d.ts +1 -0
- package/dist/engine/evalSerializer.js +43 -0
- package/dist/help.json +156 -16
- package/dist/installer/skill-template.js +43 -0
- package/dist/scenario/assertions.d.ts +13 -0
- package/dist/scenario/assertions.js +111 -0
- package/dist/scenario/reporters.d.ts +13 -0
- package/dist/scenario/reporters.js +79 -0
- package/dist/scenario/runner.d.ts +10 -0
- package/dist/scenario/runner.js +244 -0
- package/dist/scenario/schema.d.ts +1210 -0
- package/dist/scenario/schema.js +145 -0
- package/dist/utils/screenshot.d.ts +1 -0
- package/dist/utils/screenshot.js +58 -0
- package/docs/guide/installation.md +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
package/dist/cli/program.js
CHANGED
|
@@ -9,11 +9,17 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
10
10
|
const minimist_1 = require("./minimist");
|
|
11
11
|
const command_1 = require("./command");
|
|
12
|
+
const input_1 = require("./input");
|
|
12
13
|
const commands_1 = require("./commands");
|
|
13
14
|
const output_1 = require("./output");
|
|
14
15
|
const config_1 = require("../config/config");
|
|
15
16
|
const session_1 = require("../daemon/session");
|
|
16
17
|
const session_ops_1 = require("./session-ops");
|
|
18
|
+
const state_check_1 = require("./state-check");
|
|
19
|
+
const launcher_1 = require("../browser/launcher");
|
|
20
|
+
const schema_1 = require("../scenario/schema");
|
|
21
|
+
const runner_1 = require("../scenario/runner");
|
|
22
|
+
const reporters_1 = require("../scenario/reporters");
|
|
17
23
|
const mcp_1 = require("../mcp");
|
|
18
24
|
const globalOptions = ['json', 'raw', 'help', 'h', 'version', 'v', 'p', 'profile'];
|
|
19
25
|
const booleanGlobalOptions = ['help', 'json', 'raw', 'version', 'v', 'h'];
|
|
@@ -52,15 +58,25 @@ async function program() {
|
|
|
52
58
|
case 'close':
|
|
53
59
|
await handleClose(command, rawArgs, output);
|
|
54
60
|
return;
|
|
61
|
+
case 'doctor':
|
|
62
|
+
handleDoctor(command, rawArgs, output);
|
|
63
|
+
return;
|
|
64
|
+
case 'scenario':
|
|
65
|
+
await handleScenario(command, rawArgs, output);
|
|
66
|
+
return;
|
|
55
67
|
case 'status':
|
|
56
68
|
await handleStatus(output);
|
|
57
69
|
return;
|
|
58
70
|
case 'state-import':
|
|
59
71
|
await handleStateImport(command, rawArgs, output);
|
|
60
72
|
return;
|
|
73
|
+
case 'state-check':
|
|
74
|
+
handleStateCheck(command, rawArgs, output);
|
|
75
|
+
return;
|
|
61
76
|
case 'state-save':
|
|
62
77
|
await handleStateSave(command, rawArgs, output);
|
|
63
78
|
return;
|
|
79
|
+
case 'frames':
|
|
64
80
|
case 'inspect':
|
|
65
81
|
case 'tree':
|
|
66
82
|
case 'layout':
|
|
@@ -70,8 +86,17 @@ async function program() {
|
|
|
70
86
|
case 'eval':
|
|
71
87
|
case 'resize':
|
|
72
88
|
case 'playwright':
|
|
89
|
+
case 'click':
|
|
90
|
+
case 'hover':
|
|
91
|
+
case 'type':
|
|
92
|
+
case 'wait-for':
|
|
93
|
+
case 'sample':
|
|
94
|
+
case 'wait-stable':
|
|
73
95
|
await handleSessionCommand(commandName, command, rawArgs, output);
|
|
74
96
|
return;
|
|
97
|
+
case 'run':
|
|
98
|
+
await handleRun(command, rawArgs, output);
|
|
99
|
+
return;
|
|
75
100
|
}
|
|
76
101
|
output.error(`Unknown command: ${commandName}. Run 'cssprobe-cli --help' for usage.`);
|
|
77
102
|
}
|
|
@@ -141,6 +166,8 @@ async function handleOpen(command, args, output) {
|
|
|
141
166
|
headed: !!parsed.headed,
|
|
142
167
|
viewport,
|
|
143
168
|
state: parsed.state,
|
|
169
|
+
expectUrl: parsed['expect-url'],
|
|
170
|
+
failOnLogin: !!parsed['fail-on-login'],
|
|
144
171
|
});
|
|
145
172
|
if (output.json) {
|
|
146
173
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -176,11 +203,100 @@ async function handleClose(command, args, output) {
|
|
|
176
203
|
output.error(e instanceof Error ? e.message : String(e));
|
|
177
204
|
}
|
|
178
205
|
}
|
|
206
|
+
async function handleScenario(command, args, output) {
|
|
207
|
+
try {
|
|
208
|
+
const cmdArgs = splitArgs(args);
|
|
209
|
+
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
210
|
+
const file = path_1.default.resolve(parsed.file);
|
|
211
|
+
if (!fs_1.default.existsSync(file))
|
|
212
|
+
output.error(`Scenario suite not found: ${file}`);
|
|
213
|
+
const raw = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
214
|
+
const validation = schema_1.suiteSchema.safeParse(raw);
|
|
215
|
+
if (!validation.success) {
|
|
216
|
+
const issues = validation.error.issues
|
|
217
|
+
.map(issue => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
|
|
218
|
+
.join('\n');
|
|
219
|
+
output.error(`Invalid scenario suite:\n${issues}`);
|
|
220
|
+
}
|
|
221
|
+
let viewport;
|
|
222
|
+
const viewportStr = parsed.viewport;
|
|
223
|
+
if (viewportStr) {
|
|
224
|
+
const parts = viewportStr.split('x');
|
|
225
|
+
const width = parseInt(parts[0], 10);
|
|
226
|
+
const height = parseInt(parts[1], 10);
|
|
227
|
+
if (parts.length !== 2 || isNaN(width) || isNaN(height)) {
|
|
228
|
+
output.error('Invalid viewport format. Use WxH (e.g. 1280x720)');
|
|
229
|
+
}
|
|
230
|
+
viewport = { width, height };
|
|
231
|
+
}
|
|
232
|
+
const result = await (0, runner_1.runScenario)(validation.data, file, {
|
|
233
|
+
artifactsDir: parsed.artifacts,
|
|
234
|
+
junitPath: parsed.junit,
|
|
235
|
+
bail: !!parsed.bail,
|
|
236
|
+
headed: !!parsed.headed,
|
|
237
|
+
browser: parsed.browser,
|
|
238
|
+
viewport,
|
|
239
|
+
state: parsed.state,
|
|
240
|
+
});
|
|
241
|
+
if (output.json) {
|
|
242
|
+
console.log(JSON.stringify(result, null, 2));
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
console.log((0, reporters_1.formatConsole)(result.suite, result.results));
|
|
246
|
+
console.log(`Artifacts: ${result.artifactsDir}`);
|
|
247
|
+
if (parsed.junit)
|
|
248
|
+
console.log(`JUnit: ${path_1.default.resolve(parsed.junit)}`);
|
|
249
|
+
}
|
|
250
|
+
if (!result.ok)
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
output.error(e instanceof Error ? e.message : String(e));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function handleDoctor(command, args, output) {
|
|
258
|
+
try {
|
|
259
|
+
const cmdArgs = splitArgs(args);
|
|
260
|
+
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
261
|
+
const diagnosis = (0, launcher_1.diagnoseBrowser)(parsed.engine);
|
|
262
|
+
if (output.json) {
|
|
263
|
+
console.log(JSON.stringify(diagnosis, null, 2));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
console.log(`Platform: ${diagnosis.platform}`);
|
|
267
|
+
console.log(`Cache roots: ${diagnosis.cacheRoots.join(', ')}`);
|
|
268
|
+
console.log(`Expected: chromium ${diagnosis.expected.chromium ?? 'unknown'}, headless-shell ${diagnosis.expected.headlessShell ?? 'unknown'} (playwright-core ${diagnosis.expected.playwrightVersion ?? 'unknown'})`);
|
|
269
|
+
if (diagnosis.installs.length) {
|
|
270
|
+
console.log('Installed:');
|
|
271
|
+
for (const install of diagnosis.installs) {
|
|
272
|
+
console.log(` ${install.kind}-${install.revision}${install.executable ? '' : ' (no executable)'} ${install.dir}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
console.log('Installed: none');
|
|
277
|
+
}
|
|
278
|
+
console.log(`Status: ${diagnosis.status}`);
|
|
279
|
+
if (diagnosis.hint)
|
|
280
|
+
console.log(`Hint: ${diagnosis.hint}`);
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
output.error(e instanceof Error ? e.message : String(e));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
179
286
|
async function handleStatus(output) {
|
|
180
287
|
try {
|
|
181
288
|
const result = await (0, session_ops_1.getSessionStatus)();
|
|
289
|
+
let page = null;
|
|
290
|
+
if (result.alive) {
|
|
291
|
+
try {
|
|
292
|
+
page = await (0, session_ops_1.getPageInfo)();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
page = null;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
182
298
|
if (output.json) {
|
|
183
|
-
console.log(JSON.stringify(result, null, 2));
|
|
299
|
+
console.log(JSON.stringify({ ...result, page }, null, 2));
|
|
184
300
|
return;
|
|
185
301
|
}
|
|
186
302
|
if (!result.config) {
|
|
@@ -191,15 +307,67 @@ async function handleStatus(output) {
|
|
|
191
307
|
console.log(`Status: ${result.alive ? 'alive' : 'dead'}`);
|
|
192
308
|
if (result.alive) {
|
|
193
309
|
console.log(`Browser: ${result.config.browser.browserName}`);
|
|
310
|
+
if (page) {
|
|
311
|
+
console.log(`URL: ${page.url}`);
|
|
312
|
+
console.log(`Title: ${page.title}`);
|
|
313
|
+
console.log(`Frames: ${page.frames}`);
|
|
314
|
+
}
|
|
194
315
|
}
|
|
195
316
|
}
|
|
196
317
|
catch (e) {
|
|
197
318
|
output.error(e instanceof Error ? e.message : String(e));
|
|
198
319
|
}
|
|
199
320
|
}
|
|
321
|
+
function handleStateCheck(command, args, output) {
|
|
322
|
+
try {
|
|
323
|
+
const cmdArgs = splitArgs(args);
|
|
324
|
+
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
325
|
+
const file = path_1.default.resolve(parsed.state ??
|
|
326
|
+
path_1.default.join(os_1.default.homedir(), '.cssprobe-cli', 'states', 'session.json'));
|
|
327
|
+
if (!fs_1.default.existsSync(file))
|
|
328
|
+
output.error(`State file not found: ${file}`);
|
|
329
|
+
const raw = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
330
|
+
if (!raw || !Array.isArray(raw.cookies))
|
|
331
|
+
throw new Error(`Invalid state file (missing cookies): ${file}`);
|
|
332
|
+
const report = (0, state_check_1.analyzeState)(raw, parsed.url);
|
|
333
|
+
if (output.json) {
|
|
334
|
+
console.log(JSON.stringify({ file, ...report }, null, 2));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
console.log(`State: ${file}`);
|
|
338
|
+
console.log(`Cookies: ${report.cookies} (${report.sessionCookies} session)`);
|
|
339
|
+
console.log(`Domains: ${report.domains.join(', ') || '(none)'}`);
|
|
340
|
+
if (report.targetMatches !== null) {
|
|
341
|
+
console.log(`Target match: ${report.targetMatches ? 'yes' : 'no'}`);
|
|
342
|
+
}
|
|
343
|
+
console.log(`Status: ${report.ok ? 'ok' : 'attention'}`);
|
|
344
|
+
for (const warning of report.warnings)
|
|
345
|
+
console.log(`Warning: ${warning}`);
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
output.error(e instanceof Error ? e.message : String(e));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function handleRun(command, args, output) {
|
|
352
|
+
const fileArg = args._[1];
|
|
353
|
+
if (!fileArg)
|
|
354
|
+
output.error('Usage: cssprobe-cli run <file>');
|
|
355
|
+
const file = path_1.default.resolve(fileArg);
|
|
356
|
+
if (!fs_1.default.existsSync(file))
|
|
357
|
+
output.error(`Flow script not found: ${file}`);
|
|
358
|
+
args._ = ['run', file];
|
|
359
|
+
await handleSessionCommand('run', command, args, output);
|
|
360
|
+
}
|
|
200
361
|
async function handleSessionCommand(commandName, command, args, output) {
|
|
201
362
|
try {
|
|
202
363
|
const cmdArgs = splitArgs(args);
|
|
364
|
+
if (commandName === 'eval' || commandName === 'playwright') {
|
|
365
|
+
const input = cmdArgs._[1];
|
|
366
|
+
if (typeof input === 'string') {
|
|
367
|
+
cmdArgs._ = cmdArgs._.slice();
|
|
368
|
+
cmdArgs._[1] = (0, input_1.resolveInputArg)(input);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
203
371
|
const parsed = (0, command_1.parseCommand)(command, cmdArgs);
|
|
204
372
|
const result = await (0, session_ops_1.runSessionCommand)(commandName, command, parsed, !!args.json);
|
|
205
373
|
// Screenshot: save the base64 data URL to a file and report the path
|
|
@@ -214,6 +382,8 @@ async function handleSessionCommand(commandName, command, args, output) {
|
|
|
214
382
|
return;
|
|
215
383
|
}
|
|
216
384
|
console.log(result.text);
|
|
385
|
+
if (typeof result.exitCode === 'number' && result.exitCode !== 0)
|
|
386
|
+
process.exit(result.exitCode);
|
|
217
387
|
}
|
|
218
388
|
catch (e) {
|
|
219
389
|
output.error(e instanceof Error ? e.message : String(e));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Session } from '../daemon/session';
|
|
2
2
|
import type { SessionConfig } from '../daemon/session';
|
|
3
3
|
import type { AnyCommandSchema } from './command';
|
|
4
|
+
export { saveScreenshotFile } from '../utils/screenshot';
|
|
4
5
|
export interface OpenOptions {
|
|
5
6
|
url?: string;
|
|
6
7
|
browser?: string;
|
|
@@ -10,6 +11,8 @@ export interface OpenOptions {
|
|
|
10
11
|
height: number;
|
|
11
12
|
};
|
|
12
13
|
state?: string;
|
|
14
|
+
expectUrl?: string;
|
|
15
|
+
failOnLogin?: boolean;
|
|
13
16
|
}
|
|
14
17
|
export interface OpenResult {
|
|
15
18
|
sessionId: string;
|
|
@@ -20,6 +23,12 @@ export interface OpenResult {
|
|
|
20
23
|
height: number;
|
|
21
24
|
};
|
|
22
25
|
}
|
|
26
|
+
export interface PageInfo {
|
|
27
|
+
url: string;
|
|
28
|
+
title: string;
|
|
29
|
+
frames: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function getPageInfo(): Promise<PageInfo>;
|
|
23
32
|
export declare function openSession(opts: OpenOptions): Promise<OpenResult>;
|
|
24
33
|
export declare function waitForSession(timeoutMs: number): Promise<Session | undefined>;
|
|
25
34
|
export interface CloseResult {
|
|
@@ -39,5 +48,5 @@ export declare function getSessionStatus(): Promise<StatusResult>;
|
|
|
39
48
|
export declare function buildDaemonArgs(commandName: string, command: AnyCommandSchema, parsed: Record<string, unknown>): string[];
|
|
40
49
|
export declare function runSessionCommand(commandName: string, command: AnyCommandSchema, parsed: Record<string, unknown>, json?: boolean): Promise<{
|
|
41
50
|
text: string;
|
|
51
|
+
exitCode?: number;
|
|
42
52
|
}>;
|
|
43
|
-
export declare function saveScreenshotFile(dataUrl: string, out?: string): string;
|
package/dist/cli/session-ops.js
CHANGED
|
@@ -1,49 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.saveScreenshotFile = void 0;
|
|
4
|
+
exports.getPageInfo = getPageInfo;
|
|
36
5
|
exports.openSession = openSession;
|
|
37
6
|
exports.waitForSession = waitForSession;
|
|
38
7
|
exports.closeSession = closeSession;
|
|
39
8
|
exports.getSessionStatus = getSessionStatus;
|
|
40
9
|
exports.buildDaemonArgs = buildDaemonArgs;
|
|
41
10
|
exports.runSessionCommand = runSessionCommand;
|
|
42
|
-
exports.saveScreenshotFile = saveScreenshotFile;
|
|
43
|
-
const fs = __importStar(require("fs"));
|
|
44
|
-
const os = __importStar(require("os"));
|
|
45
|
-
const path = __importStar(require("path"));
|
|
46
11
|
const session_1 = require("../daemon/session");
|
|
12
|
+
const url_guard_1 = require("./url-guard");
|
|
13
|
+
var screenshot_1 = require("../utils/screenshot");
|
|
14
|
+
Object.defineProperty(exports, "saveScreenshotFile", { enumerable: true, get: function () { return screenshot_1.saveScreenshotFile; } });
|
|
15
|
+
async function getPageInfo() {
|
|
16
|
+
const session = await (0, session_1.loadSession)();
|
|
17
|
+
if (!session)
|
|
18
|
+
throw new Error('No active session. Run: cssprobe-cli open <url>');
|
|
19
|
+
const result = await session.run({ _: ['page-info'] });
|
|
20
|
+
return JSON.parse(result.text);
|
|
21
|
+
}
|
|
47
22
|
async function openSession(opts) {
|
|
48
23
|
const url = opts.url || 'about:blank';
|
|
49
24
|
const browser = opts.browser || 'chromium';
|
|
@@ -58,10 +33,24 @@ async function openSession(opts) {
|
|
|
58
33
|
if (!session)
|
|
59
34
|
throw new Error('Daemon failed to become ready within 5s. Check the daemon log for errors.');
|
|
60
35
|
await session.run({ _: ['goto', url] });
|
|
36
|
+
const info = await getPageInfo();
|
|
37
|
+
const guardError = (() => {
|
|
38
|
+
if (opts.failOnLogin && (0, url_guard_1.looksLikeLoginUrl)(info.url)) {
|
|
39
|
+
return `Login required: page redirected to ${info.url}. Refresh the state file or log in manually.`;
|
|
40
|
+
}
|
|
41
|
+
if (opts.expectUrl && !(0, url_guard_1.matchesUrlPattern)(opts.expectUrl, info.url)) {
|
|
42
|
+
return `Expected URL to match ${opts.expectUrl}, got ${info.url}`;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
})();
|
|
46
|
+
if (guardError) {
|
|
47
|
+
await closeSession();
|
|
48
|
+
throw new Error(guardError);
|
|
49
|
+
}
|
|
61
50
|
return {
|
|
62
51
|
sessionId: 'default',
|
|
63
52
|
pid,
|
|
64
|
-
url,
|
|
53
|
+
url: info.url,
|
|
65
54
|
viewport: opts.viewport || { width: 1280, height: 720 },
|
|
66
55
|
};
|
|
67
56
|
}
|
|
@@ -142,22 +131,4 @@ async function runSessionCommand(commandName, command, parsed, json = false) {
|
|
|
142
131
|
runArgs.json = true;
|
|
143
132
|
return await session.run(runArgs);
|
|
144
133
|
}
|
|
145
|
-
function saveScreenshotFile(dataUrl, out) {
|
|
146
|
-
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
147
|
-
const buffer = Buffer.from(base64, 'base64');
|
|
148
|
-
const resolvedOut = out
|
|
149
|
-
? path.resolve(out)
|
|
150
|
-
: (() => {
|
|
151
|
-
const dir = path.join(os.homedir(), '.cssprobe-cli', 'screenshots');
|
|
152
|
-
if (!fs.existsSync(dir))
|
|
153
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
154
|
-
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
155
|
-
return path.join(dir, `screenshot-${ts}.png`);
|
|
156
|
-
})();
|
|
157
|
-
const dir = path.dirname(resolvedOut);
|
|
158
|
-
if (!fs.existsSync(dir))
|
|
159
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
160
|
-
fs.writeFileSync(resolvedOut, buffer);
|
|
161
|
-
return resolvedOut;
|
|
162
|
-
}
|
|
163
134
|
//# sourceMappingURL=session-ops.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface StorageCookie {
|
|
2
|
+
name: string;
|
|
3
|
+
domain: string;
|
|
4
|
+
expires?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface StateReport {
|
|
7
|
+
cookies: number;
|
|
8
|
+
domains: string[];
|
|
9
|
+
expired: string[];
|
|
10
|
+
sessionCookies: number;
|
|
11
|
+
targetMatches: boolean | null;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
ok: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function analyzeState(state: {
|
|
16
|
+
cookies?: StorageCookie[];
|
|
17
|
+
}, targetUrl?: string, nowMs?: number): StateReport;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeState = analyzeState;
|
|
4
|
+
function normalizeDomain(domain) {
|
|
5
|
+
return domain.replace(/^\./, '').toLowerCase();
|
|
6
|
+
}
|
|
7
|
+
function analyzeState(state, targetUrl, nowMs = Date.now()) {
|
|
8
|
+
const cookies = state.cookies ?? [];
|
|
9
|
+
const expired = [];
|
|
10
|
+
let sessionCookies = 0;
|
|
11
|
+
for (const cookie of cookies) {
|
|
12
|
+
if (cookie.expires === undefined || cookie.expires <= 0) {
|
|
13
|
+
sessionCookies++;
|
|
14
|
+
}
|
|
15
|
+
else if (cookie.expires * 1000 <= nowMs) {
|
|
16
|
+
expired.push(cookie.name);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const domains = Array.from(new Set(cookies.map(cookie => normalizeDomain(cookie.domain)))).sort();
|
|
20
|
+
let targetMatches = null;
|
|
21
|
+
if (targetUrl) {
|
|
22
|
+
try {
|
|
23
|
+
const host = new URL(targetUrl).hostname.toLowerCase();
|
|
24
|
+
targetMatches = cookies.some(cookie => {
|
|
25
|
+
const domain = normalizeDomain(cookie.domain);
|
|
26
|
+
return host === domain || host.endsWith(`.${domain}`);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error(`Invalid --url value: ${targetUrl}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const warnings = [];
|
|
34
|
+
if (!cookies.length)
|
|
35
|
+
warnings.push('no cookies in state file');
|
|
36
|
+
if (expired.length)
|
|
37
|
+
warnings.push(`expired cookies: ${expired.join(', ')}`);
|
|
38
|
+
if (targetMatches === false)
|
|
39
|
+
warnings.push(`no cookies match ${targetUrl}`);
|
|
40
|
+
const ok = cookies.length > 0 && expired.length === 0 && targetMatches !== false;
|
|
41
|
+
return { cookies: cookies.length, domains, expired, sessionCookies, targetMatches, warnings, ok };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=state-check.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.looksLikeLoginUrl = looksLikeLoginUrl;
|
|
4
|
+
exports.matchesUrlPattern = matchesUrlPattern;
|
|
5
|
+
function looksLikeLoginUrl(raw) {
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(raw);
|
|
8
|
+
return /\/(?:s\/)?login(?:\/|$)/.test(url.pathname);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function matchesUrlPattern(pattern, raw) {
|
|
15
|
+
let regex;
|
|
16
|
+
try {
|
|
17
|
+
regex = new RegExp(pattern);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error(`Invalid --expect-url pattern: ${pattern}`);
|
|
21
|
+
}
|
|
22
|
+
return regex.test(raw);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=url-guard.js.map
|