@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/cli.mjs CHANGED
@@ -1432,16 +1432,22 @@ import * as os2 from "os";
1432
1432
  import * as path8 from "path";
1433
1433
  import { spawnSync as spawnSync2 } from "child_process";
1434
1434
  import { fileURLToPath as fileURLToPath2 } from "url";
1435
+ function resolveProbeCommand(candidate) {
1436
+ return resolveCommandPath(candidate) ?? candidate;
1437
+ }
1438
+ function probeCommandVersion(command) {
1439
+ return spawnSync2(command, ["--version"], {
1440
+ encoding: "utf-8",
1441
+ windowsHide: true
1442
+ });
1443
+ }
1435
1444
  function probeCommand(candidates) {
1436
1445
  for (const candidate of candidates) {
1437
- const result = spawnSync2(candidate, ["--version"], {
1438
- encoding: "utf-8",
1439
- shell: process.platform === "win32"
1440
- });
1446
+ const resolvedCommand = resolveProbeCommand(candidate);
1447
+ const result = probeCommandVersion(resolvedCommand);
1441
1448
  if (result.status === 0) {
1442
1449
  const version2 = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim() || null;
1443
- const absolutePath = resolveCommandPath(candidate);
1444
- return { command: absolutePath ?? candidate, version: version2 };
1450
+ return { command: resolvedCommand, version: version2 };
1445
1451
  }
1446
1452
  }
1447
1453
  return { command: null, version: null };
@@ -1543,19 +1549,16 @@ function findPreferredBunCommand() {
1543
1549
  const candidates = process.platform === "win32" ? [path8.join(home, ".bun", "bin", "bun.exe"), "bun", "bun.cmd"] : [path8.join(home, ".bun", "bin", "bun"), "bun"];
1544
1550
  for (const candidate of candidates) {
1545
1551
  if (path8.isAbsolute(candidate) && !fs9.existsSync(candidate)) continue;
1546
- const result = spawnSync2(candidate, ["--version"], {
1547
- encoding: "utf-8",
1548
- shell: process.platform === "win32"
1549
- });
1552
+ const resolvedCommand = resolveProbeCommand(candidate);
1553
+ const result = probeCommandVersion(resolvedCommand);
1550
1554
  if (result.status === 0) {
1551
- return path8.isAbsolute(candidate) ? toForwardSlashPath(candidate) : candidate;
1555
+ return path8.isAbsolute(resolvedCommand) ? toForwardSlashPath(resolvedCommand) : resolvedCommand;
1552
1556
  }
1553
1557
  }
1554
1558
  return null;
1555
1559
  }
1556
1560
  function buildManagedMcpServerSpec(ctx, instanceId) {
1557
1561
  const sourcePath = findTapCommsServerEntry(ctx);
1558
- const bunCommand = findPreferredBunCommand();
1559
1562
  const warnings = [];
1560
1563
  const issues = [];
1561
1564
  const env = {
@@ -1575,7 +1578,7 @@ function buildManagedMcpServerSpec(ctx, instanceId) {
1575
1578
  }
1576
1579
  const isBundled = sourcePath.endsWith(".mjs");
1577
1580
  const isEphemeralSource = isEphemeralPath(sourcePath);
1578
- let command = null;
1581
+ let command;
1579
1582
  let args = [toForwardSlashPath(sourcePath)];
1580
1583
  if (isEphemeralSource && isBundled) {
1581
1584
  command = "npx";
@@ -1589,7 +1592,7 @@ function buildManagedMcpServerSpec(ctx, instanceId) {
1589
1592
  );
1590
1593
  command = nodeProbe.command ?? "node";
1591
1594
  } else {
1592
- command = bunCommand;
1595
+ command = findPreferredBunCommand();
1593
1596
  }
1594
1597
  if (!command) {
1595
1598
  issues.push(
@@ -2930,13 +2933,14 @@ function startWindowsDetachedProcess(command, args, repoRoot, logPath, env = pro
2930
2933
  }
2931
2934
  return pid;
2932
2935
  }
2933
- function startWindowsCodexAppServer(command, url, repoRoot, logPath) {
2936
+ function startWindowsCodexAppServer(command, url, repoRoot, logPath, env = process.env) {
2934
2937
  const { command: exe, prefixArgs } = splitResolvedCommand(command);
2935
2938
  return startWindowsDetachedProcess(
2936
2939
  exe,
2937
2940
  [...prefixArgs, "app-server", "--listen", url],
2938
2941
  repoRoot,
2939
- logPath
2942
+ logPath,
2943
+ env
2940
2944
  );
2941
2945
  }
2942
2946
  function findListeningProcessId(url, platform) {
@@ -3046,14 +3050,14 @@ function startUnixDetachedProcess(command, args, repoRoot, logPath, env = proces
3046
3050
  }
3047
3051
  }
3048
3052
  }
3049
- function startUnixCodexAppServer(command, url, repoRoot, logPath, platform = DEFAULT_UNIX_PLATFORM) {
3053
+ function startUnixCodexAppServer(command, url, repoRoot, logPath, env = process.env, platform = DEFAULT_UNIX_PLATFORM) {
3050
3054
  const { command: exe, prefixArgs } = splitResolvedCommand(command);
3051
3055
  return startUnixDetachedProcess(
3052
3056
  exe,
3053
3057
  [...prefixArgs, "app-server", "--listen", url],
3054
3058
  repoRoot,
3055
3059
  logPath,
3056
- process.env,
3060
+ env,
3057
3061
  platform
3058
3062
  );
3059
3063
  }
@@ -4022,6 +4026,19 @@ import * as path21 from "path";
4022
4026
  var DEFAULT_APP_SERVER_URL3 = "ws://127.0.0.1:4501";
4023
4027
  var APP_SERVER_START_TIMEOUT_MS = 2e4;
4024
4028
  var APP_SERVER_GATEWAY_START_TIMEOUT_MS = 5e3;
4029
+ function buildCodexAppServerEnv(options) {
4030
+ return {
4031
+ ...process.env,
4032
+ TAP_COMMS_DIR: options.commsDir,
4033
+ TAP_STATE_DIR: options.stateDir,
4034
+ TAP_RUNTIME_STATE_DIR: options.runtimeStateDir,
4035
+ TAP_REPO_ROOT: options.repoRoot,
4036
+ TAP_BRIDGE_INSTANCE_ID: options.instanceId,
4037
+ TAP_AGENT_ID: options.instanceId,
4038
+ TAP_AGENT_NAME: options.agentName,
4039
+ CODEX_TAP_AGENT_NAME: options.agentName
4040
+ };
4041
+ }
4025
4042
  function isAppServerUsedByOtherBridge(stateDir, excludeInstanceId, appServer) {
4026
4043
  const pidDir = path21.join(stateDir, "pids");
4027
4044
  if (!fs24.existsSync(pidDir)) return false;
@@ -4125,6 +4142,7 @@ Start the app-server manually:
4125
4142
  const logPath = appServerLogFilePath(options.stateDir, options.instanceId);
4126
4143
  fs24.mkdirSync(path21.dirname(logPath), { recursive: true });
4127
4144
  rotateLog(logPath);
4145
+ const appServerEnv = buildCodexAppServerEnv(options);
4128
4146
  if (options.noAuth) {
4129
4147
  const manualCommand2 = formatCodexAppServerCommand("codex", effectiveUrl);
4130
4148
  let pid2;
@@ -4134,7 +4152,8 @@ Start the app-server manually:
4134
4152
  resolvedCommand,
4135
4153
  effectiveUrl,
4136
4154
  options.repoRoot,
4137
- logPath
4155
+ logPath,
4156
+ appServerEnv
4138
4157
  );
4139
4158
  } catch (err) {
4140
4159
  throw new Error(
@@ -4151,6 +4170,7 @@ Start it manually:
4151
4170
  effectiveUrl,
4152
4171
  options.repoRoot,
4153
4172
  logPath,
4173
+ appServerEnv,
4154
4174
  options.platform
4155
4175
  );
4156
4176
  } catch (err) {
@@ -4211,7 +4231,8 @@ Or start it manually:
4211
4231
  resolvedCommand,
4212
4232
  auth.upstreamUrl,
4213
4233
  options.repoRoot,
4214
- logPath
4234
+ logPath,
4235
+ appServerEnv
4215
4236
  );
4216
4237
  } catch (err) {
4217
4238
  if (auth.gatewayPid != null) {
@@ -4232,6 +4253,7 @@ Start it manually:
4232
4253
  auth.upstreamUrl,
4233
4254
  options.repoRoot,
4234
4255
  logPath,
4256
+ appServerEnv,
4235
4257
  options.platform
4236
4258
  );
4237
4259
  } catch (err) {
@@ -4444,9 +4466,12 @@ async function startBridge(options) {
4444
4466
  appServer = await ensureCodexAppServer({
4445
4467
  instanceId,
4446
4468
  stateDir,
4469
+ runtimeStateDir,
4470
+ commsDir,
4447
4471
  repoRoot,
4448
4472
  platform: options.platform,
4449
4473
  appServerUrl: effectiveAppServerUrl,
4474
+ agentName: resolvedAgent,
4450
4475
  existingAppServer: previousAppServer,
4451
4476
  noAuth: options.noAuth
4452
4477
  });
@@ -4467,7 +4492,9 @@ async function startBridge(options) {
4467
4492
  const bridgeEnv = {
4468
4493
  ...runtimeEnv,
4469
4494
  TAP_COMMS_DIR: commsDir,
4470
- TAP_STATE_DIR: runtimeStateDir,
4495
+ TAP_STATE_DIR: stateDir,
4496
+ TAP_RUNTIME_STATE_DIR: runtimeStateDir,
4497
+ TAP_REPO_ROOT: repoRoot,
4471
4498
  TAP_BRIDGE_RUNTIME: runtime,
4472
4499
  TAP_BRIDGE_INSTANCE_ID: instanceId,
4473
4500
  TAP_AGENT_ID: instanceId,
@@ -5051,6 +5078,53 @@ async function addCommand(args) {
5051
5078
  };
5052
5079
  }
5053
5080
 
5081
+ // src/engine/health-monitor.ts
5082
+ import * as fs27 from "fs";
5083
+ import * as path24 from "path";
5084
+ var DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS = 2 * 60 * 1e3;
5085
+ function getHeartbeatActivityMs2(record) {
5086
+ const timestamp = new Date(record.lastActivity ?? record.timestamp ?? 0).getTime();
5087
+ return Number.isFinite(timestamp) ? timestamp : null;
5088
+ }
5089
+ function isSameInstanceHeartbeat2(key, heartbeat, instanceId) {
5090
+ if (heartbeat.instanceId === instanceId) return true;
5091
+ if (heartbeat.connectHash === `instance:${instanceId}`) return true;
5092
+ return key === instanceId || key.replace(/_/g, "-") === instanceId || key.replace(/-/g, "_") === instanceId;
5093
+ }
5094
+ function loadLiveDispatchEvidence(commsDir, instanceId) {
5095
+ const heartbeatsPath = path24.join(commsDir, "heartbeats.json");
5096
+ if (!fs27.existsSync(heartbeatsPath)) return null;
5097
+ try {
5098
+ const store = JSON.parse(
5099
+ fs27.readFileSync(heartbeatsPath, "utf-8")
5100
+ );
5101
+ let best = null;
5102
+ let bestActivityMs = -1;
5103
+ for (const [key, heartbeat] of Object.entries(store)) {
5104
+ if (!isSameInstanceHeartbeat2(key, heartbeat, instanceId)) continue;
5105
+ if (heartbeat.source !== "bridge-dispatch") continue;
5106
+ if (heartbeat.bridgePid == null || !isProcessAlive(heartbeat.bridgePid)) {
5107
+ continue;
5108
+ }
5109
+ const activityMs = getHeartbeatActivityMs2(heartbeat);
5110
+ if (activityMs == null || Date.now() - activityMs > DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS) {
5111
+ continue;
5112
+ }
5113
+ if (activityMs > bestActivityMs) {
5114
+ bestActivityMs = activityMs;
5115
+ best = {
5116
+ bridgePid: heartbeat.bridgePid,
5117
+ lastActivity: heartbeat.lastActivity ?? heartbeat.timestamp ?? new Date(activityMs).toISOString()
5118
+ };
5119
+ }
5120
+ }
5121
+ return best;
5122
+ } catch {
5123
+ return null;
5124
+ }
5125
+ }
5126
+ var HEARTBEAT_FRESH_THRESHOLD_MS = 2 * 60 * 1e3;
5127
+
5054
5128
  // src/commands/status.ts
5055
5129
  init_config();
5056
5130
  init_utils();
@@ -5064,12 +5138,13 @@ Description:
5064
5138
  Examples:
5065
5139
  npx @hua-labs/tap status
5066
5140
  `.trim();
5067
- function resolveStatus(inst, stateDir) {
5141
+ function resolveStatus(inst, stateDir, commsDir) {
5068
5142
  if (!inst.installed) {
5069
5143
  return {
5070
5144
  status: "not installed",
5071
5145
  lifecycle: null,
5072
- session: null
5146
+ session: null,
5147
+ warnings: []
5073
5148
  };
5074
5149
  }
5075
5150
  switch (inst.bridgeMode) {
@@ -5078,9 +5153,11 @@ function resolveStatus(inst, stateDir) {
5078
5153
  return {
5079
5154
  status: inst.lastVerifiedAt ? "active" : "configured",
5080
5155
  lifecycle: null,
5081
- session: null
5156
+ session: null,
5157
+ warnings: []
5082
5158
  };
5083
5159
  case "app-server": {
5160
+ let staleLifecycle = null;
5084
5161
  if (inst.bridge) {
5085
5162
  const lifecycle = resolveBridgeLifecycleSnapshot(
5086
5163
  stateDir,
@@ -5088,21 +5165,40 @@ function resolveStatus(inst, stateDir) {
5088
5165
  inst.bridge
5089
5166
  );
5090
5167
  if (lifecycle.status === "bridge-stale") {
5168
+ staleLifecycle = lifecycle;
5091
5169
  inst.bridge = null;
5170
+ } else {
5171
+ const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(inst.bridge);
5092
5172
  return {
5093
- status: inst.lastVerifiedAt ? "configured" : "installed",
5173
+ status: "active",
5094
5174
  lifecycle,
5095
- session: null
5175
+ session: deriveCodexSessionState({
5176
+ runtimeHeartbeat,
5177
+ runtimeStateDir: inst.bridge.runtimeStateDir ?? null
5178
+ }),
5179
+ warnings: []
5096
5180
  };
5097
5181
  }
5098
- const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(inst.bridge);
5182
+ }
5183
+ const liveDispatch = loadLiveDispatchEvidence(commsDir, inst.instanceId);
5184
+ if (liveDispatch) {
5099
5185
  return {
5100
- status: "active",
5101
- lifecycle,
5102
- session: deriveCodexSessionState({
5103
- runtimeHeartbeat,
5104
- runtimeStateDir: inst.bridge.runtimeStateDir ?? null
5105
- })
5186
+ status: "dispatch-live",
5187
+ lifecycle: deriveBridgeLifecycleState({
5188
+ bridgeStatus: "stopped"
5189
+ }),
5190
+ session: deriveCodexSessionState({ runtimeHeartbeat: null }),
5191
+ warnings: [
5192
+ `fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
5193
+ ]
5194
+ };
5195
+ }
5196
+ if (staleLifecycle) {
5197
+ return {
5198
+ status: inst.lastVerifiedAt ? "configured" : "installed",
5199
+ lifecycle: staleLifecycle,
5200
+ session: null,
5201
+ warnings: []
5106
5202
  };
5107
5203
  }
5108
5204
  return {
@@ -5110,25 +5206,27 @@ function resolveStatus(inst, stateDir) {
5110
5206
  lifecycle: deriveBridgeLifecycleState({
5111
5207
  bridgeStatus: "stopped"
5112
5208
  }),
5113
- session: deriveCodexSessionState({ runtimeHeartbeat: null })
5209
+ session: deriveCodexSessionState({ runtimeHeartbeat: null }),
5210
+ warnings: []
5114
5211
  };
5115
5212
  }
5116
5213
  default:
5117
5214
  return {
5118
5215
  status: "installed",
5119
5216
  lifecycle: null,
5120
- session: null
5217
+ session: null,
5218
+ warnings: []
5121
5219
  };
5122
5220
  }
5123
5221
  }
5124
- function instanceStatusLine(inst, status, lifecycle, session) {
5222
+ function instanceStatusLine(inst, status, lifecycle, session, warnings) {
5125
5223
  const bridgeInfo = inst.bridge ? ` (pid: ${inst.bridge.pid})` : "";
5126
5224
  const lifecycleStr = lifecycle?.status ?? "-";
5127
5225
  const sessionStr = session?.status ?? "-";
5128
5226
  const mode = inst.bridgeMode;
5129
5227
  const portStr = inst.port ? ` port:${inst.port}` : "";
5130
5228
  const restart = inst.restartRequired ? " [restart required]" : "";
5131
- const warns = inst.warnings.length > 0 ? ` [${inst.warnings.length} warning(s)]` : "";
5229
+ const warns = warnings.length > 0 ? ` [${warnings.length} warning(s)]` : "";
5132
5230
  return `${inst.instanceId.padEnd(20)} ${inst.runtime.padEnd(8)} ${status.padEnd(14)} ${lifecycleStr.padEnd(20)} ${sessionStr.padEnd(18)} ${mode.padEnd(14)}${bridgeInfo}${portStr}${restart}${warns}`;
5133
5231
  }
5134
5232
  async function statusCommand(args) {
@@ -5181,10 +5279,15 @@ async function statusCommand(args) {
5181
5279
  for (const id of installed) {
5182
5280
  const inst = state.instances[id];
5183
5281
  if (inst) {
5184
- const { status, lifecycle, session } = resolveStatus(inst, stateDir);
5185
- log(instanceStatusLine(inst, status, lifecycle, session));
5186
- if (inst.warnings.length > 0) {
5187
- for (const w of inst.warnings) {
5282
+ const { status, lifecycle, session, warnings } = resolveStatus(
5283
+ inst,
5284
+ stateDir,
5285
+ state.commsDir
5286
+ );
5287
+ const mergedWarnings = [...inst.warnings, ...warnings];
5288
+ log(instanceStatusLine(inst, status, lifecycle, session, mergedWarnings));
5289
+ if (mergedWarnings.length > 0) {
5290
+ for (const w of mergedWarnings) {
5188
5291
  logWarn(` ${w}`);
5189
5292
  }
5190
5293
  }
@@ -5196,7 +5299,7 @@ async function statusCommand(args) {
5196
5299
  bridgeMode: inst.bridgeMode,
5197
5300
  bridge: inst.bridge,
5198
5301
  port: inst.port,
5199
- warnings: inst.warnings
5302
+ warnings: mergedWarnings
5200
5303
  };
5201
5304
  }
5202
5305
  }
@@ -5227,7 +5330,7 @@ async function statusCommand(args) {
5227
5330
  init_utils();
5228
5331
 
5229
5332
  // src/engine/rollback.ts
5230
- import * as fs27 from "fs";
5333
+ import * as fs28 from "fs";
5231
5334
  async function rollbackRuntime(_instanceId, runtimeState) {
5232
5335
  const errors = [];
5233
5336
  const restoredFiles = [];
@@ -5256,7 +5359,7 @@ async function rollbackRuntime(_instanceId, runtimeState) {
5256
5359
  };
5257
5360
  }
5258
5361
  function rollbackArtifact(artifact) {
5259
- if (!fs27.existsSync(artifact.path)) {
5362
+ if (!fs28.existsSync(artifact.path)) {
5260
5363
  return { restored: false, error: `File not found: ${artifact.path}` };
5261
5364
  }
5262
5365
  switch (artifact.kind) {
@@ -5274,7 +5377,7 @@ function rollbackArtifact(artifact) {
5274
5377
  }
5275
5378
  }
5276
5379
  function rollbackJsonPath(artifact) {
5277
- const raw = fs27.readFileSync(artifact.path, "utf-8");
5380
+ const raw = fs28.readFileSync(artifact.path, "utf-8");
5278
5381
  let config;
5279
5382
  try {
5280
5383
  config = JSON.parse(raw);
@@ -5300,18 +5403,18 @@ function rollbackJsonPath(artifact) {
5300
5403
  cleanEmptyParents(config, artifact.selector);
5301
5404
  }
5302
5405
  const tmp = `${artifact.path}.tmp.${process.pid}`;
5303
- fs27.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
5304
- fs27.renameSync(tmp, artifact.path);
5406
+ fs28.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
5407
+ fs28.renameSync(tmp, artifact.path);
5305
5408
  return { restored: true };
5306
5409
  }
5307
5410
  function rollbackTomlTable(artifact) {
5308
- const content = fs27.readFileSync(artifact.path, "utf-8");
5411
+ const content = fs28.readFileSync(artifact.path, "utf-8");
5309
5412
  const backup = artifact.backupPath ? readArtifactBackup(artifact.backupPath) : null;
5310
5413
  if (backup?.kind === "toml-table" && backup.selector === artifact.selector) {
5311
5414
  const nextContent = backup.existed ? replaceTomlTable(content, artifact.selector, backup.content ?? "") : removeTomlTable(content, artifact.selector);
5312
5415
  const tmp2 = `${artifact.path}.tmp.${process.pid}`;
5313
- fs27.writeFileSync(tmp2, nextContent, "utf-8");
5314
- fs27.renameSync(tmp2, artifact.path);
5416
+ fs28.writeFileSync(tmp2, nextContent, "utf-8");
5417
+ fs28.renameSync(tmp2, artifact.path);
5315
5418
  return { restored: true };
5316
5419
  }
5317
5420
  if (!extractTomlTable(content, artifact.selector)) {
@@ -5321,13 +5424,13 @@ function rollbackTomlTable(artifact) {
5321
5424
  };
5322
5425
  }
5323
5426
  const tmp = `${artifact.path}.tmp.${process.pid}`;
5324
- fs27.writeFileSync(tmp, removeTomlTable(content, artifact.selector), "utf-8");
5325
- fs27.renameSync(tmp, artifact.path);
5427
+ fs28.writeFileSync(tmp, removeTomlTable(content, artifact.selector), "utf-8");
5428
+ fs28.renameSync(tmp, artifact.path);
5326
5429
  return { restored: true };
5327
5430
  }
5328
5431
  function rollbackFile(artifact) {
5329
- if (fs27.existsSync(artifact.path)) {
5330
- fs27.unlinkSync(artifact.path);
5432
+ if (fs28.existsSync(artifact.path)) {
5433
+ fs28.unlinkSync(artifact.path);
5331
5434
  return { restored: true };
5332
5435
  }
5333
5436
  return { restored: false, error: `File not found: ${artifact.path}` };
@@ -5502,13 +5605,13 @@ async function removeCommand(args) {
5502
5605
  init_utils();
5503
5606
 
5504
5607
  // src/commands/bridge-start.ts
5505
- import * as path26 from "path";
5608
+ import * as path27 from "path";
5506
5609
  init_instance_config();
5507
5610
  init_config();
5508
5611
  init_utils();
5509
5612
 
5510
5613
  // src/commands/bridge-helpers.ts
5511
- import * as path24 from "path";
5614
+ import * as path25 from "path";
5512
5615
  function formatAge(seconds) {
5513
5616
  if (seconds < 60) return `${seconds}s ago`;
5514
5617
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
@@ -5538,8 +5641,24 @@ function resolveTuiConnectUrl(appServer) {
5538
5641
  function quoteCliArg(value) {
5539
5642
  return `"${value.replace(/"/g, '\\"')}"`;
5540
5643
  }
5541
- function formatCodexTuiAttachCommand(tuiConnectUrl, cwd) {
5542
- return `codex --enable tui_app_server --remote ${quoteCliArg(tuiConnectUrl)} --cd ${quoteCliArg(cwd)}`;
5644
+ function quoteShellEnvValue(value) {
5645
+ if (process.platform === "win32") {
5646
+ return `'${value.replace(/'/g, "''")}'`;
5647
+ }
5648
+ return `'${value.replace(/'/g, `'\\''`)}'`;
5649
+ }
5650
+ function formatCodexTuiAttachCommand(tuiConnectUrl, cwd, env = {}) {
5651
+ const base = `codex --enable tui_app_server --remote ${quoteCliArg(tuiConnectUrl)} --cd ${quoteCliArg(cwd)}`;
5652
+ const entries = Object.entries(env).filter(([, value]) => value.length > 0);
5653
+ if (entries.length === 0) {
5654
+ return base;
5655
+ }
5656
+ if (process.platform === "win32") {
5657
+ const envPrefix2 = entries.map(([key, value]) => `$env:${key} = ${quoteShellEnvValue(value)}`).join("; ");
5658
+ return `${envPrefix2}; ${base}`;
5659
+ }
5660
+ const envPrefix = entries.map(([key, value]) => `${key}=${quoteShellEnvValue(value)}`).join(" ");
5661
+ return `${envPrefix} ${base}`;
5543
5662
  }
5544
5663
  function resolveTuiAttachCwd(repoRoot, stateRepoRoot, runtimeThreadCwd, savedThreadCwd) {
5545
5664
  return runtimeThreadCwd ?? savedThreadCwd ?? stateRepoRoot ?? repoRoot;
@@ -5554,7 +5673,7 @@ function formatThreadSummary(threadId, cwd) {
5554
5673
  return cwd ? `${threadId} (${cwd})` : threadId;
5555
5674
  }
5556
5675
  function normalizeComparablePath(value) {
5557
- return path24.resolve(value).replace(/\\/g, "/").toLowerCase();
5676
+ return path25.resolve(value).replace(/\\/g, "/").toLowerCase();
5558
5677
  }
5559
5678
  function sameOptionalPath(left, right) {
5560
5679
  if (!left || !right) {
@@ -5626,22 +5745,22 @@ function transferManagedAppServerOwnership(state, stateDir, recipientId, appServ
5626
5745
  }
5627
5746
 
5628
5747
  // src/commands/bridge-heartbeat.ts
5629
- import { existsSync as existsSync25, readFileSync as readFileSync21, renameSync as renameSync13, writeFileSync as writeFileSync14 } from "fs";
5630
- import * as path25 from "path";
5748
+ import { existsSync as existsSync26, readFileSync as readFileSync22, renameSync as renameSync13, writeFileSync as writeFileSync14 } from "fs";
5749
+ import * as path26 from "path";
5631
5750
  var BRIDGE_UP_ACTIVE_HEARTBEAT_WINDOW_MS = 10 * 60 * 1e3;
5632
5751
  var BRIDGE_UP_ORPHAN_HEARTBEAT_WINDOW_MS = 24 * 60 * 60 * 1e3;
5633
5752
  var BRIDGE_UP_SIGNING_OFF_HEARTBEAT_WINDOW_MS = 5 * 60 * 1e3;
5634
5753
  function loadBridgeHeartbeatStore(commsDir) {
5635
- const heartbeatsPath = path25.join(commsDir, "heartbeats.json");
5636
- if (!existsSync25(heartbeatsPath)) return {};
5754
+ const heartbeatsPath = path26.join(commsDir, "heartbeats.json");
5755
+ if (!existsSync26(heartbeatsPath)) return {};
5637
5756
  try {
5638
- return JSON.parse(readFileSync21(heartbeatsPath, "utf-8"));
5757
+ return JSON.parse(readFileSync22(heartbeatsPath, "utf-8"));
5639
5758
  } catch {
5640
5759
  return null;
5641
5760
  }
5642
5761
  }
5643
5762
  function saveBridgeHeartbeatStore(commsDir, store) {
5644
- const heartbeatsPath = path25.join(commsDir, "heartbeats.json");
5763
+ const heartbeatsPath = path26.join(commsDir, "heartbeats.json");
5645
5764
  const tmp = `${heartbeatsPath}.tmp.${process.pid}`;
5646
5765
  writeFileSync14(tmp, JSON.stringify(store, null, 2), "utf-8");
5647
5766
  renameSync13(tmp, heartbeatsPath);
@@ -5948,7 +6067,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
5948
6067
  }
5949
6068
  }
5950
6069
  logSuccess(`Bridge started (PID: ${bridge.pid})`);
5951
- log(`Log: ${path26.join(ctx.stateDir, "logs", `bridge-${instanceId}.log`)}`);
6070
+ log(`Log: ${path27.join(ctx.stateDir, "logs", `bridge-${instanceId}.log`)}`);
5952
6071
  if (bridge.appServer) {
5953
6072
  log(`App server: ${formatAppServerState(bridge.appServer)}`);
5954
6073
  if (bridge.appServer.logPath) {
@@ -6073,7 +6192,7 @@ async function bridgeStartAll(flags = {}) {
6073
6192
  warnings.push(msg);
6074
6193
  continue;
6075
6194
  }
6076
- const stateDir = path26.join(repoRoot, ".tap-comms");
6195
+ const stateDir = path27.join(repoRoot, ".tap-comms");
6077
6196
  const currentBridgeState = loadBridgeState(stateDir, instanceId);
6078
6197
  const { manageAppServer, noAuth } = inferRestartMode(
6079
6198
  currentBridgeState,
@@ -6492,7 +6611,7 @@ async function bridgeWatch(_intervalSeconds, stuckThresholdSeconds) {
6492
6611
  }
6493
6612
 
6494
6613
  // src/commands/bridge-status.ts
6495
- import * as path27 from "path";
6614
+ import * as path28 from "path";
6496
6615
  init_config();
6497
6616
  init_utils();
6498
6617
  function bridgeStatusAll() {
@@ -6543,23 +6662,26 @@ function bridgeStatusAll() {
6543
6662
  };
6544
6663
  continue;
6545
6664
  }
6546
- const status = getBridgeStatus(stateDir, instanceId);
6665
+ const rawStatus = getBridgeStatus(stateDir, instanceId);
6547
6666
  const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
6548
- const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(bridgeState);
6549
- const savedThread = loadRuntimeBridgeThreadState(bridgeState);
6550
- const lifecycle = deriveBridgeLifecycleState({
6551
- bridgeStatus: status,
6667
+ const liveDispatch = rawStatus === "running" ? null : loadLiveDispatchEvidence(state.commsDir, instanceId);
6668
+ const surfaceBridgeState = liveDispatch ? null : bridgeState;
6669
+ const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(surfaceBridgeState);
6670
+ const savedThread = loadRuntimeBridgeThreadState(surfaceBridgeState);
6671
+ const status = liveDispatch ? "dispatch-live" : rawStatus;
6672
+ const lifecycle = liveDispatch ? deriveBridgeLifecycleState({ bridgeStatus: "stopped" }) : deriveBridgeLifecycleState({
6673
+ bridgeStatus: rawStatus,
6552
6674
  bridgeState,
6553
6675
  runtimeHeartbeat,
6554
6676
  savedThread,
6555
6677
  persistedLifecycle: inst.bridgeLifecycle ?? bridgeState?.lifecycle ?? null
6556
6678
  });
6557
- const session = status === "running" ? deriveCodexSessionState({
6679
+ const session = rawStatus === "running" || liveDispatch ? deriveCodexSessionState({
6558
6680
  runtimeHeartbeat,
6559
- runtimeStateDir: bridgeState?.runtimeStateDir ?? null
6681
+ runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
6560
6682
  }) : null;
6561
- const age = getHeartbeatAge(stateDir, instanceId);
6562
- if (lifecycle.status === "bridge-stale" && inst.bridge) {
6683
+ const age = liveDispatch ? null : getHeartbeatAge(stateDir, instanceId);
6684
+ if (rawStatus === "stale" && inst.bridge) {
6563
6685
  state.instances[instanceId] = {
6564
6686
  ...inst,
6565
6687
  bridge: null,
@@ -6571,22 +6693,22 @@ function bridgeStatusAll() {
6571
6693
  };
6572
6694
  stateChanged = true;
6573
6695
  }
6574
- const pid = bridgeState?.pid ?? null;
6575
- const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
6696
+ const pid = surfaceBridgeState?.pid ?? null;
6697
+ const heartbeat = liveDispatch ? null : getBridgeHeartbeatTimestamp(stateDir, instanceId);
6576
6698
  const pidStr = pid ? String(pid) : "-";
6577
6699
  const portStr = inst.port ? String(inst.port) : "-";
6578
6700
  const ageStr = age !== null ? formatAge(age) : "-";
6579
6701
  log(
6580
6702
  `${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}`
6581
6703
  );
6582
- if (bridgeState?.appServer) {
6583
- log(` App server: ${formatAppServerState(bridgeState.appServer)}`);
6584
- if (bridgeState.appServer.logPath) {
6585
- log(` Server log: ${bridgeState.appServer.logPath}`);
6704
+ if (surfaceBridgeState?.appServer) {
6705
+ log(` App server: ${formatAppServerState(surfaceBridgeState.appServer)}`);
6706
+ if (surfaceBridgeState.appServer.logPath) {
6707
+ log(` Server log: ${surfaceBridgeState.appServer.logPath}`);
6586
6708
  }
6587
- if (bridgeState.appServer.auth) {
6709
+ if (surfaceBridgeState.appServer.auth) {
6588
6710
  log(
6589
- ` Protected: ${redactProtectedUrl(bridgeState.appServer.auth.protectedUrl)}`
6711
+ ` Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
6590
6712
  );
6591
6713
  }
6592
6714
  }
@@ -6604,6 +6726,11 @@ function bridgeStatusAll() {
6604
6726
  if (transition) {
6605
6727
  log(` Transition: ${transition}`);
6606
6728
  }
6729
+ if (liveDispatch) {
6730
+ log(
6731
+ ` Drift: fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
6732
+ );
6733
+ }
6607
6734
  const turnInfo = getTurnInfo(stateDir, instanceId);
6608
6735
  if (turnInfo?.activeTurnId) {
6609
6736
  const ageStr2 = turnInfo.ageSeconds != null ? formatAge(turnInfo.ageSeconds) : "?";
@@ -6629,7 +6756,7 @@ function bridgeStatusAll() {
6629
6756
  threadCwd: runtimeHeartbeat?.threadCwd ?? null,
6630
6757
  savedThreadId: savedThread?.threadId ?? null,
6631
6758
  savedThreadCwd: savedThread?.cwd ?? null,
6632
- appServer: bridgeState?.appServer ?? null
6759
+ appServer: surfaceBridgeState?.appServer ?? null
6633
6760
  };
6634
6761
  }
6635
6762
  if (instanceIds.length === 0) {
@@ -6726,14 +6853,17 @@ function bridgeStatusOne(identifier) {
6726
6853
  }
6727
6854
  const { config: resolvedCfg2 } = resolveConfig({}, repoRoot);
6728
6855
  const stateDir = resolvedCfg2.stateDir;
6729
- const status = getBridgeStatus(stateDir, instanceId);
6856
+ const rawStatus = getBridgeStatus(stateDir, instanceId);
6730
6857
  const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
6731
- const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(bridgeState);
6732
- const savedThread = loadRuntimeBridgeThreadState(bridgeState);
6733
- const age = getHeartbeatAge(stateDir, instanceId);
6734
- const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
6735
- const lifecycle = deriveBridgeLifecycleState({
6736
- bridgeStatus: status,
6858
+ const liveDispatch = rawStatus === "running" ? null : loadLiveDispatchEvidence(state.commsDir, instanceId);
6859
+ const surfaceBridgeState = liveDispatch ? null : bridgeState;
6860
+ const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(surfaceBridgeState);
6861
+ const savedThread = loadRuntimeBridgeThreadState(surfaceBridgeState);
6862
+ const age = liveDispatch ? null : getHeartbeatAge(stateDir, instanceId);
6863
+ const heartbeat = liveDispatch ? null : getBridgeHeartbeatTimestamp(stateDir, instanceId);
6864
+ const status = liveDispatch ? "dispatch-live" : rawStatus;
6865
+ const lifecycle = liveDispatch ? deriveBridgeLifecycleState({ bridgeStatus: "stopped" }) : deriveBridgeLifecycleState({
6866
+ bridgeStatus: rawStatus,
6737
6867
  bridgeState,
6738
6868
  runtimeHeartbeat,
6739
6869
  savedThread,
@@ -6741,13 +6871,25 @@ function bridgeStatusOne(identifier) {
6741
6871
  });
6742
6872
  const session = deriveCodexSessionState({
6743
6873
  runtimeHeartbeat,
6744
- runtimeStateDir: bridgeState?.runtimeStateDir ?? null
6874
+ runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
6745
6875
  });
6746
6876
  log(`Status: ${status}`);
6747
6877
  log(`Lifecycle: ${lifecycle.summary}`);
6748
6878
  log(`Session: ${session.summary}`);
6749
- if (bridgeState) {
6750
- log(`PID: ${bridgeState.pid}`);
6879
+ if (rawStatus === "stale" && inst.bridge) {
6880
+ state.instances[instanceId] = {
6881
+ ...inst,
6882
+ bridge: null,
6883
+ bridgeLifecycle: transitionBridgeLifecycle(
6884
+ inst.bridgeLifecycle ?? inst.bridge?.lifecycle ?? null,
6885
+ "crashed",
6886
+ "bridge pid not alive"
6887
+ )
6888
+ };
6889
+ saveState(repoRoot, state);
6890
+ }
6891
+ if (surfaceBridgeState) {
6892
+ log(`PID: ${surfaceBridgeState.pid}`);
6751
6893
  log(
6752
6894
  `Heartbeat: ${heartbeat ?? "-"}${age !== null ? ` (${formatAge(age)})` : ""}`
6753
6895
  );
@@ -6762,35 +6904,35 @@ function bridgeStatusOne(identifier) {
6762
6904
  );
6763
6905
  }
6764
6906
  log(
6765
- `Log: ${path27.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
6907
+ `Log: ${path28.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
6766
6908
  );
6767
- if (bridgeState.appServer) {
6768
- log(`App server: ${bridgeState.appServer.url}`);
6769
- log(`Server PID: ${bridgeState.appServer.pid ?? "-"}`);
6909
+ if (surfaceBridgeState.appServer) {
6910
+ log(`App server: ${surfaceBridgeState.appServer.url}`);
6911
+ log(`Server PID: ${surfaceBridgeState.appServer.pid ?? "-"}`);
6770
6912
  log(
6771
- `Server mode: ${bridgeState.appServer.managed ? "managed" : "external"}`
6913
+ `Server mode: ${surfaceBridgeState.appServer.managed ? "managed" : "external"}`
6772
6914
  );
6773
6915
  log(
6774
- `Health: ${bridgeState.appServer.healthy ? "healthy" : "unhealthy"}`
6916
+ `Health: ${surfaceBridgeState.appServer.healthy ? "healthy" : "unhealthy"}`
6775
6917
  );
6776
- log(`Checked: ${bridgeState.appServer.lastCheckedAt}`);
6777
- if (bridgeState.appServer.logPath) {
6778
- log(`Server log: ${bridgeState.appServer.logPath}`);
6918
+ log(`Checked: ${surfaceBridgeState.appServer.lastCheckedAt}`);
6919
+ if (surfaceBridgeState.appServer.logPath) {
6920
+ log(`Server log: ${surfaceBridgeState.appServer.logPath}`);
6779
6921
  }
6780
- if (bridgeState.appServer.auth) {
6781
- log(`Auth: ${bridgeState.appServer.auth.mode}`);
6922
+ if (surfaceBridgeState.appServer.auth) {
6923
+ log(`Auth: ${surfaceBridgeState.appServer.auth.mode}`);
6782
6924
  log(
6783
- `Protected: ${redactProtectedUrl(bridgeState.appServer.auth.protectedUrl)}`
6925
+ `Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
6784
6926
  );
6785
- log(`Upstream: ${bridgeState.appServer.auth.upstreamUrl}`);
6786
- log(`TUI connect: ${bridgeState.appServer.auth.upstreamUrl}`);
6787
- log(`Gateway PID: ${bridgeState.appServer.auth.gatewayPid ?? "-"}`);
6788
- if (bridgeState.appServer.auth.gatewayLogPath) {
6789
- log(`Gateway log: ${bridgeState.appServer.auth.gatewayLogPath}`);
6927
+ log(`Upstream: ${surfaceBridgeState.appServer.auth.upstreamUrl}`);
6928
+ log(`TUI connect: ${surfaceBridgeState.appServer.auth.upstreamUrl}`);
6929
+ log(`Gateway PID: ${surfaceBridgeState.appServer.auth.gatewayPid ?? "-"}`);
6930
+ if (surfaceBridgeState.appServer.auth.gatewayLogPath) {
6931
+ log(`Gateway log: ${surfaceBridgeState.appServer.auth.gatewayLogPath}`);
6790
6932
  }
6791
- } else if (bridgeState.appServer.managed) {
6933
+ } else if (surfaceBridgeState.appServer.managed) {
6792
6934
  log(`Auth: none (--no-auth)`);
6793
- log(`TUI connect: ${bridgeState.appServer.url}`);
6935
+ log(`TUI connect: ${surfaceBridgeState.appServer.url}`);
6794
6936
  }
6795
6937
  }
6796
6938
  }
@@ -6798,6 +6940,11 @@ function bridgeStatusOne(identifier) {
6798
6940
  if (transition) {
6799
6941
  log(`Transition: ${transition}`);
6800
6942
  }
6943
+ if (liveDispatch) {
6944
+ log(
6945
+ `Drift: fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
6946
+ );
6947
+ }
6801
6948
  log("");
6802
6949
  return {
6803
6950
  ok: true,
@@ -6827,14 +6974,14 @@ function bridgeStatusOne(identifier) {
6827
6974
  lastDispatchAt: session.lastDispatchAt
6828
6975
  },
6829
6976
  bridgeMode: inst.bridgeMode,
6830
- pid: bridgeState?.pid ?? null,
6977
+ pid: surfaceBridgeState?.pid ?? null,
6831
6978
  port: inst.port,
6832
6979
  lastHeartbeat: heartbeat,
6833
6980
  threadId: runtimeHeartbeat?.threadId ?? null,
6834
6981
  threadCwd: runtimeHeartbeat?.threadCwd ?? null,
6835
6982
  savedThreadId: savedThread?.threadId ?? null,
6836
6983
  savedThreadCwd: savedThread?.cwd ?? null,
6837
- appServer: bridgeState?.appServer ?? null
6984
+ appServer: surfaceBridgeState?.appServer ?? null
6838
6985
  }
6839
6986
  };
6840
6987
  }
@@ -6929,7 +7076,23 @@ function bridgeTuiOne(identifier) {
6929
7076
  runtimeHeartbeat?.threadCwd,
6930
7077
  savedThread?.cwd
6931
7078
  );
6932
- const attachCommand = formatCodexTuiAttachCommand(tuiConnectUrl, attachCwd);
7079
+ const attachEnv = {
7080
+ TAP_BRIDGE_INSTANCE_ID: instanceId,
7081
+ TAP_AGENT_ID: instanceId,
7082
+ TAP_COMMS_DIR: resolvedConfig.commsDir,
7083
+ TAP_STATE_DIR: stateDir,
7084
+ TAP_RUNTIME_STATE_DIR: bridgeState?.runtimeStateDir ?? getBridgeRuntimeStateDir(repoRoot, instanceId),
7085
+ TAP_REPO_ROOT: repoRoot
7086
+ };
7087
+ if (typeof inst.agentName === "string" && inst.agentName.trim()) {
7088
+ attachEnv.TAP_AGENT_NAME = inst.agentName;
7089
+ attachEnv.CODEX_TAP_AGENT_NAME = inst.agentName;
7090
+ }
7091
+ const attachCommand = formatCodexTuiAttachCommand(
7092
+ tuiConnectUrl,
7093
+ attachCwd,
7094
+ attachEnv
7095
+ );
6933
7096
  const warnings = appServer.auth != null ? [
6934
7097
  "Use the upstream TUI URL, not the protected gateway URL. The protected URL is bridge-only."
6935
7098
  ] : [];
@@ -6954,6 +7117,7 @@ function bridgeTuiOne(identifier) {
6954
7117
  tuiConnectUrl,
6955
7118
  attachCwd,
6956
7119
  attachCommand,
7120
+ attachEnv,
6957
7121
  appServer
6958
7122
  }
6959
7123
  };
@@ -7266,8 +7430,8 @@ async function bridgeCommand(args) {
7266
7430
 
7267
7431
  // src/engine/dashboard.ts
7268
7432
  init_config();
7269
- import * as fs28 from "fs";
7270
- import * as path28 from "path";
7433
+ import * as fs29 from "fs";
7434
+ import * as path29 from "path";
7271
7435
  import { execSync as execSync4 } from "child_process";
7272
7436
  function formatAgentLabel(agentIdOrName, displayName) {
7273
7437
  const normalizedId = agentIdOrName.trim();
@@ -7300,10 +7464,10 @@ function resolveHeartbeatInstanceId(heartbeatId, displayName, state) {
7300
7464
  return matches.length === 1 ? matches[0].instanceId : null;
7301
7465
  }
7302
7466
  function collectAgents(commsDir, state, bridges) {
7303
- const heartbeatsPath = path28.join(commsDir, "heartbeats.json");
7304
- if (!fs28.existsSync(heartbeatsPath)) return [];
7467
+ const heartbeatsPath = path29.join(commsDir, "heartbeats.json");
7468
+ if (!fs29.existsSync(heartbeatsPath)) return [];
7305
7469
  try {
7306
- const raw = fs28.readFileSync(heartbeatsPath, "utf-8");
7470
+ const raw = fs29.readFileSync(heartbeatsPath, "utf-8");
7307
7471
  const data = JSON.parse(raw);
7308
7472
  return Object.entries(data).map(([agentId, info]) => {
7309
7473
  const instanceId = resolveHeartbeatInstanceId(
@@ -7363,22 +7527,22 @@ function collectBridges(repoRoot) {
7363
7527
  });
7364
7528
  }
7365
7529
  }
7366
- const tmpDir = path28.join(repoRoot, ".tmp");
7367
- if (fs28.existsSync(tmpDir)) {
7530
+ const tmpDir = path29.join(repoRoot, ".tmp");
7531
+ if (fs29.existsSync(tmpDir)) {
7368
7532
  try {
7369
- const dirs = fs28.readdirSync(tmpDir).filter((d) => d.startsWith("codex-app-server-bridge"));
7533
+ const dirs = fs29.readdirSync(tmpDir).filter((d) => d.startsWith("codex-app-server-bridge"));
7370
7534
  for (const dir of dirs) {
7371
- const daemonPath = path28.join(tmpDir, dir, "bridge-daemon.json");
7372
- if (!fs28.existsSync(daemonPath)) continue;
7535
+ const daemonPath = path29.join(tmpDir, dir, "bridge-daemon.json");
7536
+ if (!fs29.existsSync(daemonPath)) continue;
7373
7537
  try {
7374
- const raw = fs28.readFileSync(daemonPath, "utf-8");
7538
+ const raw = fs29.readFileSync(daemonPath, "utf-8");
7375
7539
  const daemon = JSON.parse(raw);
7376
7540
  const alreadyCovered = bridges.some(
7377
7541
  (b) => b.pid === daemon.pid && b.pid !== null
7378
7542
  );
7379
7543
  if (alreadyCovered) continue;
7380
- const agentFile = path28.join(tmpDir, dir, "agent-name.txt");
7381
- const agentName = fs28.existsSync(agentFile) ? fs28.readFileSync(agentFile, "utf-8").trim() : dir;
7544
+ const agentFile = path29.join(tmpDir, dir, "agent-name.txt");
7545
+ const agentName = fs29.existsSync(agentFile) ? fs29.readFileSync(agentFile, "utf-8").trim() : dir;
7382
7546
  const running = daemon.pid ? isProcessAlive(daemon.pid) : false;
7383
7547
  const portMatch = daemon.appServerUrl?.match(/:(\d+)/);
7384
7548
  const port = portMatch ? parseInt(portMatch[1], 10) : null;
@@ -7613,7 +7777,7 @@ async function downCommand(args) {
7613
7777
  }
7614
7778
 
7615
7779
  // src/commands/serve.ts
7616
- import * as path29 from "path";
7780
+ import * as path30 from "path";
7617
7781
  import { spawn as spawn2 } from "child_process";
7618
7782
  init_utils();
7619
7783
  init_config();
@@ -7649,10 +7813,10 @@ async function serveCommand(args) {
7649
7813
  let commsDir;
7650
7814
  const commsDirIdx = args.indexOf("--comms-dir");
7651
7815
  if (commsDirIdx !== -1 && args[commsDirIdx + 1]) {
7652
- commsDir = path29.resolve(normalizeTapPath(args[commsDirIdx + 1]));
7816
+ commsDir = path30.resolve(normalizeTapPath(args[commsDirIdx + 1]));
7653
7817
  }
7654
7818
  if (!commsDir && process.env.TAP_COMMS_DIR) {
7655
- commsDir = path29.resolve(normalizeTapPath(process.env.TAP_COMMS_DIR));
7819
+ commsDir = path30.resolve(normalizeTapPath(process.env.TAP_COMMS_DIR));
7656
7820
  }
7657
7821
  if (!commsDir) {
7658
7822
  const state = loadState(repoRoot);
@@ -7719,8 +7883,8 @@ async function serveCommand(args) {
7719
7883
  // src/commands/init-worktree.ts
7720
7884
  init_config();
7721
7885
  init_utils();
7722
- import * as fs29 from "fs";
7723
- import * as path30 from "path";
7886
+ import * as fs30 from "fs";
7887
+ import * as path31 from "path";
7724
7888
  import { execSync as execSync5 } from "child_process";
7725
7889
  var INIT_WORKTREE_HELP = `
7726
7890
  Usage:
@@ -7757,7 +7921,7 @@ function run(cmd, opts) {
7757
7921
  }
7758
7922
  }
7759
7923
  function toAbsolute(p) {
7760
- const resolved = path30.resolve(p);
7924
+ const resolved = path31.resolve(p);
7761
7925
  return resolved.replace(/\\/g, "/");
7762
7926
  }
7763
7927
  function probeBun(candidate) {
@@ -7788,18 +7952,18 @@ function findBun() {
7788
7952
  }
7789
7953
  }
7790
7954
  const home = process.env.HOME || process.env.USERPROFILE || "";
7791
- const bunHome = path30.join(
7955
+ const bunHome = path31.join(
7792
7956
  home,
7793
7957
  ".bun",
7794
7958
  "bin",
7795
7959
  process.platform === "win32" ? "bun.exe" : "bun"
7796
7960
  );
7797
- if (fs29.existsSync(bunHome) && probeBun(bunHome)) return bunHome;
7961
+ if (fs30.existsSync(bunHome) && probeBun(bunHome)) return bunHome;
7798
7962
  return null;
7799
7963
  }
7800
7964
  function step1CreateWorktree(opts) {
7801
7965
  log("Step 1/9: Creating worktree...");
7802
- if (fs29.existsSync(opts.worktreePath)) {
7966
+ if (fs30.existsSync(opts.worktreePath)) {
7803
7967
  logWarn(`Directory already exists: ${opts.worktreePath}`);
7804
7968
  try {
7805
7969
  run("git rev-parse --git-dir", { cwd: opts.worktreePath });
@@ -7861,22 +8025,22 @@ function step2MergeMain(opts, warnings) {
7861
8025
  }
7862
8026
  function step3CopyPermissions(opts, warnings) {
7863
8027
  log("Step 3/9: Copying permissions...");
7864
- const srcSettings = path30.join(
8028
+ const srcSettings = path31.join(
7865
8029
  opts.repoRoot,
7866
8030
  ".claude",
7867
8031
  "settings.local.json"
7868
8032
  );
7869
- const destDir = path30.join(opts.worktreePath, ".claude");
7870
- const destSettings = path30.join(destDir, "settings.local.json");
7871
- if (!fs29.existsSync(srcSettings)) {
8033
+ const destDir = path31.join(opts.worktreePath, ".claude");
8034
+ const destSettings = path31.join(destDir, "settings.local.json");
8035
+ if (!fs30.existsSync(srcSettings)) {
7872
8036
  warn(
7873
8037
  warnings,
7874
8038
  "No .claude/settings.local.json found in main repo. Skipping."
7875
8039
  );
7876
8040
  return;
7877
8041
  }
7878
- fs29.mkdirSync(destDir, { recursive: true });
7879
- fs29.copyFileSync(srcSettings, destSettings);
8042
+ fs30.mkdirSync(destDir, { recursive: true });
8043
+ fs30.copyFileSync(srcSettings, destSettings);
7880
8044
  logSuccess("Copied settings.local.json");
7881
8045
  try {
7882
8046
  run("git update-index --skip-worktree .claude/settings.local.json", {
@@ -7901,7 +8065,7 @@ function step4GenerateMcpJson(opts, warnings) {
7901
8065
  const wtAbs = toAbsolute(opts.worktreePath);
7902
8066
  const bunAbs = toAbsolute(bunPath);
7903
8067
  const commsAbs = toAbsolute(opts.commsDir);
7904
- const channelEntry = path30.join(
8068
+ const channelEntry = path31.join(
7905
8069
  wtAbs,
7906
8070
  "packages/tap-plugin/channels/tap-comms.ts"
7907
8071
  );
@@ -7918,8 +8082,8 @@ function step4GenerateMcpJson(opts, warnings) {
7918
8082
  }
7919
8083
  }
7920
8084
  };
7921
- const mcpPath = path30.join(opts.worktreePath, ".mcp.json");
7922
- fs29.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
8085
+ const mcpPath = path31.join(opts.worktreePath, ".mcp.json");
8086
+ fs30.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
7923
8087
  logSuccess(`.mcp.json generated (absolute paths + cwd)`);
7924
8088
  log(` bun: ${bunAbs}`);
7925
8089
  log(` comms: ${commsAbs}`);
@@ -7957,16 +8121,16 @@ function step6BuildEslintPlugin(opts, warnings) {
7957
8121
  }
7958
8122
  function step7VerifyComms(opts, warnings) {
7959
8123
  log("Step 7/9: Verifying comms directory...");
7960
- if (!fs29.existsSync(opts.commsDir)) {
8124
+ if (!fs30.existsSync(opts.commsDir)) {
7961
8125
  warn(warnings, `Comms directory not found: ${opts.commsDir}`);
7962
8126
  warn(warnings, "Create it or run: npx @hua-labs/tap init");
7963
8127
  return;
7964
8128
  }
7965
8129
  const requiredDirs = ["inbox", "findings", "reviews", "letters"];
7966
8130
  for (const dir of requiredDirs) {
7967
- const dirPath = path30.join(opts.commsDir, dir);
7968
- if (!fs29.existsSync(dirPath)) {
7969
- fs29.mkdirSync(dirPath, { recursive: true });
8131
+ const dirPath = path31.join(opts.commsDir, dir);
8132
+ if (!fs30.existsSync(dirPath)) {
8133
+ fs30.mkdirSync(dirPath, { recursive: true });
7970
8134
  logSuccess(`Created ${dir}/`);
7971
8135
  }
7972
8136
  }
@@ -8025,17 +8189,17 @@ async function initWorktreeCommand(args) {
8025
8189
  }
8026
8190
  const repoRoot = findRepoRoot();
8027
8191
  const { config } = resolveConfig({}, repoRoot);
8028
- const branch = typeof flags["branch"] === "string" ? flags["branch"] : path30.basename(path30.resolve(worktreePath));
8192
+ const branch = typeof flags["branch"] === "string" ? flags["branch"] : path31.basename(path31.resolve(worktreePath));
8029
8193
  const base = typeof flags["base"] === "string" ? flags["base"] : "origin/main";
8030
8194
  const mission = typeof flags["mission"] === "string" ? flags["mission"] : void 0;
8031
8195
  const commsDir = typeof flags["comms-dir"] === "string" ? flags["comms-dir"] : config.commsDir;
8032
8196
  const skipInstall = flags["skip-install"] === true;
8033
8197
  const opts = {
8034
- worktreePath: path30.resolve(worktreePath),
8198
+ worktreePath: path31.resolve(worktreePath),
8035
8199
  branch,
8036
8200
  base,
8037
8201
  mission,
8038
- commsDir: path30.resolve(commsDir),
8202
+ commsDir: path31.resolve(commsDir),
8039
8203
  skipInstall,
8040
8204
  repoRoot
8041
8205
  };
@@ -8260,10 +8424,10 @@ async function dashboardCommand(args) {
8260
8424
 
8261
8425
  // src/commands/doctor.ts
8262
8426
  import {
8263
- existsSync as existsSync28,
8427
+ existsSync as existsSync29,
8264
8428
  mkdirSync as mkdirSync14,
8265
8429
  readdirSync as readdirSync8,
8266
- readFileSync as readFileSync23,
8430
+ readFileSync as readFileSync24,
8267
8431
  renameSync as renameSync14,
8268
8432
  statSync as statSync3,
8269
8433
  unlinkSync as unlinkSync8,
@@ -8271,7 +8435,7 @@ import {
8271
8435
  } from "fs";
8272
8436
  import { homedir as homedir3 } from "os";
8273
8437
  import { spawnSync as spawnSync6 } from "child_process";
8274
- import { dirname as dirname15, join as join27, resolve as resolve14 } from "path";
8438
+ import { dirname as dirname15, join as join28, resolve as resolve14 } from "path";
8275
8439
  init_config();
8276
8440
  init_drift_detector();
8277
8441
  init_utils();
@@ -8302,11 +8466,43 @@ function sameCommandToken(left, right) {
8302
8466
  function sameStringArray(left, right) {
8303
8467
  return left.length === right.length && left.every((value, index) => sameCommandToken(value, right[index] ?? ""));
8304
8468
  }
8469
+ function normalizeCommandBasename(command) {
8470
+ const token = command.split(/[\\/]/).pop() ?? command;
8471
+ return token.toLowerCase().replace(/\.(cmd|exe|ps1|bat)$/i, "");
8472
+ }
8473
+ function findFirstLauncherTarget(args) {
8474
+ for (const arg of args) {
8475
+ if (!arg || arg === "--" || arg.startsWith("-")) {
8476
+ continue;
8477
+ }
8478
+ return arg;
8479
+ }
8480
+ return null;
8481
+ }
8482
+ function looksLikePackageSpecifier(value) {
8483
+ const normalized = value.trim();
8484
+ if (!normalized || /^[A-Za-z]:[\\/]/.test(normalized) || normalized.startsWith("/") || normalized.startsWith("\\") || normalized.startsWith(".") || /\.(?:[cm]?js|tsx?|json|ps1|cmd|exe)$/i.test(normalized)) {
8485
+ return false;
8486
+ }
8487
+ return /^(?:@[^/\\]+\/)?[A-Za-z0-9][A-Za-z0-9._-]*(?:@[A-Za-z0-9][A-Za-z0-9._.-]*)?$/.test(
8488
+ normalized
8489
+ );
8490
+ }
8491
+ function getNpxPackageLauncher(command, args) {
8492
+ if (normalizeCommandBasename(command) !== "npx") {
8493
+ return null;
8494
+ }
8495
+ const packageName = findFirstLauncherTarget(args);
8496
+ if (!packageName || !looksLikePackageSpecifier(packageName)) {
8497
+ return null;
8498
+ }
8499
+ return [command, ...args].join(" ");
8500
+ }
8305
8501
  function appendWarningMessage(message, extra) {
8306
8502
  return message.includes(extra) ? message : `${message}; ${extra}`;
8307
8503
  }
8308
8504
  function findCodexConfigPath3() {
8309
- return join27(homedir3(), ".codex", "config.toml");
8505
+ return join28(homedir3(), ".codex", "config.toml");
8310
8506
  }
8311
8507
  function canonicalizeTrustPath3(targetPath) {
8312
8508
  let resolved = resolve14(targetPath).replace(/\//g, "\\");
@@ -8376,7 +8572,7 @@ function repairCodexConfig(repoRoot, commsDir) {
8376
8572
  spec.managed.issues[0] ?? "Unable to resolve the managed tap MCP server for Codex."
8377
8573
  );
8378
8574
  }
8379
- const existingContent = existsSync28(spec.configPath) ? readFileSync23(spec.configPath, "utf-8") : "";
8575
+ const existingContent = existsSync29(spec.configPath) ? readFileSync24(spec.configPath, "utf-8") : "";
8380
8576
  const existingTapEnvTable = extractTomlTable(
8381
8577
  existingContent,
8382
8578
  "mcp_servers.tap.env"
@@ -8443,7 +8639,7 @@ function repairCodexConfig(repoRoot, commsDir) {
8443
8639
  return `Repaired Codex config at ${spec.configPath}. Restart Codex to reload MCP settings.`;
8444
8640
  }
8445
8641
  function countFiles(dir, ext = ".md") {
8446
- if (!existsSync28(dir)) return 0;
8642
+ if (!existsSync29(dir)) return 0;
8447
8643
  try {
8448
8644
  return readdirSync8(dir).filter((f) => f.endsWith(ext)).length;
8449
8645
  } catch {
@@ -8451,14 +8647,14 @@ function countFiles(dir, ext = ".md") {
8451
8647
  }
8452
8648
  }
8453
8649
  function recentFileCount(dir, withinMs) {
8454
- if (!existsSync28(dir)) return 0;
8650
+ if (!existsSync29(dir)) return 0;
8455
8651
  const cutoff = Date.now() - withinMs;
8456
8652
  let count = 0;
8457
8653
  try {
8458
8654
  for (const f of readdirSync8(dir)) {
8459
8655
  if (!f.endsWith(".md")) continue;
8460
8656
  try {
8461
- if (statSync3(join27(dir, f)).mtimeMs > cutoff) count++;
8657
+ if (statSync3(join28(dir, f)).mtimeMs > cutoff) count++;
8462
8658
  } catch {
8463
8659
  }
8464
8660
  }
@@ -8467,16 +8663,16 @@ function recentFileCount(dir, withinMs) {
8467
8663
  return count;
8468
8664
  }
8469
8665
  function loadDoctorHeartbeatStore(commsDir) {
8470
- const heartbeatsPath = join27(commsDir, "heartbeats.json");
8471
- if (!existsSync28(heartbeatsPath)) return null;
8666
+ const heartbeatsPath = join28(commsDir, "heartbeats.json");
8667
+ if (!existsSync29(heartbeatsPath)) return null;
8472
8668
  try {
8473
- return JSON.parse(readFileSync23(heartbeatsPath, "utf-8"));
8669
+ return JSON.parse(readFileSync24(heartbeatsPath, "utf-8"));
8474
8670
  } catch {
8475
8671
  return null;
8476
8672
  }
8477
8673
  }
8478
8674
  function saveDoctorHeartbeatStore(commsDir, store) {
8479
- const heartbeatsPath = join27(commsDir, "heartbeats.json");
8675
+ const heartbeatsPath = join28(commsDir, "heartbeats.json");
8480
8676
  const tmp = `${heartbeatsPath}.tmp.${process.pid}`;
8481
8677
  writeFileSync16(tmp, JSON.stringify(store, null, 2), "utf-8");
8482
8678
  renameSync14(tmp, heartbeatsPath);
@@ -8542,9 +8738,9 @@ function checkComms(commsDir) {
8542
8738
  const checks = [];
8543
8739
  checks.push({
8544
8740
  name: "comms directory",
8545
- status: existsSync28(commsDir) ? PASS : FAIL,
8546
- message: existsSync28(commsDir) ? commsDir : `Not found: ${commsDir}`,
8547
- fix: existsSync28(commsDir) ? void 0 : () => {
8741
+ status: existsSync29(commsDir) ? PASS : FAIL,
8742
+ message: existsSync29(commsDir) ? commsDir : `Not found: ${commsDir}`,
8743
+ fix: existsSync29(commsDir) ? void 0 : () => {
8548
8744
  mkdirSync14(commsDir, { recursive: true });
8549
8745
  return `Created ${commsDir}`;
8550
8746
  }
@@ -8554,8 +8750,8 @@ function checkComms(commsDir) {
8554
8750
  ["reviews", false],
8555
8751
  ["findings", false]
8556
8752
  ]) {
8557
- const dir = join27(commsDir, subdir);
8558
- const exists = existsSync28(dir);
8753
+ const dir = join28(commsDir, subdir);
8754
+ const exists = existsSync29(dir);
8559
8755
  checks.push({
8560
8756
  name: `${subdir} directory`,
8561
8757
  status: exists ? PASS : required ? FAIL : WARN,
@@ -8566,10 +8762,10 @@ function checkComms(commsDir) {
8566
8762
  }
8567
8763
  });
8568
8764
  }
8569
- const heartbeats = join27(commsDir, "heartbeats.json");
8570
- if (existsSync28(heartbeats)) {
8765
+ const heartbeats = join28(commsDir, "heartbeats.json");
8766
+ if (existsSync29(heartbeats)) {
8571
8767
  try {
8572
- const store = JSON.parse(readFileSync23(heartbeats, "utf-8"));
8768
+ const store = JSON.parse(readFileSync24(heartbeats, "utf-8"));
8573
8769
  const agents = Object.keys(store);
8574
8770
  const now = Date.now();
8575
8771
  const active = agents.filter((a) => {
@@ -8683,7 +8879,7 @@ function checkInstances(repoRoot, stateDir, commsDir) {
8683
8879
  }
8684
8880
  }
8685
8881
  }
8686
- const pidPath = join27(stateDir, "pids", `bridge-${id}.json`);
8882
+ const pidPath = join28(stateDir, "pids", `bridge-${id}.json`);
8687
8883
  try {
8688
8884
  unlinkSync8(pidPath);
8689
8885
  } catch {
@@ -8745,8 +8941,8 @@ function checkInstances(repoRoot, stateDir, commsDir) {
8745
8941
  }
8746
8942
  function checkMessageLifecycle(commsDir) {
8747
8943
  const checks = [];
8748
- const inbox = join27(commsDir, "inbox");
8749
- if (!existsSync28(inbox)) {
8944
+ const inbox = join28(commsDir, "inbox");
8945
+ if (!existsSync29(inbox)) {
8750
8946
  checks.push({
8751
8947
  name: "message flow",
8752
8948
  status: FAIL,
@@ -8757,15 +8953,16 @@ function checkMessageLifecycle(commsDir) {
8757
8953
  const total = countFiles(inbox);
8758
8954
  const recent1h = recentFileCount(inbox, 60 * 60 * 1e3);
8759
8955
  const recent10m = recentFileCount(inbox, 10 * 60 * 1e3);
8956
+ const messageSummary = `${total} total, ${recent1h} in last 1h, ${recent10m} in last 10m`;
8760
8957
  checks.push({
8761
8958
  name: "message flow",
8762
- status: recent10m > 0 ? PASS : total > 0 ? WARN : FAIL,
8763
- message: `${total} total, ${recent1h} in last 1h, ${recent10m} in last 10m`
8959
+ status: recent10m > 0 ? PASS : WARN,
8960
+ message: total === 0 ? `${messageSummary} (expected before first exchange)` : messageSummary
8764
8961
  });
8765
- const receiptsPath = join27(commsDir, "receipts", "receipts.json");
8766
- if (existsSync28(receiptsPath)) {
8962
+ const receiptsPath = join28(commsDir, "receipts", "receipts.json");
8963
+ if (existsSync29(receiptsPath)) {
8767
8964
  try {
8768
- const receipts = JSON.parse(readFileSync23(receiptsPath, "utf-8"));
8965
+ const receipts = JSON.parse(readFileSync24(receiptsPath, "utf-8"));
8769
8966
  const receiptCount = Object.keys(receipts).length;
8770
8967
  checks.push({
8771
8968
  name: "read receipts",
@@ -8784,8 +8981,8 @@ function checkMessageLifecycle(commsDir) {
8784
8981
  }
8785
8982
  function checkMcpServer(repoRoot) {
8786
8983
  const checks = [];
8787
- const mcpJson = join27(repoRoot, ".mcp.json");
8788
- if (!existsSync28(mcpJson)) {
8984
+ const mcpJson = join28(repoRoot, ".mcp.json");
8985
+ if (!existsSync29(mcpJson)) {
8789
8986
  checks.push({
8790
8987
  name: "MCP config (.mcp.json)",
8791
8988
  status: WARN,
@@ -8795,7 +8992,7 @@ function checkMcpServer(repoRoot) {
8795
8992
  }
8796
8993
  let config;
8797
8994
  try {
8798
- config = JSON.parse(readFileSync23(mcpJson, "utf-8"));
8995
+ config = JSON.parse(readFileSync24(mcpJson, "utf-8"));
8799
8996
  } catch {
8800
8997
  checks.push({
8801
8998
  name: "MCP config (.mcp.json)",
@@ -8838,17 +9035,9 @@ function checkMcpServer(repoRoot) {
8838
9035
  });
8839
9036
  if (hasTapComms.command) {
8840
9037
  const cmd = hasTapComms.command;
8841
- let cmdAvailable = existsSync28(cmd);
9038
+ let cmdAvailable = existsSync29(cmd);
8842
9039
  if (!cmdAvailable) {
8843
- try {
8844
- const result = spawnSync6(cmd, ["--version"], {
8845
- stdio: "pipe",
8846
- timeout: 5e3,
8847
- shell: process.platform === "win32"
8848
- });
8849
- cmdAvailable = result.status === 0;
8850
- } catch {
8851
- }
9040
+ cmdAvailable = probeCommand([cmd]).command !== null;
8852
9041
  }
8853
9042
  checks.push({
8854
9043
  name: "MCP command binary",
@@ -8856,12 +9045,19 @@ function checkMcpServer(repoRoot) {
8856
9045
  message: cmdAvailable ? cmd : `Not found: ${cmd} (checked PATH and absolute)`
8857
9046
  });
8858
9047
  }
8859
- if (hasTapComms.args?.[0]) {
9048
+ const npxPackageLauncher = hasTapComms.command && hasTapComms.args ? getNpxPackageLauncher(hasTapComms.command, hasTapComms.args) : null;
9049
+ if (npxPackageLauncher) {
9050
+ checks.push({
9051
+ name: "MCP server script",
9052
+ status: PASS,
9053
+ message: `Package launcher: ${npxPackageLauncher}`
9054
+ });
9055
+ } else if (hasTapComms.args?.[0]) {
8860
9056
  const mcpScript = hasTapComms.args[0];
8861
9057
  checks.push({
8862
9058
  name: "MCP server script",
8863
- status: existsSync28(mcpScript) ? PASS : FAIL,
8864
- message: existsSync28(mcpScript) ? mcpScript : `Not found: ${mcpScript}`
9059
+ status: existsSync29(mcpScript) ? PASS : FAIL,
9060
+ message: existsSync29(mcpScript) ? mcpScript : `Not found: ${mcpScript}`
8865
9061
  });
8866
9062
  if (mcpScript.endsWith(".mjs") && hasTapComms.command && !hasTapComms.command.includes("bun")) {
8867
9063
  checks.push({
@@ -8894,8 +9090,8 @@ function checkMcpServer(repoRoot) {
8894
9090
  } else {
8895
9091
  checks.push({
8896
9092
  name: "MCP TAP_COMMS_DIR",
8897
- status: existsSync28(envCommsDir) ? PASS : FAIL,
8898
- message: existsSync28(envCommsDir) ? envCommsDir : `Directory not found: ${envCommsDir}`
9093
+ status: existsSync29(envCommsDir) ? PASS : FAIL,
9094
+ message: existsSync29(envCommsDir) ? envCommsDir : `Directory not found: ${envCommsDir}`
8899
9095
  });
8900
9096
  }
8901
9097
  checks.push({
@@ -8912,7 +9108,7 @@ function checkCodexConfig(repoRoot, commsDir) {
8912
9108
  }
8913
9109
  const checks = [];
8914
9110
  const fixHint = 'Run "tap doctor --fix" or "tap add codex --force".';
8915
- if (!existsSync28(spec.configPath)) {
9111
+ if (!existsSync29(spec.configPath)) {
8916
9112
  checks.push({
8917
9113
  name: "MCP config (~/.codex/config.toml)",
8918
9114
  status: WARN,
@@ -8921,7 +9117,7 @@ function checkCodexConfig(repoRoot, commsDir) {
8921
9117
  });
8922
9118
  return checks;
8923
9119
  }
8924
- const content = readFileSync23(spec.configPath, "utf-8");
9120
+ const content = readFileSync24(spec.configPath, "utf-8");
8925
9121
  const tapTable = extractTomlTable(content, "mcp_servers.tap");
8926
9122
  const tapEnvTable = extractTomlTable(content, "mcp_servers.tap.env");
8927
9123
  const legacyTable = extractTomlTable(content, "mcp_servers.tap-comms");
@@ -9005,8 +9201,8 @@ function checkCodexConfig(repoRoot, commsDir) {
9005
9201
  }
9006
9202
  function checkBridgeTurnHealth(repoRoot) {
9007
9203
  const checks = [];
9008
- const tmpDir = join27(repoRoot, ".tmp");
9009
- if (!existsSync28(tmpDir)) return checks;
9204
+ const tmpDir = join28(repoRoot, ".tmp");
9205
+ if (!existsSync29(tmpDir)) return checks;
9010
9206
  const state = loadState(repoRoot);
9011
9207
  const activeMatchers = /* @__PURE__ */ new Set();
9012
9208
  if (state) {
@@ -9032,11 +9228,11 @@ function checkBridgeTurnHealth(repoRoot) {
9032
9228
  return checks;
9033
9229
  }
9034
9230
  for (const dir of dirs) {
9035
- const heartbeatPath = join27(tmpDir, dir, "heartbeat.json");
9036
- if (!existsSync28(heartbeatPath)) continue;
9231
+ const heartbeatPath = join28(tmpDir, dir, "heartbeat.json");
9232
+ if (!existsSync29(heartbeatPath)) continue;
9037
9233
  let heartbeat;
9038
9234
  try {
9039
- heartbeat = JSON.parse(readFileSync23(heartbeatPath, "utf-8"));
9235
+ heartbeat = JSON.parse(readFileSync24(heartbeatPath, "utf-8"));
9040
9236
  } catch {
9041
9237
  checks.push({
9042
9238
  name: `turn: ${dir}`,
@@ -9209,9 +9405,9 @@ async function doctorCommand(args) {
9209
9405
  inst.agentName = instConfig.agentName;
9210
9406
  inst.port = instConfig.port;
9211
9407
  inst.configHash = instConfig.configHash;
9212
- inst.configSourceFile = inst.configSourceFile || join27(config.stateDir, "instances", `${result.instanceId}.json`);
9408
+ inst.configSourceFile = inst.configSourceFile || join28(config.stateDir, "instances", `${result.instanceId}.json`);
9213
9409
  saveState(repoRoot, state);
9214
- if (inst.configPath && existsSync28(inst.configPath)) {
9410
+ if (inst.configPath && existsSync29(inst.configPath)) {
9215
9411
  const currentHash = hashFile(inst.configPath);
9216
9412
  if (instConfig.runtimeConfigHash !== currentHash) {
9217
9413
  instConfig.runtimeConfigHash = currentHash;
@@ -9322,7 +9518,7 @@ async function doctorCommand(args) {
9322
9518
  message: `${passes} passed, ${warns} warnings, ${fails} failures`,
9323
9519
  warnings: finalChecks.filter((c) => c.status === "warn").map((c) => `${c.name}: ${c.message}`),
9324
9520
  data: {
9325
- checks: finalChecks.map(({ fix, ...rest }) => rest),
9521
+ checks: finalChecks.map(({ fix: _fix, ...rest }) => rest),
9326
9522
  summary: { total: finalChecks.length, passes, warns, fails },
9327
9523
  fixed
9328
9524
  }
@@ -9332,8 +9528,8 @@ async function doctorCommand(args) {
9332
9528
  // src/commands/comms.ts
9333
9529
  init_utils();
9334
9530
  import { execSync as execSync6, spawnSync as spawnSync7 } from "child_process";
9335
- import * as fs30 from "fs";
9336
- import * as path31 from "path";
9531
+ import * as fs31 from "fs";
9532
+ import * as path32 from "path";
9337
9533
  var COMMS_HELP = `
9338
9534
  Usage:
9339
9535
  tap comms <subcommand>
@@ -9347,7 +9543,7 @@ Examples:
9347
9543
  npx @hua-labs/tap comms push
9348
9544
  `.trim();
9349
9545
  function isGitRepo(dir) {
9350
- return fs30.existsSync(path31.join(dir, ".git"));
9546
+ return fs31.existsSync(path32.join(dir, ".git"));
9351
9547
  }
9352
9548
  function commsPull(commsDir) {
9353
9549
  logHeader("tap comms pull");
@@ -9606,8 +9802,8 @@ async function watchCommand(args) {
9606
9802
  import * as http from "http";
9607
9803
 
9608
9804
  // src/engine/missions.ts
9609
- import * as fs31 from "fs";
9610
- import * as path32 from "path";
9805
+ import * as fs32 from "fs";
9806
+ import * as path33 from "path";
9611
9807
  function parseStatus(raw) {
9612
9808
  const trimmed = raw.trim();
9613
9809
  if (trimmed.includes("active")) return "active";
@@ -9633,10 +9829,10 @@ function parseRow(line) {
9633
9829
  return { id: id.toUpperCase(), title, branch, status, owner };
9634
9830
  }
9635
9831
  function parseMissionsFile(repoRoot) {
9636
- const missionsPath = path32.join(repoRoot, "docs", "missions", "MISSIONS.md");
9832
+ const missionsPath = path33.join(repoRoot, "docs", "missions", "MISSIONS.md");
9637
9833
  let content;
9638
9834
  try {
9639
- content = fs31.readFileSync(missionsPath, "utf-8");
9835
+ content = fs32.readFileSync(missionsPath, "utf-8");
9640
9836
  } catch {
9641
9837
  return [];
9642
9838
  }