friend-assistant 0.1.41 → 0.1.43

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/dist/BUILD.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "friend-assistant",
3
- "version": "0.1.41",
4
- "builtAt": "2026-10-13T04:06:44.122Z"
3
+ "version": "0.1.43",
4
+ "builtAt": "2026-10-13T05:55:13.217Z"
5
5
  }
@@ -11302,6 +11302,156 @@ var init_bridge = __esm({
11302
11302
  }
11303
11303
  });
11304
11304
 
11305
+ // ../chrome-control/src/browser.ts
11306
+ var browser_exports = {};
11307
+ __export(browser_exports, {
11308
+ DEBUG_FLAG: () => DEBUG_FLAG,
11309
+ chromeCommandLines: () => chromeCommandLines,
11310
+ countShortcuts: () => countShortcuts,
11311
+ describeMains: () => describeMains,
11312
+ flagStatus: () => flagStatus,
11313
+ hasFlag: () => hasFlag,
11314
+ isMainProcess: () => isMainProcess,
11315
+ parseShortcutRows: () => parseShortcutRows,
11316
+ patchArguments: () => patchArguments,
11317
+ patchShortcuts: () => patchShortcuts,
11318
+ restartChrome: () => restartChrome,
11319
+ shortcutScript: () => shortcutScript,
11320
+ startChrome: () => startChrome
11321
+ });
11322
+ import { spawn, spawnSync } from "node:child_process";
11323
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
11324
+ import { homedir as homedir2, tmpdir } from "node:os";
11325
+ import { join as join2 } from "node:path";
11326
+ import { rmSync, writeFileSync as writeFileSync2 } from "node:fs";
11327
+ function describeMains(cmds) {
11328
+ const mains = cmds.filter(isMainProcess);
11329
+ if (!mains.length) return { running: false, hidden: null };
11330
+ return { running: true, hidden: mains.every(hasFlag) };
11331
+ }
11332
+ function patchArguments(args) {
11333
+ if (hasFlag(args)) return { args, changed: false };
11334
+ return { args: `${args} ${DEBUG_FLAG}`.trim(), changed: true };
11335
+ }
11336
+ function shortcutScript(patch) {
11337
+ return [
11338
+ `$flag = '${DEBUG_FLAG}'`,
11339
+ "$dirs = @([Environment]::GetFolderPath('Desktop'), [Environment]::GetFolderPath('CommonDesktopDirectory'),",
11340
+ " [Environment]::GetFolderPath('Programs'), [Environment]::GetFolderPath('CommonPrograms'),",
11341
+ " (Join-Path $env:APPDATA 'Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar')) | Where-Object { $_ -and (Test-Path $_) }",
11342
+ "$sh = New-Object -ComObject WScript.Shell",
11343
+ "foreach ($d in $dirs) { Get-ChildItem -Path $d -Filter *.lnk -Recurse -ErrorAction SilentlyContinue | ForEach-Object {",
11344
+ " try { $l = $sh.CreateShortcut($_.FullName)",
11345
+ " if ($l.TargetPath -match '(chrome|msedge|brave)\\.exe$') {",
11346
+ patch ? ` if ($l.Arguments -notlike "*$flag*") { $l.Arguments = ($l.Arguments + ' ' + $flag).Trim(); $l.Save(); Write-Output ('patched|' + $_.FullName) }` : ` if ($l.Arguments -notlike "*$flag*") { Write-Output ('none|' + $_.FullName) }`,
11347
+ patch ? " else { Write-Output ('already|' + $_.FullName) } } } catch { Write-Output ('failed|' + $_.FullName) } } }" : " else { Write-Output ('has|' + $_.FullName) } } } catch { Write-Output ('failed|' + $_.FullName) } } }"
11348
+ ].join("\n");
11349
+ }
11350
+ function parseShortcutRows(out) {
11351
+ const rows = out.split(/\r?\n/).map((l) => l.split("|")[0].trim()).filter(Boolean);
11352
+ const n = (k) => rows.filter((r) => r === k).length;
11353
+ const c = { patched: n("patched"), already: n("already"), failed: n("failed"), has: n("has"), none: n("none") };
11354
+ return { patched: c.patched, already: c.already, failed: c.failed, withFlag: c.patched + c.already + c.has, total: c.patched + c.already + c.failed + c.has + c.none };
11355
+ }
11356
+ function powershell(script) {
11357
+ const f = join2(tmpdir(), `chrome-control-${process.pid}-${Date.now()}.ps1`);
11358
+ writeFileSync2(f, script);
11359
+ try {
11360
+ const r = spawnSync("powershell", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", f], { encoding: "utf8" });
11361
+ return String(r.stdout || "");
11362
+ } finally {
11363
+ rmSync(f, { force: true });
11364
+ }
11365
+ }
11366
+ function chromeCommandLines() {
11367
+ if (WIN) {
11368
+ return powershell(`Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" | ForEach-Object { $_.CommandLine }`).split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
11369
+ }
11370
+ const r = spawnSync("ps", ["-eo", "args"], { encoding: "utf8" });
11371
+ return String(r.stdout || "").split("\n").filter((l) => /(^|\/)(google-chrome(-stable)?|chromium(-browser)?|Google Chrome)(\s|$)/.test(l.trim().split(/\s--/)[0]));
11372
+ }
11373
+ function patchLinux() {
11374
+ const counts = { patched: 0, already: 0, failed: 0, withFlag: 0, total: 0 };
11375
+ for (const name of LINUX_LAUNCHERS) {
11376
+ const src = [linuxDir(), "/usr/share/applications", "/var/lib/snapd/desktop/applications"].map((d) => join2(d, name)).find(existsSync2);
11377
+ if (!src) continue;
11378
+ const text2 = readFileSync2(src, "utf8");
11379
+ counts.total++;
11380
+ if (text2.includes(DEBUG_FLAG)) {
11381
+ counts.already++;
11382
+ continue;
11383
+ }
11384
+ try {
11385
+ const out = text2.replace(/^(Exec=.*)$/gm, (l) => l.includes(DEBUG_FLAG) ? l : l.replace(/(Exec=\S+)/, `$1 ${DEBUG_FLAG}`));
11386
+ writeFileSync2(join2(linuxDir(), name), out);
11387
+ counts.patched++;
11388
+ } catch {
11389
+ counts.failed++;
11390
+ }
11391
+ }
11392
+ counts.withFlag = counts.patched + counts.already;
11393
+ return counts;
11394
+ }
11395
+ function countShortcuts() {
11396
+ if (WIN) return parseShortcutRows(powershell(shortcutScript(false)));
11397
+ if (MAC) return null;
11398
+ const total = LINUX_LAUNCHERS.filter((n) => [linuxDir(), "/usr/share/applications"].some((d) => existsSync2(join2(d, n)))).length;
11399
+ const withFlag = LINUX_LAUNCHERS.filter((n) => existsSync2(join2(linuxDir(), n)) && readFileSync2(join2(linuxDir(), n), "utf8").includes(DEBUG_FLAG)).length;
11400
+ return { patched: 0, already: 0, failed: 0, withFlag, total };
11401
+ }
11402
+ function patchShortcuts() {
11403
+ if (WIN) return parseShortcutRows(powershell(shortcutScript(true)));
11404
+ if (MAC) return null;
11405
+ return patchLinux();
11406
+ }
11407
+ function flagStatus() {
11408
+ return { platform: process.platform, ...describeMains(chromeCommandLines()), shortcuts: countShortcuts() };
11409
+ }
11410
+ function chromeExe() {
11411
+ const cands = WIN ? [join2(process.env.PROGRAMFILES ?? "", "Google", "Chrome", "Application", "chrome.exe"), join2(process.env["PROGRAMFILES(X86)"] ?? "", "Google", "Chrome", "Application", "chrome.exe"), join2(process.env.LOCALAPPDATA ?? "", "Google", "Chrome", "Application", "chrome.exe")] : MAC ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : ["/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium"];
11412
+ return cands.find(existsSync2) ?? null;
11413
+ }
11414
+ function startChrome(extra = []) {
11415
+ const exe = chromeExe();
11416
+ if (!exe) throw new Error("Chrome was not found. Start it yourself with the flag " + DEBUG_FLAG);
11417
+ spawn(exe, [DEBUG_FLAG, ...extra], { detached: true, stdio: "ignore" }).unref();
11418
+ }
11419
+ async function restartChrome() {
11420
+ const lines = chromeCommandLines();
11421
+ if (describeMains(lines).running) {
11422
+ if (WIN) {
11423
+ powershell("Get-Process chrome -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowHandle -ne 0 } | ForEach-Object { [void]$_.CloseMainWindow() }");
11424
+ const windowsOpen = () => powershell("(Get-Process chrome -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowHandle -ne 0 } | Measure-Object).Count").trim() !== "0";
11425
+ for (let i = 0; i < 20 && windowsOpen(); i++) await wait(500);
11426
+ if (windowsOpen()) return { ok: false, message: "Chrome did not close (it may be asking you to save something). Close it yourself, then press Start." };
11427
+ powershell("Get-Process chrome -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue");
11428
+ await wait(800);
11429
+ } else {
11430
+ return { ok: false, message: "Close Chrome yourself (all its windows), then press Start." };
11431
+ }
11432
+ }
11433
+ try {
11434
+ startChrome(["--restore-last-session"]);
11435
+ } catch (e) {
11436
+ return { ok: false, message: e.message };
11437
+ }
11438
+ return { ok: true, message: "Chrome was restarted with the banner hidden. Your tabs come back." };
11439
+ }
11440
+ var DEBUG_FLAG, WIN, MAC, hasFlag, isMainProcess, LINUX_LAUNCHERS, linuxDir, wait;
11441
+ var init_browser = __esm({
11442
+ "../chrome-control/src/browser.ts"() {
11443
+ "use strict";
11444
+ DEBUG_FLAG = "--silent-debugger-extension-api";
11445
+ WIN = process.platform === "win32";
11446
+ MAC = process.platform === "darwin";
11447
+ hasFlag = (cmd2) => new RegExp(`(^|\\s)${DEBUG_FLAG}(=\\S*)?(\\s|$)`).test(cmd2);
11448
+ isMainProcess = (cmd2) => !/(^|\s)--type=/.test(cmd2);
11449
+ LINUX_LAUNCHERS = ["google-chrome.desktop", "microsoft-edge.desktop", "brave-browser.desktop", "chromium.desktop"];
11450
+ linuxDir = () => join2(homedir2(), ".local", "share", "applications");
11451
+ wait = (ms2) => new Promise((r) => setTimeout(r, ms2));
11452
+ }
11453
+ });
11454
+
11305
11455
  // ../chrome-control/src/setup.ts
11306
11456
  var setup_exports = {};
11307
11457
  __export(setup_exports, {
@@ -11310,17 +11460,18 @@ __export(setup_exports, {
11310
11460
  EXT_DIR: () => EXT_DIR,
11311
11461
  HOST_DIR: () => HOST_DIR,
11312
11462
  HOST_NAME: () => HOST_NAME,
11463
+ hideBanner: () => hideBanner,
11313
11464
  launchChrome: () => launchChrome,
11314
11465
  runSetup: () => runSetup,
11315
11466
  runUninstall: () => runUninstall
11316
11467
  });
11317
- import { chmodSync, copyFileSync, cpSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
11318
- import { homedir as homedir2, tmpdir } from "node:os";
11319
- import { dirname, join as join2, resolve } from "node:path";
11320
- import { spawn, spawnSync } from "node:child_process";
11468
+ import { chmodSync, copyFileSync, cpSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
11469
+ import { homedir as homedir3 } from "node:os";
11470
+ import { dirname, join as join3, resolve } from "node:path";
11471
+ import { spawnSync as spawnSync2 } from "node:child_process";
11321
11472
  import { fileURLToPath } from "node:url";
11322
11473
  function manifestDir(b) {
11323
- return MAC ? join2(homedir2(), "Library", "Application Support", b.mac, "NativeMessagingHosts") : join2(homedir2(), ".config", b.linux, "NativeMessagingHosts");
11474
+ return MAC2 ? join3(homedir3(), "Library", "Application Support", b.mac, "NativeMessagingHosts") : join3(homedir3(), ".config", b.linux, "NativeMessagingHosts");
11324
11475
  }
11325
11476
  function parseArgs(args) {
11326
11477
  const o = { extensionIds: [DEV_EXTENSION_ID], claudeDesktop: true, claudeCode: true, force: false, open: true, flag: true };
@@ -11344,20 +11495,20 @@ function parseArgs(args) {
11344
11495
  return o;
11345
11496
  }
11346
11497
  function installHost(ids) {
11347
- const hostJs = join2(PKG_ROOT, "dist", "native-host.js");
11348
- if (!existsSync2(hostJs)) throw new Error("dist/native-host.js is missing. Run npm run build first.");
11498
+ const hostJs = join3(PKG_ROOT, "dist", "native-host.js");
11499
+ if (!existsSync3(hostJs)) throw new Error("dist/native-host.js is missing. Run npm run build first.");
11349
11500
  mkdirSync2(HOST_DIR, { recursive: true });
11350
- copyFileSync(hostJs, join2(HOST_DIR, "native-host.mjs"));
11501
+ copyFileSync(hostJs, join3(HOST_DIR, "native-host.mjs"));
11351
11502
  const node = process.execPath;
11352
11503
  let launcher;
11353
- if (WIN) {
11354
- launcher = join2(HOST_DIR, "native-host.bat");
11355
- writeFileSync2(launcher, `@echo off\r
11504
+ if (WIN2) {
11505
+ launcher = join3(HOST_DIR, "native-host.bat");
11506
+ writeFileSync3(launcher, `@echo off\r
11356
11507
  "${node}" "%~dp0native-host.mjs" %*\r
11357
11508
  `);
11358
11509
  } else {
11359
- launcher = join2(HOST_DIR, "native-host.sh");
11360
- writeFileSync2(launcher, `#!/bin/sh
11510
+ launcher = join3(HOST_DIR, "native-host.sh");
11511
+ writeFileSync3(launcher, `#!/bin/sh
11361
11512
  exec "${node}" "$(dirname "$0")/native-host.mjs" "$@"
11362
11513
  `);
11363
11514
  chmodSync(launcher, 493);
@@ -11369,19 +11520,19 @@ exec "${node}" "$(dirname "$0")/native-host.mjs" "$@"
11369
11520
  type: "stdio",
11370
11521
  allowed_origins: [...new Set(ids)].map((id) => `chrome-extension://${id}/`)
11371
11522
  };
11372
- const manifestPath = join2(HOST_DIR, `${HOST_NAME}.json`);
11373
- writeFileSync2(manifestPath, JSON.stringify(manifest, null, 2));
11523
+ const manifestPath = join3(HOST_DIR, `${HOST_NAME}.json`);
11524
+ writeFileSync3(manifestPath, JSON.stringify(manifest, null, 2));
11374
11525
  const registered = [];
11375
11526
  for (const b of BROWSERS) {
11376
- if (WIN) {
11527
+ if (WIN2) {
11377
11528
  const key = `HKCU\\${b.win}\\NativeMessagingHosts\\${HOST_NAME}`;
11378
- const r = spawnSync("reg", ["add", key, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], { encoding: "utf8" });
11529
+ const r = spawnSync2("reg", ["add", key, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], { encoding: "utf8" });
11379
11530
  if (r.status === 0) registered.push(b.name);
11380
11531
  } else {
11381
11532
  const dir = manifestDir(b);
11382
- if (!existsSync2(dirname(dir))) continue;
11533
+ if (!existsSync3(dirname(dir))) continue;
11383
11534
  mkdirSync2(dir, { recursive: true });
11384
- copyFileSync(manifestPath, join2(dir, `${HOST_NAME}.json`));
11535
+ copyFileSync(manifestPath, join3(dir, `${HOST_NAME}.json`));
11385
11536
  registered.push(b.name);
11386
11537
  }
11387
11538
  }
@@ -11389,17 +11540,17 @@ exec "${node}" "$(dirname "$0")/native-host.mjs" "$@"
11389
11540
  }
11390
11541
  function claudeDesktopConfigs() {
11391
11542
  const out = [];
11392
- if (WIN) {
11393
- if (process.env.APPDATA) out.push(join2(process.env.APPDATA, "Claude", "claude_desktop_config.json"));
11394
- const pk = process.env.LOCALAPPDATA ? join2(process.env.LOCALAPPDATA, "Packages") : "";
11395
- if (pk && existsSync2(pk)) {
11543
+ if (WIN2) {
11544
+ if (process.env.APPDATA) out.push(join3(process.env.APPDATA, "Claude", "claude_desktop_config.json"));
11545
+ const pk = process.env.LOCALAPPDATA ? join3(process.env.LOCALAPPDATA, "Packages") : "";
11546
+ if (pk && existsSync3(pk)) {
11396
11547
  for (const d of readdirSync(pk)) {
11397
- if (d.startsWith("Claude_")) out.push(join2(pk, d, "LocalCache", "Roaming", "Claude", "claude_desktop_config.json"));
11548
+ if (d.startsWith("Claude_")) out.push(join3(pk, d, "LocalCache", "Roaming", "Claude", "claude_desktop_config.json"));
11398
11549
  }
11399
11550
  }
11400
- } else if (MAC) out.push(join2(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json"));
11401
- else out.push(join2(homedir2(), ".config", "Claude", "claude_desktop_config.json"));
11402
- return out.filter((f) => existsSync2(f));
11551
+ } else if (MAC2) out.push(join3(homedir3(), "Library", "Application Support", "Claude", "claude_desktop_config.json"));
11552
+ else out.push(join3(homedir3(), ".config", "Claude", "claude_desktop_config.json"));
11553
+ return out.filter((f) => existsSync3(f));
11403
11554
  }
11404
11555
  function configureClaudeDesktop(force, log) {
11405
11556
  const files = claudeDesktopConfigs();
@@ -11407,7 +11558,7 @@ function configureClaudeDesktop(force, log) {
11407
11558
  for (const f of files) {
11408
11559
  let cfg;
11409
11560
  try {
11410
- cfg = JSON.parse(readFileSync2(f, "utf8"));
11561
+ cfg = JSON.parse(readFileSync3(f, "utf8"));
11411
11562
  } catch {
11412
11563
  log(` Could not read ${f} as JSON. Skipped.`);
11413
11564
  continue;
@@ -11418,15 +11569,15 @@ function configureClaudeDesktop(force, log) {
11418
11569
  continue;
11419
11570
  }
11420
11571
  copyFileSync(f, `${f}.bak-chrome-control`);
11421
- cfg.mcpServers["chrome-control"] = WIN ? { command: "cmd", args: ["/c", "npx", "-y", "chrome-control-mcp"] } : { command: "npx", args: ["-y", "chrome-control-mcp"] };
11422
- writeFileSync2(f, JSON.stringify(cfg, null, 2));
11572
+ cfg.mcpServers["chrome-control"] = WIN2 ? { command: "cmd", args: ["/c", "npx", "-y", "chrome-control-mcp"] } : { command: "npx", args: ["-y", "chrome-control-mcp"] };
11573
+ writeFileSync3(f, JSON.stringify(cfg, null, 2));
11423
11574
  log(` Added chrome-control to ${f} (backup saved next to it). Restart Claude Desktop.`);
11424
11575
  }
11425
11576
  }
11426
11577
  function configureClaudeCode(log) {
11427
- const v = spawnSync("claude", ["--version"], { encoding: "utf8", shell: WIN });
11578
+ const v = spawnSync2("claude", ["--version"], { encoding: "utf8", shell: WIN2 });
11428
11579
  if (v.status !== 0) return log(" Claude Code CLI not found. Skipped.");
11429
- const r = spawnSync("claude", ["mcp", "add", "chrome-control", "--", "npx", "-y", "chrome-control-mcp"], { encoding: "utf8", shell: WIN });
11580
+ const r = spawnSync2("claude", ["mcp", "add", "chrome-control", "--", "npx", "-y", "chrome-control-mcp"], { encoding: "utf8", shell: WIN2 });
11430
11581
  const msg = `${r.stderr || ""}${r.stdout || ""}`;
11431
11582
  if (r.status === 0) log(" Added chrome-control to Claude Code.");
11432
11583
  else if (/already exists/i.test(msg)) log(" Already set in Claude Code.");
@@ -11434,25 +11585,30 @@ function configureClaudeCode(log) {
11434
11585
  Run it yourself: claude mcp add chrome-control -- npx -y chrome-control-mcp`);
11435
11586
  }
11436
11587
  function copyToClipboard(text2) {
11437
- const r = WIN ? spawnSync("clip", { input: text2, shell: true }) : MAC ? spawnSync("pbcopy", { input: text2 }) : spawnSync("xclip", ["-selection", "clipboard"], { input: text2 });
11588
+ const r = WIN2 ? spawnSync2("clip", { input: text2, shell: true }) : MAC2 ? spawnSync2("pbcopy", { input: text2 }) : spawnSync2("xclip", ["-selection", "clipboard"], { input: text2 });
11438
11589
  return r.status === 0;
11439
11590
  }
11440
11591
  function openExtensionsPage() {
11441
11592
  const url = "chrome://extensions";
11442
- const r = WIN ? spawnSync("cmd", ["/c", "start", "", "chrome", url], { stdio: "ignore" }) : MAC ? spawnSync("open", ["-a", "Google Chrome", url], { stdio: "ignore" }) : spawnSync("google-chrome", [url], { stdio: "ignore" });
11593
+ const r = WIN2 ? spawnSync2("cmd", ["/c", "start", "", "chrome", url], { stdio: "ignore" }) : MAC2 ? spawnSync2("open", ["-a", "Google Chrome", url], { stdio: "ignore" }) : spawnSync2("google-chrome", [url], { stdio: "ignore" });
11443
11594
  return r.status === 0;
11444
11595
  }
11445
11596
  function launchChrome() {
11446
- const cands = WIN ? [
11447
- join2(process.env.PROGRAMFILES ?? "", "Google", "Chrome", "Application", "chrome.exe"),
11448
- join2(process.env["PROGRAMFILES(X86)"] ?? "", "Google", "Chrome", "Application", "chrome.exe"),
11449
- join2(process.env.LOCALAPPDATA ?? "", "Google", "Chrome", "Application", "chrome.exe")
11450
- ] : MAC ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : ["/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium"];
11451
- const exe = cands.find(existsSync2);
11452
- if (!exe) throw new Error("Chrome was not found. Start it yourself with the flag " + DEBUG_FLAG);
11453
- spawn(exe, [DEBUG_FLAG], { detached: true, stdio: "ignore" }).unref();
11597
+ startChrome();
11454
11598
  console.log(`Started Chrome with ${DEBUG_FLAG}. If Chrome was already running, quit it fully first and run this again.`);
11455
11599
  }
11600
+ function hideBanner(log) {
11601
+ const c = patchShortcuts();
11602
+ if (!c) {
11603
+ log(` macOS has no shortcut to edit. Start Chrome with: chrome-control-mcp chrome`);
11604
+ return c;
11605
+ }
11606
+ log(` Shortcuts patched: ${c.patched}, already set: ${c.already}, could not edit: ${c.failed}`);
11607
+ if (c.failed) log(" (Shortcuts in shared folders may need an administrator. Add the flag to them by hand.)");
11608
+ if (!c.total) log(" No Chrome shortcuts found. Start Chrome with: chrome-control-mcp chrome");
11609
+ if (c.patched || c.already) log(" The flag only counts when Chrome starts: if Chrome is open now, close it completely and open it from a shortcut (or run: chrome-control-mcp chrome-restart).");
11610
+ return c;
11611
+ }
11456
11612
  function runSetup(args) {
11457
11613
  const o = parseArgs(args);
11458
11614
  const log = (s) => console.log(s);
@@ -11461,13 +11617,13 @@ function runSetup(args) {
11461
11617
  log("1. Token ready.");
11462
11618
  if (process.env.CHROME_BRIDGE_PORT) {
11463
11619
  mkdirSync2(BASE, { recursive: true });
11464
- writeFileSync2(join2(BASE, "config.json"), JSON.stringify({ port: Number(process.env.CHROME_BRIDGE_PORT) }));
11620
+ writeFileSync3(join3(BASE, "config.json"), JSON.stringify({ port: Number(process.env.CHROME_BRIDGE_PORT) }));
11465
11621
  }
11466
11622
  const h = installHost(o.extensionIds);
11467
11623
  log(`2. Native host installed in ${HOST_DIR}`);
11468
11624
  log(` Registered for: ${h.registered.length ? h.registered.join(", ") : "no browser found"}`);
11469
11625
  mkdirSync2(BASE, { recursive: true });
11470
- cpSync(join2(PKG_ROOT, "extension"), EXT_DIR, { recursive: true, force: true });
11626
+ cpSync(join3(PKG_ROOT, "extension"), EXT_DIR, { recursive: true, force: true });
11471
11627
  log(`3. Extension copied to ${EXT_DIR}`);
11472
11628
  if (o.claudeDesktop || o.claudeCode) log("4. Claude config:");
11473
11629
  if (o.claudeDesktop) configureClaudeDesktop(o.force, log);
@@ -11480,6 +11636,11 @@ function runSetup(args) {
11480
11636
  if (openExtensionsPage()) log(" (Opened chrome://extensions for you.)");
11481
11637
  }
11482
11638
  log(" The extension gets its token by itself and turns ON. You do not paste anything.");
11639
+ if (o.flag) {
11640
+ log(`
11641
+ 5. Hiding Chrome's "started debugging this browser" banner:`);
11642
+ hideBanner(log);
11643
+ }
11483
11644
  if (!o.claudeDesktop && !o.claudeCode) {
11484
11645
  log("\nClaude was skipped (--no-claude). Run setup again without that flag to add the MCP to Claude.");
11485
11646
  }
@@ -11487,34 +11648,34 @@ function runSetup(args) {
11487
11648
  function runUninstall() {
11488
11649
  const log = (s) => console.log(s);
11489
11650
  for (const b of BROWSERS) {
11490
- if (WIN) spawnSync("reg", ["delete", `HKCU\\${b.win}\\NativeMessagingHosts\\${HOST_NAME}`, "/f"], { encoding: "utf8" });
11491
- else rmSync(join2(manifestDir(b), `${HOST_NAME}.json`), { force: true });
11651
+ if (WIN2) spawnSync2("reg", ["delete", `HKCU\\${b.win}\\NativeMessagingHosts\\${HOST_NAME}`, "/f"], { encoding: "utf8" });
11652
+ else rmSync2(join3(manifestDir(b), `${HOST_NAME}.json`), { force: true });
11492
11653
  }
11493
- rmSync(HOST_DIR, { recursive: true, force: true });
11494
- rmSync(EXT_DIR, { recursive: true, force: true });
11654
+ rmSync2(HOST_DIR, { recursive: true, force: true });
11655
+ rmSync2(EXT_DIR, { recursive: true, force: true });
11495
11656
  log("Removed the native host registration, the host files and the extension copy.");
11496
11657
  log("Your token file was kept. Remove the extension in chrome://extensions yourself.");
11497
11658
  }
11498
- var HOST_NAME, DEV_EXTENSION_ID, PKG_ROOT, BASE, HOST_DIR, EXT_DIR, WIN, MAC, BROWSERS, DEBUG_FLAG;
11659
+ var HOST_NAME, DEV_EXTENSION_ID, PKG_ROOT, BASE, HOST_DIR, EXT_DIR, WIN2, MAC2, BROWSERS;
11499
11660
  var init_setup = __esm({
11500
11661
  "../chrome-control/src/setup.ts"() {
11501
11662
  "use strict";
11502
11663
  init_bridge();
11664
+ init_browser();
11503
11665
  HOST_NAME = "com.chromecontrol.mcp";
11504
11666
  DEV_EXTENSION_ID = "ommdbcleeojmjlkimeficggcjgbapmhk";
11505
11667
  PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
11506
- BASE = join2(homedir2(), ".chrome-control-mcp");
11507
- HOST_DIR = join2(BASE, "native-host");
11508
- EXT_DIR = join2(BASE, "extension");
11509
- WIN = process.platform === "win32";
11510
- MAC = process.platform === "darwin";
11668
+ BASE = join3(homedir3(), ".chrome-control-mcp");
11669
+ HOST_DIR = join3(BASE, "native-host");
11670
+ EXT_DIR = join3(BASE, "extension");
11671
+ WIN2 = process.platform === "win32";
11672
+ MAC2 = process.platform === "darwin";
11511
11673
  BROWSERS = [
11512
11674
  { name: "Chrome", win: "Software\\Google\\Chrome", mac: "Google/Chrome", linux: "google-chrome" },
11513
11675
  { name: "Edge", win: "Software\\Microsoft\\Edge", mac: "Microsoft Edge", linux: "microsoft-edge" },
11514
11676
  { name: "Brave", win: "Software\\BraveSoftware\\Brave-Browser", mac: "BraveSoftware/Brave-Browser", linux: "BraveSoftware/Brave-Browser" },
11515
11677
  { name: "Chromium", win: "Software\\Chromium", mac: "Chromium", linux: "chromium" }
11516
11678
  ];
11517
- DEBUG_FLAG = "--silent-debugger-extension-api";
11518
11679
  }
11519
11680
  });
11520
11681
 
@@ -11523,14 +11684,14 @@ var daemon_exports = {};
11523
11684
  __export(daemon_exports, {
11524
11685
  runDaemon: () => runDaemon
11525
11686
  });
11526
- import { spawn as spawn2 } from "node:child_process";
11527
- import { closeSync, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync3, readSync, renameSync, statSync, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
11528
- import { homedir as homedir3 } from "node:os";
11529
- import { join as join3 } from "node:path";
11687
+ import { spawn as spawn3 } from "node:child_process";
11688
+ import { closeSync, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync4, readSync, renameSync, statSync, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
11689
+ import { homedir as homedir4 } from "node:os";
11690
+ import { join as join4 } from "node:path";
11530
11691
  import { fileURLToPath as fileURLToPath2 } from "node:url";
11531
11692
  function readPid() {
11532
11693
  try {
11533
- const n = Number(readFileSync3(PID_FILE, "utf8").trim());
11694
+ const n = Number(readFileSync4(PID_FILE, "utf8").trim());
11534
11695
  return n > 0 ? n : null;
11535
11696
  } catch {
11536
11697
  return null;
@@ -11560,7 +11721,7 @@ async function health() {
11560
11721
  }
11561
11722
  function tail(n) {
11562
11723
  try {
11563
- return readFileSync3(LOG_FILE, "utf8").split(/\r?\n/).filter(Boolean).slice(-n).join("\n");
11724
+ return readFileSync4(LOG_FILE, "utf8").split(/\r?\n/).filter(Boolean).slice(-n).join("\n");
11564
11725
  } catch {
11565
11726
  return "";
11566
11727
  }
@@ -11579,14 +11740,14 @@ async function start() {
11579
11740
  }
11580
11741
  const fd = openSync(LOG_FILE, "a", 384);
11581
11742
  const indexJs = fileURLToPath2(new URL("./index.js", import.meta.url));
11582
- const child = spawn2(process.execPath, [indexJs, "serve"], { detached: true, stdio: ["ignore", fd, fd], windowsHide: true, env: { ...process.env, CHROME_CONTROL_DAEMON: "1" } });
11743
+ const child = spawn3(process.execPath, [indexJs, "serve"], { detached: true, stdio: ["ignore", fd, fd], windowsHide: true, env: { ...process.env, CHROME_CONTROL_DAEMON: "1" } });
11583
11744
  closeSync(fd);
11584
11745
  child.unref();
11585
11746
  if (!child.pid) {
11586
11747
  console.error("Could not start the server.");
11587
11748
  return 1;
11588
11749
  }
11589
- writeFileSync3(PID_FILE, String(child.pid));
11750
+ writeFileSync4(PID_FILE, String(child.pid));
11590
11751
  for (let i = 0; i < 40; i++) {
11591
11752
  await sleep2(250);
11592
11753
  if (await health()) {
@@ -11654,7 +11815,7 @@ async function status() {
11654
11815
  async function logs(args) {
11655
11816
  const ni = args.findIndex((a) => a === "-n" || a === "--lines");
11656
11817
  const n = ni >= 0 ? Number(args[ni + 1]) || 40 : 40;
11657
- if (!existsSync3(LOG_FILE)) {
11818
+ if (!existsSync4(LOG_FILE)) {
11658
11819
  console.log("No log yet. Start the server first.");
11659
11820
  return 0;
11660
11821
  }
@@ -11705,9 +11866,9 @@ var DIR, PID_FILE, LOG_FILE, HTTP_PORT, sleep2;
11705
11866
  var init_daemon = __esm({
11706
11867
  "../chrome-control/src/daemon.ts"() {
11707
11868
  "use strict";
11708
- DIR = join3(homedir3(), ".chrome-control-mcp");
11709
- PID_FILE = join3(DIR, "server.pid");
11710
- LOG_FILE = join3(DIR, "server.log");
11869
+ DIR = join4(homedir4(), ".chrome-control-mcp");
11870
+ PID_FILE = join4(DIR, "server.pid");
11871
+ LOG_FILE = join4(DIR, "server.log");
11711
11872
  HTTP_PORT = Number(process.env.CHROME_BRIDGE_HTTP_PORT ?? 8766);
11712
11873
  sleep2 = (ms2) => new Promise((r) => setTimeout(r, ms2));
11713
11874
  }
@@ -28136,9 +28297,9 @@ var StreamableHTTPServerTransport = class {
28136
28297
  import { createServer as createServer2 } from "node:http";
28137
28298
  import { randomUUID } from "node:crypto";
28138
28299
  init_bridge();
28139
- import { createWriteStream, existsSync as existsSync4, readFileSync as readFileSync4, mkdirSync as mkdirSync4, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
28140
- import { homedir as homedir4 } from "node:os";
28141
- import { dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
28300
+ import { createWriteStream, existsSync as existsSync5, readFileSync as readFileSync5, mkdirSync as mkdirSync4, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
28301
+ import { homedir as homedir5 } from "node:os";
28302
+ import { dirname as dirname2, join as join5, resolve as resolve2 } from "node:path";
28142
28303
  import { gzipSync } from "node:zlib";
28143
28304
  import { fileURLToPath as fileURLToPath3 } from "node:url";
28144
28305
 
@@ -28738,9 +28899,9 @@ async function uploadFiles(tabId, uid, files) {
28738
28899
  }
28739
28900
  await cdp(tabId, "Page.setInterceptFileChooserDialog", { enabled: true });
28740
28901
  try {
28741
- const wait = waitForEvent(tabId, "Page.fileChooserOpened", 5e3);
28902
+ const wait2 = waitForEvent(tabId, "Page.fileChooserOpened", 5e3);
28742
28903
  await clickUid(tabId, uid);
28743
- const ev = await wait;
28904
+ const ev = await wait2;
28744
28905
  if (!ev) throw new Error("No file chooser opened after clicking the element");
28745
28906
  await cdp(tabId, "DOM.setFileInputFiles", { files, backendNodeId: ev.backendNodeId });
28746
28907
  } finally {
@@ -28829,6 +28990,9 @@ Usage: chrome-control-mcp <command>
28829
28990
  token Print the secret token
28830
28991
  extension Print the folder to load in chrome://extensions
28831
28992
  chrome Start Chrome with the debugger banner hidden (--silent-debugger-extension-api)
28993
+ flag Add that flag to your Chrome, Edge and Brave shortcuts (prints one line of JSON)
28994
+ flag-status Say whether the running Chrome has the flag, and how many shortcuts do (JSON)
28995
+ chrome-restart Close Chrome politely and start it again with the flag and your tabs (JSON)
28832
28996
  uninstall Remove the native host and the extension copy
28833
28997
  version Print the version
28834
28998
  help Show this help
@@ -28839,7 +29003,7 @@ if (cmd === "help" || cmd === "--help" || cmd === "-h" || cmd === void 0 && proc
28839
29003
  process.exit(0);
28840
29004
  }
28841
29005
  if (cmd === "version" || cmd === "--version" || cmd === "-v") {
28842
- console.log(JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8")).version);
29006
+ console.log(JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8")).version);
28843
29007
  process.exit(0);
28844
29008
  }
28845
29009
  if (cmd === "token") {
@@ -28847,14 +29011,29 @@ if (cmd === "token") {
28847
29011
  process.exit(0);
28848
29012
  }
28849
29013
  if (cmd === "extension") {
28850
- const stable = join4(homedir4(), ".chrome-control-mcp", "extension");
28851
- console.log(existsSync4(stable) ? stable : fileURLToPath3(new URL("../extension", import.meta.url)));
29014
+ const stable = join5(homedir5(), ".chrome-control-mcp", "extension");
29015
+ console.log(existsSync5(stable) ? stable : fileURLToPath3(new URL("../extension", import.meta.url)));
28852
29016
  process.exit(0);
28853
29017
  }
28854
29018
  if (cmd === "url") {
28855
29019
  console.log(`http://127.0.0.1:${process.env.CHROME_BRIDGE_HTTP_PORT ?? 8766}/mcp?token=${loadToken()}`);
28856
29020
  process.exit(0);
28857
29021
  }
29022
+ if (cmd === "flag" || cmd === "flag-status" || cmd === "chrome-restart") {
29023
+ try {
29024
+ const b = await Promise.resolve().then(() => (init_browser(), browser_exports));
29025
+ if (cmd === "flag-status") console.log(JSON.stringify(b.flagStatus()));
29026
+ else if (cmd === "flag") {
29027
+ const c = (await Promise.resolve().then(() => (init_setup(), setup_exports))).hideBanner(() => {
29028
+ });
29029
+ console.log(JSON.stringify({ ok: c !== null && (c.patched > 0 || c.already > 0), counts: c }));
29030
+ } else console.log(JSON.stringify(await b.restartChrome()));
29031
+ process.exit(0);
29032
+ } catch (e) {
29033
+ console.log(JSON.stringify({ ok: false, message: e.message }));
29034
+ process.exit(1);
29035
+ }
29036
+ }
28858
29037
  if (cmd === "chrome") {
28859
29038
  try {
28860
29039
  (await Promise.resolve().then(() => (init_setup(), setup_exports))).launchChrome();
@@ -28900,7 +29079,7 @@ var abs = (p) => resolve2(process.cwd(), p);
28900
29079
  function saveFile(p, data) {
28901
29080
  const f = abs(p);
28902
29081
  mkdirSync4(dirname2(f), { recursive: true });
28903
- writeFileSync4(f, data);
29082
+ writeFileSync5(f, data);
28904
29083
  return f;
28905
29084
  }
28906
29085
  var selected = null;