@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/README.md +24 -12
- package/dist/bridges/codex-app-server-bridge.d.mts +236 -5
- package/dist/bridges/codex-app-server-bridge.mjs +1168 -722
- package/dist/bridges/codex-app-server-bridge.mjs.map +1 -1
- package/dist/bridges/codex-bridge-runner.d.mts +2 -1
- package/dist/bridges/codex-bridge-runner.mjs +10 -2
- package/dist/bridges/codex-bridge-runner.mjs.map +1 -1
- package/dist/bridges/gemini-ide-companion-runner.mjs +0 -0
- package/dist/cli.mjs +415 -219
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +225 -82
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-server.mjs +267 -19
- package/dist/mcp-server.mjs.map +1 -1
- package/package.json +3 -2
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
|
|
1028
|
-
|
|
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
|
-
|
|
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
|
|
1137
|
-
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
|
4358
|
-
|
|
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/
|
|
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
|
|
5502
|
+
const rawStatus = getBridgeStatus(stateDir, instanceId);
|
|
5406
5503
|
const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
|
|
5407
|
-
const
|
|
5408
|
-
const
|
|
5409
|
-
const
|
|
5410
|
-
|
|
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 =
|
|
5516
|
+
const session = rawStatus === "running" || liveDispatch ? deriveCodexSessionState({
|
|
5417
5517
|
runtimeHeartbeat,
|
|
5418
|
-
runtimeStateDir:
|
|
5518
|
+
runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
|
|
5419
5519
|
}) : null;
|
|
5420
|
-
const age = getHeartbeatAge(stateDir, instanceId);
|
|
5421
|
-
if (
|
|
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 =
|
|
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 (
|
|
5442
|
-
log(` App server: ${formatAppServerState(
|
|
5443
|
-
if (
|
|
5444
|
-
log(` Server log: ${
|
|
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 (
|
|
5546
|
+
if (surfaceBridgeState.appServer.auth) {
|
|
5447
5547
|
log(
|
|
5448
|
-
` Protected: ${redactProtectedUrl(
|
|
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:
|
|
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
|
|
5693
|
+
const rawStatus = getBridgeStatus(stateDir, instanceId);
|
|
5589
5694
|
const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
|
|
5590
|
-
const
|
|
5591
|
-
const
|
|
5592
|
-
const
|
|
5593
|
-
const
|
|
5594
|
-
const
|
|
5595
|
-
|
|
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:
|
|
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 (
|
|
5609
|
-
|
|
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: ${
|
|
5744
|
+
`Log: ${path29.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
|
|
5625
5745
|
);
|
|
5626
|
-
if (
|
|
5627
|
-
log(`App server: ${
|
|
5628
|
-
log(`Server 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: ${
|
|
5750
|
+
`Server mode: ${surfaceBridgeState.appServer.managed ? "managed" : "external"}`
|
|
5631
5751
|
);
|
|
5632
5752
|
log(
|
|
5633
|
-
`Health: ${
|
|
5753
|
+
`Health: ${surfaceBridgeState.appServer.healthy ? "healthy" : "unhealthy"}`
|
|
5634
5754
|
);
|
|
5635
|
-
log(`Checked: ${
|
|
5636
|
-
if (
|
|
5637
|
-
log(`Server log: ${
|
|
5755
|
+
log(`Checked: ${surfaceBridgeState.appServer.lastCheckedAt}`);
|
|
5756
|
+
if (surfaceBridgeState.appServer.logPath) {
|
|
5757
|
+
log(`Server log: ${surfaceBridgeState.appServer.logPath}`);
|
|
5638
5758
|
}
|
|
5639
|
-
if (
|
|
5640
|
-
log(`Auth: ${
|
|
5759
|
+
if (surfaceBridgeState.appServer.auth) {
|
|
5760
|
+
log(`Auth: ${surfaceBridgeState.appServer.auth.mode}`);
|
|
5641
5761
|
log(
|
|
5642
|
-
`Protected: ${redactProtectedUrl(
|
|
5762
|
+
`Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
|
|
5643
5763
|
);
|
|
5644
|
-
log(`Upstream: ${
|
|
5645
|
-
log(`TUI connect: ${
|
|
5646
|
-
log(`Gateway PID: ${
|
|
5647
|
-
if (
|
|
5648
|
-
log(`Gateway log: ${
|
|
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 (
|
|
5770
|
+
} else if (surfaceBridgeState.appServer.managed) {
|
|
5651
5771
|
log(`Auth: none (--no-auth)`);
|
|
5652
|
-
log(`TUI connect: ${
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
6860
|
-
import * as
|
|
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 =
|
|
6923
|
-
if (
|
|
6924
|
-
for (const dir of
|
|
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 =
|
|
6938
|
-
if (!
|
|
7080
|
+
const hsPath = path30.join(tmpDir, dir, "headless-state.json");
|
|
7081
|
+
if (!fs27.existsSync(hsPath)) continue;
|
|
6939
7082
|
try {
|
|
6940
|
-
const hs = JSON.parse(
|
|
7083
|
+
const hs = JSON.parse(fs27.readFileSync(hsPath, "utf-8"));
|
|
6941
7084
|
headlessStates.push({ instanceDir: dir, ...hs });
|
|
6942
7085
|
} catch {
|
|
6943
7086
|
}
|