@btraut/browser-bridge 0.11.1 → 0.12.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
@@ -26,55 +26,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // packages/cli/src/index.ts
27
27
  var import_commander = require("commander");
28
28
 
29
- // packages/shared/src/errors.ts
30
- var import_zod = require("zod");
31
- var ErrorCodeSchema = import_zod.z.enum([
32
- "UNKNOWN",
33
- "INVALID_ARGUMENT",
34
- "NOT_FOUND",
35
- "ALREADY_EXISTS",
36
- "FAILED_PRECONDITION",
37
- "UNAUTHORIZED",
38
- "FORBIDDEN",
39
- "PERMISSION_REQUIRED",
40
- "PERMISSION_DENIED",
41
- "PERMISSION_PROMPT_TIMEOUT",
42
- "CONFLICT",
43
- "TIMEOUT",
44
- "CANCELLED",
45
- "UNAVAILABLE",
46
- "RATE_LIMITED",
47
- "NOT_IMPLEMENTED",
48
- "INTERNAL",
49
- "SESSION_NOT_FOUND",
50
- "SESSION_CLOSED",
51
- "SESSION_BROKEN",
52
- "DRIVE_UNAVAILABLE",
53
- "INSPECT_UNAVAILABLE",
54
- "EXTENSION_DISCONNECTED",
55
- "DEBUGGER_IN_USE",
56
- "ATTACH_DENIED",
57
- "TAB_NOT_FOUND",
58
- "NOT_SUPPORTED",
59
- "LOCATOR_NOT_FOUND",
60
- "NAVIGATION_FAILED",
61
- "EVALUATION_FAILED",
62
- "ARTIFACT_IO_ERROR"
63
- ]);
64
- var ErrorInfoSchema = import_zod.z.object({
65
- code: ErrorCodeSchema,
66
- message: import_zod.z.string(),
67
- retryable: import_zod.z.boolean(),
68
- details: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
69
- });
70
- var ErrorEnvelopeSchema = import_zod.z.object({
71
- ok: import_zod.z.literal(false),
72
- error: ErrorInfoSchema
73
- });
74
- var successEnvelopeSchema = (result) => import_zod.z.object({
75
- ok: import_zod.z.literal(true),
76
- result
77
- });
29
+ // packages/shared/src/core-readiness.ts
30
+ var import_promises = require("node:timers/promises");
78
31
 
79
32
  // packages/shared/src/logging.ts
80
33
  var import_node_fs2 = require("node:fs");
@@ -605,6 +558,248 @@ var createJsonlLogger = (options) => {
605
558
  return buildLogger(state, options.bindings ?? {});
606
559
  };
607
560
 
561
+ // packages/shared/src/core-readiness.ts
562
+ var DEFAULT_TIMEOUT_MS = 3e4;
563
+ var DEFAULT_HEALTH_RETRY_MS = 250;
564
+ var DEFAULT_HEALTH_ATTEMPTS = 20;
565
+ var DEFAULT_HEALTH_TIMEOUT_MS = 2e3;
566
+ var DEFAULT_HEALTH_BUDGET_MS = 15e3;
567
+ var resolveTimeoutMs = (timeoutMs) => {
568
+ const candidate = timeoutMs ?? (process.env.BROWSER_BRIDGE_CORE_TIMEOUT_MS ? Number.parseInt(process.env.BROWSER_BRIDGE_CORE_TIMEOUT_MS, 10) : process.env.BROWSER_VISION_CORE_TIMEOUT_MS ? Number.parseInt(process.env.BROWSER_VISION_CORE_TIMEOUT_MS, 10) : void 0);
569
+ if (candidate === void 0 || candidate === null) {
570
+ return DEFAULT_TIMEOUT_MS;
571
+ }
572
+ const parsed = typeof candidate === "number" ? candidate : Number(candidate);
573
+ if (!Number.isFinite(parsed) || parsed <= 0) {
574
+ throw new Error(`Invalid timeoutMs: ${String(candidate)}`);
575
+ }
576
+ return Math.floor(parsed);
577
+ };
578
+ var resolvePositiveInteger = (value, fallback) => {
579
+ if (value === void 0) {
580
+ return fallback;
581
+ }
582
+ if (!Number.isFinite(value) || value <= 0) {
583
+ throw new Error(`Invalid positive integer value: ${String(value)}`);
584
+ }
585
+ return Math.floor(value);
586
+ };
587
+ var hasExplicitRuntimeInput = (options) => options.host !== void 0 || options.port !== void 0 || process.env.BROWSER_BRIDGE_CORE_HOST !== void 0 || process.env.BROWSER_VISION_CORE_HOST !== void 0 || process.env.BROWSER_BRIDGE_CORE_PORT !== void 0 || process.env.BROWSER_VISION_CORE_PORT !== void 0;
588
+ var createCoreReadinessController = (options = {}) => {
589
+ const logger = options.logger ?? createJsonlLogger({
590
+ stream: "cli",
591
+ cwd: options.cwd
592
+ }).child({ scope: "core-readiness" });
593
+ const logPrefix = options.logPrefix ?? "core";
594
+ const timeoutMs = resolveTimeoutMs(options.timeoutMs);
595
+ const fetchImpl = options.fetchImpl ?? fetch;
596
+ const ensureDaemon = options.ensureDaemon ?? true;
597
+ const healthRetryMs = resolvePositiveInteger(
598
+ options.healthRetryMs,
599
+ DEFAULT_HEALTH_RETRY_MS
600
+ );
601
+ const healthAttempts = resolvePositiveInteger(
602
+ options.healthAttempts,
603
+ DEFAULT_HEALTH_ATTEMPTS
604
+ );
605
+ const healthTimeoutMs = resolvePositiveInteger(
606
+ options.healthTimeoutMs,
607
+ Math.min(timeoutMs, DEFAULT_HEALTH_TIMEOUT_MS)
608
+ );
609
+ const healthBudgetMs = resolvePositiveInteger(
610
+ options.healthBudgetMs,
611
+ DEFAULT_HEALTH_BUDGET_MS
612
+ );
613
+ let runtime = resolveCoreRuntime({
614
+ host: options.host,
615
+ port: options.port,
616
+ cwd: options.cwd,
617
+ strictEnvPort: options.strictEnvPort ?? true
618
+ });
619
+ let baseUrl = `http://${runtime.host}:${runtime.port}`;
620
+ const allowRuntimeRefresh = !hasExplicitRuntimeInput(options);
621
+ const refreshRuntime = () => {
622
+ if (!allowRuntimeRefresh) {
623
+ return;
624
+ }
625
+ runtime = resolveCoreRuntime({
626
+ cwd: options.cwd,
627
+ strictEnvPort: options.strictEnvPort ?? true
628
+ });
629
+ baseUrl = `http://${runtime.host}:${runtime.port}`;
630
+ };
631
+ const checkHealth = async () => {
632
+ try {
633
+ const controller = new AbortController();
634
+ const timeout = setTimeout(() => controller.abort(), healthTimeoutMs);
635
+ try {
636
+ let response;
637
+ try {
638
+ response = await fetchImpl(`${baseUrl}/health`, {
639
+ method: "GET",
640
+ signal: controller.signal
641
+ });
642
+ } catch (error) {
643
+ if (controller.signal.aborted || error instanceof Error && error.name === "AbortError") {
644
+ logger.warn(`${logPrefix}.health.timeout`, {
645
+ base_url: baseUrl,
646
+ timeout_ms: healthTimeoutMs
647
+ });
648
+ return false;
649
+ }
650
+ logger.warn(`${logPrefix}.health.fetch_failed`, {
651
+ base_url: baseUrl,
652
+ error
653
+ });
654
+ throw error;
655
+ }
656
+ if (!response.ok) {
657
+ logger.warn(`${logPrefix}.health.non_ok`, {
658
+ base_url: baseUrl,
659
+ status: response.status
660
+ });
661
+ return false;
662
+ }
663
+ const data = await response.json().catch(() => null);
664
+ const ok = Boolean(data?.ok);
665
+ if (!ok) {
666
+ logger.warn(`${logPrefix}.health.not_ready`, {
667
+ base_url: baseUrl
668
+ });
669
+ }
670
+ return ok;
671
+ } finally {
672
+ clearTimeout(timeout);
673
+ }
674
+ } catch (error) {
675
+ logger.warn(`${logPrefix}.health.error`, {
676
+ base_url: baseUrl,
677
+ error
678
+ });
679
+ return false;
680
+ }
681
+ };
682
+ const ensureCoreRunning = async () => {
683
+ refreshRuntime();
684
+ if (await checkHealth()) {
685
+ logger.debug(`${logPrefix}.ensure_ready.already_running`, {
686
+ base_url: baseUrl
687
+ });
688
+ return;
689
+ }
690
+ if (!options.spawnDaemon) {
691
+ logger.error(`${logPrefix}.ensure_ready.missing_spawn`, {
692
+ host: runtime.host,
693
+ port: runtime.port
694
+ });
695
+ throw new Error(
696
+ `Core daemon is not running on ${runtime.host}:${runtime.port} and spawnDaemon is not configured.`
697
+ );
698
+ }
699
+ options.spawnDaemon(runtime);
700
+ const deadlineAt = Date.now() + healthBudgetMs;
701
+ for (let attempt = 0; attempt < healthAttempts; attempt += 1) {
702
+ const remainingBudgetMs = deadlineAt - Date.now();
703
+ if (remainingBudgetMs <= 0) {
704
+ break;
705
+ }
706
+ await (0, import_promises.setTimeout)(Math.min(healthRetryMs, remainingBudgetMs));
707
+ refreshRuntime();
708
+ if (await checkHealth()) {
709
+ logger.info(`${logPrefix}.ensure_ready.ready`, {
710
+ base_url: baseUrl,
711
+ attempts: attempt + 1
712
+ });
713
+ return;
714
+ }
715
+ }
716
+ logger.error(`${logPrefix}.ensure_ready.failed`, {
717
+ host: runtime.host,
718
+ port: runtime.port,
719
+ attempts: healthAttempts,
720
+ health_budget_ms: healthBudgetMs,
721
+ health_timeout_ms: healthTimeoutMs
722
+ });
723
+ throw new Error(
724
+ `Core daemon failed to start on ${runtime.host}:${runtime.port}.`
725
+ );
726
+ };
727
+ let ensurePromise = null;
728
+ const ensureReady = async () => {
729
+ if (!ensureDaemon) {
730
+ return;
731
+ }
732
+ if (!ensurePromise) {
733
+ ensurePromise = ensureCoreRunning().catch((error) => {
734
+ ensurePromise = null;
735
+ throw error;
736
+ });
737
+ }
738
+ await ensurePromise;
739
+ };
740
+ return {
741
+ get baseUrl() {
742
+ return baseUrl;
743
+ },
744
+ get runtime() {
745
+ return runtime;
746
+ },
747
+ refreshRuntime,
748
+ ensureReady,
749
+ checkHealth
750
+ };
751
+ };
752
+
753
+ // packages/shared/src/errors.ts
754
+ var import_zod = require("zod");
755
+ var ErrorCodeSchema = import_zod.z.enum([
756
+ "UNKNOWN",
757
+ "INVALID_ARGUMENT",
758
+ "NOT_FOUND",
759
+ "ALREADY_EXISTS",
760
+ "FAILED_PRECONDITION",
761
+ "UNAUTHORIZED",
762
+ "FORBIDDEN",
763
+ "PERMISSION_REQUIRED",
764
+ "PERMISSION_DENIED",
765
+ "PERMISSION_PROMPT_TIMEOUT",
766
+ "CONFLICT",
767
+ "TIMEOUT",
768
+ "CANCELLED",
769
+ "UNAVAILABLE",
770
+ "RATE_LIMITED",
771
+ "NOT_IMPLEMENTED",
772
+ "INTERNAL",
773
+ "SESSION_NOT_FOUND",
774
+ "SESSION_CLOSED",
775
+ "SESSION_BROKEN",
776
+ "DRIVE_UNAVAILABLE",
777
+ "INSPECT_UNAVAILABLE",
778
+ "EXTENSION_DISCONNECTED",
779
+ "DEBUGGER_IN_USE",
780
+ "ATTACH_DENIED",
781
+ "TAB_NOT_FOUND",
782
+ "NOT_SUPPORTED",
783
+ "LOCATOR_NOT_FOUND",
784
+ "NAVIGATION_FAILED",
785
+ "EVALUATION_FAILED",
786
+ "ARTIFACT_IO_ERROR"
787
+ ]);
788
+ var ErrorInfoSchema = import_zod.z.object({
789
+ code: ErrorCodeSchema,
790
+ message: import_zod.z.string(),
791
+ retryable: import_zod.z.boolean(),
792
+ details: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
793
+ });
794
+ var ErrorEnvelopeSchema = import_zod.z.object({
795
+ ok: import_zod.z.literal(false),
796
+ error: ErrorInfoSchema
797
+ });
798
+ var successEnvelopeSchema = (result) => import_zod.z.object({
799
+ ok: import_zod.z.literal(true),
800
+ result
801
+ });
802
+
608
803
  // packages/shared/src/schemas.ts
609
804
  var import_zod2 = require("zod");
610
805
  var LocatorRoleSchema = import_zod2.z.object({
@@ -669,6 +864,39 @@ var DiagnosticCheckSchema = import_zod2.z.object({
669
864
  message: import_zod2.z.string().optional(),
670
865
  details: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).optional()
671
866
  });
867
+ var DiagnosticsRuntimeEndpointSchema = import_zod2.z.object({
868
+ host: import_zod2.z.string().optional(),
869
+ port: import_zod2.z.number().finite().optional(),
870
+ base_url: import_zod2.z.string().optional(),
871
+ host_source: import_zod2.z.string().optional(),
872
+ port_source: import_zod2.z.string().optional(),
873
+ metadata_path: import_zod2.z.string().optional(),
874
+ isolated_mode: import_zod2.z.boolean().optional()
875
+ });
876
+ var DiagnosticsRuntimeProcessSchema = import_zod2.z.object({
877
+ component: import_zod2.z.enum(["cli", "mcp", "core"]).optional(),
878
+ version: import_zod2.z.string().optional(),
879
+ pid: import_zod2.z.number().int().positive().optional(),
880
+ node_version: import_zod2.z.string().optional(),
881
+ binary_path: import_zod2.z.string().optional(),
882
+ argv_entry: import_zod2.z.string().optional()
883
+ });
884
+ var DiagnosticsRuntimeCallerSchema = import_zod2.z.object({
885
+ endpoint: DiagnosticsRuntimeEndpointSchema.optional(),
886
+ process: DiagnosticsRuntimeProcessSchema.optional()
887
+ });
888
+ var DiagnosticsRuntimeContextSchema = import_zod2.z.object({
889
+ caller: DiagnosticsRuntimeCallerSchema.optional(),
890
+ core: import_zod2.z.object({
891
+ endpoint: DiagnosticsRuntimeEndpointSchema.optional(),
892
+ process: DiagnosticsRuntimeProcessSchema.optional()
893
+ }).optional(),
894
+ extension: import_zod2.z.object({
895
+ version: import_zod2.z.string().optional(),
896
+ endpoint: DiagnosticsRuntimeEndpointSchema.optional(),
897
+ port_source: import_zod2.z.enum(["default", "storage"]).optional()
898
+ }).optional()
899
+ });
672
900
  var DiagnosticReportSchema = import_zod2.z.object({
673
901
  ok: import_zod2.z.boolean(),
674
902
  session_id: import_zod2.z.string().optional(),
@@ -717,7 +945,8 @@ var DiagnosticReportSchema = import_zod2.z.object({
717
945
  loop_detected: import_zod2.z.boolean().optional()
718
946
  }).optional(),
719
947
  warnings: import_zod2.z.array(import_zod2.z.string()).optional(),
720
- notes: import_zod2.z.array(import_zod2.z.string()).optional()
948
+ notes: import_zod2.z.array(import_zod2.z.string()).optional(),
949
+ runtime: DiagnosticsRuntimeContextSchema.optional()
721
950
  });
722
951
  var SessionIdSchema = import_zod2.z.object({
723
952
  session_id: import_zod2.z.string().min(1)
@@ -1109,7 +1338,8 @@ var HealthCheckOutputSchema = import_zod2.z.object({
1109
1338
  }).passthrough()
1110
1339
  }).passthrough();
1111
1340
  var DiagnosticsDoctorInputSchema = import_zod2.z.object({
1112
- session_id: import_zod2.z.string().min(1).optional()
1341
+ session_id: import_zod2.z.string().min(1).optional(),
1342
+ caller: DiagnosticsRuntimeCallerSchema.optional()
1113
1343
  });
1114
1344
  var DiagnosticsDoctorOutputSchema = DiagnosticReportSchema;
1115
1345
 
@@ -1119,7 +1349,6 @@ var import_zod3 = require("zod");
1119
1349
  // packages/cli/src/core-client.ts
1120
1350
  var import_node_child_process = require("node:child_process");
1121
1351
  var import_node_path3 = require("node:path");
1122
- var import_promises = require("node:timers/promises");
1123
1352
  var CoreClientError = class extends Error {
1124
1353
  constructor(info) {
1125
1354
  super(info.message);
@@ -1127,13 +1356,11 @@ var CoreClientError = class extends Error {
1127
1356
  this.info = info;
1128
1357
  }
1129
1358
  };
1130
- var DEFAULT_TIMEOUT_MS = 3e4;
1131
- var HEALTH_RETRY_MS = 250;
1132
- var HEALTH_ATTEMPTS = 20;
1133
- var resolveTimeoutMs = (timeoutMs) => {
1359
+ var DEFAULT_TIMEOUT_MS2 = 3e4;
1360
+ var resolveTimeoutMs2 = (timeoutMs) => {
1134
1361
  const candidate = timeoutMs ?? (process.env.BROWSER_BRIDGE_CORE_TIMEOUT_MS ? Number.parseInt(process.env.BROWSER_BRIDGE_CORE_TIMEOUT_MS, 10) : process.env.BROWSER_VISION_CORE_TIMEOUT_MS ? Number.parseInt(process.env.BROWSER_VISION_CORE_TIMEOUT_MS, 10) : void 0);
1135
1362
  if (candidate === void 0 || candidate === null) {
1136
- return DEFAULT_TIMEOUT_MS;
1363
+ return DEFAULT_TIMEOUT_MS2;
1137
1364
  }
1138
1365
  const parsed = typeof candidate === "number" ? candidate : Number(candidate);
1139
1366
  if (!Number.isFinite(parsed) || parsed <= 0) {
@@ -1148,42 +1375,70 @@ var createCoreClient = (options = {}) => {
1148
1375
  stream: "cli",
1149
1376
  cwd: options.cwd
1150
1377
  }).child({ scope: "core-client" });
1151
- let runtime = resolveCoreRuntime({
1378
+ const fetchImpl = options.fetchImpl ?? fetch;
1379
+ const spawnImpl = options.spawnImpl ?? import_node_child_process.spawn;
1380
+ const timeoutMs = resolveTimeoutMs2(options.timeoutMs);
1381
+ const readiness = createCoreReadinessController({
1152
1382
  host: options.host,
1153
1383
  port: options.port,
1154
1384
  cwd: options.cwd,
1155
- strictEnvPort: true
1156
- });
1157
- let baseUrl = `http://${runtime.host}:${runtime.port}`;
1158
- const timeoutMs = resolveTimeoutMs(options.timeoutMs);
1159
- const fetchImpl = options.fetchImpl ?? fetch;
1160
- const spawnImpl = options.spawnImpl ?? import_node_child_process.spawn;
1161
- const ensureDaemon = options.ensureDaemon ?? true;
1162
- const allowRuntimeRefresh = options.host === void 0 && options.port === void 0 && process.env.BROWSER_BRIDGE_CORE_HOST === void 0 && process.env.BROWSER_VISION_CORE_HOST === void 0 && process.env.BROWSER_BRIDGE_CORE_PORT === void 0 && process.env.BROWSER_VISION_CORE_PORT === void 0;
1163
- const refreshRuntime = () => {
1164
- if (!allowRuntimeRefresh) {
1165
- return;
1385
+ timeoutMs,
1386
+ ensureDaemon: options.ensureDaemon ?? true,
1387
+ strictEnvPort: true,
1388
+ fetchImpl,
1389
+ logger,
1390
+ logPrefix: "cli.core",
1391
+ spawnDaemon: (runtime) => {
1392
+ const coreEntry = (0, import_node_path3.resolve)(__dirname, "api.js");
1393
+ const startOptions = [];
1394
+ if (runtime.hostSource === "option" || runtime.hostSource === "env") {
1395
+ startOptions.push(`host: ${JSON.stringify(runtime.host)}`);
1396
+ }
1397
+ if (runtime.portSource === "option" || runtime.portSource === "env") {
1398
+ startOptions.push(`port: ${runtime.port}`);
1399
+ }
1400
+ const script = `const { startCoreServer } = require(${JSON.stringify(
1401
+ coreEntry
1402
+ )});
1403
+ startCoreServer({ ${startOptions.join(
1404
+ ", "
1405
+ )} })
1406
+ .catch((err) => { console.error(err); process.exit(1); });`;
1407
+ logger.info("cli.core.spawn.start", {
1408
+ host: runtime.host,
1409
+ port: runtime.port,
1410
+ host_source: runtime.hostSource,
1411
+ port_source: runtime.portSource
1412
+ });
1413
+ const child = spawnImpl(process.execPath, ["-e", script], {
1414
+ detached: true,
1415
+ stdio: "ignore",
1416
+ env: { ...process.env }
1417
+ });
1418
+ child.on("error", (error) => {
1419
+ logger.error("cli.core.spawn.error", {
1420
+ host: runtime.host,
1421
+ port: runtime.port,
1422
+ error
1423
+ });
1424
+ });
1425
+ child.unref();
1166
1426
  }
1167
- runtime = resolveCoreRuntime({
1168
- cwd: options.cwd,
1169
- strictEnvPort: true
1170
- });
1171
- baseUrl = `http://${runtime.host}:${runtime.port}`;
1172
- };
1427
+ });
1173
1428
  const requestJson = async (method, path9, body) => {
1174
1429
  const requestPath = normalizePath(path9);
1175
1430
  const startedAt = process.hrtime.bigint();
1176
1431
  logger.debug("cli.core.request.start", {
1177
1432
  method,
1178
1433
  path: requestPath,
1179
- base_url: baseUrl
1434
+ base_url: readiness.baseUrl
1180
1435
  });
1181
1436
  const controller = new AbortController();
1182
1437
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
1183
1438
  try {
1184
1439
  let response;
1185
1440
  try {
1186
- response = await fetchImpl(`${baseUrl}${requestPath}`, {
1441
+ response = await fetchImpl(`${readiness.baseUrl}${requestPath}`, {
1187
1442
  method,
1188
1443
  headers: {
1189
1444
  "content-type": "application/json"
@@ -1196,203 +1451,77 @@ var createCoreClient = (options = {}) => {
1196
1451
  logger.warn("cli.core.request.timeout", {
1197
1452
  method,
1198
1453
  path: requestPath,
1199
- base_url: baseUrl,
1454
+ base_url: readiness.baseUrl,
1200
1455
  timeout_ms: timeoutMs,
1201
- duration_ms: durationMs(startedAt)
1202
- });
1203
- throw new CoreClientError({
1204
- code: "TIMEOUT",
1205
- message: `Core request timed out after ${timeoutMs}ms.`,
1206
- retryable: true,
1207
- details: {
1208
- timeout_ms: timeoutMs,
1209
- base_url: baseUrl,
1210
- path: requestPath
1211
- }
1212
- });
1213
- }
1214
- logger.error("cli.core.request.failed", {
1215
- method,
1216
- path: requestPath,
1217
- base_url: baseUrl,
1218
- duration_ms: durationMs(startedAt),
1219
- error
1220
- });
1221
- throw error;
1222
- }
1223
- const raw = await response.text();
1224
- if (!raw) {
1225
- logger.warn("cli.core.request.empty_response", {
1226
- method,
1227
- path: requestPath,
1228
- base_url: baseUrl,
1229
- status: response.status,
1230
- duration_ms: durationMs(startedAt)
1231
- });
1232
- throw new Error(`Empty response from Core (${response.status}).`);
1233
- }
1234
- try {
1235
- const parsed = JSON.parse(raw);
1236
- logger.debug("cli.core.request.end", {
1237
- method,
1238
- path: requestPath,
1239
- base_url: baseUrl,
1240
- status: response.status,
1241
- duration_ms: durationMs(startedAt)
1242
- });
1243
- return parsed;
1244
- } catch (error) {
1245
- const message = error instanceof Error ? error.message : "Unknown JSON parse error";
1246
- logger.error("cli.core.request.invalid_json", {
1247
- method,
1248
- path: requestPath,
1249
- base_url: baseUrl,
1250
- status: response.status,
1251
- duration_ms: durationMs(startedAt),
1252
- error
1253
- });
1254
- throw new Error(`Failed to parse Core response: ${message}`);
1255
- }
1256
- } finally {
1257
- clearTimeout(timeout);
1258
- }
1259
- };
1260
- const checkHealth = async () => {
1261
- try {
1262
- const controller = new AbortController();
1263
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
1264
- try {
1265
- let response;
1266
- try {
1267
- response = await fetchImpl(`${baseUrl}/health`, {
1268
- method: "GET",
1269
- signal: controller.signal
1270
- });
1271
- } catch (error) {
1272
- if (controller.signal.aborted || error instanceof Error && error.name === "AbortError") {
1273
- logger.warn("cli.core.health.timeout", {
1274
- base_url: baseUrl,
1275
- timeout_ms: timeoutMs
1276
- });
1277
- return false;
1278
- }
1279
- logger.warn("cli.core.health.fetch_failed", {
1280
- base_url: baseUrl,
1281
- error
1282
- });
1283
- throw error;
1284
- }
1285
- if (!response.ok) {
1286
- logger.warn("cli.core.health.non_ok", {
1287
- base_url: baseUrl,
1288
- status: response.status
1289
- });
1290
- return false;
1291
- }
1292
- const data = await response.json().catch(() => null);
1293
- const ok = Boolean(data?.ok);
1294
- if (!ok) {
1295
- logger.warn("cli.core.health.not_ready", {
1296
- base_url: baseUrl
1297
- });
1298
- }
1299
- return ok;
1300
- } finally {
1301
- clearTimeout(timeout);
1302
- }
1303
- } catch (error) {
1304
- logger.warn("cli.core.health.error", {
1305
- base_url: baseUrl,
1306
- error
1307
- });
1308
- return false;
1309
- }
1310
- };
1311
- const spawnDaemon = () => {
1312
- const coreEntry = (0, import_node_path3.resolve)(__dirname, "api.js");
1313
- const startOptions = [];
1314
- if (runtime.hostSource === "option" || runtime.hostSource === "env") {
1315
- startOptions.push(`host: ${JSON.stringify(runtime.host)}`);
1316
- }
1317
- if (runtime.portSource === "option" || runtime.portSource === "env") {
1318
- startOptions.push(`port: ${runtime.port}`);
1319
- }
1320
- const script = `const { startCoreServer } = require(${JSON.stringify(
1321
- coreEntry
1322
- )});
1323
- startCoreServer({ ${startOptions.join(
1324
- ", "
1325
- )} })
1326
- .catch((err) => { console.error(err); process.exit(1); });`;
1327
- logger.info("cli.core.spawn.start", {
1328
- host: runtime.host,
1329
- port: runtime.port,
1330
- host_source: runtime.hostSource,
1331
- port_source: runtime.portSource
1332
- });
1333
- const child = spawnImpl(process.execPath, ["-e", script], {
1334
- detached: true,
1335
- stdio: "ignore",
1336
- env: { ...process.env }
1337
- });
1338
- child.on("error", (error) => {
1339
- logger.error("cli.core.spawn.error", {
1340
- host: runtime.host,
1341
- port: runtime.port,
1342
- error
1343
- });
1344
- });
1345
- child.unref();
1346
- };
1347
- const ensureCoreRunning = async () => {
1348
- refreshRuntime();
1349
- if (await checkHealth()) {
1350
- logger.debug("cli.core.ensure_ready.already_running", {
1351
- base_url: baseUrl
1352
- });
1353
- return;
1354
- }
1355
- spawnDaemon();
1356
- for (let attempt = 0; attempt < HEALTH_ATTEMPTS; attempt += 1) {
1357
- await (0, import_promises.setTimeout)(HEALTH_RETRY_MS);
1358
- refreshRuntime();
1359
- if (await checkHealth()) {
1360
- logger.info("cli.core.ensure_ready.ready", {
1361
- base_url: baseUrl,
1362
- attempts: attempt + 1
1456
+ duration_ms: durationMs(startedAt)
1457
+ });
1458
+ throw new CoreClientError({
1459
+ code: "TIMEOUT",
1460
+ message: `Core request timed out after ${timeoutMs}ms.`,
1461
+ retryable: true,
1462
+ details: {
1463
+ timeout_ms: timeoutMs,
1464
+ base_url: readiness.baseUrl,
1465
+ path: requestPath
1466
+ }
1467
+ });
1468
+ }
1469
+ logger.error("cli.core.request.failed", {
1470
+ method,
1471
+ path: requestPath,
1472
+ base_url: readiness.baseUrl,
1473
+ duration_ms: durationMs(startedAt),
1474
+ error
1363
1475
  });
1364
- return;
1476
+ throw error;
1365
1477
  }
1478
+ const raw = await response.text();
1479
+ if (!raw) {
1480
+ logger.warn("cli.core.request.empty_response", {
1481
+ method,
1482
+ path: requestPath,
1483
+ base_url: readiness.baseUrl,
1484
+ status: response.status,
1485
+ duration_ms: durationMs(startedAt)
1486
+ });
1487
+ throw new Error(`Empty response from Core (${response.status}).`);
1488
+ }
1489
+ try {
1490
+ const parsed = JSON.parse(raw);
1491
+ logger.debug("cli.core.request.end", {
1492
+ method,
1493
+ path: requestPath,
1494
+ base_url: readiness.baseUrl,
1495
+ status: response.status,
1496
+ duration_ms: durationMs(startedAt)
1497
+ });
1498
+ return parsed;
1499
+ } catch (error) {
1500
+ const message = error instanceof Error ? error.message : "Unknown JSON parse error";
1501
+ logger.error("cli.core.request.invalid_json", {
1502
+ method,
1503
+ path: requestPath,
1504
+ base_url: readiness.baseUrl,
1505
+ status: response.status,
1506
+ duration_ms: durationMs(startedAt),
1507
+ error
1508
+ });
1509
+ throw new Error(`Failed to parse Core response: ${message}`);
1510
+ }
1511
+ } finally {
1512
+ clearTimeout(timeout);
1366
1513
  }
1367
- logger.error("cli.core.ensure_ready.failed", {
1368
- host: runtime.host,
1369
- port: runtime.port,
1370
- attempts: HEALTH_ATTEMPTS
1371
- });
1372
- throw new Error(
1373
- `Core daemon failed to start on ${runtime.host}:${runtime.port}.`
1374
- );
1375
- };
1376
- let ensurePromise = null;
1377
- const ensureReady = async () => {
1378
- if (!ensureDaemon) {
1379
- return;
1380
- }
1381
- if (!ensurePromise) {
1382
- ensurePromise = ensureCoreRunning();
1383
- }
1384
- await ensurePromise;
1385
1514
  };
1386
1515
  const post = async (path9, body) => {
1387
- await ensureReady();
1388
- refreshRuntime();
1516
+ await readiness.ensureReady();
1517
+ readiness.refreshRuntime();
1389
1518
  return requestJson("POST", path9, body);
1390
1519
  };
1391
1520
  return {
1392
1521
  get baseUrl() {
1393
- return baseUrl;
1522
+ return readiness.baseUrl;
1394
1523
  },
1395
- ensureReady,
1524
+ ensureReady: readiness.ensureReady,
1396
1525
  post
1397
1526
  };
1398
1527
  };
@@ -1627,9 +1756,26 @@ var registerDialogCommands = (program2) => {
1627
1756
  var registerDiagnosticsCommands = (program2) => {
1628
1757
  const diagnostics = program2.command("diagnostics").description("Diagnostics commands");
1629
1758
  diagnostics.command("doctor").description("Run diagnostics").option("--session-id <id>", "Session identifier").action(async (options, command) => {
1630
- await runCommand(command, (client) => {
1759
+ await runCommand(command, (client, globalOptions) => {
1760
+ const runtime = resolveCoreRuntime({
1761
+ host: globalOptions.host,
1762
+ port: globalOptions.port,
1763
+ strictEnvPort: true
1764
+ });
1631
1765
  const payload = parseInput(DiagnosticsDoctorInputSchema, {
1632
- session_id: options.sessionId
1766
+ session_id: options.sessionId,
1767
+ caller: {
1768
+ endpoint: {
1769
+ host: runtime.host,
1770
+ port: runtime.port,
1771
+ base_url: `http://${runtime.host}:${runtime.port}`,
1772
+ host_source: runtime.hostSource,
1773
+ port_source: runtime.portSource
1774
+ },
1775
+ process: {
1776
+ component: "cli"
1777
+ }
1778
+ }
1633
1779
  });
1634
1780
  return client.post("/diagnostics/doctor", payload);
1635
1781
  });
@@ -2243,36 +2389,92 @@ var registerInspectCommands = (program2) => {
2243
2389
  };
2244
2390
 
2245
2391
  // packages/mcp-adapter/src/core-client.ts
2246
- var DEFAULT_TIMEOUT_MS2 = 3e4;
2392
+ var import_node_child_process3 = require("node:child_process");
2393
+ var import_node_path4 = require("node:path");
2394
+ var DEFAULT_TIMEOUT_MS3 = 3e4;
2247
2395
  var normalizePath2 = (path9) => path9.startsWith("/") ? path9 : `/${path9}`;
2248
2396
  var durationMs3 = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
2397
+ var toReadinessErrorEnvelope = (error, baseUrl) => ({
2398
+ ok: false,
2399
+ error: {
2400
+ code: "UNAVAILABLE",
2401
+ message: error instanceof Error ? `Core not ready at ${baseUrl}: ${error.message}` : `Core not ready at ${baseUrl}.`,
2402
+ retryable: true,
2403
+ details: {
2404
+ base_url: baseUrl
2405
+ }
2406
+ }
2407
+ });
2249
2408
  var createCoreClient2 = (options = {}) => {
2250
2409
  const logger = options.logger ?? createJsonlLogger({
2251
2410
  stream: "mcp-adapter",
2252
2411
  cwd: options.cwd
2253
2412
  }).child({ scope: "core-client" });
2254
- const runtime = resolveCoreRuntime({
2413
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
2414
+ const fetchImpl = options.fetchImpl ?? fetch;
2415
+ const spawnImpl = options.spawnImpl ?? import_node_child_process3.spawn;
2416
+ const ensureDaemon = options.ensureDaemon ?? false;
2417
+ const componentVersion = options.componentVersion ?? process.env.BROWSER_BRIDGE_VERSION ?? process.env.npm_package_version;
2418
+ const readiness = createCoreReadinessController({
2255
2419
  host: options.host,
2256
2420
  port: options.port,
2257
2421
  cwd: options.cwd,
2258
- strictEnvPort: true
2422
+ timeoutMs,
2423
+ ensureDaemon,
2424
+ strictEnvPort: true,
2425
+ fetchImpl,
2426
+ logger,
2427
+ logPrefix: "mcp.core",
2428
+ healthRetryMs: options.healthRetryMs,
2429
+ healthAttempts: options.healthAttempts,
2430
+ spawnDaemon: ensureDaemon ? (runtime) => {
2431
+ const coreEntry = (0, import_node_path4.resolve)(__dirname, "api.js");
2432
+ const startOptions = [];
2433
+ if (runtime.hostSource === "option" || runtime.hostSource === "env") {
2434
+ startOptions.push(`host: ${JSON.stringify(runtime.host)}`);
2435
+ }
2436
+ if (runtime.portSource === "option" || runtime.portSource === "env") {
2437
+ startOptions.push(`port: ${runtime.port}`);
2438
+ }
2439
+ const script = `const { startCoreServer } = require(${JSON.stringify(
2440
+ coreEntry
2441
+ )});
2442
+ startCoreServer({ ${startOptions.join(
2443
+ ", "
2444
+ )} })
2445
+ .catch((err) => { console.error(err); process.exit(1); });`;
2446
+ logger.info("mcp.core.spawn.start", {
2447
+ host: runtime.host,
2448
+ port: runtime.port,
2449
+ host_source: runtime.hostSource,
2450
+ port_source: runtime.portSource
2451
+ });
2452
+ const child = spawnImpl(process.execPath, ["-e", script], {
2453
+ detached: true,
2454
+ stdio: "ignore",
2455
+ env: { ...process.env }
2456
+ });
2457
+ child.on("error", (error) => {
2458
+ logger.error("mcp.core.spawn.error", {
2459
+ host: runtime.host,
2460
+ port: runtime.port,
2461
+ error
2462
+ });
2463
+ });
2464
+ child.unref();
2465
+ } : void 0
2259
2466
  });
2260
- const host = runtime.host;
2261
- const port = runtime.port;
2262
- const baseUrl = `http://${host}:${port}`;
2263
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
2264
- const fetchImpl = options.fetchImpl ?? fetch;
2265
2467
  const requestJson = async (path9, body) => {
2266
2468
  const requestPath = normalizePath2(path9);
2267
2469
  const startedAt = process.hrtime.bigint();
2268
2470
  logger.debug("mcp.core.request.start", {
2269
2471
  path: requestPath,
2270
- base_url: baseUrl
2472
+ base_url: readiness.baseUrl
2271
2473
  });
2272
2474
  const controller = new AbortController();
2273
2475
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
2274
2476
  try {
2275
- const response = await fetchImpl(`${baseUrl}${requestPath}`, {
2477
+ const response = await fetchImpl(`${readiness.baseUrl}${requestPath}`, {
2276
2478
  method: "POST",
2277
2479
  headers: {
2278
2480
  "content-type": "application/json"
@@ -2284,7 +2486,7 @@ var createCoreClient2 = (options = {}) => {
2284
2486
  if (!raw) {
2285
2487
  logger.warn("mcp.core.request.empty_response", {
2286
2488
  path: requestPath,
2287
- base_url: baseUrl,
2489
+ base_url: readiness.baseUrl,
2288
2490
  status: response.status,
2289
2491
  duration_ms: durationMs3(startedAt)
2290
2492
  });
@@ -2294,7 +2496,7 @@ var createCoreClient2 = (options = {}) => {
2294
2496
  const parsed = JSON.parse(raw);
2295
2497
  logger.debug("mcp.core.request.end", {
2296
2498
  path: requestPath,
2297
- base_url: baseUrl,
2499
+ base_url: readiness.baseUrl,
2298
2500
  status: response.status,
2299
2501
  duration_ms: durationMs3(startedAt)
2300
2502
  });
@@ -2303,7 +2505,7 @@ var createCoreClient2 = (options = {}) => {
2303
2505
  const message = error instanceof Error ? error.message : "Unknown JSON parse error";
2304
2506
  logger.error("mcp.core.request.invalid_json", {
2305
2507
  path: requestPath,
2306
- base_url: baseUrl,
2508
+ base_url: readiness.baseUrl,
2307
2509
  status: response.status,
2308
2510
  duration_ms: durationMs3(startedAt),
2309
2511
  error
@@ -2313,7 +2515,7 @@ var createCoreClient2 = (options = {}) => {
2313
2515
  } catch (error) {
2314
2516
  logger.error("mcp.core.request.failed", {
2315
2517
  path: requestPath,
2316
- base_url: baseUrl,
2518
+ base_url: readiness.baseUrl,
2317
2519
  duration_ms: durationMs3(startedAt),
2318
2520
  error
2319
2521
  });
@@ -2323,9 +2525,47 @@ var createCoreClient2 = (options = {}) => {
2323
2525
  }
2324
2526
  };
2325
2527
  const post = async (path9, body) => {
2326
- return requestJson(path9, body);
2528
+ try {
2529
+ await readiness.ensureReady();
2530
+ } catch (error) {
2531
+ logger.warn("mcp.core.ensure_ready.unavailable", {
2532
+ base_url: readiness.baseUrl,
2533
+ error
2534
+ });
2535
+ throw toReadinessErrorEnvelope(error, readiness.baseUrl);
2536
+ }
2537
+ readiness.refreshRuntime();
2538
+ const payload = path9 === "/diagnostics/doctor" && (!body || typeof body === "object" && !Array.isArray(body)) ? {
2539
+ ...body && typeof body === "object" ? body : {},
2540
+ caller: {
2541
+ endpoint: {
2542
+ host: readiness.runtime.host,
2543
+ port: readiness.runtime.port,
2544
+ base_url: readiness.baseUrl,
2545
+ host_source: readiness.runtime.hostSource,
2546
+ port_source: readiness.runtime.portSource,
2547
+ metadata_path: readiness.runtime.metadataPath,
2548
+ isolated_mode: readiness.runtime.isolatedMode
2549
+ },
2550
+ process: {
2551
+ component: "mcp",
2552
+ version: componentVersion,
2553
+ pid: process.pid,
2554
+ node_version: process.version,
2555
+ binary_path: process.execPath,
2556
+ argv_entry: process.argv[1]
2557
+ }
2558
+ }
2559
+ } : body;
2560
+ return requestJson(path9, payload);
2561
+ };
2562
+ return {
2563
+ get baseUrl() {
2564
+ return readiness.baseUrl;
2565
+ },
2566
+ ensureReady: readiness.ensureReady,
2567
+ post
2327
2568
  };
2328
- return { baseUrl, post };
2329
2569
  };
2330
2570
 
2331
2571
  // packages/mcp-adapter/src/tools.ts
@@ -2722,10 +2962,11 @@ var TOOL_DEFINITIONS = [
2722
2962
  }
2723
2963
  }
2724
2964
  ];
2725
- var createToolHandler = (client, corePath) => {
2965
+ var createToolHandler = (clientProvider, corePath) => {
2726
2966
  return (async (args, _extra) => {
2727
2967
  void _extra;
2728
2968
  try {
2969
+ const client = typeof clientProvider === "function" ? await clientProvider() : clientProvider;
2729
2970
  const envelopeResult = await client.post(corePath, args);
2730
2971
  return toToolResult(envelopeResult);
2731
2972
  } catch (error) {
@@ -2737,7 +2978,7 @@ var createToolHandler = (client, corePath) => {
2737
2978
  }
2738
2979
  });
2739
2980
  };
2740
- var registerBrowserBridgeTools = (server, client) => {
2981
+ var registerBrowserBridgeTools = (server, clientProvider) => {
2741
2982
  for (const tool of TOOL_DEFINITIONS) {
2742
2983
  server.registerTool(
2743
2984
  tool.name,
@@ -2747,73 +2988,310 @@ var registerBrowserBridgeTools = (server, client) => {
2747
2988
  inputSchema: tool.config.inputSchema,
2748
2989
  outputSchema: tool.config.outputSchema
2749
2990
  },
2750
- createToolHandler(client, tool.config.corePath)
2991
+ createToolHandler(clientProvider, tool.config.corePath)
2751
2992
  );
2752
2993
  }
2753
2994
  };
2754
2995
 
2755
2996
  // packages/mcp-adapter/src/server.ts
2756
- var import_node_http = require("node:http");
2757
2997
  var import_node_crypto = require("node:crypto");
2998
+ var import_node_http = require("node:http");
2758
2999
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
2759
3000
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
2760
3001
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
2761
3002
  var import_types = require("@modelcontextprotocol/sdk/types.js");
3003
+
3004
+ // packages/mcp-adapter/src/deferred-logger.ts
3005
+ var LOG_LEVEL_PRIORITY2 = {
3006
+ debug: 10,
3007
+ info: 20,
3008
+ warn: 30,
3009
+ error: 40
3010
+ };
3011
+ var isEnabled = (state, level) => LOG_LEVEL_PRIORITY2[level] >= LOG_LEVEL_PRIORITY2[state.level];
3012
+ var enqueue = (state, entry) => {
3013
+ if (state.buffered.length >= state.maxBufferEntries) {
3014
+ state.buffered.shift();
3015
+ state.droppedEntries += 1;
3016
+ }
3017
+ state.buffered.push(entry);
3018
+ };
3019
+ var flushBuffered = (state) => {
3020
+ const destination = state.destination;
3021
+ if (!destination) {
3022
+ return;
3023
+ }
3024
+ if (state.droppedEntries > 0) {
3025
+ destination.warn("mcp.log.buffer.dropped", {
3026
+ dropped_entries: state.droppedEntries
3027
+ });
3028
+ state.droppedEntries = 0;
3029
+ }
3030
+ for (const entry of state.buffered) {
3031
+ destination.log(entry.level, entry.event, {
3032
+ ...entry.bindings,
3033
+ ...entry.fields
3034
+ });
3035
+ }
3036
+ state.buffered.length = 0;
3037
+ };
3038
+ var buildLogger2 = (state, bindings) => {
3039
+ const log = (level, event, fields = {}) => {
3040
+ if (!isEnabled(state, level)) {
3041
+ return;
3042
+ }
3043
+ if (state.destination) {
3044
+ state.destination.log(level, event, {
3045
+ ...bindings,
3046
+ ...fields
3047
+ });
3048
+ return;
3049
+ }
3050
+ enqueue(state, {
3051
+ level,
3052
+ event,
3053
+ fields: { ...fields },
3054
+ bindings: { ...bindings }
3055
+ });
3056
+ };
3057
+ return {
3058
+ stream: state.stream,
3059
+ get level() {
3060
+ return state.destination?.level ?? state.level;
3061
+ },
3062
+ get logDir() {
3063
+ return state.destination?.logDir ?? "";
3064
+ },
3065
+ get filePath() {
3066
+ return state.destination?.filePath ?? "";
3067
+ },
3068
+ child: (childBindings) => buildLogger2(state, {
3069
+ ...bindings,
3070
+ ...childBindings
3071
+ }),
3072
+ log,
3073
+ debug: (event, fields) => log("debug", event, fields),
3074
+ info: (event, fields) => log("info", event, fields),
3075
+ warn: (event, fields) => log("warn", event, fields),
3076
+ error: (event, fields) => log("error", event, fields)
3077
+ };
3078
+ };
3079
+ var createDeferredJsonlLogger = (options) => {
3080
+ const state = {
3081
+ stream: options.stream,
3082
+ level: options.level ?? "debug",
3083
+ destination: null,
3084
+ buffered: [],
3085
+ droppedEntries: 0,
3086
+ maxBufferEntries: Math.max(1, options.maxBufferEntries ?? 2e3)
3087
+ };
3088
+ return {
3089
+ logger: buildLogger2(state, options.bindings ?? {}),
3090
+ activate: () => {
3091
+ if (!state.destination) {
3092
+ state.destination = createJsonlLogger(options);
3093
+ }
3094
+ flushBuffered(state);
3095
+ return state.destination;
3096
+ },
3097
+ isActivated: () => state.destination !== null
3098
+ };
3099
+ };
3100
+
3101
+ // packages/mcp-adapter/src/server.ts
2762
3102
  var DEFAULT_SERVER_NAME = "browser-bridge";
2763
3103
  var DEFAULT_SERVER_VERSION = "0.0.0";
2764
3104
  var DEFAULT_HTTP_HOST = "127.0.0.1";
2765
3105
  var DEFAULT_HTTP_PATH = "/mcp";
3106
+ var ENV_MCP_EAGER = "BROWSER_BRIDGE_MCP_EAGER";
3107
+ var ENV_LEGACY_MCP_EAGER = "BROWSER_VISION_MCP_EAGER";
2766
3108
  var durationMs4 = (startedAt) => Number((Number(process.hrtime.bigint() - startedAt) / 1e6).toFixed(3));
2767
- var resolveAdapterLogger = (options) => options.logger ?? createJsonlLogger({
2768
- stream: "mcp-adapter",
2769
- cwd: options.cwd
3109
+ var parseBoolean2 = (value) => {
3110
+ if (value === void 0) {
3111
+ return void 0;
3112
+ }
3113
+ const normalized = value.trim().toLowerCase();
3114
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
3115
+ return true;
3116
+ }
3117
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
3118
+ return false;
3119
+ }
3120
+ return void 0;
3121
+ };
3122
+ var resolveEagerMode = (explicit) => {
3123
+ if (typeof explicit === "boolean") {
3124
+ return explicit;
3125
+ }
3126
+ const envValue = parseBoolean2(process.env[ENV_MCP_EAGER]) ?? parseBoolean2(process.env[ENV_LEGACY_MCP_EAGER]);
3127
+ return envValue ?? false;
3128
+ };
3129
+ var toCoreClientOptions = (options, logger) => ({
3130
+ host: options.host,
3131
+ port: options.port,
3132
+ cwd: options.cwd,
3133
+ timeoutMs: options.timeoutMs,
3134
+ ensureDaemon: options.ensureDaemon ?? true,
3135
+ componentVersion: options.version ?? DEFAULT_SERVER_VERSION,
3136
+ healthRetryMs: options.healthRetryMs,
3137
+ healthAttempts: options.healthAttempts,
3138
+ fetchImpl: options.fetchImpl,
3139
+ spawnImpl: options.spawnImpl,
3140
+ logger
2770
3141
  });
2771
- var createMcpServer = (options = {}) => {
2772
- const logger = resolveAdapterLogger(options);
3142
+ var buildInitializationError = (error) => {
3143
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown initialization failure.";
3144
+ return {
3145
+ ok: false,
3146
+ error: {
3147
+ code: "UNAVAILABLE",
3148
+ message: `MCP runtime initialization failed: ${message}`,
3149
+ retryable: true,
3150
+ details: {
3151
+ phase: "mcp_runtime_init"
3152
+ }
3153
+ }
3154
+ };
3155
+ };
3156
+ var resolveDeferredLoggerController = (options) => {
3157
+ if (options.logger) {
3158
+ return {
3159
+ logger: options.logger,
3160
+ activate: () => void 0
3161
+ };
3162
+ }
3163
+ const deferred = createDeferredJsonlLogger({
3164
+ stream: "mcp-adapter",
3165
+ cwd: options.cwd
3166
+ });
3167
+ return {
3168
+ logger: deferred.logger,
3169
+ activate: () => {
3170
+ deferred.activate();
3171
+ }
3172
+ };
3173
+ };
3174
+ var createRuntimeController = (options) => {
3175
+ const deferredLogger = resolveDeferredLoggerController(options);
3176
+ const logger = deferredLogger.logger;
3177
+ const coreLogger = logger.child({ scope: "core-client" });
3178
+ let initializedClient = null;
3179
+ let initializationPromise = null;
3180
+ const createClient = async () => {
3181
+ if (options.coreClient) {
3182
+ return options.coreClient;
3183
+ }
3184
+ if (options.coreClientFactory) {
3185
+ return await options.coreClientFactory(coreLogger);
3186
+ }
3187
+ return createCoreClient2(toCoreClientOptions(options, coreLogger));
3188
+ };
3189
+ const proxyClient = {
3190
+ get baseUrl() {
3191
+ return initializedClient?.baseUrl ?? "";
3192
+ },
3193
+ ensureReady: async () => {
3194
+ await ensureInitialized();
3195
+ },
3196
+ post: async (path9, body) => {
3197
+ const client = await ensureInitialized();
3198
+ return client.post(path9, body);
3199
+ }
3200
+ };
3201
+ const ensureInitialized = async () => {
3202
+ if (initializedClient) {
3203
+ return initializedClient;
3204
+ }
3205
+ if (!initializationPromise) {
3206
+ initializationPromise = (async () => {
3207
+ logger.info("mcp.runtime.init.begin");
3208
+ try {
3209
+ const candidate = await createClient();
3210
+ const maybeEnsureReady = candidate.ensureReady;
3211
+ if (typeof maybeEnsureReady === "function") {
3212
+ await maybeEnsureReady.call(candidate);
3213
+ }
3214
+ deferredLogger.activate();
3215
+ initializedClient = candidate;
3216
+ logger.info("mcp.runtime.init.ready", {
3217
+ core_base_url: candidate.baseUrl
3218
+ });
3219
+ return candidate;
3220
+ } catch (error) {
3221
+ logger.error("mcp.runtime.init.failed", {
3222
+ error
3223
+ });
3224
+ throw buildInitializationError(error);
3225
+ }
3226
+ })();
3227
+ initializationPromise = initializationPromise.catch((error) => {
3228
+ initializationPromise = null;
3229
+ throw error;
3230
+ });
3231
+ }
3232
+ return initializationPromise;
3233
+ };
3234
+ return {
3235
+ logger,
3236
+ client: options.coreClient ?? proxyClient,
3237
+ ensureInitialized,
3238
+ isInitialized: () => initializedClient !== null
3239
+ };
3240
+ };
3241
+ var createMcpServerBootstrap = (options = {}) => {
3242
+ const runtime = createRuntimeController(options);
2773
3243
  const server = new import_mcp.McpServer({
2774
3244
  name: options.name ?? DEFAULT_SERVER_NAME,
2775
3245
  version: options.version ?? DEFAULT_SERVER_VERSION
2776
3246
  });
2777
- const client = options.coreClient ?? createCoreClient2({
2778
- ...options,
2779
- logger: logger.child({ scope: "core-client" })
2780
- });
2781
- registerBrowserBridgeTools(server, client);
2782
- logger.info("mcp.server.created", {
3247
+ registerBrowserBridgeTools(server, runtime.ensureInitialized);
3248
+ runtime.logger.info("mcp.server.created", {
2783
3249
  name: options.name ?? DEFAULT_SERVER_NAME,
2784
3250
  version: options.version ?? DEFAULT_SERVER_VERSION,
2785
- core_base_url: client.baseUrl
3251
+ lazy_init: true
2786
3252
  });
2787
- return { server, client };
3253
+ return {
3254
+ server,
3255
+ client: runtime.client,
3256
+ logger: runtime.logger,
3257
+ ensureInitialized: runtime.ensureInitialized,
3258
+ isInitialized: runtime.isInitialized
3259
+ };
2788
3260
  };
2789
3261
  var startMcpServer = async (options = {}) => {
2790
- const logger = resolveAdapterLogger(options);
2791
- logger.info("mcp.stdio.start.begin", {
3262
+ const eager = resolveEagerMode(options.eager);
3263
+ const handle = createMcpServerBootstrap(options);
3264
+ handle.logger.info("mcp.stdio.start.begin", {
2792
3265
  name: options.name ?? DEFAULT_SERVER_NAME,
2793
- version: options.version ?? DEFAULT_SERVER_VERSION
2794
- });
2795
- const handle = createMcpServer({
2796
- ...options,
2797
- logger
3266
+ version: options.version ?? DEFAULT_SERVER_VERSION,
3267
+ eager
2798
3268
  });
2799
3269
  const transport = new import_stdio.StdioServerTransport();
2800
3270
  try {
3271
+ if (eager) {
3272
+ await handle.ensureInitialized();
3273
+ }
2801
3274
  await handle.server.connect(transport);
2802
- logger.info("mcp.stdio.start.ready", {
2803
- core_base_url: handle.client.baseUrl
3275
+ handle.logger.info("mcp.stdio.start.ready", {
3276
+ core_base_url: handle.isInitialized() ? handle.client.baseUrl : null,
3277
+ eager
2804
3278
  });
2805
3279
  } catch (error) {
2806
- logger.error("mcp.stdio.start.failed", {
3280
+ handle.logger.error("mcp.stdio.start.failed", {
2807
3281
  error
2808
3282
  });
2809
3283
  throw error;
2810
3284
  }
2811
- return { ...handle, transport };
3285
+ return {
3286
+ server: handle.server,
3287
+ client: handle.client,
3288
+ transport
3289
+ };
2812
3290
  };
2813
3291
  var readJsonBody = async (req, maxBytes = 5 * 1024 * 1024) => {
2814
3292
  const chunks = [];
2815
3293
  let total = 0;
2816
- await new Promise((resolve3, reject) => {
3294
+ await new Promise((resolve4, reject) => {
2817
3295
  req.on("data", (chunk) => {
2818
3296
  total += chunk.length;
2819
3297
  if (total > maxBytes) {
@@ -2823,7 +3301,7 @@ var readJsonBody = async (req, maxBytes = 5 * 1024 * 1024) => {
2823
3301
  }
2824
3302
  chunks.push(chunk);
2825
3303
  });
2826
- req.on("end", () => resolve3());
3304
+ req.on("end", () => resolve4());
2827
3305
  req.on("error", (err) => reject(err));
2828
3306
  });
2829
3307
  const raw = Buffer.concat(chunks).toString("utf8");
@@ -2842,19 +3320,21 @@ var getHeaderValue = (value) => {
2842
3320
  return void 0;
2843
3321
  };
2844
3322
  var startMcpHttpServer = async (options = {}) => {
2845
- const logger = resolveAdapterLogger(options).child({ scope: "http-server" });
3323
+ const eager = resolveEagerMode(options.eager);
3324
+ const runtime = createRuntimeController(options);
3325
+ const logger = runtime.logger.child({ scope: "http-server" });
2846
3326
  const host = options.host ?? DEFAULT_HTTP_HOST;
2847
3327
  const port = typeof options.port === "number" ? options.port : 0;
2848
3328
  const path9 = options.path ?? DEFAULT_HTTP_PATH;
2849
3329
  logger.info("mcp.http.start.begin", {
2850
3330
  host,
2851
3331
  port,
2852
- path: path9
2853
- });
2854
- const client = options.coreClient ?? createCoreClient2({
2855
- ...options,
2856
- logger: logger.child({ scope: "core-client" })
3332
+ path: path9,
3333
+ eager
2857
3334
  });
3335
+ if (eager) {
3336
+ await runtime.ensureInitialized();
3337
+ }
2858
3338
  const sessions = /* @__PURE__ */ new Map();
2859
3339
  const closeAllSessions = async () => {
2860
3340
  const entries = Array.from(sessions.values());
@@ -2965,7 +3445,7 @@ var startMcpHttpServer = async (options = {}) => {
2965
3445
  name: options.name ?? DEFAULT_SERVER_NAME,
2966
3446
  version: options.version ?? DEFAULT_SERVER_VERSION
2967
3447
  });
2968
- registerBrowserBridgeTools(sessionServer, client);
3448
+ registerBrowserBridgeTools(sessionServer, runtime.ensureInitialized);
2969
3449
  await sessionServer.connect(transport);
2970
3450
  sessionEntry = { transport, server: sessionServer };
2971
3451
  await transport.handleRequest(req, res, parsedBody);
@@ -2989,10 +3469,10 @@ var startMcpHttpServer = async (options = {}) => {
2989
3469
  );
2990
3470
  }
2991
3471
  });
2992
- const resolvedPort = await new Promise((resolve3, reject) => {
3472
+ const resolvedPort = await new Promise((resolve4, reject) => {
2993
3473
  httpServer.listen(port, host, () => {
2994
3474
  const address = httpServer.address();
2995
- resolve3(typeof address === "object" && address ? address.port : port);
3475
+ resolve4(typeof address === "object" && address ? address.port : port);
2996
3476
  });
2997
3477
  httpServer.on("error", reject);
2998
3478
  });
@@ -3000,10 +3480,11 @@ var startMcpHttpServer = async (options = {}) => {
3000
3480
  host,
3001
3481
  port: resolvedPort,
3002
3482
  path: path9,
3003
- core_base_url: client.baseUrl
3483
+ core_base_url: runtime.isInitialized() ? runtime.client.baseUrl : null,
3484
+ eager
3004
3485
  });
3005
3486
  return {
3006
- client,
3487
+ client: runtime.client,
3007
3488
  host,
3008
3489
  port: resolvedPort,
3009
3490
  path: path9,
@@ -3012,12 +3493,12 @@ var startMcpHttpServer = async (options = {}) => {
3012
3493
  host,
3013
3494
  port: resolvedPort
3014
3495
  });
3015
- await new Promise((resolve3, reject) => {
3496
+ await new Promise((resolve4, reject) => {
3016
3497
  httpServer.close((err) => {
3017
3498
  if (err) {
3018
3499
  reject(err);
3019
3500
  } else {
3020
- resolve3();
3501
+ resolve4();
3021
3502
  }
3022
3503
  });
3023
3504
  });
@@ -3054,19 +3535,19 @@ var checkboxPrompt = async (options) => {
3054
3535
  };
3055
3536
 
3056
3537
  // packages/cli/src/installer/mcp-install.ts
3057
- var import_node_child_process3 = require("node:child_process");
3538
+ var import_node_child_process4 = require("node:child_process");
3058
3539
 
3059
3540
  // packages/cli/src/installer/cursor-mcp.ts
3060
3541
  var import_promises2 = __toESM(require("node:fs/promises"));
3061
3542
  var import_node_os = __toESM(require("node:os"));
3062
- var import_node_path4 = __toESM(require("node:path"));
3543
+ var import_node_path5 = __toESM(require("node:path"));
3063
3544
  var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3064
3545
  var resolveCursorUserSettingsPath = (options) => {
3065
3546
  const platform = options?.platform ?? process.platform;
3066
3547
  const homeDir = options?.homeDir ?? import_node_os.default.homedir();
3067
3548
  const env = options?.env ?? process.env;
3068
3549
  if (platform === "darwin") {
3069
- return import_node_path4.default.join(
3550
+ return import_node_path5.default.join(
3070
3551
  homeDir,
3071
3552
  "Library",
3072
3553
  "Application Support",
@@ -3082,13 +3563,13 @@ var resolveCursorUserSettingsPath = (options) => {
3082
3563
  "APPDATA is not set; cannot resolve Cursor settings path."
3083
3564
  );
3084
3565
  }
3085
- return import_node_path4.default.join(appData, "Cursor", "User", "settings.json");
3566
+ return import_node_path5.default.join(appData, "Cursor", "User", "settings.json");
3086
3567
  }
3087
- return import_node_path4.default.join(homeDir, ".config", "Cursor", "User", "settings.json");
3568
+ return import_node_path5.default.join(homeDir, ".config", "Cursor", "User", "settings.json");
3088
3569
  };
3089
3570
  var installCursorMcp = async (settingsPath) => {
3090
3571
  const filePath = settingsPath ?? resolveCursorUserSettingsPath();
3091
- const dir = import_node_path4.default.dirname(filePath);
3572
+ const dir = import_node_path5.default.dirname(filePath);
3092
3573
  await import_promises2.default.mkdir(dir, { recursive: true });
3093
3574
  let settings = {};
3094
3575
  try {
@@ -3124,15 +3605,15 @@ var installCursorMcp = async (settingsPath) => {
3124
3605
 
3125
3606
  // packages/cli/src/installer/mcp-install.ts
3126
3607
  var runQuiet = async (cmd, args) => {
3127
- await new Promise((resolve3, reject) => {
3128
- const child = (0, import_node_child_process3.spawn)(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
3608
+ await new Promise((resolve4, reject) => {
3609
+ const child = (0, import_node_child_process4.spawn)(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
3129
3610
  let stderr = "";
3130
3611
  child.stderr?.on("data", (chunk) => {
3131
3612
  stderr += String(chunk);
3132
3613
  });
3133
3614
  child.on("error", reject);
3134
3615
  child.on("exit", (code) => {
3135
- if (code === 0) resolve3();
3616
+ if (code === 0) resolve4();
3136
3617
  else {
3137
3618
  const suffix = stderr.trim() ? `: ${stderr.trim()}` : "";
3138
3619
  reject(new Error(`${cmd} exited with ${code ?? "unknown"}${suffix}`));
@@ -3211,16 +3692,17 @@ var installMcp = async (harness) => {
3211
3692
  var registerMcpCommand = (program2) => {
3212
3693
  const startServer = async (options, command) => {
3213
3694
  const globals = getGlobalOptions(command);
3214
- const coreClient = createCoreClient({
3215
- host: globals.host,
3216
- port: globals.port,
3217
- ensureDaemon: globals.daemon !== false
3218
- });
3219
3695
  try {
3220
3696
  await startMcpServer({
3221
3697
  name: options.name,
3222
3698
  version: options.version,
3223
- coreClient
3699
+ eager: options.eager,
3700
+ coreClientFactory: (logger) => createCoreClient({
3701
+ host: globals.host,
3702
+ port: globals.port,
3703
+ ensureDaemon: globals.daemon !== false,
3704
+ logger
3705
+ })
3224
3706
  });
3225
3707
  } catch (error) {
3226
3708
  console.error(error);
@@ -3257,15 +3739,10 @@ var registerMcpCommand = (program2) => {
3257
3739
  return { ok: true, result: { installed: results } };
3258
3740
  });
3259
3741
  });
3260
- mcp.option("--name <name>", "MCP server name").option("--version <version>", "MCP server version").action(startServer);
3261
- mcp.command("serve").description("Run the MCP server over stdio").option("--name <name>", "MCP server name").option("--version <version>", "MCP server version").action(startServer);
3262
- mcp.command("serve-http").description("Run the MCP server over Streamable HTTP").option("--name <name>", "MCP server name").option("--version <version>", "MCP server version").option("--host <host>", "HTTP host (default 127.0.0.1)").option("--port <port>", "HTTP port (default random available port)").option("--path <path>", "HTTP path (default /mcp)").action(async (options, command) => {
3742
+ mcp.option("--name <name>", "MCP server name").option("--version <version>", "MCP server version").option("--eager", "Initialize runtime on startup (debug only)").action(startServer);
3743
+ mcp.command("serve").description("Run the MCP server over stdio").option("--name <name>", "MCP server name").option("--version <version>", "MCP server version").option("--eager", "Initialize runtime on startup (debug only)").action(startServer);
3744
+ mcp.command("serve-http").description("Run the MCP server over Streamable HTTP").option("--name <name>", "MCP server name").option("--version <version>", "MCP server version").option("--host <host>", "HTTP host (default 127.0.0.1)").option("--port <port>", "HTTP port (default random available port)").option("--path <path>", "HTTP path (default /mcp)").option("--eager", "Initialize runtime on startup (debug only)").action(async (options, command) => {
3263
3745
  const globals = getGlobalOptions(command);
3264
- const coreClient = createCoreClient({
3265
- host: globals.host,
3266
- port: globals.port,
3267
- ensureDaemon: globals.daemon !== false
3268
- });
3269
3746
  const parsedPort = typeof options.port === "string" && options.port.length > 0 ? Number(options.port) : void 0;
3270
3747
  try {
3271
3748
  const handle = await startMcpHttpServer({
@@ -3274,7 +3751,13 @@ var registerMcpCommand = (program2) => {
3274
3751
  host: options.host,
3275
3752
  port: typeof parsedPort === "number" && Number.isFinite(parsedPort) ? parsedPort : void 0,
3276
3753
  path: options.path,
3277
- coreClient
3754
+ eager: options.eager,
3755
+ coreClientFactory: (logger) => createCoreClient({
3756
+ host: globals.host,
3757
+ port: globals.port,
3758
+ ensureDaemon: globals.daemon !== false,
3759
+ logger
3760
+ })
3278
3761
  });
3279
3762
  console.error(
3280
3763
  `MCP HTTP server listening on http://${handle.host}:${handle.port}${handle.path}`
@@ -3300,10 +3783,10 @@ var import_turndown = __toESM(require("turndown"));
3300
3783
  // packages/core/src/artifacts.ts
3301
3784
  var import_promises3 = require("node:fs/promises");
3302
3785
  var import_node_os2 = __toESM(require("node:os"));
3303
- var import_node_path5 = __toESM(require("node:path"));
3786
+ var import_node_path6 = __toESM(require("node:path"));
3304
3787
  var ARTIFACTS_DIR_NAME = "browser-agent";
3305
3788
  var resolveTempRoot = () => process.env.TMPDIR || process.env.TEMP || process.env.TMP || import_node_os2.default.tmpdir();
3306
- var getArtifactRootDir = (sessionId) => import_node_path5.default.join(resolveTempRoot(), ARTIFACTS_DIR_NAME, sessionId);
3789
+ var getArtifactRootDir = (sessionId) => import_node_path6.default.join(resolveTempRoot(), ARTIFACTS_DIR_NAME, sessionId);
3307
3790
  var ensureArtifactRootDir = async (sessionId) => {
3308
3791
  const rootDir = getArtifactRootDir(sessionId);
3309
3792
  await (0, import_promises3.mkdir)(rootDir, { recursive: true });
@@ -3490,60 +3973,60 @@ var registerSessionCommands = (program2) => {
3490
3973
  // packages/cli/src/commands/skill.ts
3491
3974
  var import_promises7 = __toESM(require("node:fs/promises"));
3492
3975
  var import_node_os4 = __toESM(require("node:os"));
3493
- var import_node_path10 = __toESM(require("node:path"));
3976
+ var import_node_path11 = __toESM(require("node:path"));
3494
3977
 
3495
3978
  // packages/cli/src/installer/harness-targets.ts
3496
3979
  var import_node_os3 = __toESM(require("node:os"));
3497
- var import_node_path6 = __toESM(require("node:path"));
3980
+ var import_node_path7 = __toESM(require("node:path"));
3498
3981
  var getDefaultHarnessTargets = (homeDir) => {
3499
3982
  const home = homeDir ?? import_node_os3.default.homedir();
3500
3983
  return [
3501
3984
  {
3502
3985
  id: "codex",
3503
3986
  label: "Codex",
3504
- skillsDir: import_node_path6.default.join(home, ".agents", "skills"),
3987
+ skillsDir: import_node_path7.default.join(home, ".agents", "skills"),
3505
3988
  supportsMcpInstall: true
3506
3989
  },
3507
3990
  {
3508
3991
  id: "claude",
3509
3992
  label: "Claude",
3510
- skillsDir: import_node_path6.default.join(home, ".claude", "skills"),
3993
+ skillsDir: import_node_path7.default.join(home, ".claude", "skills"),
3511
3994
  supportsMcpInstall: true
3512
3995
  },
3513
3996
  {
3514
3997
  id: "cursor",
3515
3998
  label: "Cursor",
3516
- skillsDir: import_node_path6.default.join(home, ".cursor", "skills"),
3999
+ skillsDir: import_node_path7.default.join(home, ".cursor", "skills"),
3517
4000
  supportsMcpInstall: true
3518
4001
  },
3519
4002
  {
3520
4003
  id: "factory",
3521
4004
  label: "Factory",
3522
- skillsDir: import_node_path6.default.join(home, ".factory", "skills"),
4005
+ skillsDir: import_node_path7.default.join(home, ".factory", "skills"),
3523
4006
  supportsMcpInstall: false
3524
4007
  },
3525
4008
  {
3526
4009
  id: "opencode",
3527
4010
  label: "OpenCode",
3528
- skillsDir: import_node_path6.default.join(home, ".opencode", "skills"),
4011
+ skillsDir: import_node_path7.default.join(home, ".opencode", "skills"),
3529
4012
  supportsMcpInstall: false
3530
4013
  },
3531
4014
  {
3532
4015
  id: "gemini",
3533
4016
  label: "Gemini",
3534
- skillsDir: import_node_path6.default.join(home, ".gemini", "skills"),
4017
+ skillsDir: import_node_path7.default.join(home, ".gemini", "skills"),
3535
4018
  supportsMcpInstall: false
3536
4019
  },
3537
4020
  {
3538
4021
  id: "github",
3539
4022
  label: "GitHub",
3540
- skillsDir: import_node_path6.default.join(home, ".github", "skills"),
4023
+ skillsDir: import_node_path7.default.join(home, ".github", "skills"),
3541
4024
  supportsMcpInstall: false
3542
4025
  },
3543
4026
  {
3544
4027
  id: "ampcode",
3545
4028
  label: "Ampcode",
3546
- skillsDir: import_node_path6.default.join(home, ".ampcode", "skills"),
4029
+ skillsDir: import_node_path7.default.join(home, ".ampcode", "skills"),
3547
4030
  supportsMcpInstall: false
3548
4031
  }
3549
4032
  ];
@@ -3551,7 +4034,7 @@ var getDefaultHarnessTargets = (homeDir) => {
3551
4034
 
3552
4035
  // packages/cli/src/installer/package-info.ts
3553
4036
  var import_promises4 = __toESM(require("node:fs/promises"));
3554
- var import_node_path7 = __toESM(require("node:path"));
4037
+ var import_node_path8 = __toESM(require("node:path"));
3555
4038
  var PACKAGE_NAME = "@btraut/browser-bridge";
3556
4039
  var tryReadJson = async (filePath) => {
3557
4040
  try {
@@ -3564,12 +4047,12 @@ var tryReadJson = async (filePath) => {
3564
4047
  var resolveCliPackageRootDir = async () => {
3565
4048
  let dir = __dirname;
3566
4049
  for (let i = 0; i < 12; i++) {
3567
- const candidate = import_node_path7.default.join(dir, "package.json");
4050
+ const candidate = import_node_path8.default.join(dir, "package.json");
3568
4051
  const parsed = await tryReadJson(candidate);
3569
4052
  if (parsed && parsed.name === PACKAGE_NAME) {
3570
4053
  return dir;
3571
4054
  }
3572
- const parent = import_node_path7.default.dirname(dir);
4055
+ const parent = import_node_path8.default.dirname(dir);
3573
4056
  if (parent === dir) {
3574
4057
  break;
3575
4058
  }
@@ -3581,7 +4064,7 @@ var resolveCliPackageRootDir = async () => {
3581
4064
  };
3582
4065
  var readCliPackageVersion = async () => {
3583
4066
  const rootDir = await resolveCliPackageRootDir();
3584
- const pkgPath = import_node_path7.default.join(rootDir, "package.json");
4067
+ const pkgPath = import_node_path8.default.join(rootDir, "package.json");
3585
4068
  const parsed = await tryReadJson(pkgPath);
3586
4069
  if (!parsed || typeof parsed.version !== "string" || !parsed.version) {
3587
4070
  throw new Error(`Unable to read version from ${pkgPath}`);
@@ -3590,14 +4073,14 @@ var readCliPackageVersion = async () => {
3590
4073
  };
3591
4074
  var resolveSkillSourceDir = async () => {
3592
4075
  const rootDir = await resolveCliPackageRootDir();
3593
- const packaged = import_node_path7.default.join(rootDir, "skills", "browser-bridge");
4076
+ const packaged = import_node_path8.default.join(rootDir, "skills", "browser-bridge");
3594
4077
  try {
3595
4078
  await import_promises4.default.stat(packaged);
3596
4079
  return packaged;
3597
4080
  } catch {
3598
4081
  }
3599
- const repoRoot = import_node_path7.default.resolve(rootDir, "..", "..");
3600
- const docsSkill = import_node_path7.default.join(repoRoot, "docs", "skills", "browser-bridge");
4082
+ const repoRoot = import_node_path8.default.resolve(rootDir, "..", "..");
4083
+ const docsSkill = import_node_path8.default.join(repoRoot, "docs", "skills", "browser-bridge");
3601
4084
  try {
3602
4085
  await import_promises4.default.stat(docsSkill);
3603
4086
  return docsSkill;
@@ -3610,16 +4093,16 @@ var resolveSkillSourceDir = async () => {
3610
4093
 
3611
4094
  // packages/cli/src/installer/skill-install.ts
3612
4095
  var import_promises6 = __toESM(require("node:fs/promises"));
3613
- var import_node_path9 = __toESM(require("node:path"));
4096
+ var import_node_path10 = __toESM(require("node:path"));
3614
4097
 
3615
4098
  // packages/cli/src/installer/skill-manifest.ts
3616
4099
  var import_promises5 = __toESM(require("node:fs/promises"));
3617
- var import_node_path8 = __toESM(require("node:path"));
4100
+ var import_node_path9 = __toESM(require("node:path"));
3618
4101
  var SKILL_MANIFEST_FILENAME = "skill.json";
3619
4102
  var readSkillManifest = async (skillDir) => {
3620
4103
  try {
3621
4104
  const raw = await import_promises5.default.readFile(
3622
- import_node_path8.default.join(skillDir, SKILL_MANIFEST_FILENAME),
4105
+ import_node_path9.default.join(skillDir, SKILL_MANIFEST_FILENAME),
3623
4106
  "utf8"
3624
4107
  );
3625
4108
  const parsed = JSON.parse(raw);
@@ -3634,7 +4117,7 @@ var readSkillManifest = async (skillDir) => {
3634
4117
  var writeSkillManifest = async (skillDir, version) => {
3635
4118
  const payload = { name: "browser-bridge", version };
3636
4119
  await import_promises5.default.writeFile(
3637
- import_node_path8.default.join(skillDir, SKILL_MANIFEST_FILENAME),
4120
+ import_node_path9.default.join(skillDir, SKILL_MANIFEST_FILENAME),
3638
4121
  JSON.stringify(payload, null, 2) + "\n",
3639
4122
  "utf8"
3640
4123
  );
@@ -3642,7 +4125,7 @@ var writeSkillManifest = async (skillDir, version) => {
3642
4125
 
3643
4126
  // packages/cli/src/installer/skill-install.ts
3644
4127
  var installBrowserBridgeSkill = async (options) => {
3645
- const destDir = import_node_path9.default.join(options.destSkillsDir, "browser-bridge");
4128
+ const destDir = import_node_path10.default.join(options.destSkillsDir, "browser-bridge");
3646
4129
  await import_promises6.default.mkdir(options.destSkillsDir, { recursive: true });
3647
4130
  await import_promises6.default.rm(destDir, { recursive: true, force: true });
3648
4131
  await import_promises6.default.cp(options.srcSkillDir, destDir, { recursive: true });
@@ -3654,9 +4137,9 @@ var installBrowserBridgeSkill = async (options) => {
3654
4137
  var getHarnessMarkerDir = (homeDir, harness) => {
3655
4138
  switch (harness) {
3656
4139
  case "codex":
3657
- return import_node_path10.default.join(homeDir, ".agents");
4140
+ return import_node_path11.default.join(homeDir, ".agents");
3658
4141
  default:
3659
- return import_node_path10.default.join(homeDir, `.${harness}`);
4142
+ return import_node_path11.default.join(homeDir, `.${harness}`);
3660
4143
  }
3661
4144
  };
3662
4145
  var registerSkillCommands = (program2) => {
@@ -3734,7 +4217,7 @@ var registerSkillCommands = (program2) => {
3734
4217
  const targets = getDefaultHarnessTargets();
3735
4218
  const rows = [];
3736
4219
  for (const t of targets) {
3737
- const skillDir = import_node_path10.default.join(t.skillsDir, "browser-bridge");
4220
+ const skillDir = import_node_path11.default.join(t.skillsDir, "browser-bridge");
3738
4221
  let installed = false;
3739
4222
  try {
3740
4223
  await import_promises7.default.stat(skillDir);
@@ -3762,7 +4245,7 @@ var registerSkillCommands = (program2) => {
3762
4245
  // packages/cli/src/commands/install.ts
3763
4246
  var import_promises8 = __toESM(require("node:fs/promises"));
3764
4247
  var import_node_os5 = __toESM(require("node:os"));
3765
- var import_node_path11 = __toESM(require("node:path"));
4248
+ var import_node_path12 = __toESM(require("node:path"));
3766
4249
  var formatInstallSummary = (options) => {
3767
4250
  const wantsSkill = options.setup.includes("skill");
3768
4251
  const wantsMcp = options.setup.includes("mcp");
@@ -3797,9 +4280,9 @@ var getHarnessMarkerDir2 = (harness) => {
3797
4280
  const homeDir = import_node_os5.default.homedir();
3798
4281
  switch (harness) {
3799
4282
  case "codex":
3800
- return import_node_path11.default.join(homeDir, ".agents");
4283
+ return import_node_path12.default.join(homeDir, ".agents");
3801
4284
  default:
3802
- return import_node_path11.default.join(homeDir, `.${harness}`);
4285
+ return import_node_path12.default.join(homeDir, `.${harness}`);
3803
4286
  }
3804
4287
  };
3805
4288
  var registerInstallCommand = (program2) => {