@btraut/browser-bridge 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -663,9 +663,10 @@ var createCoreReadinessController = (options = {}) => {
663
663
  return;
664
664
  }
665
665
  if (!ensurePromise) {
666
- ensurePromise = ensureCoreRunning().catch((error) => {
666
+ ensurePromise = (async () => {
667
+ await ensureCoreRunning();
668
+ })().finally(() => {
667
669
  ensurePromise = null;
668
- throw error;
669
670
  });
670
671
  }
671
672
  await ensurePromise;
@@ -938,6 +939,63 @@ var SessionCloseInputSchema = SessionIdSchema;
938
939
  var SessionCloseOutputSchema = import_zod3.z.object({
939
940
  ok: import_zod3.z.boolean()
940
941
  });
942
+ var PermissionsModeSchema = import_zod3.z.enum(["granular", "bypass"]);
943
+ var PermissionsRequestSourceSchema = import_zod3.z.enum(["cli", "mcp", "api"]);
944
+ var PermissionsPendingRequestKindSchema = import_zod3.z.enum([
945
+ "allow_site",
946
+ "revoke_site",
947
+ "set_mode"
948
+ ]);
949
+ var PermissionsPendingRequestStatusSchema = import_zod3.z.enum([
950
+ "pending",
951
+ "approved",
952
+ "denied",
953
+ "timed_out"
954
+ ]);
955
+ var PermissionsSiteEntrySchema = import_zod3.z.object({
956
+ site: import_zod3.z.string().min(1),
957
+ created_at: import_zod3.z.string().datetime(),
958
+ last_used_at: import_zod3.z.string().datetime()
959
+ });
960
+ var PermissionsListInputSchema = import_zod3.z.object({}).strict().default({});
961
+ var PermissionsListOutputSchema = import_zod3.z.object({
962
+ sites: import_zod3.z.array(PermissionsSiteEntrySchema)
963
+ });
964
+ var PermissionsGetModeInputSchema = import_zod3.z.object({}).strict().default({});
965
+ var PermissionsGetModeOutputSchema = import_zod3.z.object({
966
+ mode: PermissionsModeSchema
967
+ });
968
+ var PermissionsPendingRequestSchema = import_zod3.z.object({
969
+ request_id: import_zod3.z.string().min(1),
970
+ kind: PermissionsPendingRequestKindSchema,
971
+ status: PermissionsPendingRequestStatusSchema,
972
+ requested_at: import_zod3.z.string().datetime(),
973
+ site: import_zod3.z.string().min(1).optional(),
974
+ mode: PermissionsModeSchema.optional(),
975
+ source: PermissionsRequestSourceSchema.optional(),
976
+ warning: import_zod3.z.string().optional(),
977
+ message: import_zod3.z.string().optional()
978
+ });
979
+ var PermissionsListPendingRequestsInputSchema = import_zod3.z.object({}).strict().default({});
980
+ var PermissionsListPendingRequestsOutputSchema = import_zod3.z.object({
981
+ requests: import_zod3.z.array(PermissionsPendingRequestSchema)
982
+ });
983
+ var PermissionsRequestAllowSiteInputSchema = import_zod3.z.object({
984
+ site: import_zod3.z.string().min(1),
985
+ timeout_ms: import_zod3.z.number().int().positive().max(3e5).optional(),
986
+ source: PermissionsRequestSourceSchema.optional()
987
+ });
988
+ var PermissionsRequestRevokeSiteInputSchema = import_zod3.z.object({
989
+ site: import_zod3.z.string().min(1),
990
+ timeout_ms: import_zod3.z.number().int().positive().max(3e5).optional(),
991
+ source: PermissionsRequestSourceSchema.optional()
992
+ });
993
+ var PermissionsRequestSetModeInputSchema = import_zod3.z.object({
994
+ mode: PermissionsModeSchema,
995
+ timeout_ms: import_zod3.z.number().int().positive().max(3e5).optional(),
996
+ source: PermissionsRequestSourceSchema.optional()
997
+ });
998
+ var PermissionsRequestOutputSchema = PermissionsPendingRequestSchema;
941
999
  var DriveWaitConditionSchema = import_zod3.z.object({
942
1000
  kind: import_zod3.z.enum(["locator_visible", "text_present", "url_matches"]),
943
1001
  value: import_zod3.z.string().min(1)
@@ -1124,6 +1182,12 @@ var FormFieldInfoSchema = import_zod3.z.object({
1124
1182
  name: import_zod3.z.string(),
1125
1183
  type: import_zod3.z.string(),
1126
1184
  value: import_zod3.z.string(),
1185
+ selector: import_zod3.z.string().optional(),
1186
+ label: import_zod3.z.string().optional(),
1187
+ placeholder: import_zod3.z.string().optional(),
1188
+ checked: import_zod3.z.boolean().optional(),
1189
+ disabled: import_zod3.z.boolean().optional(),
1190
+ visible: import_zod3.z.boolean().optional(),
1127
1191
  options: import_zod3.z.array(import_zod3.z.string()).optional()
1128
1192
  });
1129
1193
  var FormInfoSchema = import_zod3.z.object({
@@ -1136,7 +1200,31 @@ var StorageEntrySchema = import_zod3.z.object({
1136
1200
  key: import_zod3.z.string(),
1137
1201
  value: import_zod3.z.string()
1138
1202
  });
1203
+ var FocusedElementSchema = import_zod3.z.object({
1204
+ selector: import_zod3.z.string().optional(),
1205
+ name: import_zod3.z.string().optional(),
1206
+ label: import_zod3.z.string().optional(),
1207
+ role: import_zod3.z.string().optional(),
1208
+ type: import_zod3.z.string().optional(),
1209
+ text: import_zod3.z.string().optional()
1210
+ });
1211
+ var PageActionSchema = import_zod3.z.object({
1212
+ selector: import_zod3.z.string(),
1213
+ role: import_zod3.z.string(),
1214
+ name: import_zod3.z.string()
1215
+ });
1216
+ var StorageSummarySchema = import_zod3.z.object({
1217
+ localStorageCount: import_zod3.z.number().int().nonnegative(),
1218
+ sessionStorageCount: import_zod3.z.number().int().nonnegative(),
1219
+ cookieCount: import_zod3.z.number().int().nonnegative()
1220
+ });
1139
1221
  var PageStateSchema = import_zod3.z.object({
1222
+ url: import_zod3.z.string().optional(),
1223
+ title: import_zod3.z.string().optional(),
1224
+ readyState: import_zod3.z.string().optional(),
1225
+ focused: FocusedElementSchema.optional(),
1226
+ primaryActions: import_zod3.z.array(PageActionSchema).optional(),
1227
+ storageSummary: StorageSummarySchema.optional(),
1140
1228
  forms: import_zod3.z.array(FormInfoSchema),
1141
1229
  localStorage: import_zod3.z.array(StorageEntrySchema),
1142
1230
  sessionStorage: import_zod3.z.array(StorageEntrySchema),
@@ -1199,6 +1287,7 @@ var InspectFindOutputSchema = import_zod3.z.object({
1199
1287
  warnings: import_zod3.z.array(import_zod3.z.string()).optional()
1200
1288
  });
1201
1289
  var InspectPageStateInputSchema = SessionIdSchema.extend({
1290
+ include_values: import_zod3.z.boolean().default(false),
1202
1291
  target: TargetHintSchema.optional()
1203
1292
  });
1204
1293
  var InspectPageStateOutputSchema = PageStateSchema;
@@ -1209,6 +1298,7 @@ var InspectExtractContentFormatSchema = import_zod3.z.enum([
1209
1298
  ]);
1210
1299
  var InspectExtractContentInputSchema = SessionIdSchema.extend({
1211
1300
  format: InspectExtractContentFormatSchema.default("markdown"),
1301
+ consistency: InspectConsistencySchema.default("quiesce"),
1212
1302
  include_metadata: import_zod3.z.boolean().default(true),
1213
1303
  target: TargetHintSchema.optional()
1214
1304
  });
@@ -1221,6 +1311,7 @@ var InspectExtractContentOutputSchema = import_zod3.z.object({
1221
1311
  warnings: import_zod3.z.array(import_zod3.z.string()).optional()
1222
1312
  });
1223
1313
  var InspectConsoleListInputSchema = SessionIdSchema.extend({
1314
+ since: import_zod3.z.string().optional(),
1224
1315
  target: TargetHintSchema.optional()
1225
1316
  });
1226
1317
  var ConsoleSourceLocationSchema = import_zod3.z.object({
@@ -1326,6 +1417,7 @@ var import_zod4 = require("zod");
1326
1417
 
1327
1418
  // packages/cli/src/core-client.ts
1328
1419
  var import_node_child_process = require("node:child_process");
1420
+ var import_node_fs3 = require("node:fs");
1329
1421
  var import_node_path3 = require("node:path");
1330
1422
  var CoreClientError = class extends Error {
1331
1423
  constructor(info) {
@@ -1349,6 +1441,7 @@ var resolveTimeoutMs2 = (timeoutMs) => {
1349
1441
  var normalizePath = (path9) => path9.startsWith("/") ? path9 : `/${path9}`;
1350
1442
  var durationMs = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
1351
1443
  var MAX_RESPONSE_PREVIEW_LENGTH = 200;
1444
+ var STALE_DAEMON_GRACE_MS = 1e3;
1352
1445
  var normalizeResponsePreview = (raw) => {
1353
1446
  const normalized = raw.replace(/\s+/g, " ").trim();
1354
1447
  if (normalized.length <= MAX_RESPONSE_PREVIEW_LENGTH) {
@@ -1400,6 +1493,15 @@ var createCoreClient = (options = {}) => {
1400
1493
  const spawnImpl = options.spawnImpl ?? import_node_child_process.spawn;
1401
1494
  const timeoutMs = resolveTimeoutMs2(options.timeoutMs);
1402
1495
  const componentVersion = process.env.BROWSER_BRIDGE_VERSION ?? process.env.npm_package_version;
1496
+ const coreEntry = (0, import_node_path3.resolve)(__dirname, "api.js");
1497
+ const currentBuildTimeMs = options.currentBuildTimeMs ?? (() => {
1498
+ try {
1499
+ return (0, import_node_fs3.statSync)(coreEntry).mtimeMs;
1500
+ } catch {
1501
+ return void 0;
1502
+ }
1503
+ })();
1504
+ const killProcess = options.killProcess ?? ((pid) => process.kill(pid));
1403
1505
  const readiness = createCoreReadinessController({
1404
1506
  host: options.host,
1405
1507
  port: options.port,
@@ -1411,7 +1513,7 @@ var createCoreClient = (options = {}) => {
1411
1513
  logger,
1412
1514
  logPrefix: "cli.core",
1413
1515
  spawnDaemon: (runtime) => {
1414
- const coreEntry = (0, import_node_path3.resolve)(__dirname, "api.js");
1516
+ const coreEntry2 = (0, import_node_path3.resolve)(__dirname, "api.js");
1415
1517
  const startOptions = [];
1416
1518
  if (runtime.hostSource === "option" || runtime.hostSource === "env") {
1417
1519
  startOptions.push(`host: ${JSON.stringify(runtime.host)}`);
@@ -1420,7 +1522,7 @@ var createCoreClient = (options = {}) => {
1420
1522
  startOptions.push(`port: ${runtime.port}`);
1421
1523
  }
1422
1524
  const script = `const { startCoreServer } = require(${JSON.stringify(
1423
- coreEntry
1525
+ coreEntry2
1424
1526
  )});
1425
1527
  startCoreServer({ ${startOptions.join(
1426
1528
  ", "
@@ -1557,8 +1659,60 @@ startCoreServer({ ${startOptions.join(
1557
1659
  clearTimeout(timeout);
1558
1660
  }
1559
1661
  };
1560
- const post = async (path9, body) => {
1662
+ const maybeRestartStaleDaemon = async () => {
1663
+ if (currentBuildTimeMs === void 0) {
1664
+ return;
1665
+ }
1666
+ let payload;
1667
+ try {
1668
+ payload = await requestJson(
1669
+ "POST",
1670
+ "/health/check",
1671
+ {}
1672
+ );
1673
+ } catch {
1674
+ return;
1675
+ }
1676
+ if (!payload.ok || !payload.result?.started_at) {
1677
+ return;
1678
+ }
1679
+ const startedAtMs = Date.parse(payload.result.started_at);
1680
+ if (!Number.isFinite(startedAtMs)) {
1681
+ return;
1682
+ }
1683
+ if (startedAtMs + STALE_DAEMON_GRACE_MS >= currentBuildTimeMs) {
1684
+ return;
1685
+ }
1686
+ const pidValue = payload.result.pid;
1687
+ if (!Number.isInteger(pidValue) || pidValue === process.pid) {
1688
+ return;
1689
+ }
1690
+ const pid = pidValue;
1691
+ logger.warn("cli.core.ensure_ready.stale_daemon", {
1692
+ base_url: readiness.baseUrl,
1693
+ pid,
1694
+ started_at: payload.result.started_at,
1695
+ current_build_time_ms: currentBuildTimeMs
1696
+ });
1697
+ try {
1698
+ killProcess(pid);
1699
+ } catch (error) {
1700
+ logger.warn("cli.core.ensure_ready.stale_daemon_kill_failed", {
1701
+ base_url: readiness.baseUrl,
1702
+ pid,
1703
+ error
1704
+ });
1705
+ return;
1706
+ }
1707
+ await new Promise((resolve4) => setTimeout(resolve4, 150));
1561
1708
  await readiness.ensureReady();
1709
+ };
1710
+ const ensureReady = async () => {
1711
+ await readiness.ensureReady();
1712
+ await maybeRestartStaleDaemon();
1713
+ };
1714
+ const post = async (path9, body) => {
1715
+ await ensureReady();
1562
1716
  readiness.refreshRuntime();
1563
1717
  const payload = path9 === "/diagnostics/doctor" && (!body || typeof body === "object" && !Array.isArray(body)) ? {
1564
1718
  ...body && typeof body === "object" ? body : {},
@@ -1586,7 +1740,7 @@ startCoreServer({ ${startOptions.join(
1586
1740
  get baseUrl() {
1587
1741
  return readiness.baseUrl;
1588
1742
  },
1589
- ensureReady: readiness.ensureReady,
1743
+ ensureReady,
1590
1744
  post
1591
1745
  };
1592
1746
  };
@@ -1865,6 +2019,7 @@ var registerDiagnosticsCommands = (program2) => {
1865
2019
 
1866
2020
  // packages/cli/src/commands/dev.ts
1867
2021
  var ENV_EXTENSION_ID = "BROWSER_BRIDGE_EXTENSION_ID";
2022
+ var INSPECT_PROBE_RETRY_DELAYS_MS = [150, 300, 600];
1868
2023
  var normalizeToken = (value) => {
1869
2024
  if (typeof value !== "string") {
1870
2025
  return void 0;
@@ -1898,6 +2053,21 @@ var inspectCapabilityReady = (report, expectedExtensionId) => {
1898
2053
  const extensionIdMatch = !expectedExtensionId || !reportedExtensionId || reportedExtensionId === expectedExtensionId;
1899
2054
  return extensionIdMatch && hasPassingCheck(report, "inspect.capability");
1900
2055
  };
2056
+ var sleep = async (ms) => {
2057
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
2058
+ };
2059
+ var shouldRetryInspectProbe = (report, expectedExtensionId) => {
2060
+ if (inspectCapabilityReady(report, expectedExtensionId)) {
2061
+ return false;
2062
+ }
2063
+ const observedExtensionId = getReportedExtensionId(report);
2064
+ if (expectedExtensionId && observedExtensionId && observedExtensionId !== expectedExtensionId) {
2065
+ return false;
2066
+ }
2067
+ const extensionConnected = report?.extension?.connected ?? false;
2068
+ const capabilityNegotiated = report?.runtime?.extension?.capability_negotiated === true || hasPassingCheck(report, "runtime.extension.capability_negotiated");
2069
+ return !extensionConnected || !capabilityNegotiated;
2070
+ };
1901
2071
  var readDiagnosticReport = async (runtime) => {
1902
2072
  const client = createCoreClient({
1903
2073
  host: runtime.host,
@@ -1910,6 +2080,22 @@ var readDiagnosticReport = async (runtime) => {
1910
2080
  );
1911
2081
  return envelope2.ok ? envelope2.result : void 0;
1912
2082
  };
2083
+ var readDiagnosticReportWithRetry = async (runtime, expectedExtensionId) => {
2084
+ let report;
2085
+ for (let attempt = 0; attempt <= INSPECT_PROBE_RETRY_DELAYS_MS.length; attempt += 1) {
2086
+ if (attempt > 0) {
2087
+ const delayMs = INSPECT_PROBE_RETRY_DELAYS_MS[attempt - 1] ?? 0;
2088
+ if (delayMs > 0) {
2089
+ await sleep(delayMs);
2090
+ }
2091
+ }
2092
+ report = await readDiagnosticReport(runtime);
2093
+ if (!shouldRetryInspectProbe(report, expectedExtensionId)) {
2094
+ return report;
2095
+ }
2096
+ }
2097
+ return report;
2098
+ };
1913
2099
  var buildInspectCapabilityError = (runtime, report, expectedExtension) => {
1914
2100
  const observedExtensionId = getReportedExtensionId(report);
1915
2101
  if (expectedExtension && observedExtensionId && observedExtensionId !== expectedExtension.extensionId) {
@@ -1973,7 +2159,10 @@ var registerDevCommands = (program2) => {
1973
2159
  optionExtensionId: options.extensionId,
1974
2160
  envExtensionId: process.env[ENV_EXTENSION_ID]
1975
2161
  });
1976
- const report = await readDiagnosticReport(runtime);
2162
+ const report = await readDiagnosticReportWithRetry(
2163
+ runtime,
2164
+ resolvedExtension?.extensionId
2165
+ );
1977
2166
  if (!resolvedExtension) {
1978
2167
  const reportedExtensionId = getReportedExtensionId(report);
1979
2168
  if (reportedExtensionId) {
@@ -2440,30 +2629,36 @@ var registerInspectCommands = (program2) => {
2440
2629
  return client.post("/inspect/find", parsed);
2441
2630
  });
2442
2631
  });
2443
- inspect.command("extract-content").description("Extract main content from the page").requiredOption("--session-id <id>", "Session identifier").option("--format <format>", "Output format (markdown, text, article_json)").option("--include-metadata", "Include article metadata").option("--no-include-metadata", "Exclude article metadata").option("--tab-id <id>", "Explicit tab identifier").action(async (options, command) => {
2632
+ inspect.command("extract-content").description("Extract main content from the page").requiredOption("--session-id <id>", "Session identifier").option("--format <format>", "Output format (markdown, text, article_json)").option(
2633
+ "--consistency <mode>",
2634
+ "Capture consistency (best_effort, quiesce)"
2635
+ ).option("--include-metadata", "Include article metadata").option("--no-include-metadata", "Exclude article metadata").option("--tab-id <id>", "Explicit tab identifier").action(async (options, command) => {
2444
2636
  await runCommand(command, (client) => {
2445
2637
  const payload = parseInput(InspectExtractContentInputSchema, {
2446
2638
  session_id: options.sessionId,
2447
2639
  format: options.format,
2640
+ consistency: options.consistency,
2448
2641
  include_metadata: options.includeMetadata,
2449
2642
  target: buildTargetHint(options)
2450
2643
  });
2451
2644
  return client.post("/inspect/extract_content", payload);
2452
2645
  });
2453
2646
  });
2454
- inspect.command("page-state").description("Capture form, storage, and cookie state").requiredOption("--session-id <id>", "Session identifier").option("--tab-id <id>", "Explicit tab identifier").action(async (options, command) => {
2647
+ inspect.command("page-state").description("Capture form, storage, and cookie state").requiredOption("--session-id <id>", "Session identifier").option("--include-values", "Include captured values instead of redacting").option("--tab-id <id>", "Explicit tab identifier").action(async (options, command) => {
2455
2648
  await runCommand(command, (client) => {
2456
2649
  const payload = parseInput(InspectPageStateInputSchema, {
2457
2650
  session_id: options.sessionId,
2651
+ include_values: options.includeValues,
2458
2652
  target: buildTargetHint(options)
2459
2653
  });
2460
2654
  return client.post("/inspect/page_state", payload);
2461
2655
  });
2462
2656
  });
2463
- inspect.command("console-list").description("Fetch console entries").requiredOption("--session-id <id>", "Session identifier").option("--tab-id <id>", "Explicit tab identifier").action(async (options, command) => {
2657
+ inspect.command("console-list").description("Fetch console entries").requiredOption("--session-id <id>", "Session identifier").option("--since <iso>", "Only include entries at or after this timestamp").option("--tab-id <id>", "Explicit tab identifier").action(async (options, command) => {
2464
2658
  await runCommand(command, (client) => {
2465
2659
  const payload = parseInput(InspectConsoleListInputSchema, {
2466
2660
  session_id: options.sessionId,
2661
+ since: options.since,
2467
2662
  target: buildTargetHint(options)
2468
2663
  });
2469
2664
  return client.post("/inspect/console_list", payload);
@@ -2714,6 +2909,10 @@ var readSessionId = (args) => {
2714
2909
  const sessionId = args.session_id;
2715
2910
  return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
2716
2911
  };
2912
+ var withPermissionsSource = (source) => (args) => isRecord(args) ? {
2913
+ ...args,
2914
+ source
2915
+ } : args;
2717
2916
  var supportsSessionMigration = (corePath) => corePath.startsWith("/drive/") || corePath.startsWith("/inspect/") || corePath.startsWith("/artifacts/") || corePath === "/diagnostics/doctor";
2718
2917
  var isSessionNotFoundEnvelope = (envelopeResult) => {
2719
2918
  if (envelopeResult.ok) {
@@ -2827,6 +3026,69 @@ var TOOL_DEFINITIONS = [
2827
3026
  corePath: "/session/close"
2828
3027
  }
2829
3028
  },
3029
+ {
3030
+ name: "permissions.list",
3031
+ config: {
3032
+ title: "Permissions List",
3033
+ description: "List allowlisted Browser Bridge sites.",
3034
+ inputSchema: PermissionsListInputSchema,
3035
+ outputSchema: envelope(PermissionsListOutputSchema),
3036
+ corePath: "/permissions/list"
3037
+ }
3038
+ },
3039
+ {
3040
+ name: "permissions.get_mode",
3041
+ config: {
3042
+ title: "Permissions Get Mode",
3043
+ description: "Read the current Browser Bridge permissions mode.",
3044
+ inputSchema: PermissionsGetModeInputSchema,
3045
+ outputSchema: envelope(PermissionsGetModeOutputSchema),
3046
+ corePath: "/permissions/get_mode"
3047
+ }
3048
+ },
3049
+ {
3050
+ name: "permissions.list_pending_requests",
3051
+ config: {
3052
+ title: "Permissions List Pending Requests",
3053
+ description: "List pending external Browser Bridge permission-change requests.",
3054
+ inputSchema: PermissionsListPendingRequestsInputSchema,
3055
+ outputSchema: envelope(PermissionsListPendingRequestsOutputSchema),
3056
+ corePath: "/permissions/list_pending_requests"
3057
+ }
3058
+ },
3059
+ {
3060
+ name: "permissions.request_allow_site",
3061
+ config: {
3062
+ title: "Permissions Request Allow Site",
3063
+ description: "Request allowlisting a site. A human must approve the change in Chrome before it applies.",
3064
+ inputSchema: PermissionsRequestAllowSiteInputSchema,
3065
+ outputSchema: envelope(PermissionsRequestOutputSchema),
3066
+ corePath: "/permissions/request_allow_site",
3067
+ transformInput: withPermissionsSource("mcp")
3068
+ }
3069
+ },
3070
+ {
3071
+ name: "permissions.request_revoke_site",
3072
+ config: {
3073
+ title: "Permissions Request Revoke Site",
3074
+ description: "Request revoking a site from the allowlist. A human must approve the change in Chrome before it applies.",
3075
+ inputSchema: PermissionsRequestRevokeSiteInputSchema,
3076
+ outputSchema: envelope(PermissionsRequestOutputSchema),
3077
+ corePath: "/permissions/request_revoke_site",
3078
+ transformInput: withPermissionsSource("mcp")
3079
+ }
3080
+ },
3081
+ {
3082
+ name: "permissions.request_set_mode",
3083
+ config: {
3084
+ title: "Permissions Request Set Mode",
3085
+ description: "Request changing Browser Bridge permission mode. A human must approve the change in Chrome before it applies.",
3086
+ inputSchema: PermissionsRequestSetModeInputSchema,
3087
+ outputSchema: envelope(PermissionsRequestOutputSchema),
3088
+ corePath: "/permissions/request_set_mode",
3089
+ transformInput: withPermissionsSource("mcp")
3090
+ }
3091
+ },
2830
3092
  {
2831
3093
  name: "drive.navigate",
2832
3094
  config: {
@@ -4052,107 +4314,6 @@ var import_readability = require("@mozilla/readability");
4052
4314
  var import_jsdom = require("jsdom");
4053
4315
  var import_turndown = __toESM(require("turndown"));
4054
4316
 
4055
- // packages/core/src/page-state-script.ts
4056
- var PAGE_STATE_SCRIPT = [
4057
- "(() => {",
4058
- " const escape = (value) => {",
4059
- " if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {",
4060
- " return CSS.escape(value);",
4061
- " }",
4062
- ` return String(value).replace(/["'\\\\]/g, '\\\\$&');`,
4063
- " };",
4064
- " const truncate = (value, max) => {",
4065
- " const text = String(value ?? '');",
4066
- " return text.length > max ? text.slice(0, max) : text;",
4067
- " };",
4068
- " const selectorFor = (element) => {",
4069
- " if (element.id) {",
4070
- " return `#${escape(element.id)}`;",
4071
- " }",
4072
- " const name = element.getAttribute('name');",
4073
- " if (name) {",
4074
- ' return `${element.tagName.toLowerCase()}[name="${escape(name)}"]`;',
4075
- " }",
4076
- " const parts = [];",
4077
- " let node = element;",
4078
- " while (node && node.nodeType === 1 && parts.length < 4) {",
4079
- " let part = node.tagName.toLowerCase();",
4080
- " const parent = node.parentElement;",
4081
- " if (parent) {",
4082
- " const siblings = Array.from(parent.children).filter(",
4083
- " (child) => child.tagName === node.tagName",
4084
- " );",
4085
- " if (siblings.length > 1) {",
4086
- " part += `:nth-of-type(${siblings.indexOf(node) + 1})`;",
4087
- " }",
4088
- " }",
4089
- " parts.unshift(part);",
4090
- " node = parent;",
4091
- " }",
4092
- " return parts.join('>');",
4093
- " };",
4094
- " const readStorage = (storage, limit) => {",
4095
- " try {",
4096
- " return Object.keys(storage)",
4097
- " .slice(0, limit)",
4098
- " .map((key) => ({",
4099
- " key,",
4100
- " value: truncate(storage.getItem(key), 500),",
4101
- " }));",
4102
- " } catch {",
4103
- " return [];",
4104
- " }",
4105
- " };",
4106
- " const forms = Array.from(document.querySelectorAll('form')).map((form) => {",
4107
- " const fields = Array.from(form.elements)",
4108
- " .filter((element) => element && element.tagName)",
4109
- " .map((element) => {",
4110
- " const tag = element.tagName.toLowerCase();",
4111
- " const type = 'type' in element && element.type ? element.type : tag;",
4112
- " const name = element.name || element.getAttribute('name') || element.id || '';",
4113
- " let value = '';",
4114
- " let options;",
4115
- " if (tag === 'select') {",
4116
- " const select = element;",
4117
- " value = select.value ?? '';",
4118
- " options = Array.from(select.options).map((option) => option.text);",
4119
- " } else if (tag === 'input' && element.type === 'password') {",
4120
- " value = '[redacted]';",
4121
- " } else if (tag === 'input' || tag === 'textarea') {",
4122
- " value = element.value ?? '';",
4123
- " } else if (element.isContentEditable) {",
4124
- " value = element.textContent ?? '';",
4125
- " } else if ('value' in element) {",
4126
- " value = element.value ?? '';",
4127
- " }",
4128
- " return {",
4129
- " name,",
4130
- " type,",
4131
- " value: truncate(value, 500),",
4132
- " ...(options ? { options } : {}),",
4133
- " };",
4134
- " });",
4135
- " return {",
4136
- " selector: selectorFor(form),",
4137
- " action: form.getAttribute('action') || undefined,",
4138
- " method: form.getAttribute('method') || undefined,",
4139
- " fields,",
4140
- " };",
4141
- " });",
4142
- " const localStorage = readStorage(window.localStorage, 100);",
4143
- " const sessionStorage = readStorage(window.sessionStorage, 100);",
4144
- " const cookies = (document.cookie ? document.cookie.split(';') : [])",
4145
- " .map((entry) => entry.trim())",
4146
- " .filter((entry) => entry.length > 0)",
4147
- " .slice(0, 50)",
4148
- " .map((entry) => {",
4149
- " const [key, ...rest] = entry.split('=');",
4150
- " return { key, value: truncate(rest.join('='), 500) };",
4151
- " });",
4152
- " return { forms, localStorage, sessionStorage, cookies };",
4153
- "})()"
4154
- ].join("\n");
4155
-
4156
4317
  // packages/core/src/diagnostics.ts
4157
4318
  var STALE_ERROR_THRESHOLD_MS = 2 * 60 * 1e3;
4158
4319
 
@@ -4201,6 +4362,78 @@ var registerOpenArtifactsCommand = (program2) => {
4201
4362
  });
4202
4363
  };
4203
4364
 
4365
+ // packages/cli/src/commands/permissions.ts
4366
+ var parseNumber5 = (value) => {
4367
+ if (value === void 0 || value === null || value === "") {
4368
+ return void 0;
4369
+ }
4370
+ const parsed = Number(value);
4371
+ return Number.isFinite(parsed) ? parsed : Number.NaN;
4372
+ };
4373
+ var registerPermissionsCommands = (program2) => {
4374
+ const permissions = program2.command("permissions").description("Manage Browser Bridge permissions");
4375
+ permissions.command("list").description("List allowlisted sites").action(async (_options, command) => {
4376
+ await runCommand(command, (client) => {
4377
+ const payload = parseInput(PermissionsListInputSchema, {});
4378
+ return client.post("/permissions/list", payload);
4379
+ });
4380
+ });
4381
+ permissions.command("mode").description("Show the current permissions mode").action(async (_options, command) => {
4382
+ await runCommand(command, (client) => {
4383
+ const payload = parseInput(PermissionsGetModeInputSchema, {});
4384
+ return client.post("/permissions/get_mode", payload);
4385
+ });
4386
+ });
4387
+ permissions.command("pending").description("List pending external permission-change requests").action(async (_options, command) => {
4388
+ await runCommand(command, (client) => {
4389
+ const payload = parseInput(
4390
+ PermissionsListPendingRequestsInputSchema,
4391
+ {}
4392
+ );
4393
+ return client.post("/permissions/list_pending_requests", payload);
4394
+ });
4395
+ });
4396
+ permissions.command("allow-site").description("Request allowlisting a site").requiredOption("--site <site>", "Site hostname[:port] to allowlist").option(
4397
+ "--timeout-ms <ms>",
4398
+ "How long to wait for approval before returning"
4399
+ ).action(async (options, command) => {
4400
+ await runCommand(command, (client) => {
4401
+ const payload = parseInput(PermissionsRequestAllowSiteInputSchema, {
4402
+ site: options.site,
4403
+ timeout_ms: parseNumber5(options.timeoutMs),
4404
+ source: "cli"
4405
+ });
4406
+ return client.post("/permissions/request_allow_site", payload);
4407
+ });
4408
+ });
4409
+ permissions.command("revoke-site").description("Request revoking a site from the allowlist").requiredOption("--site <site>", "Site hostname[:port] to revoke").option(
4410
+ "--timeout-ms <ms>",
4411
+ "How long to wait for approval before returning"
4412
+ ).action(async (options, command) => {
4413
+ await runCommand(command, (client) => {
4414
+ const payload = parseInput(PermissionsRequestRevokeSiteInputSchema, {
4415
+ site: options.site,
4416
+ timeout_ms: parseNumber5(options.timeoutMs),
4417
+ source: "cli"
4418
+ });
4419
+ return client.post("/permissions/request_revoke_site", payload);
4420
+ });
4421
+ });
4422
+ permissions.command("set-mode").description("Request changing the permissions mode").requiredOption("--mode <mode>", "Mode to request (granular or bypass)").option(
4423
+ "--timeout-ms <ms>",
4424
+ "How long to wait for approval before returning"
4425
+ ).action(async (options, command) => {
4426
+ await runCommand(command, (client) => {
4427
+ const payload = parseInput(PermissionsRequestSetModeInputSchema, {
4428
+ mode: options.mode,
4429
+ timeout_ms: parseNumber5(options.timeoutMs),
4430
+ source: "cli"
4431
+ });
4432
+ return client.post("/permissions/request_set_mode", payload);
4433
+ });
4434
+ });
4435
+ };
4436
+
4204
4437
  // packages/cli/src/commands/session.ts
4205
4438
  var registerSessionCommands = (program2) => {
4206
4439
  const session = program2.command("session").description("Manage Core sessions");
@@ -4346,14 +4579,14 @@ var resolveSkillSourceDir = async () => {
4346
4579
  } catch {
4347
4580
  }
4348
4581
  const repoRoot = import_node_path8.default.resolve(rootDir, "..", "..");
4349
- const docsSkill = import_node_path8.default.join(repoRoot, "skills", "browser-bridge");
4582
+ const repoSkill = import_node_path8.default.join(repoRoot, ".agents", "skills", "browser-bridge");
4350
4583
  try {
4351
- await import_promises4.default.stat(docsSkill);
4352
- return docsSkill;
4584
+ await import_promises4.default.stat(repoSkill);
4585
+ return repoSkill;
4353
4586
  } catch {
4354
4587
  }
4355
4588
  throw new Error(
4356
- `Unable to locate packaged skill. Expected ${packaged} (npm install) or ${docsSkill} (repo dev).`
4589
+ `Unable to locate packaged skill. Expected ${packaged} (npm install) or ${repoSkill} (repo dev).`
4357
4590
  );
4358
4591
  };
4359
4592
 
@@ -4650,6 +4883,7 @@ var main = async () => {
4650
4883
  "Output Browser Bridge CLI version"
4651
4884
  ).option("--host <host>", "Core host (default: 127.0.0.1)").option("--port <port>", "Core port (default: 3210)").option("--json", "Output JSON").option("--no-daemon", "Disable auto-starting Core");
4652
4885
  registerSessionCommands(program);
4886
+ registerPermissionsCommands(program);
4653
4887
  registerDriveCommands(program);
4654
4888
  registerInspectCommands(program);
4655
4889
  registerArtifactsCommands(program);