@hua-labs/tap 0.5.0 → 0.5.2

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.mjs CHANGED
@@ -1022,16 +1022,22 @@ import * as os3 from "os";
1022
1022
  import * as path10 from "path";
1023
1023
  import { spawnSync as spawnSync2 } from "child_process";
1024
1024
  import { fileURLToPath as fileURLToPath2 } from "url";
1025
+ function resolveProbeCommand(candidate) {
1026
+ return resolveCommandPath(candidate) ?? candidate;
1027
+ }
1028
+ function probeCommandVersion(command) {
1029
+ return spawnSync2(command, ["--version"], {
1030
+ encoding: "utf-8",
1031
+ windowsHide: true
1032
+ });
1033
+ }
1025
1034
  function probeCommand(candidates) {
1026
1035
  for (const candidate of candidates) {
1027
- const result = spawnSync2(candidate, ["--version"], {
1028
- encoding: "utf-8",
1029
- shell: process.platform === "win32"
1030
- });
1036
+ const resolvedCommand = resolveProbeCommand(candidate);
1037
+ const result = probeCommandVersion(resolvedCommand);
1031
1038
  if (result.status === 0) {
1032
1039
  const version2 = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim() || null;
1033
- const absolutePath = resolveCommandPath(candidate);
1034
- return { command: absolutePath ?? candidate, version: version2 };
1040
+ return { command: resolvedCommand, version: version2 };
1035
1041
  }
1036
1042
  }
1037
1043
  return { command: null, version: null };
@@ -1133,19 +1139,16 @@ function findPreferredBunCommand() {
1133
1139
  const candidates = process.platform === "win32" ? [path10.join(home, ".bun", "bin", "bun.exe"), "bun", "bun.cmd"] : [path10.join(home, ".bun", "bin", "bun"), "bun"];
1134
1140
  for (const candidate of candidates) {
1135
1141
  if (path10.isAbsolute(candidate) && !fs10.existsSync(candidate)) continue;
1136
- const result = spawnSync2(candidate, ["--version"], {
1137
- encoding: "utf-8",
1138
- shell: process.platform === "win32"
1139
- });
1142
+ const resolvedCommand = resolveProbeCommand(candidate);
1143
+ const result = probeCommandVersion(resolvedCommand);
1140
1144
  if (result.status === 0) {
1141
- return path10.isAbsolute(candidate) ? toForwardSlashPath(candidate) : candidate;
1145
+ return path10.isAbsolute(resolvedCommand) ? toForwardSlashPath(resolvedCommand) : resolvedCommand;
1142
1146
  }
1143
1147
  }
1144
1148
  return null;
1145
1149
  }
1146
1150
  function buildManagedMcpServerSpec(ctx, instanceId) {
1147
1151
  const sourcePath = findTapCommsServerEntry(ctx);
1148
- const bunCommand = findPreferredBunCommand();
1149
1152
  const warnings = [];
1150
1153
  const issues = [];
1151
1154
  const env = {
@@ -1165,7 +1168,7 @@ function buildManagedMcpServerSpec(ctx, instanceId) {
1165
1168
  }
1166
1169
  const isBundled = sourcePath.endsWith(".mjs");
1167
1170
  const isEphemeralSource = isEphemeralPath(sourcePath);
1168
- let command = null;
1171
+ let command;
1169
1172
  let args = [toForwardSlashPath(sourcePath)];
1170
1173
  if (isEphemeralSource && isBundled) {
1171
1174
  command = "npx";
@@ -1179,7 +1182,7 @@ function buildManagedMcpServerSpec(ctx, instanceId) {
1179
1182
  );
1180
1183
  command = nodeProbe.command ?? "node";
1181
1184
  } else {
1182
- command = bunCommand;
1185
+ command = findPreferredBunCommand();
1183
1186
  }
1184
1187
  if (!command) {
1185
1188
  issues.push(
@@ -1388,13 +1391,14 @@ function startWindowsDetachedProcess(command, args, repoRoot, logPath, env = pro
1388
1391
  }
1389
1392
  return pid;
1390
1393
  }
1391
- function startWindowsCodexAppServer(command, url, repoRoot, logPath) {
1394
+ function startWindowsCodexAppServer(command, url, repoRoot, logPath, env = process.env) {
1392
1395
  const { command: exe, prefixArgs } = splitResolvedCommand(command);
1393
1396
  return startWindowsDetachedProcess(
1394
1397
  exe,
1395
1398
  [...prefixArgs, "app-server", "--listen", url],
1396
1399
  repoRoot,
1397
- logPath
1400
+ logPath,
1401
+ env
1398
1402
  );
1399
1403
  }
1400
1404
  function findListeningProcessId(url, platform) {
@@ -1514,14 +1518,14 @@ function startUnixDetachedProcess(command, args, repoRoot, logPath, env = proces
1514
1518
  }
1515
1519
  }
1516
1520
  }
1517
- function startUnixCodexAppServer(command, url, repoRoot, logPath, platform = DEFAULT_UNIX_PLATFORM) {
1521
+ function startUnixCodexAppServer(command, url, repoRoot, logPath, env = process.env, platform = DEFAULT_UNIX_PLATFORM) {
1518
1522
  const { command: exe, prefixArgs } = splitResolvedCommand(command);
1519
1523
  return startUnixDetachedProcess(
1520
1524
  exe,
1521
1525
  [...prefixArgs, "app-server", "--listen", url],
1522
1526
  repoRoot,
1523
1527
  logPath,
1524
- process.env,
1528
+ env,
1525
1529
  platform
1526
1530
  );
1527
1531
  }
@@ -2337,6 +2341,19 @@ var init_bridge_app_server_auth = __esm({
2337
2341
  // src/engine/bridge-app-server-lifecycle.ts
2338
2342
  import * as fs18 from "fs";
2339
2343
  import * as path17 from "path";
2344
+ function buildCodexAppServerEnv(options) {
2345
+ return {
2346
+ ...process.env,
2347
+ TAP_COMMS_DIR: options.commsDir,
2348
+ TAP_STATE_DIR: options.stateDir,
2349
+ TAP_RUNTIME_STATE_DIR: options.runtimeStateDir,
2350
+ TAP_REPO_ROOT: options.repoRoot,
2351
+ TAP_BRIDGE_INSTANCE_ID: options.instanceId,
2352
+ TAP_AGENT_ID: options.instanceId,
2353
+ TAP_AGENT_NAME: options.agentName,
2354
+ CODEX_TAP_AGENT_NAME: options.agentName
2355
+ };
2356
+ }
2340
2357
  function isAppServerUsedByOtherBridge(stateDir, excludeInstanceId, appServer) {
2341
2358
  const pidDir = path17.join(stateDir, "pids");
2342
2359
  if (!fs18.existsSync(pidDir)) return false;
@@ -2440,6 +2457,7 @@ Start the app-server manually:
2440
2457
  const logPath = appServerLogFilePath(options.stateDir, options.instanceId);
2441
2458
  fs18.mkdirSync(path17.dirname(logPath), { recursive: true });
2442
2459
  rotateLog(logPath);
2460
+ const appServerEnv = buildCodexAppServerEnv(options);
2443
2461
  if (options.noAuth) {
2444
2462
  const manualCommand2 = formatCodexAppServerCommand("codex", effectiveUrl);
2445
2463
  let pid2;
@@ -2449,7 +2467,8 @@ Start the app-server manually:
2449
2467
  resolvedCommand,
2450
2468
  effectiveUrl,
2451
2469
  options.repoRoot,
2452
- logPath
2470
+ logPath,
2471
+ appServerEnv
2453
2472
  );
2454
2473
  } catch (err) {
2455
2474
  throw new Error(
@@ -2466,6 +2485,7 @@ Start it manually:
2466
2485
  effectiveUrl,
2467
2486
  options.repoRoot,
2468
2487
  logPath,
2488
+ appServerEnv,
2469
2489
  options.platform
2470
2490
  );
2471
2491
  } catch (err) {
@@ -2526,7 +2546,8 @@ Or start it manually:
2526
2546
  resolvedCommand,
2527
2547
  auth.upstreamUrl,
2528
2548
  options.repoRoot,
2529
- logPath
2549
+ logPath,
2550
+ appServerEnv
2530
2551
  );
2531
2552
  } catch (err) {
2532
2553
  if (auth.gatewayPid != null) {
@@ -2547,6 +2568,7 @@ Start it manually:
2547
2568
  auth.upstreamUrl,
2548
2569
  options.repoRoot,
2549
2570
  logPath,
2571
+ appServerEnv,
2550
2572
  options.platform
2551
2573
  );
2552
2574
  } catch (err) {
@@ -2777,9 +2799,12 @@ async function startBridge(options) {
2777
2799
  appServer = await ensureCodexAppServer({
2778
2800
  instanceId,
2779
2801
  stateDir,
2802
+ runtimeStateDir,
2803
+ commsDir,
2780
2804
  repoRoot,
2781
2805
  platform: options.platform,
2782
2806
  appServerUrl: effectiveAppServerUrl,
2807
+ agentName: resolvedAgent,
2783
2808
  existingAppServer: previousAppServer,
2784
2809
  noAuth: options.noAuth
2785
2810
  });
@@ -2800,7 +2825,9 @@ async function startBridge(options) {
2800
2825
  const bridgeEnv = {
2801
2826
  ...runtimeEnv,
2802
2827
  TAP_COMMS_DIR: commsDir,
2803
- TAP_STATE_DIR: runtimeStateDir,
2828
+ TAP_STATE_DIR: stateDir,
2829
+ TAP_RUNTIME_STATE_DIR: runtimeStateDir,
2830
+ TAP_REPO_ROOT: repoRoot,
2804
2831
  TAP_BRIDGE_RUNTIME: runtime,
2805
2832
  TAP_BRIDGE_INSTANCE_ID: instanceId,
2806
2833
  TAP_AGENT_ID: instanceId,
@@ -4354,8 +4381,24 @@ function resolveTuiConnectUrl(appServer) {
4354
4381
  function quoteCliArg(value) {
4355
4382
  return `"${value.replace(/"/g, '\\"')}"`;
4356
4383
  }
4357
- function formatCodexTuiAttachCommand(tuiConnectUrl, cwd) {
4358
- return `codex --enable tui_app_server --remote ${quoteCliArg(tuiConnectUrl)} --cd ${quoteCliArg(cwd)}`;
4384
+ function quoteShellEnvValue(value) {
4385
+ if (process.platform === "win32") {
4386
+ return `'${value.replace(/'/g, "''")}'`;
4387
+ }
4388
+ return `'${value.replace(/'/g, `'\\''`)}'`;
4389
+ }
4390
+ function formatCodexTuiAttachCommand(tuiConnectUrl, cwd, env = {}) {
4391
+ const base = `codex --enable tui_app_server --remote ${quoteCliArg(tuiConnectUrl)} --cd ${quoteCliArg(cwd)}`;
4392
+ const entries = Object.entries(env).filter(([, value]) => value.length > 0);
4393
+ if (entries.length === 0) {
4394
+ return base;
4395
+ }
4396
+ if (process.platform === "win32") {
4397
+ const envPrefix2 = entries.map(([key, value]) => `$env:${key} = ${quoteShellEnvValue(value)}`).join("; ");
4398
+ return `${envPrefix2}; ${base}`;
4399
+ }
4400
+ const envPrefix = entries.map(([key, value]) => `${key}=${quoteShellEnvValue(value)}`).join(" ");
4401
+ return `${envPrefix} ${base}`;
4359
4402
  }
4360
4403
  function resolveTuiAttachCwd(repoRoot, stateRepoRoot, runtimeThreadCwd, savedThreadCwd) {
4361
4404
  return runtimeThreadCwd ?? savedThreadCwd ?? stateRepoRoot ?? repoRoot;
@@ -5352,8 +5395,62 @@ var init_bridge_watch = __esm({
5352
5395
  }
5353
5396
  });
5354
5397
 
5355
- // src/commands/bridge-status.ts
5398
+ // src/engine/health-monitor.ts
5399
+ import * as fs26 from "fs";
5356
5400
  import * as path28 from "path";
5401
+ function getHeartbeatActivityMs2(record) {
5402
+ const timestamp = new Date(record.lastActivity ?? record.timestamp ?? 0).getTime();
5403
+ return Number.isFinite(timestamp) ? timestamp : null;
5404
+ }
5405
+ function isSameInstanceHeartbeat2(key, heartbeat, instanceId) {
5406
+ if (heartbeat.instanceId === instanceId) return true;
5407
+ if (heartbeat.connectHash === `instance:${instanceId}`) return true;
5408
+ return key === instanceId || key.replace(/_/g, "-") === instanceId || key.replace(/-/g, "_") === instanceId;
5409
+ }
5410
+ function loadLiveDispatchEvidence(commsDir, instanceId) {
5411
+ const heartbeatsPath = path28.join(commsDir, "heartbeats.json");
5412
+ if (!fs26.existsSync(heartbeatsPath)) return null;
5413
+ try {
5414
+ const store = JSON.parse(
5415
+ fs26.readFileSync(heartbeatsPath, "utf-8")
5416
+ );
5417
+ let best = null;
5418
+ let bestActivityMs = -1;
5419
+ for (const [key, heartbeat] of Object.entries(store)) {
5420
+ if (!isSameInstanceHeartbeat2(key, heartbeat, instanceId)) continue;
5421
+ if (heartbeat.source !== "bridge-dispatch") continue;
5422
+ if (heartbeat.bridgePid == null || !isProcessAlive(heartbeat.bridgePid)) {
5423
+ continue;
5424
+ }
5425
+ const activityMs = getHeartbeatActivityMs2(heartbeat);
5426
+ if (activityMs == null || Date.now() - activityMs > DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS) {
5427
+ continue;
5428
+ }
5429
+ if (activityMs > bestActivityMs) {
5430
+ bestActivityMs = activityMs;
5431
+ best = {
5432
+ bridgePid: heartbeat.bridgePid,
5433
+ lastActivity: heartbeat.lastActivity ?? heartbeat.timestamp ?? new Date(activityMs).toISOString()
5434
+ };
5435
+ }
5436
+ }
5437
+ return best;
5438
+ } catch {
5439
+ return null;
5440
+ }
5441
+ }
5442
+ var DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS, HEARTBEAT_FRESH_THRESHOLD_MS;
5443
+ var init_health_monitor = __esm({
5444
+ "src/engine/health-monitor.ts"() {
5445
+ "use strict";
5446
+ init_bridge_process_control();
5447
+ DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS = 2 * 60 * 1e3;
5448
+ HEARTBEAT_FRESH_THRESHOLD_MS = 2 * 60 * 1e3;
5449
+ }
5450
+ });
5451
+
5452
+ // src/commands/bridge-status.ts
5453
+ import * as path29 from "path";
5357
5454
  function bridgeStatusAll() {
5358
5455
  const repoRoot = findRepoRoot();
5359
5456
  const state = loadState(repoRoot);
@@ -5402,23 +5499,26 @@ function bridgeStatusAll() {
5402
5499
  };
5403
5500
  continue;
5404
5501
  }
5405
- const status = getBridgeStatus(stateDir, instanceId);
5502
+ const rawStatus = getBridgeStatus(stateDir, instanceId);
5406
5503
  const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
5407
- const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(bridgeState);
5408
- const savedThread = loadRuntimeBridgeThreadState(bridgeState);
5409
- const lifecycle = deriveBridgeLifecycleState({
5410
- bridgeStatus: status,
5504
+ const liveDispatch = rawStatus === "running" ? null : loadLiveDispatchEvidence(state.commsDir, instanceId);
5505
+ const surfaceBridgeState = liveDispatch ? null : bridgeState;
5506
+ const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(surfaceBridgeState);
5507
+ const savedThread = loadRuntimeBridgeThreadState(surfaceBridgeState);
5508
+ const status = liveDispatch ? "dispatch-live" : rawStatus;
5509
+ const lifecycle = liveDispatch ? deriveBridgeLifecycleState({ bridgeStatus: "stopped" }) : deriveBridgeLifecycleState({
5510
+ bridgeStatus: rawStatus,
5411
5511
  bridgeState,
5412
5512
  runtimeHeartbeat,
5413
5513
  savedThread,
5414
5514
  persistedLifecycle: inst.bridgeLifecycle ?? bridgeState?.lifecycle ?? null
5415
5515
  });
5416
- const session = status === "running" ? deriveCodexSessionState({
5516
+ const session = rawStatus === "running" || liveDispatch ? deriveCodexSessionState({
5417
5517
  runtimeHeartbeat,
5418
- runtimeStateDir: bridgeState?.runtimeStateDir ?? null
5518
+ runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
5419
5519
  }) : null;
5420
- const age = getHeartbeatAge(stateDir, instanceId);
5421
- if (lifecycle.status === "bridge-stale" && inst.bridge) {
5520
+ const age = liveDispatch ? null : getHeartbeatAge(stateDir, instanceId);
5521
+ if (rawStatus === "stale" && inst.bridge) {
5422
5522
  state.instances[instanceId] = {
5423
5523
  ...inst,
5424
5524
  bridge: null,
@@ -5430,22 +5530,22 @@ function bridgeStatusAll() {
5430
5530
  };
5431
5531
  stateChanged = true;
5432
5532
  }
5433
- const pid = bridgeState?.pid ?? null;
5434
- const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
5533
+ const pid = surfaceBridgeState?.pid ?? null;
5534
+ const heartbeat = liveDispatch ? null : getBridgeHeartbeatTimestamp(stateDir, instanceId);
5435
5535
  const pidStr = pid ? String(pid) : "-";
5436
5536
  const portStr = inst.port ? String(inst.port) : "-";
5437
5537
  const ageStr = age !== null ? formatAge(age) : "-";
5438
5538
  log(
5439
5539
  `${instanceId.padEnd(20)} ${inst.runtime.padEnd(8)} ${status.padEnd(10)} ${lifecycle.status.padEnd(20)} ${(session?.status ?? "-").padEnd(18)} ${pidStr.padEnd(8)} ${portStr.padEnd(6)} ${ageStr}`
5440
5540
  );
5441
- if (bridgeState?.appServer) {
5442
- log(` App server: ${formatAppServerState(bridgeState.appServer)}`);
5443
- if (bridgeState.appServer.logPath) {
5444
- log(` Server log: ${bridgeState.appServer.logPath}`);
5541
+ if (surfaceBridgeState?.appServer) {
5542
+ log(` App server: ${formatAppServerState(surfaceBridgeState.appServer)}`);
5543
+ if (surfaceBridgeState.appServer.logPath) {
5544
+ log(` Server log: ${surfaceBridgeState.appServer.logPath}`);
5445
5545
  }
5446
- if (bridgeState.appServer.auth) {
5546
+ if (surfaceBridgeState.appServer.auth) {
5447
5547
  log(
5448
- ` Protected: ${redactProtectedUrl(bridgeState.appServer.auth.protectedUrl)}`
5548
+ ` Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
5449
5549
  );
5450
5550
  }
5451
5551
  }
@@ -5463,6 +5563,11 @@ function bridgeStatusAll() {
5463
5563
  if (transition) {
5464
5564
  log(` Transition: ${transition}`);
5465
5565
  }
5566
+ if (liveDispatch) {
5567
+ log(
5568
+ ` Drift: fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
5569
+ );
5570
+ }
5466
5571
  const turnInfo = getTurnInfo(stateDir, instanceId);
5467
5572
  if (turnInfo?.activeTurnId) {
5468
5573
  const ageStr2 = turnInfo.ageSeconds != null ? formatAge(turnInfo.ageSeconds) : "?";
@@ -5488,7 +5593,7 @@ function bridgeStatusAll() {
5488
5593
  threadCwd: runtimeHeartbeat?.threadCwd ?? null,
5489
5594
  savedThreadId: savedThread?.threadId ?? null,
5490
5595
  savedThreadCwd: savedThread?.cwd ?? null,
5491
- appServer: bridgeState?.appServer ?? null
5596
+ appServer: surfaceBridgeState?.appServer ?? null
5492
5597
  };
5493
5598
  }
5494
5599
  if (instanceIds.length === 0) {
@@ -5585,14 +5690,17 @@ function bridgeStatusOne(identifier) {
5585
5690
  }
5586
5691
  const { config: resolvedCfg2 } = resolveConfig({}, repoRoot);
5587
5692
  const stateDir = resolvedCfg2.stateDir;
5588
- const status = getBridgeStatus(stateDir, instanceId);
5693
+ const rawStatus = getBridgeStatus(stateDir, instanceId);
5589
5694
  const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
5590
- const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(bridgeState);
5591
- const savedThread = loadRuntimeBridgeThreadState(bridgeState);
5592
- const age = getHeartbeatAge(stateDir, instanceId);
5593
- const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
5594
- const lifecycle = deriveBridgeLifecycleState({
5595
- bridgeStatus: status,
5695
+ const liveDispatch = rawStatus === "running" ? null : loadLiveDispatchEvidence(state.commsDir, instanceId);
5696
+ const surfaceBridgeState = liveDispatch ? null : bridgeState;
5697
+ const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(surfaceBridgeState);
5698
+ const savedThread = loadRuntimeBridgeThreadState(surfaceBridgeState);
5699
+ const age = liveDispatch ? null : getHeartbeatAge(stateDir, instanceId);
5700
+ const heartbeat = liveDispatch ? null : getBridgeHeartbeatTimestamp(stateDir, instanceId);
5701
+ const status = liveDispatch ? "dispatch-live" : rawStatus;
5702
+ const lifecycle = liveDispatch ? deriveBridgeLifecycleState({ bridgeStatus: "stopped" }) : deriveBridgeLifecycleState({
5703
+ bridgeStatus: rawStatus,
5596
5704
  bridgeState,
5597
5705
  runtimeHeartbeat,
5598
5706
  savedThread,
@@ -5600,13 +5708,25 @@ function bridgeStatusOne(identifier) {
5600
5708
  });
5601
5709
  const session = deriveCodexSessionState({
5602
5710
  runtimeHeartbeat,
5603
- runtimeStateDir: bridgeState?.runtimeStateDir ?? null
5711
+ runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
5604
5712
  });
5605
5713
  log(`Status: ${status}`);
5606
5714
  log(`Lifecycle: ${lifecycle.summary}`);
5607
5715
  log(`Session: ${session.summary}`);
5608
- if (bridgeState) {
5609
- log(`PID: ${bridgeState.pid}`);
5716
+ if (rawStatus === "stale" && inst.bridge) {
5717
+ state.instances[instanceId] = {
5718
+ ...inst,
5719
+ bridge: null,
5720
+ bridgeLifecycle: transitionBridgeLifecycle(
5721
+ inst.bridgeLifecycle ?? inst.bridge?.lifecycle ?? null,
5722
+ "crashed",
5723
+ "bridge pid not alive"
5724
+ )
5725
+ };
5726
+ saveState(repoRoot, state);
5727
+ }
5728
+ if (surfaceBridgeState) {
5729
+ log(`PID: ${surfaceBridgeState.pid}`);
5610
5730
  log(
5611
5731
  `Heartbeat: ${heartbeat ?? "-"}${age !== null ? ` (${formatAge(age)})` : ""}`
5612
5732
  );
@@ -5621,35 +5741,35 @@ function bridgeStatusOne(identifier) {
5621
5741
  );
5622
5742
  }
5623
5743
  log(
5624
- `Log: ${path28.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
5744
+ `Log: ${path29.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
5625
5745
  );
5626
- if (bridgeState.appServer) {
5627
- log(`App server: ${bridgeState.appServer.url}`);
5628
- log(`Server PID: ${bridgeState.appServer.pid ?? "-"}`);
5746
+ if (surfaceBridgeState.appServer) {
5747
+ log(`App server: ${surfaceBridgeState.appServer.url}`);
5748
+ log(`Server PID: ${surfaceBridgeState.appServer.pid ?? "-"}`);
5629
5749
  log(
5630
- `Server mode: ${bridgeState.appServer.managed ? "managed" : "external"}`
5750
+ `Server mode: ${surfaceBridgeState.appServer.managed ? "managed" : "external"}`
5631
5751
  );
5632
5752
  log(
5633
- `Health: ${bridgeState.appServer.healthy ? "healthy" : "unhealthy"}`
5753
+ `Health: ${surfaceBridgeState.appServer.healthy ? "healthy" : "unhealthy"}`
5634
5754
  );
5635
- log(`Checked: ${bridgeState.appServer.lastCheckedAt}`);
5636
- if (bridgeState.appServer.logPath) {
5637
- log(`Server log: ${bridgeState.appServer.logPath}`);
5755
+ log(`Checked: ${surfaceBridgeState.appServer.lastCheckedAt}`);
5756
+ if (surfaceBridgeState.appServer.logPath) {
5757
+ log(`Server log: ${surfaceBridgeState.appServer.logPath}`);
5638
5758
  }
5639
- if (bridgeState.appServer.auth) {
5640
- log(`Auth: ${bridgeState.appServer.auth.mode}`);
5759
+ if (surfaceBridgeState.appServer.auth) {
5760
+ log(`Auth: ${surfaceBridgeState.appServer.auth.mode}`);
5641
5761
  log(
5642
- `Protected: ${redactProtectedUrl(bridgeState.appServer.auth.protectedUrl)}`
5762
+ `Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
5643
5763
  );
5644
- log(`Upstream: ${bridgeState.appServer.auth.upstreamUrl}`);
5645
- log(`TUI connect: ${bridgeState.appServer.auth.upstreamUrl}`);
5646
- log(`Gateway PID: ${bridgeState.appServer.auth.gatewayPid ?? "-"}`);
5647
- if (bridgeState.appServer.auth.gatewayLogPath) {
5648
- log(`Gateway log: ${bridgeState.appServer.auth.gatewayLogPath}`);
5764
+ log(`Upstream: ${surfaceBridgeState.appServer.auth.upstreamUrl}`);
5765
+ log(`TUI connect: ${surfaceBridgeState.appServer.auth.upstreamUrl}`);
5766
+ log(`Gateway PID: ${surfaceBridgeState.appServer.auth.gatewayPid ?? "-"}`);
5767
+ if (surfaceBridgeState.appServer.auth.gatewayLogPath) {
5768
+ log(`Gateway log: ${surfaceBridgeState.appServer.auth.gatewayLogPath}`);
5649
5769
  }
5650
- } else if (bridgeState.appServer.managed) {
5770
+ } else if (surfaceBridgeState.appServer.managed) {
5651
5771
  log(`Auth: none (--no-auth)`);
5652
- log(`TUI connect: ${bridgeState.appServer.url}`);
5772
+ log(`TUI connect: ${surfaceBridgeState.appServer.url}`);
5653
5773
  }
5654
5774
  }
5655
5775
  }
@@ -5657,6 +5777,11 @@ function bridgeStatusOne(identifier) {
5657
5777
  if (transition) {
5658
5778
  log(`Transition: ${transition}`);
5659
5779
  }
5780
+ if (liveDispatch) {
5781
+ log(
5782
+ `Drift: fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
5783
+ );
5784
+ }
5660
5785
  log("");
5661
5786
  return {
5662
5787
  ok: true,
@@ -5686,14 +5811,14 @@ function bridgeStatusOne(identifier) {
5686
5811
  lastDispatchAt: session.lastDispatchAt
5687
5812
  },
5688
5813
  bridgeMode: inst.bridgeMode,
5689
- pid: bridgeState?.pid ?? null,
5814
+ pid: surfaceBridgeState?.pid ?? null,
5690
5815
  port: inst.port,
5691
5816
  lastHeartbeat: heartbeat,
5692
5817
  threadId: runtimeHeartbeat?.threadId ?? null,
5693
5818
  threadCwd: runtimeHeartbeat?.threadCwd ?? null,
5694
5819
  savedThreadId: savedThread?.threadId ?? null,
5695
5820
  savedThreadCwd: savedThread?.cwd ?? null,
5696
- appServer: bridgeState?.appServer ?? null
5821
+ appServer: surfaceBridgeState?.appServer ?? null
5697
5822
  }
5698
5823
  };
5699
5824
  }
@@ -5702,6 +5827,7 @@ var init_bridge_status = __esm({
5702
5827
  "use strict";
5703
5828
  init_state();
5704
5829
  init_bridge();
5830
+ init_health_monitor();
5705
5831
  init_config();
5706
5832
  init_utils();
5707
5833
  init_bridge_helpers();
@@ -5796,7 +5922,23 @@ function bridgeTuiOne(identifier) {
5796
5922
  runtimeHeartbeat?.threadCwd,
5797
5923
  savedThread?.cwd
5798
5924
  );
5799
- const attachCommand = formatCodexTuiAttachCommand(tuiConnectUrl, attachCwd);
5925
+ const attachEnv = {
5926
+ TAP_BRIDGE_INSTANCE_ID: instanceId,
5927
+ TAP_AGENT_ID: instanceId,
5928
+ TAP_COMMS_DIR: resolvedConfig.commsDir,
5929
+ TAP_STATE_DIR: stateDir,
5930
+ TAP_RUNTIME_STATE_DIR: bridgeState?.runtimeStateDir ?? getBridgeRuntimeStateDir(repoRoot, instanceId),
5931
+ TAP_REPO_ROOT: repoRoot
5932
+ };
5933
+ if (typeof inst.agentName === "string" && inst.agentName.trim()) {
5934
+ attachEnv.TAP_AGENT_NAME = inst.agentName;
5935
+ attachEnv.CODEX_TAP_AGENT_NAME = inst.agentName;
5936
+ }
5937
+ const attachCommand = formatCodexTuiAttachCommand(
5938
+ tuiConnectUrl,
5939
+ attachCwd,
5940
+ attachEnv
5941
+ );
5800
5942
  const warnings = appServer.auth != null ? [
5801
5943
  "Use the upstream TUI URL, not the protected gateway URL. The protected URL is bridge-only."
5802
5944
  ] : [];
@@ -5821,6 +5963,7 @@ function bridgeTuiOne(identifier) {
5821
5963
  tuiConnectUrl,
5822
5964
  attachCwd,
5823
5965
  attachCommand,
5966
+ attachEnv,
5824
5967
  appServer
5825
5968
  }
5826
5969
  };
@@ -6856,8 +6999,8 @@ init_dashboard();
6856
6999
  init_utils();
6857
7000
  init_config();
6858
7001
  init_state();
6859
- import * as fs26 from "fs";
6860
- import * as path29 from "path";
7002
+ import * as fs27 from "fs";
7003
+ import * as path30 from "path";
6861
7004
  function getDashboardSnapshot(options) {
6862
7005
  const repoRoot = options?.repoRoot ?? findRepoRoot();
6863
7006
  return collectDashboardSnapshot(repoRoot, options?.commsDir);
@@ -6919,9 +7062,9 @@ function getHealthReport(options) {
6919
7062
  }
6920
7063
  }
6921
7064
  }
6922
- const tmpDir = path29.join(repoRoot, ".tmp");
6923
- if (fs26.existsSync(tmpDir)) {
6924
- for (const dir of fs26.readdirSync(tmpDir)) {
7065
+ const tmpDir = path30.join(repoRoot, ".tmp");
7066
+ if (fs27.existsSync(tmpDir)) {
7067
+ for (const dir of fs27.readdirSync(tmpDir)) {
6925
7068
  if (!dir.startsWith("codex-app-server-bridge")) continue;
6926
7069
  const suffix = dir.replace("codex-app-server-bridge-", "");
6927
7070
  if (activeMatchers.size > 0) {
@@ -6934,10 +7077,10 @@ function getHealthReport(options) {
6934
7077
  }
6935
7078
  if (!matched) continue;
6936
7079
  }
6937
- const hsPath = path29.join(tmpDir, dir, "headless-state.json");
6938
- if (!fs26.existsSync(hsPath)) continue;
7080
+ const hsPath = path30.join(tmpDir, dir, "headless-state.json");
7081
+ if (!fs27.existsSync(hsPath)) continue;
6939
7082
  try {
6940
- const hs = JSON.parse(fs26.readFileSync(hsPath, "utf-8"));
7083
+ const hs = JSON.parse(fs27.readFileSync(hsPath, "utf-8"));
6941
7084
  headlessStates.push({ instanceDir: dir, ...hs });
6942
7085
  } catch {
6943
7086
  }