@jobshimo/browser-link 0.0.1
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 +21 -0
- package/README.md +83 -0
- package/dist/auth/allowlist.d.ts +2 -0
- package/dist/auth/allowlist.js +53 -0
- package/dist/auth/allowlist.js.map +1 -0
- package/dist/auth/process-identity.d.ts +19 -0
- package/dist/auth/process-identity.js +106 -0
- package/dist/auth/process-identity.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/about.d.ts +23 -0
- package/dist/commands/about.js +248 -0
- package/dist/commands/about.js.map +1 -0
- package/dist/commands/doctor.d.ts +29 -0
- package/dist/commands/doctor.js +110 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/extension.d.ts +12 -0
- package/dist/commands/extension.js +76 -0
- package/dist/commands/extension.js.map +1 -0
- package/dist/commands/install.d.ts +19 -0
- package/dist/commands/install.js +52 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/menu.d.ts +26 -0
- package/dist/commands/menu.js +187 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/tty.d.ts +51 -0
- package/dist/commands/tty.js +148 -0
- package/dist/commands/tty.js.map +1 -0
- package/dist/commands/uninstall.d.ts +8 -0
- package/dist/commands/uninstall.js +10 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/welcome.d.ts +33 -0
- package/dist/commands/welcome.js +177 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +35 -0
- package/dist/config.js.map +1 -0
- package/dist/entry-info.d.ts +12 -0
- package/dist/entry-info.js +15 -0
- package/dist/entry-info.js.map +1 -0
- package/dist/extension/background.d.ts +1 -0
- package/dist/extension/background.js +490 -0
- package/dist/extension/background.js.map +1 -0
- package/dist/extension/icons/icon-128.png +0 -0
- package/dist/extension/icons/icon-16.png +0 -0
- package/dist/extension/icons/icon-32.png +0 -0
- package/dist/extension/icons/icon-48.png +0 -0
- package/dist/extension/icons/icon.svg +14 -0
- package/dist/extension/manifest.json +28 -0
- package/dist/extension/popup.d.ts +13 -0
- package/dist/extension/popup.html +88 -0
- package/dist/extension/popup.js +78 -0
- package/dist/extension/popup.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/installers/claude.d.ts +2 -0
- package/dist/installers/claude.js +77 -0
- package/dist/installers/claude.js.map +1 -0
- package/dist/installers/index.d.ts +9 -0
- package/dist/installers/index.js +14 -0
- package/dist/installers/index.js.map +1 -0
- package/dist/installers/opencode.d.ts +2 -0
- package/dist/installers/opencode.js +39 -0
- package/dist/installers/opencode.js.map +1 -0
- package/dist/installers/types.d.ts +30 -0
- package/dist/installers/types.js +2 -0
- package/dist/installers/types.js.map +1 -0
- package/dist/map/db.d.ts +3 -0
- package/dist/map/db.js +81 -0
- package/dist/map/db.js.map +1 -0
- package/dist/map/paths.d.ts +12 -0
- package/dist/map/paths.js +22 -0
- package/dist/map/paths.js.map +1 -0
- package/dist/map/queries.d.ts +72 -0
- package/dist/map/queries.js +162 -0
- package/dist/map/queries.js.map +1 -0
- package/dist/map/tools.d.ts +8 -0
- package/dist/map/tools.js +143 -0
- package/dist/map/tools.js.map +1 -0
- package/dist/messages.d.ts +42 -0
- package/dist/messages.js +9 -0
- package/dist/messages.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/browser-definitions.d.ts +7 -0
- package/dist/tools/browser-definitions.js +127 -0
- package/dist/tools/browser-definitions.js.map +1 -0
- package/dist/tools/browser-dispatch.d.ts +20 -0
- package/dist/tools/browser-dispatch.js +75 -0
- package/dist/tools/browser-dispatch.js.map +1 -0
- package/dist/tools/responses.d.ts +19 -0
- package/dist/tools/responses.js +27 -0
- package/dist/tools/responses.js.map +1 -0
- package/dist/tools/server-instructions.d.ts +3 -0
- package/dist/tools/server-instructions.js +50 -0
- package/dist/tools/server-instructions.js.map +1 -0
- package/dist/tools/types.d.ts +6 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/open-url.d.ts +4 -0
- package/dist/utils/open-url.js +37 -0
- package/dist/utils/open-url.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Martín Miguel Bernal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @jobshimo/browser-link
|
|
2
|
+
|
|
3
|
+
> ⚠️ **Read this before installing**
|
|
4
|
+
>
|
|
5
|
+
> This package opens a bridge between Claude Code and the Chrome tabs you
|
|
6
|
+
> explicitly enable through a companion extension. On every tab where you
|
|
7
|
+
> press "Conectar" in the extension popup, the agent can read its DOM,
|
|
8
|
+
> click, type, run arbitrary JavaScript, and follow links — including any
|
|
9
|
+
> logged-in session, saved card, wallet, banking page or admin panel that
|
|
10
|
+
> tab is currently showing.
|
|
11
|
+
>
|
|
12
|
+
> Treat the agent like a junior dev with remote control of those tabs.
|
|
13
|
+
> Only enable tabs where you would let an automated process act on your
|
|
14
|
+
> behalf, and disconnect them when you are done. You are responsible for
|
|
15
|
+
> every action the agent performs on the tabs you explicitly enable.
|
|
16
|
+
|
|
17
|
+
MCP server that bridges Claude Code to the Chrome tabs you grant access to,
|
|
18
|
+
through a small WebSocket relay and a companion Chrome extension. Ships
|
|
19
|
+
with a persistent UI map so the agent remembers selectors, flows and
|
|
20
|
+
gotchas it learned about each app, across sessions.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @jobshimo/browser-link
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This puts the `browser-link` binary on your PATH on macOS, Linux and Windows.
|
|
29
|
+
|
|
30
|
+
## Set it up
|
|
31
|
+
|
|
32
|
+
The fastest path is the interactive menu:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
browser-link
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That opens the welcome / disclaimer screen (English or Spanish), and then
|
|
39
|
+
the setup menu where you can register browser-link with Claude Code, see
|
|
40
|
+
the Chrome extension install steps, run a doctor diagnose, and open the
|
|
41
|
+
about / help page.
|
|
42
|
+
|
|
43
|
+
If you prefer direct commands:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
browser-link install # register browser-link in Claude Code
|
|
47
|
+
browser-link extension # show the Chrome extension assets path + steps
|
|
48
|
+
browser-link doctor # diagnose current setup
|
|
49
|
+
browser-link about # what this is, how it works, every tool
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
After `install`, restart Claude Code. After `extension`, follow the printed
|
|
53
|
+
steps to load the unpacked extension in Chrome. Then click "Conectar" on
|
|
54
|
+
every tab you want the agent to reach — and only on those.
|
|
55
|
+
|
|
56
|
+
## Tools exposed
|
|
57
|
+
|
|
58
|
+
Browser bridge: `browser.list_tabs`, `browser.ping`, `browser.navigate`,
|
|
59
|
+
`browser.snapshot`, `browser.click`, `browser.type`, `browser.evaluate`,
|
|
60
|
+
`browser.console`, `browser.network`, `browser.network_body`.
|
|
61
|
+
|
|
62
|
+
UI map (persistent across sessions): `browser.map.recall`,
|
|
63
|
+
`browser.map.save`, `browser.map.record_use`, `browser.map.forget`,
|
|
64
|
+
`browser.map.rename_app`, `browser.map.apps`.
|
|
65
|
+
|
|
66
|
+
The server also ships usage instructions for the agent via the MCP
|
|
67
|
+
`initialize` handshake — no manual prompt setup required.
|
|
68
|
+
|
|
69
|
+
## Where the data lives
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
macOS ~/Library/Application Support/browser-link/map.db
|
|
73
|
+
Linux $XDG_DATA_HOME/browser-link/map.db (default: ~/.local/share/browser-link/map.db)
|
|
74
|
+
Windows %APPDATA%/browser-link/map.db
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Override with `BROWSER_LINK_DATA_DIR`. The database is local to your
|
|
78
|
+
machine and never uploaded anywhere by this package. The WebSocket relay
|
|
79
|
+
binds to `127.0.0.1:17529` (loopback only).
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT — see [LICENSE](https://github.com/jobshimo/browser-link/blob/main/LICENSE).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { platform } from 'node:os';
|
|
2
|
+
/**
|
|
3
|
+
* Names of Chromium-based browser binaries the WebSocket bridge accepts as
|
|
4
|
+
* legitimate peers. Strict equality after stripping any path component.
|
|
5
|
+
*
|
|
6
|
+
* The match is per-OS because the binary that owns a TCP socket reports a
|
|
7
|
+
* different name on macOS ("Google Chrome Helper"), Linux ("chrome") and
|
|
8
|
+
* Windows ("chrome.exe"). Helper processes are included because in
|
|
9
|
+
* Chromium-based browsers the network stack often lives in a separate
|
|
10
|
+
* "Network Service" helper, not the main process.
|
|
11
|
+
*/
|
|
12
|
+
const ALLOWED_BY_OS = {
|
|
13
|
+
darwin: [
|
|
14
|
+
'Google Chrome',
|
|
15
|
+
'Google Chrome Helper',
|
|
16
|
+
'Google Chrome Helper (Renderer)',
|
|
17
|
+
'Google Chrome Helper (GPU)',
|
|
18
|
+
'Google Chrome Helper (Plugin)',
|
|
19
|
+
'Google Chrome Helper (Alerts)',
|
|
20
|
+
'Chromium',
|
|
21
|
+
'Chromium Helper',
|
|
22
|
+
'Chromium Helper (Renderer)',
|
|
23
|
+
'Microsoft Edge',
|
|
24
|
+
'Microsoft Edge Helper',
|
|
25
|
+
'Microsoft Edge Helper (Renderer)',
|
|
26
|
+
'Brave Browser',
|
|
27
|
+
'Brave Browser Helper',
|
|
28
|
+
'Brave Browser Helper (Renderer)',
|
|
29
|
+
'Vivaldi',
|
|
30
|
+
'Vivaldi Helper',
|
|
31
|
+
],
|
|
32
|
+
linux: [
|
|
33
|
+
'chrome',
|
|
34
|
+
'chromium',
|
|
35
|
+
'chromium-browser',
|
|
36
|
+
'google-chrome',
|
|
37
|
+
'google-chrome-stable',
|
|
38
|
+
'msedge',
|
|
39
|
+
'microsoft-edge',
|
|
40
|
+
'brave',
|
|
41
|
+
'brave-browser',
|
|
42
|
+
'vivaldi-bin',
|
|
43
|
+
'vivaldi-stable',
|
|
44
|
+
],
|
|
45
|
+
win32: ['chrome.exe', 'chromium.exe', 'msedge.exe', 'brave.exe', 'vivaldi.exe'],
|
|
46
|
+
};
|
|
47
|
+
export function getAllowedBrowsers(os = platform()) {
|
|
48
|
+
return ALLOWED_BY_OS[os] ?? [];
|
|
49
|
+
}
|
|
50
|
+
export function isAllowedBrowser(binaryName, os = platform()) {
|
|
51
|
+
return getAllowedBrowsers(os).includes(binaryName);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=allowlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allowlist.js","sourceRoot":"","sources":["../../src/auth/allowlist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;;;;;;;;GASG;AACH,MAAM,aAAa,GAAwD;IACzE,MAAM,EAAE;QACN,eAAe;QACf,sBAAsB;QACtB,iCAAiC;QACjC,4BAA4B;QAC5B,+BAA+B;QAC/B,+BAA+B;QAC/B,UAAU;QACV,iBAAiB;QACjB,4BAA4B;QAC5B,gBAAgB;QAChB,uBAAuB;QACvB,kCAAkC;QAClC,eAAe;QACf,sBAAsB;QACtB,iCAAiC;QACjC,SAAS;QACT,gBAAgB;KACjB;IACD,KAAK,EAAE;QACL,QAAQ;QACR,UAAU;QACV,kBAAkB;QAClB,eAAe;QACf,sBAAsB;QACtB,QAAQ;QACR,gBAAgB;QAChB,OAAO;QACP,eAAe;QACf,aAAa;QACb,gBAAgB;KACjB;IACD,KAAK,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,CAAC;CAChF,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,KAAsB,QAAQ,EAAE;IACjE,OAAO,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,KAAsB,QAAQ,EAAE;IACnF,OAAO,kBAAkB,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface PeerProcess {
|
|
2
|
+
pid: number;
|
|
3
|
+
binaryName: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Identify which OS process owns the local end of a loopback TCP connection
|
|
7
|
+
* at `host:port`. Returns null when the lookup cannot resolve a single owner
|
|
8
|
+
* (no result, ambiguous result, command not available, timeout).
|
|
9
|
+
*
|
|
10
|
+
* The caller decides how to treat null: this module's only job is to ask the
|
|
11
|
+
* kernel honestly. Concretely the server treats null as "reject", so users
|
|
12
|
+
* on systems without lsof / netstat fail closed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function lookupPeerProcess(host: string, port: number): Promise<PeerProcess | null>;
|
|
15
|
+
export declare function parseLsofOutput(out: string): PeerProcess | null;
|
|
16
|
+
/** lsof escapes spaces and tabs in command names as \xHH. Reverse that. */
|
|
17
|
+
export declare function decodeLsofString(s: string): string;
|
|
18
|
+
export declare function parseNetstatForLocal(out: string, host: string, port: number): number | null;
|
|
19
|
+
export declare function parseTasklistImage(out: string): string | null;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
2
|
+
import { platform } from 'node:os';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
const LOOKUP_TIMEOUT_MS = 1500;
|
|
6
|
+
/**
|
|
7
|
+
* Identify which OS process owns the local end of a loopback TCP connection
|
|
8
|
+
* at `host:port`. Returns null when the lookup cannot resolve a single owner
|
|
9
|
+
* (no result, ambiguous result, command not available, timeout).
|
|
10
|
+
*
|
|
11
|
+
* The caller decides how to treat null: this module's only job is to ask the
|
|
12
|
+
* kernel honestly. Concretely the server treats null as "reject", so users
|
|
13
|
+
* on systems without lsof / netstat fail closed.
|
|
14
|
+
*/
|
|
15
|
+
export async function lookupPeerProcess(host, port) {
|
|
16
|
+
const os = platform();
|
|
17
|
+
try {
|
|
18
|
+
if (os === 'darwin' || os === 'linux') {
|
|
19
|
+
return await lookupUnix(host, port);
|
|
20
|
+
}
|
|
21
|
+
if (os === 'win32') {
|
|
22
|
+
return await lookupWindows(host, port);
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* macOS / Linux path: `lsof` is the broadest tool available. The -F flag
|
|
32
|
+
* gives us a stable, field-tagged output (one field per line, prefixed with
|
|
33
|
+
* the field letter) that survives binary names containing spaces — common
|
|
34
|
+
* on macOS ("Google Chrome Helper").
|
|
35
|
+
*/
|
|
36
|
+
async function lookupUnix(host, port) {
|
|
37
|
+
const cmd = `lsof -nP -F pc -iTCP@${host}:${port} -sTCP:ESTABLISHED`;
|
|
38
|
+
const { stdout } = await execAsync(cmd, { timeout: LOOKUP_TIMEOUT_MS });
|
|
39
|
+
return parseLsofOutput(stdout);
|
|
40
|
+
}
|
|
41
|
+
export function parseLsofOutput(out) {
|
|
42
|
+
let pid = null;
|
|
43
|
+
for (const line of out.split('\n')) {
|
|
44
|
+
if (line.startsWith('p')) {
|
|
45
|
+
const n = Number.parseInt(line.slice(1), 10);
|
|
46
|
+
pid = Number.isFinite(n) ? n : null;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (line.startsWith('c') && pid !== null) {
|
|
50
|
+
return { pid, binaryName: decodeLsofString(line.slice(1)) };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/** lsof escapes spaces and tabs in command names as \xHH. Reverse that. */
|
|
56
|
+
export function decodeLsofString(s) {
|
|
57
|
+
return s.replace(/\\x([0-9a-fA-F]{2})/g, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Windows path: `netstat -ano` lists every TCP connection with its owning
|
|
61
|
+
* PID. We then ask `tasklist` for the image name of that PID. Both ship by
|
|
62
|
+
* default with Windows; no extra tooling needed.
|
|
63
|
+
*/
|
|
64
|
+
async function lookupWindows(host, port) {
|
|
65
|
+
const { stdout: netstatOut } = await execAsync('netstat -ano -p TCP', {
|
|
66
|
+
timeout: LOOKUP_TIMEOUT_MS,
|
|
67
|
+
});
|
|
68
|
+
const pid = parseNetstatForLocal(netstatOut, host, port);
|
|
69
|
+
if (pid === null)
|
|
70
|
+
return null;
|
|
71
|
+
const { stdout: tasklistOut } = await execAsync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, {
|
|
72
|
+
timeout: LOOKUP_TIMEOUT_MS,
|
|
73
|
+
});
|
|
74
|
+
const binaryName = parseTasklistImage(tasklistOut);
|
|
75
|
+
return binaryName ? { pid, binaryName } : null;
|
|
76
|
+
}
|
|
77
|
+
export function parseNetstatForLocal(out, host, port) {
|
|
78
|
+
const target = `${host}:${port}`;
|
|
79
|
+
for (const line of out.split(/\r?\n/)) {
|
|
80
|
+
if (!line.includes(target))
|
|
81
|
+
continue;
|
|
82
|
+
// Format: Proto LocalAddress ForeignAddress State PID
|
|
83
|
+
const fields = line.trim().split(/\s+/);
|
|
84
|
+
if (fields.length < 5)
|
|
85
|
+
continue;
|
|
86
|
+
if (fields[1] !== target)
|
|
87
|
+
continue;
|
|
88
|
+
const pid = Number.parseInt(fields[4] ?? '', 10);
|
|
89
|
+
if (Number.isFinite(pid))
|
|
90
|
+
return pid;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
export function parseTasklistImage(out) {
|
|
95
|
+
// CSV with no header: "image.exe","PID","Session Name","Session#","Mem Usage"
|
|
96
|
+
for (const line of out.split(/\r?\n/)) {
|
|
97
|
+
const trimmed = line.trim();
|
|
98
|
+
if (!trimmed)
|
|
99
|
+
continue;
|
|
100
|
+
const match = trimmed.match(/^"([^"]+)"/);
|
|
101
|
+
if (match?.[1])
|
|
102
|
+
return match[1];
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=process-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-identity.js","sourceRoot":"","sources":["../../src/auth/process-identity.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { formatDoctor, runDoctor } from './commands/doctor.js';
|
|
3
|
+
import { installAll, installFor } from './commands/install.js';
|
|
4
|
+
import { uninstallAll, uninstallFor } from './commands/uninstall.js';
|
|
5
|
+
import { printExtensionInstructions } from './commands/extension.js';
|
|
6
|
+
import { printAbout } from './commands/about.js';
|
|
7
|
+
import { loadConfig } from './config.js';
|
|
8
|
+
const HELP = `browser-link — bridge Claude Code to the Chrome tabs you enable.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
browser-link When invoked from an interactive terminal,
|
|
12
|
+
opens a setup menu (register Claude Code,
|
|
13
|
+
show extension steps, run doctor, about).
|
|
14
|
+
When invoked by Claude Code (no TTY),
|
|
15
|
+
starts the MCP server over stdio.
|
|
16
|
+
browser-link install Register browser-link with Claude Code.
|
|
17
|
+
browser-link uninstall Remove the registration.
|
|
18
|
+
browser-link extension Show the path of the Chrome extension assets
|
|
19
|
+
and per-OS install instructions.
|
|
20
|
+
browser-link doctor Diagnose current setup (Claude Code, server, extension, map DB).
|
|
21
|
+
browser-link about Show the full explanation of what this is and how it works.
|
|
22
|
+
browser-link help This message.
|
|
23
|
+
|
|
24
|
+
Environment:
|
|
25
|
+
BROWSER_LINK_DATA_DIR Override the DB location (defaults per OS).
|
|
26
|
+
BROWSER_LINK_BIN Override the command stored in client configs
|
|
27
|
+
(e.g. "node /path/to/dist/index.js" for dev).`;
|
|
28
|
+
function parseClient(argv) {
|
|
29
|
+
const idx = argv.findIndex((a) => a === '--client');
|
|
30
|
+
if (idx === -1 || idx === argv.length - 1)
|
|
31
|
+
return null;
|
|
32
|
+
const val = argv[idx + 1];
|
|
33
|
+
if (val === 'claude' || val === 'opencode')
|
|
34
|
+
return val;
|
|
35
|
+
throw new Error(`Unknown --client value: ${val}. Use claude or opencode.`);
|
|
36
|
+
}
|
|
37
|
+
async function dispatch(argv) {
|
|
38
|
+
const [cmd, ...rest] = argv;
|
|
39
|
+
switch (cmd) {
|
|
40
|
+
case undefined:
|
|
41
|
+
case 'start': {
|
|
42
|
+
const { runServer } = await import('./server.js');
|
|
43
|
+
await runServer();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
case 'help':
|
|
47
|
+
case '-h':
|
|
48
|
+
case '--help': {
|
|
49
|
+
console.log(HELP);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
case 'install': {
|
|
53
|
+
const client = parseClient(rest);
|
|
54
|
+
const reports = client ? [installFor(client)] : installAll();
|
|
55
|
+
for (const r of reports) {
|
|
56
|
+
const prefix = r.installedClient ? '✓' : '·';
|
|
57
|
+
console.log(`${prefix} ${r.displayName}: ${r.message}`);
|
|
58
|
+
}
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log('Restart the MCP client so it picks up the registration.');
|
|
61
|
+
console.log('Next: install the Chrome extension — run `browser-link extension`.');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
case 'uninstall': {
|
|
65
|
+
const client = parseClient(rest);
|
|
66
|
+
const reports = client ? [uninstallFor(client)] : uninstallAll();
|
|
67
|
+
for (const r of reports)
|
|
68
|
+
console.log(`· ${r.displayName}: ${r.message}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
case 'extension': {
|
|
72
|
+
printExtensionInstructions();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
case 'doctor': {
|
|
76
|
+
const report = await runDoctor();
|
|
77
|
+
console.log(formatDoctor(report));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
case 'about': {
|
|
81
|
+
const cfg = loadConfig();
|
|
82
|
+
printAbout(cfg.language ?? 'en');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
default: {
|
|
86
|
+
console.error(`Unknown command: ${cmd}`);
|
|
87
|
+
console.error('');
|
|
88
|
+
console.error(HELP);
|
|
89
|
+
process.exit(2);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const argv = process.argv.slice(2);
|
|
94
|
+
// No args + both stdin and stdout are TTYs → human in a terminal: show the
|
|
95
|
+
// welcome / disclaimer screen (unless previously dismissed) and then the
|
|
96
|
+
// setup menu in the chosen language.
|
|
97
|
+
// Otherwise (no TTY anywhere, or output piped) → start the MCP server over stdio.
|
|
98
|
+
if (argv.length === 0 && process.stdin.isTTY && process.stdout.isTTY) {
|
|
99
|
+
const cfg = loadConfig();
|
|
100
|
+
const { runMenu } = await import('./commands/menu.js');
|
|
101
|
+
let language = cfg.language ?? 'en';
|
|
102
|
+
if (!cfg.skipWelcome) {
|
|
103
|
+
const { runWelcome } = await import('./commands/welcome.js');
|
|
104
|
+
const welcome = await runWelcome({ initial: language });
|
|
105
|
+
if (welcome.action === 'quit')
|
|
106
|
+
process.exit(0);
|
|
107
|
+
language = welcome.language;
|
|
108
|
+
}
|
|
109
|
+
await runMenu(language);
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
dispatch(argv).catch((err) => {
|
|
113
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
114
|
+
process.exit(1);
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Language } from './welcome.js';
|
|
2
|
+
interface AboutI18n {
|
|
3
|
+
title: string;
|
|
4
|
+
whatItIs: string[];
|
|
5
|
+
howItWorks: string[];
|
|
6
|
+
bridgeTools: {
|
|
7
|
+
title: string;
|
|
8
|
+
items: string[];
|
|
9
|
+
};
|
|
10
|
+
mapTools: {
|
|
11
|
+
title: string;
|
|
12
|
+
items: string[];
|
|
13
|
+
};
|
|
14
|
+
privacy: string[];
|
|
15
|
+
whereToGetHelp: string[];
|
|
16
|
+
prompt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const I18N_ABOUT: Record<Language, AboutI18n>;
|
|
19
|
+
export declare function buildAboutScreen(t: AboutI18n): string;
|
|
20
|
+
export declare function runAbout(language?: Language): Promise<void>;
|
|
21
|
+
/** Plain-text render of About for non-interactive output (browser-link about). */
|
|
22
|
+
export declare function printAbout(language?: Language): void;
|
|
23
|
+
export {};
|