@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
package/dist/bin.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The `page-scanner` command. Argument parsing, dispatch, exit code.
|
|
4
|
+
*
|
|
5
|
+
* Everything else lives behind `runCommand`, so the same behaviour is
|
|
6
|
+
* reachable from `import { scan } from '@page-scanner/cli'` without a process.
|
|
7
|
+
*/
|
|
8
|
+
import { parseArgs } from './cli/args.js';
|
|
9
|
+
import { runCommand } from './cli/commands.js';
|
|
10
|
+
import { exitCodeFor, toPageScannerError } from './errors.js';
|
|
11
|
+
const io = {
|
|
12
|
+
stdout: (text) => void process.stdout.write(text),
|
|
13
|
+
stderr: (text) => void process.stderr.write(text),
|
|
14
|
+
};
|
|
15
|
+
async function main() {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
18
|
+
return { code: await runCommand(parsed, io), command: parsed.command };
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
// Only parseArgs can land here, and it failed before it could tell us
|
|
22
|
+
// whether --json was asked for. The flag is read off the raw argv rather
|
|
23
|
+
// than assumed absent: `--json` promises one JSON document on stdout for
|
|
24
|
+
// a failure as much as for a success, and a parse error is the failure a
|
|
25
|
+
// script is most likely to hit.
|
|
26
|
+
const failure = toPageScannerError(error);
|
|
27
|
+
const code = exitCodeFor(failure.code);
|
|
28
|
+
if (process.argv.slice(2).includes('--json')) {
|
|
29
|
+
const envelope = { ok: false, code, error: failure.code, message: failure.message };
|
|
30
|
+
process.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
process.stderr.write(`${failure.message}\n`);
|
|
34
|
+
if (failure.hint)
|
|
35
|
+
process.stderr.write(`${failure.hint}\n`);
|
|
36
|
+
process.stderr.write('Run `page-scanner --help` for the full surface.\n');
|
|
37
|
+
}
|
|
38
|
+
return { code, command: 'help' };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const { code, command } = await main();
|
|
42
|
+
process.exitCode = code;
|
|
43
|
+
if (command !== 'serve') {
|
|
44
|
+
// `fetch` keeps its connection pooled, which holds the event loop open for
|
|
45
|
+
// several seconds after the answer has been printed. Leave once the streams
|
|
46
|
+
// have flushed rather than making every command look slow.
|
|
47
|
+
process.stdout.write('', () => process.exit(code));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE9D,MAAM,EAAE,GAAG;IACT,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;IACzD,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;CAC1D,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sEAAsE;QACtE,yEAAyE;QACzE,yEAAyE;QACzE,yEAAyE;QACzE,gCAAgC;QAChC,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7C,MAAM,QAAQ,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;YACpF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;YAC7C,IAAI,OAAO,CAAC,IAAI;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;YAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;AACvC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAExB,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;IACxB,2EAA2E;IAC3E,4EAA4E;IAC5E,2DAA2D;IAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server half of the bridge: one WebSocket listener, many browsers.
|
|
3
|
+
*
|
|
4
|
+
* Chrome runs a separate copy of the extension in every profile, so a machine
|
|
5
|
+
* with a work profile and a personal profile has two extensions dialling in
|
|
6
|
+
* here. Each announces a `browserId` and a `label` in its handshake, and a
|
|
7
|
+
* caller names one of those when it asks for something.
|
|
8
|
+
*
|
|
9
|
+
* Bound to 127.0.0.1 only. Three checks on every connection: the `Origin` has
|
|
10
|
+
* to be a `chrome-extension://` one (which stops a web page in any browser
|
|
11
|
+
* talking to us), the first frame has to carry the shared token (which stops
|
|
12
|
+
* any other program on this machine doing so, since a local process can forge
|
|
13
|
+
* an Origin header), and the protocol number has to match.
|
|
14
|
+
*
|
|
15
|
+
* Ported from mcp/src/bridge-server.js with three defects fixed:
|
|
16
|
+
*
|
|
17
|
+
* - Pending requests were keyed `req-N` in one map shared by every browser,
|
|
18
|
+
* so a reply from one profile could settle a request sent to another. Each
|
|
19
|
+
* connection now owns its own map and its own counter, and a frame is only
|
|
20
|
+
* matched against the map of the socket it arrived on.
|
|
21
|
+
* - `close()` cleared the pending timers without settling the promises, so a
|
|
22
|
+
* caller waiting on a scan when the server shut down waited for ever. Every
|
|
23
|
+
* outstanding request is now rejected, on disconnect and on shutdown.
|
|
24
|
+
* - There were no connection events and no way to wait for one, which is what
|
|
25
|
+
* `--wait` needs: the extension's service worker retires after about thirty
|
|
26
|
+
* seconds and reconnects when it wakes.
|
|
27
|
+
*/
|
|
28
|
+
import { EventEmitter } from 'node:events';
|
|
29
|
+
import { type ScanRequest, type ScanResultResponse, type TabsResponse } from '../protocol.js';
|
|
30
|
+
/** How long a request waits for a browser to answer before it is given up on. */
|
|
31
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 120000;
|
|
32
|
+
/** Close codes. 4000-4999 is the range an application may define. */
|
|
33
|
+
export declare const CLOSE_REPLACED = 4000;
|
|
34
|
+
export declare const CLOSE_BAD_TOKEN = 4001;
|
|
35
|
+
export declare const CLOSE_BAD_PROTOCOL = 4002;
|
|
36
|
+
export declare const CLOSE_BAD_ORIGIN = 4003;
|
|
37
|
+
export declare const CLOSE_NO_HANDSHAKE = 4008;
|
|
38
|
+
export interface ConnectedBrowser {
|
|
39
|
+
browserId: string;
|
|
40
|
+
label: string;
|
|
41
|
+
extensionVersion: string;
|
|
42
|
+
connectedForMs: number;
|
|
43
|
+
}
|
|
44
|
+
export interface BridgeServerOptions {
|
|
45
|
+
port: number;
|
|
46
|
+
token: string;
|
|
47
|
+
log?: (message: string) => void;
|
|
48
|
+
requestTimeoutMs?: number;
|
|
49
|
+
/** Injectable so the tests are not at the mercy of the wall clock. */
|
|
50
|
+
now?: () => number;
|
|
51
|
+
}
|
|
52
|
+
export declare class BridgeServer extends EventEmitter {
|
|
53
|
+
/**
|
|
54
|
+
* The port actually in use. It starts as the one that was asked for and
|
|
55
|
+
* becomes the bound one once `start()` resolves, which is the difference
|
|
56
|
+
* that matters when 0 was requested: `daemon.json` records this, and a
|
|
57
|
+
* recorded 0 would send the extension nowhere.
|
|
58
|
+
*/
|
|
59
|
+
port: number;
|
|
60
|
+
private readonly token;
|
|
61
|
+
private readonly log;
|
|
62
|
+
private readonly requestTimeoutMs;
|
|
63
|
+
private readonly now;
|
|
64
|
+
private readonly browsers;
|
|
65
|
+
private wss;
|
|
66
|
+
constructor(options: BridgeServerOptions);
|
|
67
|
+
/** Resolves with the port actually bound, which matters when 0 was asked for. */
|
|
68
|
+
start(): Promise<number>;
|
|
69
|
+
private accept;
|
|
70
|
+
private rejectPending;
|
|
71
|
+
private describe;
|
|
72
|
+
/** Everything currently paired and connected. */
|
|
73
|
+
listBrowsers(): ConnectedBrowser[];
|
|
74
|
+
/**
|
|
75
|
+
* Picks the browser a request meant.
|
|
76
|
+
*
|
|
77
|
+
* With one browser connected, naming it is optional: that is the common case
|
|
78
|
+
* and making a caller look it up first would be noise. With several, the
|
|
79
|
+
* caller has to choose, and the error says what the options are. A label
|
|
80
|
+
* works as well as an id, because an id is a uuid nobody wants to type.
|
|
81
|
+
*/
|
|
82
|
+
resolveBrowser(browserId?: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Waits for a browser to connect, which is what `--wait` is for: Chrome
|
|
85
|
+
* retires the extension's service worker after about thirty seconds of
|
|
86
|
+
* silence, and it dials back in when something wakes it.
|
|
87
|
+
*/
|
|
88
|
+
waitForBrowser({ timeoutMs, browserId, }: {
|
|
89
|
+
timeoutMs: number;
|
|
90
|
+
browserId?: string;
|
|
91
|
+
}): Promise<string>;
|
|
92
|
+
private request;
|
|
93
|
+
listTabs(browserId: string, timeoutMs?: number): Promise<TabsResponse>;
|
|
94
|
+
scan(browserId: string, request: Omit<ScanRequest, 'type' | 'id'>, timeoutMs?: number): Promise<ScanResultResponse>;
|
|
95
|
+
close(): Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,OAAO,EAQL,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAGxB,iFAAiF;AACjF,eAAO,MAAM,0BAA0B,SAAU,CAAC;AAKlD,qEAAqE;AACrE,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAmBD,qBAAa,YAAa,SAAQ,YAAY;IAC5C;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,GAAG,CAAgC;gBAE/B,OAAO,EAAE,mBAAmB;IASxC,iFAAiF;IACjF,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAoBxB,OAAO,CAAC,MAAM;IAoHd,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,QAAQ;IAShB,iDAAiD;IACjD,YAAY,IAAI,gBAAgB,EAAE;IAIlC;;;;;;;OAOG;IACH,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAyC1C;;;;OAIG;IACH,cAAc,CAAC,EACb,SAAS,EACT,SAAS,GACV,EAAE;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,MAAM,CAAC;IAyCnB,OAAO,CAAC,OAAO;IAqCT,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,SAAwB,GAAG,OAAO,CAAC,YAAY,CAAC;IAQrF,IAAI,CACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC,EACzC,SAAS,SAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAQxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAW7B"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server half of the bridge: one WebSocket listener, many browsers.
|
|
3
|
+
*
|
|
4
|
+
* Chrome runs a separate copy of the extension in every profile, so a machine
|
|
5
|
+
* with a work profile and a personal profile has two extensions dialling in
|
|
6
|
+
* here. Each announces a `browserId` and a `label` in its handshake, and a
|
|
7
|
+
* caller names one of those when it asks for something.
|
|
8
|
+
*
|
|
9
|
+
* Bound to 127.0.0.1 only. Three checks on every connection: the `Origin` has
|
|
10
|
+
* to be a `chrome-extension://` one (which stops a web page in any browser
|
|
11
|
+
* talking to us), the first frame has to carry the shared token (which stops
|
|
12
|
+
* any other program on this machine doing so, since a local process can forge
|
|
13
|
+
* an Origin header), and the protocol number has to match.
|
|
14
|
+
*
|
|
15
|
+
* Ported from mcp/src/bridge-server.js with three defects fixed:
|
|
16
|
+
*
|
|
17
|
+
* - Pending requests were keyed `req-N` in one map shared by every browser,
|
|
18
|
+
* so a reply from one profile could settle a request sent to another. Each
|
|
19
|
+
* connection now owns its own map and its own counter, and a frame is only
|
|
20
|
+
* matched against the map of the socket it arrived on.
|
|
21
|
+
* - `close()` cleared the pending timers without settling the promises, so a
|
|
22
|
+
* caller waiting on a scan when the server shut down waited for ever. Every
|
|
23
|
+
* outstanding request is now rejected, on disconnect and on shutdown.
|
|
24
|
+
* - There were no connection events and no way to wait for one, which is what
|
|
25
|
+
* `--wait` needs: the extension's service worker retires after about thirty
|
|
26
|
+
* seconds and reconnects when it wakes.
|
|
27
|
+
*/
|
|
28
|
+
import { EventEmitter } from 'node:events';
|
|
29
|
+
import { WebSocketServer } from 'ws';
|
|
30
|
+
import { PageScannerError } from '../errors.js';
|
|
31
|
+
import { BRIDGE_PROTOCOL_VERSION, helloAck, helloReject, isHelloFrame, isPingFrame, parseInbound, } from '../protocol.js';
|
|
32
|
+
import { tokensMatch } from './tokens.js';
|
|
33
|
+
/** How long a request waits for a browser to answer before it is given up on. */
|
|
34
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
|
|
35
|
+
/** A connection that never says hello is not allowed to linger. */
|
|
36
|
+
const HANDSHAKE_TIMEOUT_MS = 10_000;
|
|
37
|
+
/** Close codes. 4000-4999 is the range an application may define. */
|
|
38
|
+
export const CLOSE_REPLACED = 4000;
|
|
39
|
+
export const CLOSE_BAD_TOKEN = 4001;
|
|
40
|
+
export const CLOSE_BAD_PROTOCOL = 4002;
|
|
41
|
+
export const CLOSE_BAD_ORIGIN = 4003;
|
|
42
|
+
export const CLOSE_NO_HANDSHAKE = 4008;
|
|
43
|
+
export class BridgeServer extends EventEmitter {
|
|
44
|
+
/**
|
|
45
|
+
* The port actually in use. It starts as the one that was asked for and
|
|
46
|
+
* becomes the bound one once `start()` resolves, which is the difference
|
|
47
|
+
* that matters when 0 was requested: `daemon.json` records this, and a
|
|
48
|
+
* recorded 0 would send the extension nowhere.
|
|
49
|
+
*/
|
|
50
|
+
port;
|
|
51
|
+
token;
|
|
52
|
+
log;
|
|
53
|
+
requestTimeoutMs;
|
|
54
|
+
now;
|
|
55
|
+
browsers = new Map();
|
|
56
|
+
wss = null;
|
|
57
|
+
constructor(options) {
|
|
58
|
+
super();
|
|
59
|
+
this.port = options.port;
|
|
60
|
+
this.token = options.token;
|
|
61
|
+
this.log = options.log ?? (() => { });
|
|
62
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
63
|
+
this.now = options.now ?? Date.now;
|
|
64
|
+
}
|
|
65
|
+
/** Resolves with the port actually bound, which matters when 0 was asked for. */
|
|
66
|
+
start() {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const wss = new WebSocketServer({ host: '127.0.0.1', port: this.port });
|
|
69
|
+
this.wss = wss;
|
|
70
|
+
const onError = (error) => reject(error);
|
|
71
|
+
wss.once('error', onError);
|
|
72
|
+
wss.on('listening', () => {
|
|
73
|
+
wss.off('error', onError);
|
|
74
|
+
// Keep listening for later errors, or an EPIPE takes the process down.
|
|
75
|
+
wss.on('error', (error) => this.log(`bridge socket error: ${error.message}`));
|
|
76
|
+
const address = wss.address();
|
|
77
|
+
const bound = typeof address === 'object' && address !== null ? address.port : this.port;
|
|
78
|
+
this.port = bound;
|
|
79
|
+
this.log(`bridge listening on 127.0.0.1:${bound}`);
|
|
80
|
+
resolve(bound);
|
|
81
|
+
});
|
|
82
|
+
wss.on('connection', (socket, request) => this.accept(socket, request));
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
accept(socket, request) {
|
|
86
|
+
const origin = request.headers.origin ?? '';
|
|
87
|
+
if (!origin.startsWith('chrome-extension://')) {
|
|
88
|
+
this.log(`refused a connection from origin ${origin || '(none)'}`);
|
|
89
|
+
socket.close(CLOSE_BAD_ORIGIN, 'extension origins only');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let entry = null;
|
|
93
|
+
const handshakeTimer = setTimeout(() => {
|
|
94
|
+
if (!entry)
|
|
95
|
+
socket.close(CLOSE_NO_HANDSHAKE, 'no handshake');
|
|
96
|
+
}, HANDSHAKE_TIMEOUT_MS);
|
|
97
|
+
const refuse = (code, reason, spoken) => {
|
|
98
|
+
if (spoken !== undefined) {
|
|
99
|
+
// Say why before closing: the settings page can show this, where a bare
|
|
100
|
+
// close code leaves the user guessing.
|
|
101
|
+
try {
|
|
102
|
+
socket.send(JSON.stringify(helloReject(spoken)));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// The socket is going away regardless.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
socket.close(code, reason);
|
|
109
|
+
};
|
|
110
|
+
socket.on('message', (data) => {
|
|
111
|
+
const frame = parseInbound(String(data));
|
|
112
|
+
if (!frame)
|
|
113
|
+
return;
|
|
114
|
+
if (isHelloFrame(frame)) {
|
|
115
|
+
if (entry)
|
|
116
|
+
return;
|
|
117
|
+
if (frame.protocol !== BRIDGE_PROTOCOL_VERSION) {
|
|
118
|
+
this.log(`refused protocol ${frame.protocol}, this server speaks ${BRIDGE_PROTOCOL_VERSION}`);
|
|
119
|
+
clearTimeout(handshakeTimer);
|
|
120
|
+
refuse(CLOSE_BAD_PROTOCOL, 'protocol mismatch', `This Page Scanner speaks bridge protocol ${frame.protocol}, and page-scanner speaks ` +
|
|
121
|
+
`${BRIDGE_PROTOCOL_VERSION}. Update whichever is older.`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!tokensMatch(frame.token, this.token)) {
|
|
125
|
+
this.log('refused a connection with a bad token');
|
|
126
|
+
clearTimeout(handshakeTimer);
|
|
127
|
+
refuse(CLOSE_BAD_TOKEN, 'bad token', "That token does not match this machine's pairing. Run `page-scanner pair` and " +
|
|
128
|
+
'paste the new one.');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
clearTimeout(handshakeTimer);
|
|
132
|
+
// A profile that reconnects replaces its old socket rather than doubling
|
|
133
|
+
// up, and the requests waiting on the old socket fail rather than hang.
|
|
134
|
+
const previous = this.browsers.get(frame.browserId);
|
|
135
|
+
if (previous) {
|
|
136
|
+
this.rejectPending(previous, 'The browser reconnected while this request was in flight.');
|
|
137
|
+
previous.socket.close(CLOSE_REPLACED, 'replaced');
|
|
138
|
+
}
|
|
139
|
+
entry = {
|
|
140
|
+
socket,
|
|
141
|
+
browserId: frame.browserId,
|
|
142
|
+
label: frame.label || 'Chrome',
|
|
143
|
+
version: frame.extensionVersion,
|
|
144
|
+
since: this.now(),
|
|
145
|
+
pending: new Map(),
|
|
146
|
+
nextId: 0,
|
|
147
|
+
};
|
|
148
|
+
this.browsers.set(frame.browserId, entry);
|
|
149
|
+
socket.send(JSON.stringify(helloAck()));
|
|
150
|
+
this.log(`browser "${entry.label}" connected (${entry.browserId})`);
|
|
151
|
+
this.emit('browser-connected', this.describe(entry));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// Nothing is answered before the handshake, and a ping is only there to
|
|
155
|
+
// keep Chrome's service worker alive.
|
|
156
|
+
if (!entry || isPingFrame(frame))
|
|
157
|
+
return;
|
|
158
|
+
const waiting = entry.pending.get(frame.id);
|
|
159
|
+
if (!waiting)
|
|
160
|
+
return;
|
|
161
|
+
clearTimeout(waiting.timer);
|
|
162
|
+
entry.pending.delete(frame.id);
|
|
163
|
+
if (frame.type === 'error') {
|
|
164
|
+
waiting.reject(new PageScannerError('SCAN_FAILED', frame.message || 'The scan failed.'));
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
waiting.resolve(frame);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
socket.on('close', () => {
|
|
171
|
+
clearTimeout(handshakeTimer);
|
|
172
|
+
if (!entry)
|
|
173
|
+
return;
|
|
174
|
+
const current = this.browsers.get(entry.browserId);
|
|
175
|
+
this.rejectPending(entry, 'The browser disconnected before it answered.');
|
|
176
|
+
// Only forget the browser if this socket is still the one registered: a
|
|
177
|
+
// replaced socket closes after its successor has already taken the slot.
|
|
178
|
+
if (current === entry) {
|
|
179
|
+
this.browsers.delete(entry.browserId);
|
|
180
|
+
this.log(`browser ${entry.browserId} disconnected`);
|
|
181
|
+
this.emit('browser-disconnected', { browserId: entry.browserId, label: entry.label });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
socket.on('error', () => socket.close());
|
|
185
|
+
}
|
|
186
|
+
rejectPending(entry, message) {
|
|
187
|
+
for (const [id, waiting] of entry.pending) {
|
|
188
|
+
clearTimeout(waiting.timer);
|
|
189
|
+
entry.pending.delete(id);
|
|
190
|
+
waiting.reject(new PageScannerError('BROWSER_GONE', message));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
describe(entry) {
|
|
194
|
+
return {
|
|
195
|
+
browserId: entry.browserId,
|
|
196
|
+
label: entry.label,
|
|
197
|
+
extensionVersion: entry.version,
|
|
198
|
+
connectedForMs: this.now() - entry.since,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** Everything currently paired and connected. */
|
|
202
|
+
listBrowsers() {
|
|
203
|
+
return [...this.browsers.values()].map((entry) => this.describe(entry));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Picks the browser a request meant.
|
|
207
|
+
*
|
|
208
|
+
* With one browser connected, naming it is optional: that is the common case
|
|
209
|
+
* and making a caller look it up first would be noise. With several, the
|
|
210
|
+
* caller has to choose, and the error says what the options are. A label
|
|
211
|
+
* works as well as an id, because an id is a uuid nobody wants to type.
|
|
212
|
+
*/
|
|
213
|
+
resolveBrowser(browserId) {
|
|
214
|
+
const connected = this.listBrowsers();
|
|
215
|
+
const listing = connected.map((b) => `${b.label} (${b.browserId})`).join(', ');
|
|
216
|
+
if (browserId) {
|
|
217
|
+
if (this.browsers.has(browserId))
|
|
218
|
+
return browserId;
|
|
219
|
+
const byLabel = connected.filter((b) => b.label.toLowerCase() === browserId.toLowerCase());
|
|
220
|
+
const only = byLabel[0];
|
|
221
|
+
if (byLabel.length === 1 && only)
|
|
222
|
+
return only.browserId;
|
|
223
|
+
if (byLabel.length > 1) {
|
|
224
|
+
throw new PageScannerError('AMBIGUOUS_BROWSER', `More than one connected browser is called "${browserId}", so name one by id. ` +
|
|
225
|
+
`Connected: ${listing}.`);
|
|
226
|
+
}
|
|
227
|
+
throw new PageScannerError('UNKNOWN_BROWSER', `No connected browser with id or label "${browserId}".` +
|
|
228
|
+
(listing ? ` Connected: ${listing}.` : ' None are connected.'));
|
|
229
|
+
}
|
|
230
|
+
const first = connected[0];
|
|
231
|
+
if (!first) {
|
|
232
|
+
throw new PageScannerError('NO_BROWSER', 'No browser is connected.', 'Open the Page Scanner settings page in Chrome and press Connect. ' +
|
|
233
|
+
'Run `page-scanner pair` first if it has never been paired.');
|
|
234
|
+
}
|
|
235
|
+
if (connected.length > 1) {
|
|
236
|
+
throw new PageScannerError('AMBIGUOUS_BROWSER', `More than one browser is connected, so name one with --browser. Connected: ${listing}.`);
|
|
237
|
+
}
|
|
238
|
+
return first.browserId;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Waits for a browser to connect, which is what `--wait` is for: Chrome
|
|
242
|
+
* retires the extension's service worker after about thirty seconds of
|
|
243
|
+
* silence, and it dials back in when something wakes it.
|
|
244
|
+
*/
|
|
245
|
+
waitForBrowser({ timeoutMs, browserId, }) {
|
|
246
|
+
try {
|
|
247
|
+
return Promise.resolve(this.resolveBrowser(browserId));
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
// Only "nothing is connected" is worth waiting through. Two browsers
|
|
251
|
+
// connected, or a name that does not match one, will not fix itself.
|
|
252
|
+
if (!(error instanceof PageScannerError) || error.code !== 'NO_BROWSER') {
|
|
253
|
+
return Promise.reject(error);
|
|
254
|
+
}
|
|
255
|
+
if (timeoutMs <= 0)
|
|
256
|
+
return Promise.reject(error);
|
|
257
|
+
}
|
|
258
|
+
return new Promise((resolve, reject) => {
|
|
259
|
+
const settle = (fn) => {
|
|
260
|
+
clearTimeout(timer);
|
|
261
|
+
this.off('browser-connected', onConnected);
|
|
262
|
+
fn();
|
|
263
|
+
};
|
|
264
|
+
const onConnected = () => {
|
|
265
|
+
try {
|
|
266
|
+
const id = this.resolveBrowser(browserId);
|
|
267
|
+
settle(() => resolve(id));
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Not the one asked for, or still ambiguous. Keep waiting.
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const timer = setTimeout(() => {
|
|
274
|
+
settle(() => reject(new PageScannerError('NO_BROWSER', `No browser connected within ${Math.round(timeoutMs / 1000)}s.`, 'Open the Page Scanner settings page in Chrome and press Connect.')));
|
|
275
|
+
}, timeoutMs);
|
|
276
|
+
this.on('browser-connected', onConnected);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
request(browserId, payload, timeoutMs) {
|
|
280
|
+
const entry = this.browsers.get(browserId);
|
|
281
|
+
if (!entry) {
|
|
282
|
+
throw new PageScannerError('BROWSER_GONE', `Browser ${browserId} is no longer connected.`);
|
|
283
|
+
}
|
|
284
|
+
entry.nextId += 1;
|
|
285
|
+
const id = `req-${entry.nextId}`;
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const timer = setTimeout(() => {
|
|
288
|
+
entry.pending.delete(id);
|
|
289
|
+
reject(new PageScannerError('TIMEOUT', `The browser did not answer in ${Math.round(timeoutMs / 1000)}s.`));
|
|
290
|
+
}, timeoutMs);
|
|
291
|
+
entry.pending.set(id, { resolve, reject, timer });
|
|
292
|
+
try {
|
|
293
|
+
entry.socket.send(JSON.stringify({ ...payload, id }));
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
clearTimeout(timer);
|
|
297
|
+
entry.pending.delete(id);
|
|
298
|
+
reject(new PageScannerError('BROWSER_GONE', error instanceof Error ? error.message : String(error)));
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async listTabs(browserId, timeoutMs = this.requestTimeoutMs) {
|
|
303
|
+
const answer = await this.request(browserId, { type: 'list-tabs' }, timeoutMs);
|
|
304
|
+
if (answer.type !== 'tabs') {
|
|
305
|
+
throw new PageScannerError('SCAN_FAILED', 'The browser answered list-tabs with a scan.');
|
|
306
|
+
}
|
|
307
|
+
return answer;
|
|
308
|
+
}
|
|
309
|
+
async scan(browserId, request, timeoutMs = this.requestTimeoutMs) {
|
|
310
|
+
const answer = await this.request(browserId, { type: 'scan', ...request }, timeoutMs);
|
|
311
|
+
if (answer.type !== 'scan-result') {
|
|
312
|
+
throw new PageScannerError('SCAN_FAILED', 'The browser answered a scan with a tab list.');
|
|
313
|
+
}
|
|
314
|
+
return answer;
|
|
315
|
+
}
|
|
316
|
+
async close() {
|
|
317
|
+
for (const entry of this.browsers.values()) {
|
|
318
|
+
this.rejectPending(entry, 'The bridge shut down before the browser answered.');
|
|
319
|
+
entry.socket.close(1001, 'server shutting down');
|
|
320
|
+
}
|
|
321
|
+
this.browsers.clear();
|
|
322
|
+
const wss = this.wss;
|
|
323
|
+
this.wss = null;
|
|
324
|
+
if (!wss)
|
|
325
|
+
return;
|
|
326
|
+
await new Promise((resolve) => wss.close(() => resolve()));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/bridge/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constant-time token comparison.
|
|
3
|
+
*
|
|
4
|
+
* The lengths are compared separately because `timingSafeEqual` throws on a
|
|
5
|
+
* length mismatch rather than returning false, and a thrown error inside a
|
|
6
|
+
* socket handler would be a worse leak than the length itself, which an
|
|
7
|
+
* attacker can already guess from the protocol.
|
|
8
|
+
*/
|
|
9
|
+
export declare function tokensMatch(a: string, b: string): boolean;
|
|
10
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/bridge/tokens.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAIzD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Constant-time token comparison.
|
|
4
|
+
*
|
|
5
|
+
* The lengths are compared separately because `timingSafeEqual` throws on a
|
|
6
|
+
* length mismatch rather than returning false, and a thrown error inside a
|
|
7
|
+
* socket handler would be a worse leak than the length itself, which an
|
|
8
|
+
* attacker can already guess from the protocol.
|
|
9
|
+
*/
|
|
10
|
+
export function tokensMatch(a, b) {
|
|
11
|
+
const left = Buffer.from(String(a));
|
|
12
|
+
const right = Buffer.from(String(b));
|
|
13
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/bridge/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ExportFormatId, type PageSizeId, type VideoHandling } from '../protocol.js';
|
|
2
|
+
export declare const COMMANDS: readonly ["pair", "status", "browsers", "tabs", "scan", "serve", "stop", "help", "version"];
|
|
3
|
+
export type CommandName = (typeof COMMANDS)[number];
|
|
4
|
+
export interface PairArgs {
|
|
5
|
+
command: 'pair';
|
|
6
|
+
port?: number;
|
|
7
|
+
rotate: boolean;
|
|
8
|
+
waitSeconds: number;
|
|
9
|
+
json: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface StatusArgs {
|
|
12
|
+
command: 'status';
|
|
13
|
+
json: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface BrowsersArgs {
|
|
16
|
+
command: 'browsers';
|
|
17
|
+
json: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface TabsArgs {
|
|
20
|
+
command: 'tabs';
|
|
21
|
+
browser?: string;
|
|
22
|
+
waitSeconds: number;
|
|
23
|
+
json: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface ScanArgs {
|
|
26
|
+
command: 'scan';
|
|
27
|
+
url?: string;
|
|
28
|
+
tabId?: number;
|
|
29
|
+
windowId?: number;
|
|
30
|
+
browser?: string;
|
|
31
|
+
format: ExportFormatId;
|
|
32
|
+
pageSize: PageSizeId;
|
|
33
|
+
quality?: number;
|
|
34
|
+
videoHandling?: VideoHandling;
|
|
35
|
+
openEditor: boolean;
|
|
36
|
+
out?: string;
|
|
37
|
+
waitSeconds: number;
|
|
38
|
+
timeoutSeconds: number;
|
|
39
|
+
json: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ServeArgs {
|
|
42
|
+
command: 'serve';
|
|
43
|
+
daemon: boolean;
|
|
44
|
+
idleMinutes?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface StopArgs {
|
|
47
|
+
command: 'stop';
|
|
48
|
+
json: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface HelpArgs {
|
|
51
|
+
command: 'help';
|
|
52
|
+
}
|
|
53
|
+
export interface VersionArgs {
|
|
54
|
+
command: 'version';
|
|
55
|
+
}
|
|
56
|
+
export type ParsedArgs = PairArgs | StatusArgs | BrowsersArgs | TabsArgs | ScanArgs | ServeArgs | StopArgs | HelpArgs | VersionArgs;
|
|
57
|
+
export declare const USAGE = "page-scanner - capture a whole web page from the command line,\nthrough the Page Scanner Chrome extension.\n\nUsage:\n page-scanner pair [--port <n>] [--rotate] [--wait <s>=120] [--no-wait]\n [--json]\n page-scanner status [--json]\n page-scanner browsers [--json]\n page-scanner tabs [--browser <id|label>] [--wait <s>=30] [--json]\n page-scanner scan (--url <u> | --tab <id>) [--window <id>]\n [--browser <id|label>] [--format pdf|png|jpeg=pdf]\n [--page-size auto|a4|letter=a4] [--quality <0-1>]\n [--video frame|blank] [--open-editor]\n [--out <file|dir>] [--wait <s>=30]\n [--timeout <s>=120]\n [--json]\n page-scanner serve [--daemon] [--idle <min>]\n page-scanner stop [--json]\n page-scanner --version | --help (-v and -h also work)\n\nCommands:\n pair Write the port and token the browser needs, then wait for it.\n status Say whether there is a pairing, a daemon and a browser.\n browsers List the browsers connected right now.\n tabs List one browser's windows and tabs.\n scan Capture a page and write it to a file.\n serve Run the bridge in this terminal, or start it in the background.\n stop Stop the background bridge.\n\n--browser takes a browserId or a label, matched without regard to case, and\nis needed only when more than one browser is connected. --wait 0 means do\nnot wait at all. --json prints one JSON document on stdout, on success and\non failure alike.\n\nExit codes:\n 0 success\n 1 the browser was reached and the work failed\n 2 the arguments were wrong\n 3 no usable browser: none connected, several connected, or the one\n named is not\n 4 not paired: run `page-scanner pair`\n 5 the daemon would not start\n";
|
|
58
|
+
/**
|
|
59
|
+
* `argv` is what follows the node executable and the script, i.e.
|
|
60
|
+
* `process.argv.slice(2)`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
63
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAeA,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,QAAQ,6FAUX,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,UAAU,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,SAAS,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,UAAU,GACV,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,WAAW,CAAC;AAQhB,eAAO,MAAM,KAAK,22DA0CjB,CAAC;AAkVF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAmCpD"}
|