@jobshimo/browser-link 0.39.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +23 -8
  2. package/dist/bridge/dispatch.js +17 -7
  3. package/dist/bridge/dispatch.js.map +1 -1
  4. package/dist/bridge/ws-bridge.d.ts +28 -3
  5. package/dist/bridge/ws-bridge.js +51 -7
  6. package/dist/bridge/ws-bridge.js.map +1 -1
  7. package/dist/cli.js +6 -2
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands/about.js +10 -0
  10. package/dist/commands/about.js.map +1 -1
  11. package/dist/commands/doctor.d.ts +24 -0
  12. package/dist/commands/doctor.js +62 -1
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/free-port.d.ts +40 -1
  15. package/dist/commands/free-port.js +72 -7
  16. package/dist/commands/free-port.js.map +1 -1
  17. package/dist/config.d.ts +7 -0
  18. package/dist/config.js +9 -0
  19. package/dist/config.js.map +1 -1
  20. package/dist/extension/background.js +100 -8
  21. package/dist/extension/background.js.map +1 -1
  22. package/dist/extension/manifest.json +1 -1
  23. package/dist/extension/notice-policy.d.ts +48 -0
  24. package/dist/extension/notice-policy.js +83 -0
  25. package/dist/extension/notice-policy.js.map +1 -0
  26. package/dist/extension/popup.html +99 -0
  27. package/dist/extension/popup.js +139 -1
  28. package/dist/extension/popup.js.map +1 -1
  29. package/dist/extension/repair-policy.d.ts +60 -0
  30. package/dist/extension/repair-policy.js +79 -0
  31. package/dist/extension/repair-policy.js.map +1 -0
  32. package/dist/extension/version-status.d.ts +44 -0
  33. package/dist/extension/version-status.js +60 -0
  34. package/dist/extension/version-status.js.map +1 -0
  35. package/dist/lifecycle/election.d.ts +98 -0
  36. package/dist/lifecycle/election.js +187 -0
  37. package/dist/lifecycle/election.js.map +1 -0
  38. package/dist/lifecycle/processes.d.ts +67 -0
  39. package/dist/lifecycle/processes.js +239 -0
  40. package/dist/lifecycle/processes.js.map +1 -0
  41. package/dist/lifecycle/repair.d.ts +48 -0
  42. package/dist/lifecycle/repair.js +67 -0
  43. package/dist/lifecycle/repair.js.map +1 -0
  44. package/dist/lifecycle/version-endpoint.d.ts +16 -0
  45. package/dist/lifecycle/version-endpoint.js +50 -0
  46. package/dist/lifecycle/version-endpoint.js.map +1 -0
  47. package/dist/manual/topics.js +1 -1
  48. package/dist/manual/topics.js.map +1 -1
  49. package/dist/messages.d.ts +31 -2
  50. package/dist/notice.d.ts +14 -0
  51. package/dist/notice.js +25 -0
  52. package/dist/notice.js.map +1 -0
  53. package/dist/server.js +90 -1
  54. package/dist/server.js.map +1 -1
  55. package/dist/tools/browser-dispatch.d.ts +8 -0
  56. package/dist/tools/browser-dispatch.js +15 -1
  57. package/dist/tools/browser-dispatch.js.map +1 -1
  58. package/dist/ui/app.d.ts +4 -1
  59. package/dist/ui/app.js +12 -3
  60. package/dist/ui/app.js.map +1 -1
  61. package/dist/ui/screens/about.js +3 -1
  62. package/dist/ui/screens/about.js.map +1 -1
  63. package/dist/ui/screens/free-port.js +4 -2
  64. package/dist/ui/screens/free-port.js.map +1 -1
  65. package/dist/ui/screens/index.d.ts +1 -0
  66. package/dist/ui/screens/index.js +1 -0
  67. package/dist/ui/screens/index.js.map +1 -1
  68. package/dist/ui/screens/notice.d.ts +6 -0
  69. package/dist/ui/screens/notice.js +17 -0
  70. package/dist/ui/screens/notice.js.map +1 -0
  71. package/dist/ui/start.js +2 -1
  72. package/dist/ui/start.js.map +1 -1
  73. package/dist/version.d.ts +8 -0
  74. package/dist/version.js +16 -0
  75. package/dist/version.js.map +1 -1
  76. package/package.json +1 -1
@@ -0,0 +1,239 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { platform } from 'node:os';
3
+ /** WMI cold starts are slow: same budget `silent-debugger.ts` documents. */
4
+ const LIST_TIMEOUT_MS = 5000;
5
+ export const defaultExecFile = (file, args, options) => execFileSync(file, args, {
6
+ encoding: 'utf8',
7
+ stdio: ['ignore', 'pipe', 'ignore'],
8
+ windowsHide: true,
9
+ timeout: options.timeout,
10
+ // `ps` prints lstart in the C locale only when told so.
11
+ env: { ...process.env, LC_ALL: 'C' },
12
+ });
13
+ /**
14
+ * Parse a process start time in any of the forms the OS tools print:
15
+ * PowerShell 5.1's JSON `/Date(ms)/`, ISO 8601 (PowerShell 7 JSON,
16
+ * `ToString('o')`), a WMI datetime `yyyyMMddHHmmss.ffffff±UUU` (offset in
17
+ * minutes) and `ps`'s `lstart` (`Mon Nov 9 02:17:44 2026`, local time).
18
+ */
19
+ export function parseProcessTime(raw) {
20
+ const text = raw.trim();
21
+ if (text.length === 0)
22
+ return null;
23
+ const msDate = /^\/Date\((-?\d+)\)\/$/.exec(text);
24
+ if (msDate)
25
+ return Number(msDate[1]);
26
+ const wmi = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d{3})\d*([+-])(\d{3})$/.exec(text);
27
+ if (wmi) {
28
+ const [, y, mo, d, h, mi, s, ms, sign, off] = wmi;
29
+ const utc = Date.UTC(+y, +mo - 1, +d, +h, +mi, +s, +ms);
30
+ const offsetMs = Number(off) * 60_000 * (sign === '-' ? -1 : 1);
31
+ return utc - offsetMs;
32
+ }
33
+ const parsed = Date.parse(text);
34
+ return Number.isFinite(parsed) ? parsed : null;
35
+ }
36
+ /** Parse `Get-CimInstance … | ConvertTo-Json -Compress`. One process comes
37
+ * out as a bare object, several as an array; both are accepted. */
38
+ export function parseWindowsProcessList(stdout) {
39
+ let data;
40
+ try {
41
+ data = JSON.parse(stdout);
42
+ }
43
+ catch {
44
+ return [];
45
+ }
46
+ const rows = (Array.isArray(data) ? data : [data]);
47
+ const out = [];
48
+ for (const row of rows) {
49
+ if (typeof row !== 'object' || row === null)
50
+ continue;
51
+ const { ProcessId, ParentProcessId, CreationDate, CommandLine } = row;
52
+ const pid = Number(ProcessId);
53
+ if (!Number.isInteger(pid) || pid <= 0)
54
+ continue;
55
+ const ppid = Number(ParentProcessId);
56
+ out.push({
57
+ pid,
58
+ ppid: Number.isInteger(ppid) ? ppid : 0,
59
+ startedAt: typeof CreationDate === 'string' ? parseProcessTime(CreationDate) : null,
60
+ command: typeof CommandLine === 'string' ? CommandLine.trim() : '',
61
+ });
62
+ }
63
+ return out;
64
+ }
65
+ const UNIX_ROW = /^\s*(\d+)\s+(\d+)\s+(\w{3}\s+\w{3}\s+\d{1,2}\s+\d{1,2}:\d{2}:\d{2}\s+\d{4})\s+(.*)$/;
66
+ /** Parse `ps -axo pid=,ppid=,lstart=,command=` (C locale). */
67
+ export function parseUnixProcessList(stdout) {
68
+ const out = [];
69
+ for (const line of stdout.split(/\r?\n/)) {
70
+ const m = UNIX_ROW.exec(line);
71
+ if (!m)
72
+ continue;
73
+ out.push({
74
+ pid: Number(m[1]),
75
+ ppid: Number(m[2]),
76
+ startedAt: parseProcessTime(m[3]),
77
+ command: m[4].trim(),
78
+ });
79
+ }
80
+ return out;
81
+ }
82
+ const WINDOWS_LIST_SCRIPT = 'Get-CimInstance Win32_Process | ' +
83
+ 'Select-Object ProcessId, ParentProcessId, CreationDate, CommandLine | ' +
84
+ 'ConvertTo-Json -Compress';
85
+ function powershell(script) {
86
+ return ['powershell', ['-NoProfile', '-NonInteractive', '-Command', script]];
87
+ }
88
+ /** The whole process table, or `[]` when it cannot be read. */
89
+ export function listProcesses(os = platform(), exec = defaultExecFile) {
90
+ try {
91
+ if (os === 'win32') {
92
+ const [file, args] = powershell(WINDOWS_LIST_SCRIPT);
93
+ return parseWindowsProcessList(exec(file, args, { timeout: LIST_TIMEOUT_MS }));
94
+ }
95
+ return parseUnixProcessList(exec('ps', ['-axo', 'pid=,ppid=,lstart=,command='], { timeout: LIST_TIMEOUT_MS }));
96
+ }
97
+ catch {
98
+ return [];
99
+ }
100
+ }
101
+ /** Start time of one process (epoch ms), or `null` when unknown. */
102
+ export function readProcessStartTime(pid, opts = {}) {
103
+ const os = opts.os ?? platform();
104
+ const exec = opts.exec ?? defaultExecFile;
105
+ try {
106
+ if (os === 'win32') {
107
+ const [file, args] = powershell(`Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}' | ` +
108
+ `Select-Object -ExpandProperty CreationDate | ` +
109
+ `ForEach-Object { $_.ToUniversalTime().ToString('o') }`);
110
+ return parseProcessTime(exec(file, args, { timeout: LIST_TIMEOUT_MS }));
111
+ }
112
+ return parseProcessTime(exec('ps', ['-o', 'lstart=', '-p', String(pid)], { timeout: LIST_TIMEOUT_MS }));
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ /** Split a command line into arguments. Double quotes group (Windows
119
+ * command lines quote paths with spaces); backslashes stay literal. */
120
+ function tokenize(command) {
121
+ const tokens = [];
122
+ let current = '';
123
+ let inQuotes = false;
124
+ let hasToken = false;
125
+ for (const ch of command) {
126
+ if (ch === '"') {
127
+ inQuotes = !inQuotes;
128
+ hasToken = true;
129
+ continue;
130
+ }
131
+ if (!inQuotes && /\s/.test(ch)) {
132
+ if (hasToken)
133
+ tokens.push(current);
134
+ current = '';
135
+ hasToken = false;
136
+ continue;
137
+ }
138
+ current += ch;
139
+ hasToken = true;
140
+ }
141
+ if (hasToken)
142
+ tokens.push(current);
143
+ return tokens;
144
+ }
145
+ function normalise(token) {
146
+ return token.replace(/\\+/g, '/').toLowerCase();
147
+ }
148
+ function basename(token) {
149
+ const n = normalise(token);
150
+ return n.slice(n.lastIndexOf('/') + 1);
151
+ }
152
+ /** Runtimes a browser-link process runs under. */
153
+ const RUNTIMES = new Set(['node', 'node.exe', 'nodejs', 'bun', 'bun.exe']);
154
+ /** npm's own entry scripts: the package spec is a later argument. */
155
+ const NPM_ENTRIES = new Set(['npx-cli.js', 'npm-cli.js']);
156
+ function isBrowserLinkEntry(token) {
157
+ const n = normalise(token);
158
+ if (n.includes('@jobshimo/browser-link'))
159
+ return true;
160
+ if (n.endsWith('browser-link/dist/cli.js'))
161
+ return true;
162
+ // The global bin shim (a symlink on Unix): `node /usr/local/bin/browser-link`.
163
+ const base = basename(token);
164
+ return n.includes('/') && (base === 'browser-link' || base === 'browser-link.js');
165
+ }
166
+ /**
167
+ * Whether a command line runs this package, and with which subcommand
168
+ * (`null` = none, i.e. the MCP server or the TUI). Only the script argument
169
+ * counts (the first non-flag argument after the runtime, or the package
170
+ * spec after npm's `npx-cli.js`), so a process that merely mentions a
171
+ * browser-link path as an option value is never matched.
172
+ */
173
+ export function matchBrowserLinkCommand(command) {
174
+ const tokens = tokenize(command);
175
+ if (tokens.length < 2 || !RUNTIMES.has(basename(tokens[0])))
176
+ return null;
177
+ let i = 1;
178
+ while (i < tokens.length && tokens[i].startsWith('-'))
179
+ i += 1;
180
+ if (i >= tokens.length)
181
+ return null;
182
+ if (NPM_ENTRIES.has(basename(tokens[i]))) {
183
+ i += 1;
184
+ while (i < tokens.length && (tokens[i].startsWith('-') || tokens[i] === 'exec'))
185
+ i += 1;
186
+ if (i >= tokens.length)
187
+ return null;
188
+ }
189
+ if (!isBrowserLinkEntry(tokens[i]))
190
+ return null;
191
+ return { subcommand: tokens[i + 1] ?? null };
192
+ }
193
+ /** Subcommands that run the bridge (see `cli.ts`'s dispatch switch: only a
194
+ * bare invocation and `start` call `runServer`). Every other subcommand
195
+ * (`stop`, `doctor`, `version`, `help`, `about`, `config`, `map`,
196
+ * `instructions`, `activity`, `self-update`, …) is a short-lived command
197
+ * that `stop` must never kill. */
198
+ function isBridgeSubcommand(subcommand) {
199
+ return subcommand === null || subcommand === 'start';
200
+ }
201
+ /** `selfPid` and every ancestor reachable through `ppid` in `list`. */
202
+ function selfAndAncestors(list, selfPid) {
203
+ const byPid = new Map(list.map((p) => [p.pid, p]));
204
+ const out = new Set();
205
+ let pid = selfPid;
206
+ while (pid !== undefined && pid > 0 && !out.has(pid)) {
207
+ out.add(pid);
208
+ pid = byPid.get(pid)?.ppid;
209
+ }
210
+ return out;
211
+ }
212
+ /** Live browser-link bridges in `list`, excluding `selfPid`, its ancestor
213
+ * chain (the shell or client that launched it) and non-bridge commands. */
214
+ export function findBridgeProcesses(list, selfPid) {
215
+ const excluded = selfAndAncestors(list, selfPid);
216
+ return list.filter((p) => {
217
+ if (excluded.has(p.pid))
218
+ return false;
219
+ const match = matchBrowserLinkCommand(p.command);
220
+ return match !== null && isBridgeSubcommand(match.subcommand);
221
+ });
222
+ }
223
+ export function listBridgeProcesses(opts = {}) {
224
+ return findBridgeProcesses(listProcesses(opts.os, opts.exec), opts.selfPid ?? process.pid);
225
+ }
226
+ /** Short, display-only form: runtime name, `browser-link`, then arguments. */
227
+ export function shortCommand(command, max = 80) {
228
+ const tokens = tokenize(command);
229
+ if (tokens.length === 0)
230
+ return '';
231
+ const parts = tokens.map((t, i) => {
232
+ if (i === 0)
233
+ return basename(t).replace(/\.exe$/, '');
234
+ return isBrowserLinkEntry(t) ? 'browser-link' : t;
235
+ });
236
+ const text = parts.join(' ');
237
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
238
+ }
239
+ //# sourceMappingURL=processes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.js","sourceRoot":"","sources":["../../src/lifecycle/processes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { BridgeRepairMessage, ServerToExtension } from '../messages.js';
2
+ /**
3
+ * Popup "Repair connection", server side (S4).
4
+ *
5
+ * On `bridge.repair` the bridge answers first, then this process restarts
6
+ * itself on the installed code: spawn the installed CLI detached, terminate
7
+ * every extension socket uncleanly (`ws.terminate()`, which the extension's
8
+ * auto-reconnect treats as involuntary), stop IPC (proxies get
9
+ * `primary-closing`) and exit 0. The child wins the port through the
10
+ * election's bind retries, and the extension reconnects to it by itself.
11
+ *
12
+ * The policy is pure; `server.ts` injects the real spawn/terminate/exit.
13
+ */
14
+ export interface RepairDeps {
15
+ /** `process.execPath`. */
16
+ execPath: string;
17
+ /** The installed `dist/cli.js` (same file the process started from). */
18
+ cliPath: string;
19
+ spawn: (command: string, args: string[], options: {
20
+ detached: true;
21
+ stdio: 'ignore';
22
+ windowsHide: true;
23
+ }) => {
24
+ unref(): void;
25
+ };
26
+ terminateSockets: () => void;
27
+ stopIpc: () => Promise<void>;
28
+ exit: (code: number) => void;
29
+ /** Waits long enough for the ack frame to be flushed to the socket. */
30
+ delay: (ms: number) => Promise<void>;
31
+ log?: (msg: string) => void;
32
+ }
33
+ /** Time given to the `bridge.repair.result` frame before sockets die. */
34
+ export declare const REPAIR_FLUSH_DELAY_MS = 150;
35
+ export interface RepairController {
36
+ /** Start the restart (once). Returns whether it is (now) in progress. */
37
+ start(): boolean;
38
+ /** Settles when the current restart attempt finished (tests). */
39
+ readonly done: Promise<void>;
40
+ }
41
+ export declare function createRepairController(deps: RepairDeps): RepairController;
42
+ /**
43
+ * Answer one `bridge.repair` frame: the ack goes out BEFORE the repair
44
+ * starts, so the popup learns the outcome even though its socket is about to
45
+ * be terminated. `startRepair` is undefined in a process that cannot repair
46
+ * itself (tests, a bridge started without the wiring).
47
+ */
48
+ export declare function answerRepairRequest(msg: BridgeRepairMessage, send: (reply: ServerToExtension) => void, startRepair: (() => boolean) | undefined): void;
@@ -0,0 +1,67 @@
1
+ /** Time given to the `bridge.repair.result` frame before sockets die. */
2
+ export const REPAIR_FLUSH_DELAY_MS = 150;
3
+ export function createRepairController(deps) {
4
+ let inProgress = false;
5
+ let done = Promise.resolve();
6
+ const run = async () => {
7
+ await deps.delay(REPAIR_FLUSH_DELAY_MS);
8
+ try {
9
+ deps
10
+ .spawn(deps.execPath, [deps.cliPath], {
11
+ detached: true,
12
+ stdio: 'ignore',
13
+ windowsHide: true,
14
+ })
15
+ .unref();
16
+ }
17
+ catch (err) {
18
+ // Without a successor, exiting would leave no server at all: keep
19
+ // serving and let the user fall back to `browser-link stop`.
20
+ inProgress = false;
21
+ deps.log?.(`Repair: could not start the installed server (${err instanceof Error ? err.message : String(err)}); still serving.`);
22
+ return;
23
+ }
24
+ deps.log?.('Repair: installed server started; handing over the port.');
25
+ deps.terminateSockets();
26
+ try {
27
+ await deps.stopIpc();
28
+ }
29
+ catch {
30
+ /* already closed; exiting anyway */
31
+ }
32
+ deps.exit(0);
33
+ };
34
+ return {
35
+ start() {
36
+ if (inProgress)
37
+ return true;
38
+ inProgress = true;
39
+ done = run();
40
+ return true;
41
+ },
42
+ get done() {
43
+ return done;
44
+ },
45
+ };
46
+ }
47
+ /**
48
+ * Answer one `bridge.repair` frame: the ack goes out BEFORE the repair
49
+ * starts, so the popup learns the outcome even though its socket is about to
50
+ * be terminated. `startRepair` is undefined in a process that cannot repair
51
+ * itself (tests, a bridge started without the wiring).
52
+ */
53
+ export function answerRepairRequest(msg, send, startRepair) {
54
+ const requestId = typeof msg.request_id === 'string' ? msg.request_id : '';
55
+ if (!startRepair) {
56
+ send({
57
+ kind: 'bridge.repair.result',
58
+ request_id: requestId,
59
+ ok: false,
60
+ error: 'repair is not available in this server process',
61
+ });
62
+ return;
63
+ }
64
+ send({ kind: 'bridge.repair.result', request_id: requestId, ok: true, action: 'restarting' });
65
+ startRepair();
66
+ }
67
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../../src/lifecycle/repair.ts"],"names":[],"mappings":"AAiCA,yEAAyE;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AASzC,MAAM,UAAU,sBAAsB,CAAC,IAAgB;IACrD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAE5C,MAAM,GAAG,GAAG,KAAK,IAAmB,EAAE;QACpC,MAAM,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACxC,IAAI,CAAC;YACH,IAAI;iBACD,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;gBACpC,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,QAAQ;gBACf,WAAW,EAAE,IAAI;aAClB,CAAC;iBACD,KAAK,EAAE,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kEAAkE;YAClE,6DAA6D;YAC7D,UAAU,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,GAAG,EAAE,CACR,iDAAiD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CACrH,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,GAAG,EAAE,CAAC,0DAA0D,CAAC,CAAC;QACvE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACf,CAAC,CAAC;IAEF,OAAO;QACL,KAAK;YACH,IAAI,UAAU;gBAAE,OAAO,IAAI,CAAC;YAC5B,UAAU,GAAG,IAAI,CAAC;YAClB,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI;YACN,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAwB,EACxB,IAAwC,EACxC,WAAwC;IAExC,MAAM,SAAS,GAAG,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,EAAE,sBAAsB;YAC5B,UAAU,EAAE,SAAS;YACrB,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,gDAAgD;SACxD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IAC9F,WAAW,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { VersionInfo } from './election.js';
3
+ /**
4
+ * The installed CLI entry point: `<package>/dist/cli.js`. This module lives
5
+ * in `dist/lifecycle/` (installed) or `src/lifecycle/` (dev), two levels
6
+ * below the package root either way. After `npm install -g` this is the
7
+ * same path the running process started from, now holding the new code.
8
+ */
9
+ export declare const INSTALLED_CLI_PATH: string;
10
+ /** Last-modified time of the installed CLI (epoch ms), or `null`. */
11
+ export declare function readInstalledCodeMtime(path?: string): number | null;
12
+ /** Approximate wall-clock start of this process, epoch ms. */
13
+ export declare const PROCESS_STARTED_AT: number;
14
+ export declare function buildVersionInfo(version: string, pid?: number, startedAt?: number, installed?: () => string | null): VersionInfo;
15
+ /** The bridge's plain-HTTP handler: `/version` or 404. */
16
+ export declare function handleBridgeHttpRequest(req: IncomingMessage, res: ServerResponse, version: string): void;
@@ -0,0 +1,50 @@
1
+ import { statSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { PACKAGE_NAME, readInstalledVersion } from '../version.js';
5
+ /**
6
+ * `GET /version` on the bridge port (S5). The election (`election.ts`),
7
+ * `doctor` and anything else that needs to know "which browser-link is the
8
+ * primary, and is it outdated?" read it. Every other plain HTTP request is a
9
+ * 404; WebSocket upgrades never reach this handler.
10
+ */
11
+ const here = dirname(fileURLToPath(import.meta.url));
12
+ /**
13
+ * The installed CLI entry point: `<package>/dist/cli.js`. This module lives
14
+ * in `dist/lifecycle/` (installed) or `src/lifecycle/` (dev), two levels
15
+ * below the package root either way. After `npm install -g` this is the
16
+ * same path the running process started from, now holding the new code.
17
+ */
18
+ export const INSTALLED_CLI_PATH = join(here, '..', '..', 'dist', 'cli.js');
19
+ /** Last-modified time of the installed CLI (epoch ms), or `null`. */
20
+ export function readInstalledCodeMtime(path = INSTALLED_CLI_PATH) {
21
+ try {
22
+ return statSync(path).mtimeMs;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ /** Approximate wall-clock start of this process, epoch ms. */
29
+ export const PROCESS_STARTED_AT = Date.now() - Math.round(process.uptime() * 1000);
30
+ export function buildVersionInfo(version, pid = process.pid, startedAt = PROCESS_STARTED_AT, installed = readInstalledVersion) {
31
+ return {
32
+ name: PACKAGE_NAME,
33
+ version,
34
+ pid,
35
+ startedAt: new Date(startedAt).toISOString(),
36
+ installedVersion: installed(),
37
+ };
38
+ }
39
+ /** The bridge's plain-HTTP handler: `/version` or 404. */
40
+ export function handleBridgeHttpRequest(req, res, version) {
41
+ const path = (req.url ?? '').split('?')[0];
42
+ if (req.method === 'GET' && path === '/version') {
43
+ res.writeHead(200, { 'content-type': 'application/json' });
44
+ res.end(JSON.stringify(buildVersionInfo(version)));
45
+ return;
46
+ }
47
+ res.writeHead(404, { 'content-type': 'text/plain' });
48
+ res.end('Not found');
49
+ }
50
+ //# sourceMappingURL=version-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-endpoint.js","sourceRoot":"","sources":["../../src/lifecycle/version-endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGnE;;;;;GAKG;AAEH,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAW,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEnF,qEAAqE;AACrE,MAAM,UAAU,sBAAsB,CAAC,OAAe,kBAAkB;IACtE,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,kBAAkB,GAAW,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;AAE3F,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,MAAc,OAAO,CAAC,GAAG,EACzB,YAAoB,kBAAkB,EACtC,YAAiC,oBAAoB;IAErD,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO;QACP,GAAG;QACH,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;QAC5C,gBAAgB,EAAE,SAAS,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,uBAAuB,CACrC,GAAoB,EACpB,GAAmB,EACnB,OAAe;IAEf,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QAChD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IACD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;IACrD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACvB,CAAC"}
@@ -45,7 +45,7 @@ export const TOPICS = {
45
45
  },
46
46
  'multi-agent': {
47
47
  title: 'Sharing tabs with other agents (multi-agent mode)',
48
- body: lines('Several MCP clients can share one bridge; a cooperative claim layer keeps two agents off the same tab.', '- Action tools (`browser.click`, `browser.type`, `browser.navigate`, `browser.evaluate`) auto-claim a free tab on first use. If another agent holds it they fail naming the owner: ask the user whose tab it is, or use a different one.', '- Read tools (`browser.snapshot`, `browser.state`, `browser.console`, `browser.network`, `browser.network_body`, `browser.events`, `browser.ping`) ignore claims.', '- Give `browser.claim_tab` a stable `label` ("claude-code", "opencode"); claims also end on disconnect and after the inactivity TTL. The label is display only: ownership rests on the IPC session id, not on what an agent calls itself.'),
48
+ body: lines('Several MCP clients can share one bridge; a cooperative claim layer keeps two agents off the same tab.', '- Action tools (`browser.click`, `browser.type`, `browser.navigate`, `browser.evaluate`) auto-claim a free tab on first use. If another agent holds it they fail naming the owner: ask the user whose tab it is, or use a different one.', '- Read tools (`browser.snapshot`, `browser.state`, `browser.console`, `browser.network`, `browser.network_body`, `browser.events`, `browser.ping`) ignore claims.', '- Give `browser.claim_tab` a stable `label` ("claude-code", "opencode"); claims also end on disconnect and after the inactivity TTL. The label is display only: ownership rests on the IPC session id, not on what an agent calls itself.', '- One server process is the primary. A newer browser-link that starts displaces an older primary (it reads `GET /version` on the bridge port, then stops the old process), and the extension reconnects by itself.', '- A `list_tabs` hint "server running X, installed Y" means the package was updated under a live server: ask the user to run `browser-link stop` and restart the agent, or to click "Repair connection" in the extension popup. Do not kill processes yourself.'),
49
49
  },
50
50
  'cdp-direct': {
51
51
  title: 'cdp-direct tabs (optional, off by default, human-gated)',
@@ -1 +1 @@
1
- {"version":3,"file":"topics.js","sourceRoot":"","sources":["../../src/manual/topics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,KAAK;IACL,kBAAkB;IAClB,aAAa;IACb,YAAY;CACJ,CAAC;AASX,MAAM,KAAK,GAAG,CAAC,GAAG,CAAW,EAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEvD,MAAM,CAAC,MAAM,MAAM,GAA6B;IAC9C,SAAS,EAAE;QACT,KAAK,EAAE,2CAA2C;QAClD,IAAI,EAAE,KAAK,CACT,kOAAkO,EAClO,+OAA+O,EAC/O,0EAA0E,EAC1E,uHAAuH,CACxH;KACF;IACD,MAAM,EAAE;QACN,KAAK,EAAE,0DAA0D;QACjE,IAAI,EAAE,KAAK,CACT,+OAA+O,EAC/O,oPAAoP,EACpP,oSAAoS,CACrS;KACF;IACD,UAAU,EAAE;QACV,KAAK,EAAE,mCAAmC;QAC1C,IAAI,EAAE,KAAK,CACT,wKAAwK,EACxK,oLAAoL,EACpL,oFAAoF,CACrF;KACF;IACD,KAAK,EAAE;QACL,KAAK,EAAE,wDAAwD;QAC/D,IAAI,EAAE,KAAK,CACT,gaAAga,EACha,EAAE,EACF,iJAAiJ,EACjJ,6HAA6H,EAC7H,iOAAiO,EACjO,gWAAgW,EAChW,qIAAqI,EACrI,sXAAsX,EACtX,kMAAkM,EAClM,EAAE,EACF,6aAA6a,EAC7a,EAAE,EACF,2XAA2X,EAC3X,kNAAkN,EAClN,qGAAqG,EACrG,uGAAuG,EACvG,mLAAmL,CACpL;KACF;IACD,GAAG,EAAE;QACH,KAAK,EAAE,mEAAmE;QAC1E,IAAI,EAAE,KAAK,CACT,qRAAqR,EACrR,0MAA0M,EAC1M,0MAA0M,EAC1M,+UAA+U,EAC/U,yRAAyR,EACzR,2IAA2I,CAC5I;KACF;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,2CAA2C;QAClD,IAAI,EAAE,KAAK,CACT,kGAAkG,EAClG,qRAAqR,EACrR,iJAAiJ,EACjJ,gNAAgN,EAChN,wFAAwF,EACxF,2HAA2H,EAC3H,iGAAiG,EACjG,sPAAsP,EACtP,uaAAua,EACva,sQAAsQ,EACtQ,+HAA+H,EAC/H,2QAA2Q,EAC3Q,oNAAoN,CACrN;KACF;IACD,aAAa,EAAE;QACb,KAAK,EAAE,mDAAmD;QAC1D,IAAI,EAAE,KAAK,CACT,wGAAwG,EACxG,0OAA0O,EAC1O,mKAAmK,EACnK,2OAA2O,CAC5O;KACF;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,yDAAyD;QAChE,IAAI,EAAE,KAAK,CACT,yOAAyO,EACzO,gIAAgI,EAChI,qHAAqH,EACrH,0GAA0G,CAC3G;KACF;CACF,CAAC;AAEF,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,IAAe;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,OAAO,KAAK,CAAC,KAAK,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"topics.js","sourceRoot":"","sources":["../../src/manual/topics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,KAAK;IACL,kBAAkB;IAClB,aAAa;IACb,YAAY;CACJ,CAAC;AASX,MAAM,KAAK,GAAG,CAAC,GAAG,CAAW,EAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEvD,MAAM,CAAC,MAAM,MAAM,GAA6B;IAC9C,SAAS,EAAE;QACT,KAAK,EAAE,2CAA2C;QAClD,IAAI,EAAE,KAAK,CACT,kOAAkO,EAClO,+OAA+O,EAC/O,0EAA0E,EAC1E,uHAAuH,CACxH;KACF;IACD,MAAM,EAAE;QACN,KAAK,EAAE,0DAA0D;QACjE,IAAI,EAAE,KAAK,CACT,+OAA+O,EAC/O,oPAAoP,EACpP,oSAAoS,CACrS;KACF;IACD,UAAU,EAAE;QACV,KAAK,EAAE,mCAAmC;QAC1C,IAAI,EAAE,KAAK,CACT,wKAAwK,EACxK,oLAAoL,EACpL,oFAAoF,CACrF;KACF;IACD,KAAK,EAAE;QACL,KAAK,EAAE,wDAAwD;QAC/D,IAAI,EAAE,KAAK,CACT,gaAAga,EACha,EAAE,EACF,iJAAiJ,EACjJ,6HAA6H,EAC7H,iOAAiO,EACjO,gWAAgW,EAChW,qIAAqI,EACrI,sXAAsX,EACtX,kMAAkM,EAClM,EAAE,EACF,6aAA6a,EAC7a,EAAE,EACF,2XAA2X,EAC3X,kNAAkN,EAClN,qGAAqG,EACrG,uGAAuG,EACvG,mLAAmL,CACpL;KACF;IACD,GAAG,EAAE;QACH,KAAK,EAAE,mEAAmE;QAC1E,IAAI,EAAE,KAAK,CACT,qRAAqR,EACrR,0MAA0M,EAC1M,0MAA0M,EAC1M,+UAA+U,EAC/U,yRAAyR,EACzR,2IAA2I,CAC5I;KACF;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,2CAA2C;QAClD,IAAI,EAAE,KAAK,CACT,kGAAkG,EAClG,qRAAqR,EACrR,iJAAiJ,EACjJ,gNAAgN,EAChN,wFAAwF,EACxF,2HAA2H,EAC3H,iGAAiG,EACjG,sPAAsP,EACtP,uaAAua,EACva,sQAAsQ,EACtQ,+HAA+H,EAC/H,2QAA2Q,EAC3Q,oNAAoN,CACrN;KACF;IACD,aAAa,EAAE;QACb,KAAK,EAAE,mDAAmD;QAC1D,IAAI,EAAE,KAAK,CACT,wGAAwG,EACxG,0OAA0O,EAC1O,mKAAmK,EACnK,2OAA2O,EAC3O,oNAAoN,EACpN,gQAAgQ,CACjQ;KACF;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,yDAAyD;QAChE,IAAI,EAAE,KAAK,CACT,yOAAyO,EACzO,gIAAgI,EAChI,qHAAqH,EACrH,0GAA0G,CAC3G;KACF;CACF,CAAC;AAEF,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,IAAe;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,OAAO,KAAK,CAAC,KAAK,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC"}
@@ -35,6 +35,14 @@ export interface TabRegisteredPayload {
35
35
  * extensions that don't know about the field can still parse the message.
36
36
  */
37
37
  serverVersion?: string;
38
+ /**
39
+ * Version installed on disk right now (re-read on every register). When it
40
+ * differs from `serverVersion`, the running server is outdated and the
41
+ * popup offers "Repair connection". Absent from servers older than 0.41.0.
42
+ */
43
+ installedVersion?: string;
44
+ /** PID of the server process that handed out this tabId (0.41.0+). */
45
+ serverPid?: number;
38
46
  }
39
47
  export interface TabRegisterMessage {
40
48
  kind: 'tab.register';
@@ -254,5 +262,26 @@ export type ActivityPurgeResultMessage = {
254
262
  ok: false;
255
263
  error: string;
256
264
  };
257
- export type ExtensionToServer = TabRegisterMessage | ToolResponseMessage | BridgeEventMessage | FlowRecordedMessage | ActivityQueryMessage | ActivityPurgeMessage;
258
- export type ServerToExtension = TabRegisteredMessage | ToolRequestMessage | ToolCancelMessage | SettingsUpdateMessage | FlowRecordedResultMessage | ActivityResultMessage | ActivityPurgeResultMessage;
265
+ /**
266
+ * Popup "Repair connection": ask the running server to restart itself on
267
+ * the installed code. The server answers with `bridge.repair.result` first,
268
+ * then spawns the installed CLI detached, terminates every extension socket
269
+ * uncleanly (so the extension auto-reconnects) and exits.
270
+ */
271
+ export interface BridgeRepairMessage {
272
+ kind: 'bridge.repair';
273
+ request_id: string;
274
+ }
275
+ export type BridgeRepairResultMessage = {
276
+ kind: 'bridge.repair.result';
277
+ request_id: string;
278
+ ok: true;
279
+ action: 'restarting';
280
+ } | {
281
+ kind: 'bridge.repair.result';
282
+ request_id: string;
283
+ ok: false;
284
+ error: string;
285
+ };
286
+ export type ExtensionToServer = TabRegisterMessage | ToolResponseMessage | BridgeEventMessage | FlowRecordedMessage | ActivityQueryMessage | ActivityPurgeMessage | BridgeRepairMessage;
287
+ export type ServerToExtension = TabRegisteredMessage | ToolRequestMessage | ToolCancelMessage | SettingsUpdateMessage | FlowRecordedResultMessage | ActivityResultMessage | ActivityPurgeResultMessage | BridgeRepairResultMessage;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Verbatim copy of `packages/shared/src/notice.ts` (everything below this
3
+ * header). The server publishes to npm standalone and cannot import
4
+ * `@browser-link/shared`; `notice.test.ts` fails the build if the two
5
+ * copies drift. Change both together.
6
+ */
7
+ export interface NoticeText {
8
+ title: string;
9
+ paragraphs: string[];
10
+ }
11
+ export declare const NOTICE: {
12
+ es: NoticeText;
13
+ en: NoticeText;
14
+ };
package/dist/notice.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Verbatim copy of `packages/shared/src/notice.ts` (everything below this
3
+ * header). The server publishes to npm standalone and cannot import
4
+ * `@browser-link/shared`; `notice.test.ts` fails the build if the two
5
+ * copies drift. Change both together.
6
+ */
7
+ export const NOTICE = {
8
+ es: {
9
+ title: 'Aviso importante',
10
+ paragraphs: [
11
+ 'browser-link es una herramienta de desarrollo. Conecta un agente de IA a las pestañas de Chrome que usted elija, y en ellas el agente actúa con su propia sesión: ve y puede usar todo lo que el navegador tenga en esas pestañas, incluidos inicios de sesión activos, cookies y tokens, contraseñas y tarjetas de pago autocompletadas, correos, documentos y cualquier dato que la página muestre o permita enviar. Puede hacer clic, escribir, enviar formularios y descargar archivos como lo haría usted.',
12
+ 'Conecte únicamente pestañas que un agente deba manejar, desconéctelas al terminar y no la utilice en sitios cuyos términos lo prohíban ni con datos que no pueda permitirse exponer. La decisión de a qué dar acceso, y lo que el agente haga con ese acceso, es responsabilidad exclusiva del usuario.',
13
+ 'El software se distribuye "tal cual", sin garantía de ningún tipo, bajo licencia MIT. Los autores no se hacen responsables de pérdidas, daños o usos indebidos derivados de su utilización.',
14
+ ],
15
+ },
16
+ en: {
17
+ title: 'Important notice',
18
+ paragraphs: [
19
+ 'browser-link is a development tool. It connects an AI agent to the Chrome tabs you choose, and in those tabs the agent acts with your own session: it sees and can use everything the browser holds in them, including active logins, cookies and tokens, autofilled passwords and payment cards, email, documents and any data the page shows or lets you submit. It can click, type, submit forms and download files as you would.',
20
+ "Connect only tabs an agent should operate, disconnect them when you are done, and do not use it on sites whose terms forbid it or with data you cannot afford to expose. Deciding what to grant access to, and what the agent does with that access, is solely the user's responsibility.",
21
+ 'The software is provided "as is", without warranty of any kind, under the MIT license. The authors accept no liability for losses, damage or misuse arising from its use.',
22
+ ],
23
+ },
24
+ };
25
+ //# sourceMappingURL=notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notice.js","sourceRoot":"","sources":["../src/notice.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,CAAC,MAAM,MAAM,GAAuC;IACxD,EAAE,EAAE;QACF,KAAK,EAAE,kBAAkB;QACzB,UAAU,EAAE;YACV,ifAAif;YACjf,ySAAyS;YACzS,6LAA6L;SAC9L;KACF;IACD,EAAE,EAAE;QACF,KAAK,EAAE,kBAAkB;QACzB,UAAU,EAAE;YACV,saAAsa;YACta,2RAA2R;YAC3R,2KAA2K;SAC5K;KACF;CACF,CAAC"}