@page-scanner/cli 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +217 -0
  3. package/dist/api.d.ts +87 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +132 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +49 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/bridge/server.d.ts +97 -0
  12. package/dist/bridge/server.d.ts.map +1 -0
  13. package/dist/bridge/server.js +329 -0
  14. package/dist/bridge/server.js.map +1 -0
  15. package/dist/bridge/tokens.d.ts +10 -0
  16. package/dist/bridge/tokens.d.ts.map +1 -0
  17. package/dist/bridge/tokens.js +15 -0
  18. package/dist/bridge/tokens.js.map +1 -0
  19. package/dist/cli/args.d.ts +63 -0
  20. package/dist/cli/args.d.ts.map +1 -0
  21. package/dist/cli/args.js +388 -0
  22. package/dist/cli/args.js.map +1 -0
  23. package/dist/cli/commands.d.ts +8 -0
  24. package/dist/cli/commands.d.ts.map +1 -0
  25. package/dist/cli/commands.js +234 -0
  26. package/dist/cli/commands.js.map +1 -0
  27. package/dist/cli/format.d.ts +22 -0
  28. package/dist/cli/format.d.ts.map +1 -0
  29. package/dist/cli/format.js +152 -0
  30. package/dist/cli/format.js.map +1 -0
  31. package/dist/config.d.ts +17 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +54 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/daemon/client.d.ts +28 -0
  36. package/dist/daemon/client.d.ts.map +1 -0
  37. package/dist/daemon/client.js +139 -0
  38. package/dist/daemon/client.js.map +1 -0
  39. package/dist/daemon/rpc-server.d.ts +31 -0
  40. package/dist/daemon/rpc-server.d.ts.map +1 -0
  41. package/dist/daemon/rpc-server.js +254 -0
  42. package/dist/daemon/rpc-server.js.map +1 -0
  43. package/dist/daemon/run.d.ts +20 -0
  44. package/dist/daemon/run.d.ts.map +1 -0
  45. package/dist/daemon/run.js +129 -0
  46. package/dist/daemon/run.js.map +1 -0
  47. package/dist/daemon/self.d.ts +25 -0
  48. package/dist/daemon/self.d.ts.map +1 -0
  49. package/dist/daemon/self.js +113 -0
  50. package/dist/daemon/self.js.map +1 -0
  51. package/dist/daemon/state.d.ts +25 -0
  52. package/dist/daemon/state.d.ts.map +1 -0
  53. package/dist/daemon/state.js +116 -0
  54. package/dist/daemon/state.js.map +1 -0
  55. package/dist/errors.d.ts +32 -0
  56. package/dist/errors.d.ts.map +1 -0
  57. package/dist/errors.js +83 -0
  58. package/dist/errors.js.map +1 -0
  59. package/dist/index.d.ts +17 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +16 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/output.d.ts +13 -0
  64. package/dist/output.d.ts.map +1 -0
  65. package/dist/output.js +71 -0
  66. package/dist/output.js.map +1 -0
  67. package/dist/protocol.d.ts +144 -0
  68. package/dist/protocol.d.ts.map +1 -0
  69. package/dist/protocol.js +128 -0
  70. package/dist/protocol.js.map +1 -0
  71. package/dist/version.d.ts +11 -0
  72. package/dist/version.d.ts.map +1 -0
  73. package/dist/version.js +11 -0
  74. package/dist/version.js.map +1 -0
  75. package/package.json +77 -0
@@ -0,0 +1,388 @@
1
+ /**
2
+ * The whole argument surface, parsed with `node:util.parseArgs`.
3
+ *
4
+ * A CLI that ships one runtime dependency should not spend it on an argument
5
+ * parser, and the surface here is small enough that the platform's own parser
6
+ * covers it: one command, a handful of flags, no subcommand trees and no
7
+ * short aliases beyond `-h` and `-v`.
8
+ *
9
+ * Every rejection leaves through `PageScannerError('BAD_REQUEST', …)`, which
10
+ * the caller turns into exit code 2, so a script never has to read the prose
11
+ * to know it passed the wrong thing. The messages name the flag, what it
12
+ * expected and what it got, because that is the whole diagnosis.
13
+ */
14
+ import { parseArgs as parseNodeArgs } from 'node:util';
15
+ import { PageScannerError } from '../errors.js';
16
+ import { DEFAULT_PAGE_SIZE, EXPORT_FORMAT_IDS, PAGE_SIZE_IDS, VIDEO_HANDLINGS, } from '../protocol.js';
17
+ export const COMMANDS = [
18
+ 'pair',
19
+ 'status',
20
+ 'browsers',
21
+ 'tabs',
22
+ 'scan',
23
+ 'serve',
24
+ 'stop',
25
+ 'help',
26
+ 'version',
27
+ ];
28
+ /** Named once and interpolated into USAGE, so the help cannot drift from them. */
29
+ const PAIR_WAIT_SECONDS = 120;
30
+ const TABS_WAIT_SECONDS = 30;
31
+ const SCAN_WAIT_SECONDS = 30;
32
+ const SCAN_TIMEOUT_SECONDS = 120;
33
+ export const USAGE = `page-scanner - capture a whole web page from the command line,
34
+ through the Page Scanner Chrome extension.
35
+
36
+ Usage:
37
+ page-scanner pair [--port <n>] [--rotate] [--wait <s>=${PAIR_WAIT_SECONDS}] [--no-wait]
38
+ [--json]
39
+ page-scanner status [--json]
40
+ page-scanner browsers [--json]
41
+ page-scanner tabs [--browser <id|label>] [--wait <s>=${TABS_WAIT_SECONDS}] [--json]
42
+ page-scanner scan (--url <u> | --tab <id>) [--window <id>]
43
+ [--browser <id|label>] [--format pdf|png|jpeg=pdf]
44
+ [--page-size auto|a4|letter=a4] [--quality <0-1>]
45
+ [--video frame|blank] [--open-editor]
46
+ [--out <file|dir>] [--wait <s>=${SCAN_WAIT_SECONDS}]
47
+ [--timeout <s>=${SCAN_TIMEOUT_SECONDS}]
48
+ [--json]
49
+ page-scanner serve [--daemon] [--idle <min>]
50
+ page-scanner stop [--json]
51
+ page-scanner --version | --help (-v and -h also work)
52
+
53
+ Commands:
54
+ pair Write the port and token the browser needs, then wait for it.
55
+ status Say whether there is a pairing, a daemon and a browser.
56
+ browsers List the browsers connected right now.
57
+ tabs List one browser's windows and tabs.
58
+ scan Capture a page and write it to a file.
59
+ serve Run the bridge in this terminal, or start it in the background.
60
+ stop Stop the background bridge.
61
+
62
+ --browser takes a browserId or a label, matched without regard to case, and
63
+ is needed only when more than one browser is connected. --wait 0 means do
64
+ not wait at all. --json prints one JSON document on stdout, on success and
65
+ on failure alike.
66
+
67
+ Exit codes:
68
+ 0 success
69
+ 1 the browser was reached and the work failed
70
+ 2 the arguments were wrong
71
+ 3 no usable browser: none connected, several connected, or the one
72
+ named is not
73
+ 4 not paired: run \`page-scanner pair\`
74
+ 5 the daemon would not start
75
+ `;
76
+ function badRequest(message, hint) {
77
+ return new PageScannerError('BAD_REQUEST', message, hint);
78
+ }
79
+ /** Quoting the offending text keeps an empty or space-only value visible. */
80
+ function quote(raw) {
81
+ return JSON.stringify(raw);
82
+ }
83
+ /**
84
+ * One numeric gate for every numeric flag. `accept` carries the range, and
85
+ * `expectation` the same range in words, so the check and the message cannot
86
+ * describe different things.
87
+ */
88
+ function requireNumber(flag, raw, expectation, accept) {
89
+ // Number('') and Number(' ') are both 0, so an empty value would otherwise
90
+ // pass every range below as a perfectly legal zero.
91
+ const value = raw.trim() === '' ? Number.NaN : Number(raw);
92
+ if (!Number.isFinite(value) || !accept(value)) {
93
+ throw badRequest(`${flag} must be ${expectation} (got ${quote(raw)}).`);
94
+ }
95
+ return value;
96
+ }
97
+ const DECIMAL_INTEGER = /^[+-]?\d+$/;
98
+ /**
99
+ * `Number` is more permissive than "an integer" in three ways that matter
100
+ * here: it reads `0x10` as 16, it accepts `+7`, and it rounds
101
+ * `9007199254740993` down to `...992`. The last is the dangerous one, because
102
+ * a tab id would be quietly turned into a different tab id rather than
103
+ * rejected.
104
+ */
105
+ function requireInteger(flag, raw) {
106
+ if (!DECIMAL_INTEGER.test(raw.trim())) {
107
+ throw badRequest(`${flag} must be an integer (got ${quote(raw)}).`);
108
+ }
109
+ return requireNumber(flag, raw, 'an integer', Number.isSafeInteger);
110
+ }
111
+ function requireSeconds(flag, raw) {
112
+ return requireNumber(flag, raw, 'a number of seconds, 0 or more', (value) => value >= 0);
113
+ }
114
+ function requireEnum(flag, raw, allowed) {
115
+ const match = allowed.find((candidate) => candidate === raw);
116
+ if (match === undefined) {
117
+ throw badRequest(`${flag} must be one of ${allowed.join(', ')} (got ${quote(raw)}).`);
118
+ }
119
+ return match;
120
+ }
121
+ function requireText(flag, raw) {
122
+ if (raw.trim() === '')
123
+ throw badRequest(`${flag} needs a value.`);
124
+ return raw;
125
+ }
126
+ function optionalInteger(flag, raw) {
127
+ return raw === undefined ? undefined : requireInteger(flag, raw);
128
+ }
129
+ function optionalText(flag, raw) {
130
+ return raw === undefined ? undefined : requireText(flag, raw);
131
+ }
132
+ /**
133
+ * `parseArgs` throws its own ERR_PARSE_ARGS_* errors for an unknown option, a
134
+ * missing value or a stray positional. Those are usage mistakes like any
135
+ * other, so they become BAD_REQUEST and exit 2 rather than an uncaught crash
136
+ * with Node's own exit code; the original sentence is the clearest account of
137
+ * what happened, so it is kept verbatim.
138
+ */
139
+ function readOptions(read) {
140
+ try {
141
+ return read();
142
+ }
143
+ catch (error) {
144
+ const message = error instanceof Error ? error.message : String(error);
145
+ throw badRequest(message, 'Run `page-scanner --help` for the commands and their options.');
146
+ }
147
+ }
148
+ /**
149
+ * `page-scanner scan --help` asks for the help, not for a scan.
150
+ *
151
+ * Every command declares `--help`, so this is read AFTER Node has parsed the
152
+ * whole line. Sweeping the raw tokens for it beforehand was wrong: in
153
+ * `page-scanner tabs --browser --help` the `--help` is the missing value of
154
+ * `--browser`, and the sweep turned a usage error into a successful exit 0.
155
+ */
156
+ function wantsHelp(values) {
157
+ return values.help === true;
158
+ }
159
+ function parsePair(argv) {
160
+ const { values } = readOptions(() => parseNodeArgs({
161
+ args: argv,
162
+ strict: true,
163
+ allowPositionals: false,
164
+ options: {
165
+ help: { type: 'boolean', short: 'h' },
166
+ port: { type: 'string' },
167
+ rotate: { type: 'boolean' },
168
+ wait: { type: 'string' },
169
+ 'no-wait': { type: 'boolean' },
170
+ json: { type: 'boolean' },
171
+ },
172
+ }));
173
+ if (wantsHelp(values))
174
+ return { command: 'help' };
175
+ // Validate first, then let --no-wait override. Short-circuiting on --no-wait
176
+ // meant `--wait oops --no-wait` was accepted, so whether a typo was caught
177
+ // depended on an unrelated flag.
178
+ const requested = values.wait === undefined ? PAIR_WAIT_SECONDS : requireSeconds('--wait', values.wait);
179
+ // --no-wait wins over --wait: it is the more explicit of the two, and a
180
+ // script that sets both meant the one that says "return immediately".
181
+ const waitSeconds = values['no-wait'] === true ? 0 : requested;
182
+ return {
183
+ command: 'pair',
184
+ port: values.port === undefined
185
+ ? undefined
186
+ : requireNumber('--port', values.port, 'an integer from 1 to 65535',
187
+ // Port 0 would ask the kernel for any free port, which the browser
188
+ // then has no way of learning: the pairing file is the only thing
189
+ // that tells it where to dial.
190
+ (value) => Number.isInteger(value) && value >= 1 && value <= 65535),
191
+ rotate: values.rotate === true,
192
+ waitSeconds,
193
+ json: values.json === true,
194
+ };
195
+ }
196
+ function parseStatus(argv) {
197
+ const { values } = readOptions(() => parseNodeArgs({
198
+ args: argv,
199
+ strict: true,
200
+ allowPositionals: false,
201
+ options: {
202
+ help: { type: 'boolean', short: 'h' },
203
+ json: { type: 'boolean' },
204
+ },
205
+ }));
206
+ if (wantsHelp(values))
207
+ return { command: 'help' };
208
+ return { command: 'status', json: values.json === true };
209
+ }
210
+ function parseBrowsers(argv) {
211
+ const { values } = readOptions(() => parseNodeArgs({
212
+ args: argv,
213
+ strict: true,
214
+ allowPositionals: false,
215
+ options: {
216
+ help: { type: 'boolean', short: 'h' },
217
+ json: { type: 'boolean' },
218
+ },
219
+ }));
220
+ if (wantsHelp(values))
221
+ return { command: 'help' };
222
+ return { command: 'browsers', json: values.json === true };
223
+ }
224
+ function parseTabs(argv) {
225
+ const { values } = readOptions(() => parseNodeArgs({
226
+ args: argv,
227
+ strict: true,
228
+ allowPositionals: false,
229
+ options: {
230
+ help: { type: 'boolean', short: 'h' },
231
+ browser: { type: 'string' },
232
+ wait: { type: 'string' },
233
+ json: { type: 'boolean' },
234
+ },
235
+ }));
236
+ if (wantsHelp(values))
237
+ return { command: 'help' };
238
+ return {
239
+ command: 'tabs',
240
+ browser: optionalText('--browser', values.browser),
241
+ waitSeconds: values.wait === undefined ? TABS_WAIT_SECONDS : requireSeconds('--wait', values.wait),
242
+ json: values.json === true,
243
+ };
244
+ }
245
+ function parseScan(argv) {
246
+ const { values } = readOptions(() => parseNodeArgs({
247
+ args: argv,
248
+ strict: true,
249
+ allowPositionals: false,
250
+ options: {
251
+ help: { type: 'boolean', short: 'h' },
252
+ url: { type: 'string' },
253
+ tab: { type: 'string' },
254
+ window: { type: 'string' },
255
+ browser: { type: 'string' },
256
+ format: { type: 'string' },
257
+ 'page-size': { type: 'string' },
258
+ quality: { type: 'string' },
259
+ video: { type: 'string' },
260
+ 'open-editor': { type: 'boolean' },
261
+ out: { type: 'string' },
262
+ wait: { type: 'string' },
263
+ timeout: { type: 'string' },
264
+ json: { type: 'boolean' },
265
+ },
266
+ }));
267
+ if (wantsHelp(values))
268
+ return { command: 'help' };
269
+ // Exactly one target. Neither leaves nothing to scan; both would make the
270
+ // extension choose, and the two mean different things (open a tab, or use
271
+ // the one already open).
272
+ if (values.url === undefined && values.tab === undefined) {
273
+ throw badRequest('scan needs a page to capture: give --url <address> or --tab <id>.', 'Run `page-scanner tabs` to see the tabs that are open.');
274
+ }
275
+ if (values.url !== undefined && values.tab !== undefined) {
276
+ throw badRequest('scan takes --url or --tab, not both.');
277
+ }
278
+ return {
279
+ command: 'scan',
280
+ url: optionalText('--url', values.url),
281
+ tabId: optionalInteger('--tab', values.tab),
282
+ windowId: optionalInteger('--window', values.window),
283
+ browser: optionalText('--browser', values.browser),
284
+ format: values.format === undefined
285
+ ? 'pdf'
286
+ : requireEnum('--format', values.format, EXPORT_FORMAT_IDS),
287
+ pageSize: values['page-size'] === undefined
288
+ ? DEFAULT_PAGE_SIZE
289
+ : requireEnum('--page-size', values['page-size'], PAGE_SIZE_IDS),
290
+ quality: values.quality === undefined
291
+ ? undefined
292
+ : requireNumber('--quality', values.quality, 'a number greater than 0 and at most 1', (value) => value > 0 && value <= 1),
293
+ videoHandling: values.video === undefined
294
+ ? undefined
295
+ : requireEnum('--video', values.video, VIDEO_HANDLINGS),
296
+ openEditor: values['open-editor'] === true,
297
+ out: optionalText('--out', values.out),
298
+ waitSeconds: values.wait === undefined ? SCAN_WAIT_SECONDS : requireSeconds('--wait', values.wait),
299
+ timeoutSeconds: values.timeout === undefined
300
+ ? SCAN_TIMEOUT_SECONDS
301
+ : requireSeconds('--timeout', values.timeout),
302
+ json: values.json === true,
303
+ };
304
+ }
305
+ function parseServe(argv) {
306
+ const { values } = readOptions(() => parseNodeArgs({
307
+ args: argv,
308
+ strict: true,
309
+ allowPositionals: false,
310
+ options: {
311
+ help: { type: 'boolean', short: 'h' },
312
+ daemon: { type: 'boolean' },
313
+ idle: { type: 'string' },
314
+ },
315
+ }));
316
+ if (wantsHelp(values))
317
+ return { command: 'help' };
318
+ return {
319
+ command: 'serve',
320
+ daemon: values.daemon === true,
321
+ idleMinutes: values.idle === undefined
322
+ ? undefined
323
+ : requireNumber('--idle', values.idle, 'a number of minutes, 0 or more', (v) => v >= 0),
324
+ };
325
+ }
326
+ function parseStop(argv) {
327
+ const { values } = readOptions(() => parseNodeArgs({
328
+ args: argv,
329
+ strict: true,
330
+ allowPositionals: false,
331
+ options: {
332
+ help: { type: 'boolean', short: 'h' },
333
+ json: { type: 'boolean' },
334
+ },
335
+ }));
336
+ if (wantsHelp(values))
337
+ return { command: 'help' };
338
+ return { command: 'stop', json: values.json === true };
339
+ }
340
+ /** The two commands that are also spelled as flags, in both spellings. */
341
+ const COMMAND_FLAGS = new Map([
342
+ ['--help', 'help'],
343
+ ['-h', 'help'],
344
+ ['--version', 'version'],
345
+ ['-v', 'version'],
346
+ ]);
347
+ function isCommandName(value) {
348
+ return COMMANDS.some((command) => command === value);
349
+ }
350
+ /** What a user may reasonably be asked to type: `help` and `version` are extras. */
351
+ const RUNNABLE_COMMANDS = COMMANDS.filter((command) => command !== 'help' && command !== 'version').join(', ');
352
+ /**
353
+ * `argv` is what follows the node executable and the script, i.e.
354
+ * `process.argv.slice(2)`.
355
+ */
356
+ export function parseArgs(argv) {
357
+ const first = argv[0];
358
+ if (first === undefined)
359
+ return { command: 'help' };
360
+ const command = COMMAND_FLAGS.get(first) ?? (isCommandName(first) ? first : undefined);
361
+ if (command === undefined) {
362
+ throw first.startsWith('-')
363
+ ? badRequest(`Unknown option ${quote(first)}. A command comes first: one of ${RUNNABLE_COMMANDS}.`)
364
+ : badRequest(`Unknown command ${quote(first)}. Expected one of ${RUNNABLE_COMMANDS}.`);
365
+ }
366
+ const rest = argv.slice(1);
367
+ switch (command) {
368
+ case 'pair':
369
+ return parsePair(rest);
370
+ case 'status':
371
+ return parseStatus(rest);
372
+ case 'browsers':
373
+ return parseBrowsers(rest);
374
+ case 'tabs':
375
+ return parseTabs(rest);
376
+ case 'scan':
377
+ return parseScan(rest);
378
+ case 'serve':
379
+ return parseServe(rest);
380
+ case 'stop':
381
+ return parseStop(rest);
382
+ case 'help':
383
+ return { command: 'help' };
384
+ case 'version':
385
+ return { command: 'version' };
386
+ }
387
+ }
388
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ParsedArgs } from './args.js';
2
+ export interface CommandIo {
3
+ stdout: (text: string) => void;
4
+ stderr: (text: string) => void;
5
+ }
6
+ /** Runs one parsed command and returns the process exit code. */
7
+ export declare function runCommand(parsed: ParsedArgs, io: CommandIo): Promise<number>;
8
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAI5C,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AA0FD,iEAAiE;AACjE,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAqJnF"}
@@ -0,0 +1,234 @@
1
+ /**
2
+ * What each command prints, and what it exits with.
3
+ *
4
+ * The output contract, which the README states and `bin.test.ts` enforces:
5
+ *
6
+ * - stdout carries the answer and nothing else. For a scan that is one line,
7
+ * the absolute path; for a listing it is a column-aligned table. Both are
8
+ * meant to be piped.
9
+ * - stderr carries anything addressed to a person: progress, warnings, the
10
+ * reason something failed.
11
+ * - `--json` puts exactly one JSON document on stdout, for success and for
12
+ * failure alike, and stderr stays empty. A script should never have to
13
+ * parse prose.
14
+ * - The pairing token is printed by `pair`, on stdout, and nowhere else ever.
15
+ */
16
+ import { listBrowsers, listTabs, pair, scan, status, stop, waitForBrowser, } from '../api.js';
17
+ import { exitCodeFor, toPageScannerError } from '../errors.js';
18
+ import { formatBrowsers, formatDuration, formatTabs, formatTruncation } from './format.js';
19
+ import { USAGE } from './args.js';
20
+ import { CLI_VERSION } from '../version.js';
21
+ function line(io, text) {
22
+ io.stdout(`${text}\n`);
23
+ }
24
+ function note(io, text) {
25
+ io.stderr(`${text}\n`);
26
+ }
27
+ function json(io, payload) {
28
+ io.stdout(`${JSON.stringify(payload, null, 2)}\n`);
29
+ }
30
+ /**
31
+ * Says once, on stderr, that nothing is connected yet, so a person watching a
32
+ * command that appears to hang knows what it is waiting for and what to do.
33
+ * Skipped for `--json`, whose consumer is not watching.
34
+ */
35
+ async function warnIfWaiting(io, waitSeconds, useJson) {
36
+ if (useJson || waitSeconds <= 0)
37
+ return;
38
+ try {
39
+ const browsers = await listBrowsers();
40
+ if (browsers.length > 0)
41
+ return;
42
+ }
43
+ catch {
44
+ // The daemon is not up yet, or something else is wrong. Either way the
45
+ // real command is about to report it properly.
46
+ return;
47
+ }
48
+ note(io, `Waiting for Chrome to connect (up to ${waitSeconds}s). Open the Page Scanner settings ` +
49
+ 'page and press Connect.');
50
+ }
51
+ function reportScan(io, result, format, useJson) {
52
+ if (useJson) {
53
+ json(io, { ok: true, ...result });
54
+ return;
55
+ }
56
+ if (result.truncated)
57
+ note(io, formatTruncation(result.truncated));
58
+ if (result.mode === 'raster') {
59
+ // Worth saying for a PDF, where it is the difference between a document
60
+ // and a picture of one. For a PNG or a JPEG the output is an image
61
+ // either way, and the only thing that changed is how it was assembled.
62
+ note(io, format === 'pdf'
63
+ ? 'Chrome would not attach its debugger, so this was stitched from screenshots: the ' +
64
+ "PDF's text is an image, not text."
65
+ : 'Chrome would not attach its debugger, so this was stitched from screenshots.');
66
+ }
67
+ line(io, result.path);
68
+ }
69
+ function reportStatus(io, report, useJson) {
70
+ if (useJson) {
71
+ json(io, { ok: true, ...report });
72
+ return;
73
+ }
74
+ const rows = [
75
+ `page-scanner ${report.version}`,
76
+ `pairing ${report.paired ? `port ${report.bridgePort}` : 'none, run `page-scanner pair`'}`,
77
+ `config ${report.configPath}${report.configOwnerOnly ? '' : ' (not owner-only on this platform)'}`,
78
+ report.daemon.running
79
+ ? `daemon running, pid ${report.daemon.pid}, version ${report.daemon.version}`
80
+ : 'daemon not running',
81
+ ];
82
+ for (const row of rows)
83
+ line(io, row);
84
+ if (report.browsers.length === 0) {
85
+ line(io, 'browsers none connected');
86
+ return;
87
+ }
88
+ line(io, 'browsers');
89
+ for (const browser of report.browsers) {
90
+ line(io, ` ${browser.label} (${browser.browserId}) extension ${browser.extensionVersion}, ` +
91
+ `connected ${formatDuration(browser.connectedForMs)}`);
92
+ }
93
+ }
94
+ /** Runs one parsed command and returns the process exit code. */
95
+ export async function runCommand(parsed, io) {
96
+ try {
97
+ switch (parsed.command) {
98
+ case 'help':
99
+ line(io, USAGE);
100
+ return 0;
101
+ case 'version':
102
+ line(io, CLI_VERSION);
103
+ return 0;
104
+ case 'pair': {
105
+ const result = await pair({
106
+ ...(parsed.port !== undefined ? { port: parsed.port } : {}),
107
+ rotate: parsed.rotate,
108
+ });
109
+ if (parsed.json) {
110
+ json(io, { ok: true, ...result });
111
+ }
112
+ else {
113
+ line(io, '');
114
+ line(io, 'Paste these into Chrome: Page Scanner settings, "Local agents".');
115
+ line(io, '');
116
+ line(io, ` Port ${result.port}`);
117
+ line(io, ` Token ${result.token}`);
118
+ line(io, '');
119
+ line(io, `Saved to ${result.configPath}${result.ownerOnly ? ' (owner-readable only).' : '.'}`);
120
+ if (!result.ownerOnly) {
121
+ note(io, 'Windows does not enforce the owner-only mode this file is written with. On a ' +
122
+ 'shared machine, restrict it yourself: see the README.');
123
+ }
124
+ line(io, 'Name the browser too, so an agent can tell your profiles apart, then press Connect.');
125
+ }
126
+ if (parsed.waitSeconds > 0) {
127
+ if (!parsed.json) {
128
+ note(io, `Waiting up to ${parsed.waitSeconds}s for the browser to connect.`);
129
+ }
130
+ // Not connecting is not a failure of `pair`: the pairing is written
131
+ // and correct either way, and the user may simply not have Chrome
132
+ // open yet. Say so and exit 0.
133
+ try {
134
+ const connected = await waitForBrowser({ waitSeconds: parsed.waitSeconds });
135
+ if (!parsed.json) {
136
+ note(io, `Connected as "${connected.label}" (${connected.browserId}).`);
137
+ }
138
+ }
139
+ catch {
140
+ if (!parsed.json) {
141
+ note(io, 'No browser connected yet. The pairing is saved; press Connect when ready.');
142
+ }
143
+ }
144
+ }
145
+ return 0;
146
+ }
147
+ case 'status':
148
+ reportStatus(io, await status(), parsed.json);
149
+ return 0;
150
+ case 'browsers': {
151
+ const browsers = await listBrowsers();
152
+ if (parsed.json) {
153
+ json(io, { ok: true, browsers });
154
+ }
155
+ else if (browsers.length === 0) {
156
+ note(io, 'No browser is connected. Open the Page Scanner settings page in Chrome and press Connect.');
157
+ return 3;
158
+ }
159
+ else {
160
+ line(io, formatBrowsers(browsers));
161
+ }
162
+ return 0;
163
+ }
164
+ case 'tabs': {
165
+ await warnIfWaiting(io, parsed.waitSeconds, parsed.json);
166
+ const result = await listTabs({
167
+ ...(parsed.browser !== undefined ? { browser: parsed.browser } : {}),
168
+ waitSeconds: parsed.waitSeconds,
169
+ });
170
+ if (parsed.json) {
171
+ json(io, { ok: true, ...result });
172
+ }
173
+ else {
174
+ line(io, formatTabs(result.windows, result.tabs));
175
+ }
176
+ return 0;
177
+ }
178
+ case 'scan': {
179
+ await warnIfWaiting(io, parsed.waitSeconds, parsed.json);
180
+ const result = await scan({
181
+ ...(parsed.url !== undefined ? { url: parsed.url } : {}),
182
+ ...(parsed.tabId !== undefined ? { tabId: parsed.tabId } : {}),
183
+ ...(parsed.windowId !== undefined ? { windowId: parsed.windowId } : {}),
184
+ ...(parsed.browser !== undefined ? { browser: parsed.browser } : {}),
185
+ ...(parsed.quality !== undefined ? { quality: parsed.quality } : {}),
186
+ ...(parsed.videoHandling !== undefined ? { videoHandling: parsed.videoHandling } : {}),
187
+ ...(parsed.out !== undefined ? { out: parsed.out } : {}),
188
+ format: parsed.format,
189
+ pageSize: parsed.pageSize,
190
+ openEditor: parsed.openEditor,
191
+ waitSeconds: parsed.waitSeconds,
192
+ timeoutSeconds: parsed.timeoutSeconds,
193
+ });
194
+ reportScan(io, result, parsed.format, parsed.json);
195
+ return 0;
196
+ }
197
+ case 'stop': {
198
+ const wasRunning = await stop();
199
+ if (parsed.json)
200
+ json(io, { ok: true, stopped: wasRunning });
201
+ else
202
+ note(io, wasRunning ? 'Stopped.' : 'No daemon was running.');
203
+ return 0;
204
+ }
205
+ case 'serve': {
206
+ // Only this branch loads the WebSocket server, so every other command
207
+ // stays free of `ws`. The Bun binary bundles it either way; the npm
208
+ // package does not pay for it on a scan.
209
+ const { runDaemon } = await import('../daemon/run.js');
210
+ await runDaemon({
211
+ detached: parsed.daemon,
212
+ ...(parsed.idleMinutes !== undefined ? { idleMinutes: parsed.idleMinutes } : {}),
213
+ log: (message) => note(io, message),
214
+ });
215
+ return 0;
216
+ }
217
+ }
218
+ }
219
+ catch (error) {
220
+ const failure = toPageScannerError(error);
221
+ const code = exitCodeFor(failure.code);
222
+ const useJson = 'json' in parsed && parsed.json === true;
223
+ if (useJson) {
224
+ json(io, { ok: false, code, error: failure.code, message: failure.message });
225
+ }
226
+ else {
227
+ note(io, failure.message);
228
+ if (failure.hint)
229
+ note(io, failure.hint);
230
+ }
231
+ return code;
232
+ }
233
+ }
234
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}