@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.
- package/LICENSE +201 -0
- package/README.md +217 -0
- package/dist/api.d.ts +87 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +132 -0
- package/dist/api.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +49 -0
- package/dist/bin.js.map +1 -0
- package/dist/bridge/server.d.ts +97 -0
- package/dist/bridge/server.d.ts.map +1 -0
- package/dist/bridge/server.js +329 -0
- package/dist/bridge/server.js.map +1 -0
- package/dist/bridge/tokens.d.ts +10 -0
- package/dist/bridge/tokens.d.ts.map +1 -0
- package/dist/bridge/tokens.js +15 -0
- package/dist/bridge/tokens.js.map +1 -0
- package/dist/cli/args.d.ts +63 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +388 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +234 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/format.d.ts +22 -0
- package/dist/cli/format.d.ts.map +1 -0
- package/dist/cli/format.js +152 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +54 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/client.d.ts +28 -0
- package/dist/daemon/client.d.ts.map +1 -0
- package/dist/daemon/client.js +139 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/rpc-server.d.ts +31 -0
- package/dist/daemon/rpc-server.d.ts.map +1 -0
- package/dist/daemon/rpc-server.js +254 -0
- package/dist/daemon/rpc-server.js.map +1 -0
- package/dist/daemon/run.d.ts +20 -0
- package/dist/daemon/run.d.ts.map +1 -0
- package/dist/daemon/run.js +129 -0
- package/dist/daemon/run.js.map +1 -0
- package/dist/daemon/self.d.ts +25 -0
- package/dist/daemon/self.d.ts.map +1 -0
- package/dist/daemon/self.js +113 -0
- package/dist/daemon/self.js.map +1 -0
- package/dist/daemon/state.d.ts +25 -0
- package/dist/daemon/state.d.ts.map +1 -0
- package/dist/daemon/state.js +116 -0
- package/dist/daemon/state.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +83 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/output.d.ts +13 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +71 -0
- package/dist/output.js.map +1 -0
- package/dist/protocol.d.ts +144 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +128 -0
- package/dist/protocol.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daemon's record of itself, at `<configDir()>/daemon.json`.
|
|
3
|
+
*
|
|
4
|
+
* Every other process — a second `page-scanner scan`, the MCP server, a
|
|
5
|
+
* Claude Desktop bundle — finds the running daemon through this file and
|
|
6
|
+
* nothing else. There is no discovery: the RPC port is ephemeral and the
|
|
7
|
+
* bearer secret is regenerated per run, so the file is the only place either
|
|
8
|
+
* is written down. Mode 0600 for the same reason `config.json` is: the secret
|
|
9
|
+
* in it buys a scan of any tab the user has open. On Windows that mode is a
|
|
10
|
+
* no-op, as the README says.
|
|
11
|
+
*
|
|
12
|
+
* The file is deleted on a clean exit, but a daemon that is killed leaves it
|
|
13
|
+
* behind, so a reader must never trust it on its own. That is what the pid is
|
|
14
|
+
* for: `readDaemonState` reports a record whose process is gone as no record
|
|
15
|
+
* at all, and the caller starts a fresh daemon instead of posting RPC at a
|
|
16
|
+
* port nothing is listening on.
|
|
17
|
+
*/
|
|
18
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { configDir } from '../config.js';
|
|
21
|
+
export function daemonStatePath() {
|
|
22
|
+
return join(configDir(), 'daemon.json');
|
|
23
|
+
}
|
|
24
|
+
export function daemonLogPath() {
|
|
25
|
+
return join(configDir(), 'daemon.log');
|
|
26
|
+
}
|
|
27
|
+
/** A port number as `listen` would have produced it, never 0: the daemon records what it bound. */
|
|
28
|
+
function isPort(value) {
|
|
29
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 && value <= 65535;
|
|
30
|
+
}
|
|
31
|
+
function isNonEmptyString(value) {
|
|
32
|
+
return typeof value === 'string' && value.length > 0;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reads the record, or null if there is nothing usable to read: no file, bad
|
|
36
|
+
* JSON, a field missing or of the wrong type, or a pid that no longer exists.
|
|
37
|
+
* It never throws — it is on the path every command takes before it can do
|
|
38
|
+
* anything, and a corrupt file there should cost a restart, not a crash.
|
|
39
|
+
*/
|
|
40
|
+
export function readDaemonState() {
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(readFileSync(daemonStatePath(), 'utf8'));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
49
|
+
return null;
|
|
50
|
+
const record = parsed;
|
|
51
|
+
const { pid, rpcPort, secret, bridgePort, version, startedAt } = record;
|
|
52
|
+
if (!isPort(rpcPort) || !isPort(bridgePort))
|
|
53
|
+
return null;
|
|
54
|
+
if (!isNonEmptyString(secret) || !isNonEmptyString(version))
|
|
55
|
+
return null;
|
|
56
|
+
if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0)
|
|
57
|
+
return null;
|
|
58
|
+
if (typeof startedAt !== 'number' || !Number.isFinite(startedAt))
|
|
59
|
+
return null;
|
|
60
|
+
// The liveness check comes last so a malformed file is rejected on its own
|
|
61
|
+
// terms rather than by signalling whatever the garbage in `pid` points at.
|
|
62
|
+
if (!processAlive(pid))
|
|
63
|
+
return null;
|
|
64
|
+
return { pid, rpcPort, secret, bridgePort, version, startedAt };
|
|
65
|
+
}
|
|
66
|
+
export function writeDaemonState(state) {
|
|
67
|
+
mkdirSync(configDir(), { recursive: true, mode: 0o700 });
|
|
68
|
+
// Written to one side and renamed over, because every other process reads
|
|
69
|
+
// this file while the daemon is coming up. `writeFileSync` truncates first,
|
|
70
|
+
// and a reader landing in that window parses an empty file and concludes
|
|
71
|
+
// there is no daemon; `rename` is atomic on the same filesystem, so a reader
|
|
72
|
+
// sees either the old record or the new one.
|
|
73
|
+
const temporary = `${daemonStatePath()}.tmp`;
|
|
74
|
+
writeFileSync(temporary, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
|
|
75
|
+
// `writeFileSync`'s mode only applies when it creates the file, and a
|
|
76
|
+
// restart overwrites one the previous run left behind.
|
|
77
|
+
chmodSync(temporary, 0o600);
|
|
78
|
+
renameSync(temporary, daemonStatePath());
|
|
79
|
+
}
|
|
80
|
+
export function clearDaemonState() {
|
|
81
|
+
try {
|
|
82
|
+
rmSync(daemonStatePath(), { force: true });
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Deleting is housekeeping on the way out, and the exit it runs from
|
|
86
|
+
// matters more than the file. A record that survives is stale, not
|
|
87
|
+
// dangerous: `readDaemonState` drops it once the pid is gone.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** The `code` of a Node system error, when that is what was thrown. */
|
|
91
|
+
function errorCode(error) {
|
|
92
|
+
if (typeof error !== 'object' || error === null || !('code' in error))
|
|
93
|
+
return undefined;
|
|
94
|
+
return typeof error.code === 'string' ? error.code : undefined;
|
|
95
|
+
}
|
|
96
|
+
export function processAlive(pid) {
|
|
97
|
+
// `kill(2)` reads pid 0 as "every process in my own group" and a negative
|
|
98
|
+
// pid as a group id, both of which exist, so a record with a zeroed or
|
|
99
|
+
// corrupt pid would otherwise always report alive.
|
|
100
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
101
|
+
return false;
|
|
102
|
+
try {
|
|
103
|
+
// Signal 0 delivers nothing; it runs the existence and permission checks
|
|
104
|
+
// and reports through errno.
|
|
105
|
+
process.kill(pid, 0);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// EPERM means the process is there and belongs to another user — alive,
|
|
110
|
+
// and the RPC secret will settle whether we may talk to it. ESRCH means
|
|
111
|
+
// there is no such process. Anything else leaves us unable to confirm a
|
|
112
|
+
// daemon we could use, which is the same thing as not having one.
|
|
113
|
+
return errorCode(error) === 'EPERM';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/daemon/state.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One error type, one exit code per class of failure.
|
|
3
|
+
*
|
|
4
|
+
* A script wrapping this CLI should not have to read the message to know what
|
|
5
|
+
* happened. The code says which of the handful of things went wrong, and
|
|
6
|
+
* `exitCodeFor` maps it onto the process exit status documented in the README.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ERROR_CODES: readonly ["NOT_PAIRED", "NO_BROWSER", "AMBIGUOUS_BROWSER", "UNKNOWN_BROWSER", "BAD_REQUEST", "TIMEOUT", "BROWSER_GONE", "SCAN_FAILED", "DAEMON_FAILED", "PORT_IN_USE"];
|
|
9
|
+
export type ErrorCode = (typeof ERROR_CODES)[number];
|
|
10
|
+
export declare class PageScannerError extends Error {
|
|
11
|
+
readonly code: ErrorCode;
|
|
12
|
+
/** A second line of help, printed under the message. */
|
|
13
|
+
readonly hint: string | undefined;
|
|
14
|
+
constructor(code: ErrorCode, message: string, hint?: string);
|
|
15
|
+
}
|
|
16
|
+
export declare function isPageScannerError(value: unknown): value is PageScannerError;
|
|
17
|
+
/**
|
|
18
|
+
* Turns whatever was thrown into a PageScannerError, so every exit goes
|
|
19
|
+
* through the same reporting path. An unrecognised failure is SCAN_FAILED
|
|
20
|
+
* rather than a crash: the caller still gets a code and a message.
|
|
21
|
+
*/
|
|
22
|
+
export declare function toPageScannerError(value: unknown): PageScannerError;
|
|
23
|
+
/**
|
|
24
|
+
* 0 success
|
|
25
|
+
* 1 the browser was reached and the work failed
|
|
26
|
+
* 2 the arguments were wrong
|
|
27
|
+
* 3 no usable browser: none connected, several connected, or one named that is not
|
|
28
|
+
* 4 not paired
|
|
29
|
+
* 5 the daemon would not start
|
|
30
|
+
*/
|
|
31
|
+
export declare function exitCodeFor(code: ErrorCode): number;
|
|
32
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,WAAW,wKAqBd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEtB,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAM5D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAInE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAkBnD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One error type, one exit code per class of failure.
|
|
3
|
+
*
|
|
4
|
+
* A script wrapping this CLI should not have to read the message to know what
|
|
5
|
+
* happened. The code says which of the handful of things went wrong, and
|
|
6
|
+
* `exitCodeFor` maps it onto the process exit status documented in the README.
|
|
7
|
+
*/
|
|
8
|
+
export const ERROR_CODES = [
|
|
9
|
+
/** No pairing on disk. Run `page-scanner pair`. */
|
|
10
|
+
'NOT_PAIRED',
|
|
11
|
+
/** Nothing connected, or nothing connected before `--wait` ran out. */
|
|
12
|
+
'NO_BROWSER',
|
|
13
|
+
/** Several browsers are connected and none was named. */
|
|
14
|
+
'AMBIGUOUS_BROWSER',
|
|
15
|
+
/** A browser was named and it is not connected. */
|
|
16
|
+
'UNKNOWN_BROWSER',
|
|
17
|
+
/** The arguments do not make sense. */
|
|
18
|
+
'BAD_REQUEST',
|
|
19
|
+
/** The browser accepted the request and never answered. */
|
|
20
|
+
'TIMEOUT',
|
|
21
|
+
/** The browser disconnected with the request still outstanding. */
|
|
22
|
+
'BROWSER_GONE',
|
|
23
|
+
/** The extension reported a failure. */
|
|
24
|
+
'SCAN_FAILED',
|
|
25
|
+
/** The daemon could not be started, or could not be reached once started. */
|
|
26
|
+
'DAEMON_FAILED',
|
|
27
|
+
/** Something already holds the bridge port. */
|
|
28
|
+
'PORT_IN_USE',
|
|
29
|
+
];
|
|
30
|
+
export class PageScannerError extends Error {
|
|
31
|
+
code;
|
|
32
|
+
/** A second line of help, printed under the message. */
|
|
33
|
+
hint;
|
|
34
|
+
constructor(code, message, hint) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'PageScannerError';
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.hint = hint;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function isPageScannerError(value) {
|
|
42
|
+
return value instanceof PageScannerError;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Turns whatever was thrown into a PageScannerError, so every exit goes
|
|
46
|
+
* through the same reporting path. An unrecognised failure is SCAN_FAILED
|
|
47
|
+
* rather than a crash: the caller still gets a code and a message.
|
|
48
|
+
*/
|
|
49
|
+
export function toPageScannerError(value) {
|
|
50
|
+
if (isPageScannerError(value))
|
|
51
|
+
return value;
|
|
52
|
+
if (value instanceof Error)
|
|
53
|
+
return new PageScannerError('SCAN_FAILED', value.message);
|
|
54
|
+
return new PageScannerError('SCAN_FAILED', String(value));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 0 success
|
|
58
|
+
* 1 the browser was reached and the work failed
|
|
59
|
+
* 2 the arguments were wrong
|
|
60
|
+
* 3 no usable browser: none connected, several connected, or one named that is not
|
|
61
|
+
* 4 not paired
|
|
62
|
+
* 5 the daemon would not start
|
|
63
|
+
*/
|
|
64
|
+
export function exitCodeFor(code) {
|
|
65
|
+
switch (code) {
|
|
66
|
+
case 'BAD_REQUEST':
|
|
67
|
+
return 2;
|
|
68
|
+
case 'NO_BROWSER':
|
|
69
|
+
case 'AMBIGUOUS_BROWSER':
|
|
70
|
+
case 'UNKNOWN_BROWSER':
|
|
71
|
+
return 3;
|
|
72
|
+
case 'NOT_PAIRED':
|
|
73
|
+
return 4;
|
|
74
|
+
case 'DAEMON_FAILED':
|
|
75
|
+
case 'PORT_IN_USE':
|
|
76
|
+
return 5;
|
|
77
|
+
case 'TIMEOUT':
|
|
78
|
+
case 'BROWSER_GONE':
|
|
79
|
+
case 'SCAN_FAILED':
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,mDAAmD;IACnD,YAAY;IACZ,uEAAuE;IACvE,YAAY;IACZ,yDAAyD;IACzD,mBAAmB;IACnB,mDAAmD;IACnD,iBAAiB;IACjB,uCAAuC;IACvC,aAAa;IACb,2DAA2D;IAC3D,SAAS;IACT,mEAAmE;IACnE,cAAc;IACd,wCAAwC;IACxC,aAAa;IACb,6EAA6E;IAC7E,eAAe;IACf,+CAA+C;IAC/C,aAAa;CACL,CAAC;AAIX,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAY;IACzB,wDAAwD;IAC/C,IAAI,CAAqB;IAElC,YAAY,IAAe,EAAE,OAAe,EAAE,IAAa;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,KAAK,YAAY,gBAAgB,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,kBAAkB,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,IAAI,gBAAgB,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACtF,OAAO,IAAI,gBAAgB,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,IAAe;IACzC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,aAAa;YAChB,OAAO,CAAC,CAAC;QACX,KAAK,YAAY,CAAC;QAClB,KAAK,mBAAmB,CAAC;QACzB,KAAK,iBAAiB;YACpB,OAAO,CAAC,CAAC;QACX,KAAK,YAAY;YACf,OAAO,CAAC,CAAC;QACX,KAAK,eAAe,CAAC;QACrB,KAAK,aAAa;YAChB,OAAO,CAAC,CAAC;QACX,KAAK,SAAS,CAAC;QACf,KAAK,cAAc,CAAC;QACpB,KAAK,aAAa;YAChB,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The programmatic surface of the `@page-scanner/cli` package.
|
|
3
|
+
*
|
|
4
|
+
* Importing this does not load `ws`: the bridge runs in the daemon, which is
|
|
5
|
+
* a separate process. `@page-scanner/cli/daemon` is the entry point that starts
|
|
6
|
+
* one in-process, and it is the only one that pulls the WebSocket server in.
|
|
7
|
+
*/
|
|
8
|
+
export { listBrowsers, listTabs, pair, scan, status, stop, waitForBrowser, type CommonOptions, type PairResult, type ScanOptions, type ScanResult, type StatusReport, type TabsResult, } from './api.js';
|
|
9
|
+
export { configDir, configPath, configFileIsOwnerOnly, ensureConfig, readConfig, DEFAULT_PORT, type PairingConfig, } from './config.js';
|
|
10
|
+
export { exitCodeFor, isPageScannerError, toPageScannerError, PageScannerError, ERROR_CODES, type ErrorCode, } from './errors.js';
|
|
11
|
+
export { resolveOutputPath, writeScanFile } from './output.js';
|
|
12
|
+
export { connectDaemon, stopDaemon, DAEMON_START_TIMEOUT_MS, type ConnectOptions, type DaemonClient, } from './daemon/client.js';
|
|
13
|
+
export { daemonLogPath, daemonStatePath, readDaemonState, type DaemonState, } from './daemon/state.js';
|
|
14
|
+
export type { ConnectedBrowser } from './bridge/server.js';
|
|
15
|
+
export { BRIDGE_PING_INTERVAL_MS, BRIDGE_PROTOCOL_VERSION, DEFAULT_PAGE_SIZE, EXPORT_FORMAT_IDS, PAGE_SIZE_IDS, VIDEO_HANDLINGS, type ExportFormatId, type PageSizeId, type TabSummary, type TruncationReport, type VideoHandling, type WindowSummary, } from './protocol.js';
|
|
16
|
+
export { CLI_VERSION } from './version.js';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,KAAK,SAAS,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EACL,aAAa,EACb,UAAU,EACV,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,aAAa,EACb,eAAe,EACf,eAAe,EACf,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The programmatic surface of the `@page-scanner/cli` package.
|
|
3
|
+
*
|
|
4
|
+
* Importing this does not load `ws`: the bridge runs in the daemon, which is
|
|
5
|
+
* a separate process. `@page-scanner/cli/daemon` is the entry point that starts
|
|
6
|
+
* one in-process, and it is the only one that pulls the WebSocket server in.
|
|
7
|
+
*/
|
|
8
|
+
export { listBrowsers, listTabs, pair, scan, status, stop, waitForBrowser, } from './api.js';
|
|
9
|
+
export { configDir, configPath, configFileIsOwnerOnly, ensureConfig, readConfig, DEFAULT_PORT, } from './config.js';
|
|
10
|
+
export { exitCodeFor, isPageScannerError, toPageScannerError, PageScannerError, ERROR_CODES, } from './errors.js';
|
|
11
|
+
export { resolveOutputPath, writeScanFile } from './output.js';
|
|
12
|
+
export { connectDaemon, stopDaemon, DAEMON_START_TIMEOUT_MS, } from './daemon/client.js';
|
|
13
|
+
export { daemonLogPath, daemonStatePath, readDaemonState, } from './daemon/state.js';
|
|
14
|
+
export { BRIDGE_PING_INTERVAL_MS, BRIDGE_PROTOCOL_VERSION, DEFAULT_PAGE_SIZE, EXPORT_FORMAT_IDS, PAGE_SIZE_IDS, VIDEO_HANDLINGS, } from './protocol.js';
|
|
15
|
+
export { CLI_VERSION } from './version.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,cAAc,GAOf,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,SAAS,EACT,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,UAAU,EACV,YAAY,GAEb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,GAEZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EACL,aAAa,EACb,UAAU,EACV,uBAAuB,GAGxB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,aAAa,EACb,eAAe,EACf,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,eAAe,GAOhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns `--out` into the absolute path to write.
|
|
3
|
+
*
|
|
4
|
+
* `cwd` is a parameter so the tests do not have to chdir the process, and so a
|
|
5
|
+
* caller holding someone else's working directory can pass it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveOutputPath(outputPath: string | undefined, suggestedName: string, cwd?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* `--out captures/2026/page.pdf` should not fail because `2026` is not there
|
|
10
|
+
* yet, so the parent chain is created on the way in.
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeScanFile(target: string, bytesBase64: string): void;
|
|
13
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AA+BA;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,MAAsB,GAC1B,MAAM,CASR;AAqBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAGvE"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a scan lands on disk, and putting it there.
|
|
3
|
+
*
|
|
4
|
+
* The daemon never writes the file. It hands back bytes and the client that
|
|
5
|
+
* asked resolves `--out` against its own working directory, because the daemon
|
|
6
|
+
* was started from some other terminal at some other time and its cwd has
|
|
7
|
+
* nothing to do with where the person typing the command is standing.
|
|
8
|
+
*/
|
|
9
|
+
import { mkdirSync, statSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { dirname, join, resolve } from 'node:path';
|
|
11
|
+
/**
|
|
12
|
+
* A trailing separator is how the user says "this is a directory", and the only
|
|
13
|
+
* way to say it about a directory that does not exist yet.
|
|
14
|
+
*
|
|
15
|
+
* The test accepts `\` as well as `/` instead of `path.sep`, because this
|
|
16
|
+
* string came from the user's shell rather than from this platform's path API:
|
|
17
|
+
* a Windows shell hands over `C:\out\`, and a POSIX test run has to be able to
|
|
18
|
+
* reproduce that argument. It earns its keep on Windows, where `path.resolve`
|
|
19
|
+
* strips the trailing `\` before the extension test below ever sees it, so
|
|
20
|
+
* `C:\out\page.pdf\` would otherwise be read as a file called `page.pdf`.
|
|
21
|
+
*/
|
|
22
|
+
const TRAILING_SEPARATOR = /[\\/]$/;
|
|
23
|
+
/**
|
|
24
|
+
* Two to five alphanumerics after a dot is what separates `report.pdf` from a
|
|
25
|
+
* directory called `captures`. Case is ignored: `report.PDF` off a Windows
|
|
26
|
+
* shell names the same file, and nothing downstream cares about the spelling.
|
|
27
|
+
*/
|
|
28
|
+
const FILE_EXTENSION = /\.[a-z0-9]{2,5}$/i;
|
|
29
|
+
/**
|
|
30
|
+
* Turns `--out` into the absolute path to write.
|
|
31
|
+
*
|
|
32
|
+
* `cwd` is a parameter so the tests do not have to chdir the process, and so a
|
|
33
|
+
* caller holding someone else's working directory can pass it.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveOutputPath(outputPath, suggestedName, cwd = process.cwd()) {
|
|
36
|
+
if (!outputPath)
|
|
37
|
+
return resolve(cwd, suggestedName);
|
|
38
|
+
const target = resolve(cwd, outputPath);
|
|
39
|
+
const namesAFile = !TRAILING_SEPARATOR.test(outputPath) &&
|
|
40
|
+
FILE_EXTENSION.test(target) &&
|
|
41
|
+
!isExistingDirectory(target);
|
|
42
|
+
return namesAFile ? target : join(target, suggestedName);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* An existing directory wins over the extension rule, so `--out ./v1.2` writes
|
|
46
|
+
* into the directory the user already made rather than over it.
|
|
47
|
+
*/
|
|
48
|
+
function isExistingDirectory(candidate) {
|
|
49
|
+
try {
|
|
50
|
+
// `throwIfNoEntry: false` already covers both "not there yet" and "a
|
|
51
|
+
// parent component is a file": stat returns undefined for each. The catch
|
|
52
|
+
// is for what it cannot swallow, a lookup the OS refuses (EACCES on a
|
|
53
|
+
// share, say). "Not a directory" is the right answer there too, and if the
|
|
54
|
+
// path really is unusable the write reports it with the operation that
|
|
55
|
+
// actually failed rather than this function throwing a stat error out of
|
|
56
|
+
// argument handling.
|
|
57
|
+
return statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `--out captures/2026/page.pdf` should not fail because `2026` is not there
|
|
65
|
+
* yet, so the parent chain is created on the way in.
|
|
66
|
+
*/
|
|
67
|
+
export function writeScanFile(target, bytesBase64) {
|
|
68
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
69
|
+
writeFileSync(target, Buffer.from(bytesBase64, 'base64'));
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAEpC;;;;GAIG;AACH,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAA8B,EAC9B,aAAqB,EACrB,MAAc,OAAO,CAAC,GAAG,EAAE;IAE3B,IAAI,CAAC,UAAU;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACxC,MAAM,UAAU,GACd,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;QACpC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAC3B,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,IAAI,CAAC;QACH,qEAAqE;QACrE,0EAA0E;QAC1E,sEAAsE;QACtE,2EAA2E;QAC3E,uEAAuE;QACvE,yEAAyE;QACzE,qBAAqB;QACrB,OAAO,QAAQ,CAAC,SAAS,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,IAAI,KAAK,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,WAAmB;IAC/D,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mirror of `extension/src/lib/bridge/protocol.ts`.
|
|
3
|
+
*
|
|
4
|
+
* The extension owns the wire contract, because it owns the features the
|
|
5
|
+
* contract describes: the export formats, the paper sizes, what to do with a
|
|
6
|
+
* video. This file restates them for a package that cannot import the
|
|
7
|
+
* extension's source, and `protocol.contract.test.ts` fails if the two drift.
|
|
8
|
+
*
|
|
9
|
+
* The direction is the other half of the mirror, too. Over there the guards
|
|
10
|
+
* check what arrives *at the extension*; here they check what arrives *at the
|
|
11
|
+
* server*: a handshake, a ping, and the frames that answer a request. A
|
|
12
|
+
* loopback port is reachable by every process on the machine, so nothing here
|
|
13
|
+
* assumes a shape it has not checked, and a frame that fails a guard is
|
|
14
|
+
* dropped rather than coerced.
|
|
15
|
+
*/
|
|
16
|
+
/** Bumped when a change would make an older server and a newer extension disagree. */
|
|
17
|
+
export declare const BRIDGE_PROTOCOL_VERSION = 1;
|
|
18
|
+
/** Ping cadence. Chrome resets the service worker's idle timer on every frame. */
|
|
19
|
+
export declare const BRIDGE_PING_INTERVAL_MS = 20000;
|
|
20
|
+
/** Mirrors EXPORT_FORMAT_IDS in `extension/src/lib/export/formats.ts`. */
|
|
21
|
+
export declare const EXPORT_FORMAT_IDS: readonly ["pdf", "png", "jpeg"];
|
|
22
|
+
export type ExportFormatId = (typeof EXPORT_FORMAT_IDS)[number];
|
|
23
|
+
/** Mirrors the keys of PAGE_SIZE_LABELS in `extension/src/lib/pdf/layout.ts`. */
|
|
24
|
+
export declare const PAGE_SIZE_IDS: readonly ["auto", "a4", "letter"];
|
|
25
|
+
export type PageSizeId = (typeof PAGE_SIZE_IDS)[number];
|
|
26
|
+
/** Mirrors DEFAULT_PAGE_SIZE in `extension/src/lib/pdf/layout.ts`. */
|
|
27
|
+
export declare const DEFAULT_PAGE_SIZE: PageSizeId;
|
|
28
|
+
/** Mirrors VIDEO_HANDLINGS in `extension/src/lib/capture/viewport-units.ts`. */
|
|
29
|
+
export declare const VIDEO_HANDLINGS: readonly ["frame", "blank"];
|
|
30
|
+
export type VideoHandling = (typeof VIDEO_HANDLINGS)[number];
|
|
31
|
+
/** First frame the extension sends. The server answers `hello-ok` or closes. */
|
|
32
|
+
export interface HelloFrame {
|
|
33
|
+
type: 'hello';
|
|
34
|
+
protocol: number;
|
|
35
|
+
token: string;
|
|
36
|
+
extensionVersion: string;
|
|
37
|
+
/** Stable per install, so an agent can name the same browser across restarts. */
|
|
38
|
+
browserId: string;
|
|
39
|
+
/** What the user called this browser in the settings page, e.g. "work". */
|
|
40
|
+
label: string;
|
|
41
|
+
}
|
|
42
|
+
export interface HelloAck {
|
|
43
|
+
type: 'hello-ok';
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sent instead of `hello-ok` when the handshake is refused for a reason the
|
|
47
|
+
* user can act on, so the settings page can say what is wrong rather than
|
|
48
|
+
* showing a bare close code. The socket closes straight after.
|
|
49
|
+
*/
|
|
50
|
+
export interface HelloReject {
|
|
51
|
+
type: 'hello-error';
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PingFrame {
|
|
55
|
+
type: 'ping';
|
|
56
|
+
}
|
|
57
|
+
export interface ListTabsRequest {
|
|
58
|
+
type: 'list-tabs';
|
|
59
|
+
id: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ScanRequest {
|
|
62
|
+
type: 'scan';
|
|
63
|
+
id: string;
|
|
64
|
+
/** Scan a tab the user already has open, or open one on `url` and scan that. */
|
|
65
|
+
tabId?: number;
|
|
66
|
+
url?: string;
|
|
67
|
+
/** Which window to open `url` in. Ignored when `tabId` is given. */
|
|
68
|
+
windowId?: number;
|
|
69
|
+
format: ExportFormatId;
|
|
70
|
+
pageSize: PageSizeId;
|
|
71
|
+
/** 0..1, only meaningful for a lossy format. */
|
|
72
|
+
quality?: number;
|
|
73
|
+
videoHandling?: VideoHandling;
|
|
74
|
+
/** Leave the capture open in an editor tab afterwards. Default false. */
|
|
75
|
+
openEditor?: boolean;
|
|
76
|
+
}
|
|
77
|
+
export interface WindowSummary {
|
|
78
|
+
windowId: number;
|
|
79
|
+
focused: boolean;
|
|
80
|
+
/** `normal`, `popup`, and so on, straight from `chrome.windows`. */
|
|
81
|
+
windowType: string;
|
|
82
|
+
tabCount: number;
|
|
83
|
+
}
|
|
84
|
+
export interface TabSummary {
|
|
85
|
+
tabId: number;
|
|
86
|
+
windowId: number;
|
|
87
|
+
url: string;
|
|
88
|
+
title: string;
|
|
89
|
+
active: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* What was left out, when the page was larger than the capture ceiling.
|
|
93
|
+
*
|
|
94
|
+
* The sentence travels on the wire rather than being rebuilt here: the CLI
|
|
95
|
+
* would otherwise have to keep its own copy of a limit that lives in the
|
|
96
|
+
* extension.
|
|
97
|
+
*/
|
|
98
|
+
export interface TruncationReport {
|
|
99
|
+
/** What the page measured, in CSS px, before the ceiling clamped it. */
|
|
100
|
+
requestedWidth: number;
|
|
101
|
+
requestedHeight: number;
|
|
102
|
+
/** What was captured instead. */
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
/** One sentence naming the gap, ready to hand to a person. */
|
|
106
|
+
message: string;
|
|
107
|
+
}
|
|
108
|
+
export interface TabsResponse {
|
|
109
|
+
type: 'tabs';
|
|
110
|
+
id: string;
|
|
111
|
+
windows: WindowSummary[];
|
|
112
|
+
tabs: TabSummary[];
|
|
113
|
+
}
|
|
114
|
+
export interface ScanResultResponse {
|
|
115
|
+
type: 'scan-result';
|
|
116
|
+
id: string;
|
|
117
|
+
width: number;
|
|
118
|
+
height: number;
|
|
119
|
+
mode: 'vector' | 'raster';
|
|
120
|
+
/** Absent unless the capture stopped short of the whole page. */
|
|
121
|
+
truncated?: TruncationReport;
|
|
122
|
+
fileName: string;
|
|
123
|
+
/** The built file. Base64 because JSON is the frame format. */
|
|
124
|
+
bytesBase64: string;
|
|
125
|
+
}
|
|
126
|
+
export interface ErrorResponse {
|
|
127
|
+
type: 'error';
|
|
128
|
+
id: string;
|
|
129
|
+
message: string;
|
|
130
|
+
}
|
|
131
|
+
export type BridgeResponse = TabsResponse | ScanResultResponse | ErrorResponse;
|
|
132
|
+
/** Anything the extension may send to the server. */
|
|
133
|
+
export type InboundFrame = HelloFrame | PingFrame | BridgeResponse;
|
|
134
|
+
export declare function isHelloFrame(value: unknown): value is HelloFrame;
|
|
135
|
+
export declare function isPingFrame(value: unknown): value is PingFrame;
|
|
136
|
+
export declare function isTabsResponse(value: unknown): value is TabsResponse;
|
|
137
|
+
export declare function isScanResultResponse(value: unknown): value is ScanResultResponse;
|
|
138
|
+
export declare function isErrorResponse(value: unknown): value is ErrorResponse;
|
|
139
|
+
export declare function isBridgeResponse(value: unknown): value is BridgeResponse;
|
|
140
|
+
/** Everything the extension may send, parsed from one socket message. */
|
|
141
|
+
export declare function parseInbound(raw: string): InboundFrame | null;
|
|
142
|
+
export declare function helloAck(): HelloAck;
|
|
143
|
+
export declare function helloReject(message: string): HelloReject;
|
|
144
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|