@hua-labs/tap 0.5.0 → 0.5.1
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 +9 -0
- 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 +353 -192
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +208 -68
- 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/cli.mjs
CHANGED
|
@@ -2930,13 +2930,14 @@ function startWindowsDetachedProcess(command, args, repoRoot, logPath, env = pro
|
|
|
2930
2930
|
}
|
|
2931
2931
|
return pid;
|
|
2932
2932
|
}
|
|
2933
|
-
function startWindowsCodexAppServer(command, url, repoRoot, logPath) {
|
|
2933
|
+
function startWindowsCodexAppServer(command, url, repoRoot, logPath, env = process.env) {
|
|
2934
2934
|
const { command: exe, prefixArgs } = splitResolvedCommand(command);
|
|
2935
2935
|
return startWindowsDetachedProcess(
|
|
2936
2936
|
exe,
|
|
2937
2937
|
[...prefixArgs, "app-server", "--listen", url],
|
|
2938
2938
|
repoRoot,
|
|
2939
|
-
logPath
|
|
2939
|
+
logPath,
|
|
2940
|
+
env
|
|
2940
2941
|
);
|
|
2941
2942
|
}
|
|
2942
2943
|
function findListeningProcessId(url, platform) {
|
|
@@ -3046,14 +3047,14 @@ function startUnixDetachedProcess(command, args, repoRoot, logPath, env = proces
|
|
|
3046
3047
|
}
|
|
3047
3048
|
}
|
|
3048
3049
|
}
|
|
3049
|
-
function startUnixCodexAppServer(command, url, repoRoot, logPath, platform = DEFAULT_UNIX_PLATFORM) {
|
|
3050
|
+
function startUnixCodexAppServer(command, url, repoRoot, logPath, env = process.env, platform = DEFAULT_UNIX_PLATFORM) {
|
|
3050
3051
|
const { command: exe, prefixArgs } = splitResolvedCommand(command);
|
|
3051
3052
|
return startUnixDetachedProcess(
|
|
3052
3053
|
exe,
|
|
3053
3054
|
[...prefixArgs, "app-server", "--listen", url],
|
|
3054
3055
|
repoRoot,
|
|
3055
3056
|
logPath,
|
|
3056
|
-
|
|
3057
|
+
env,
|
|
3057
3058
|
platform
|
|
3058
3059
|
);
|
|
3059
3060
|
}
|
|
@@ -4022,6 +4023,19 @@ import * as path21 from "path";
|
|
|
4022
4023
|
var DEFAULT_APP_SERVER_URL3 = "ws://127.0.0.1:4501";
|
|
4023
4024
|
var APP_SERVER_START_TIMEOUT_MS = 2e4;
|
|
4024
4025
|
var APP_SERVER_GATEWAY_START_TIMEOUT_MS = 5e3;
|
|
4026
|
+
function buildCodexAppServerEnv(options) {
|
|
4027
|
+
return {
|
|
4028
|
+
...process.env,
|
|
4029
|
+
TAP_COMMS_DIR: options.commsDir,
|
|
4030
|
+
TAP_STATE_DIR: options.stateDir,
|
|
4031
|
+
TAP_RUNTIME_STATE_DIR: options.runtimeStateDir,
|
|
4032
|
+
TAP_REPO_ROOT: options.repoRoot,
|
|
4033
|
+
TAP_BRIDGE_INSTANCE_ID: options.instanceId,
|
|
4034
|
+
TAP_AGENT_ID: options.instanceId,
|
|
4035
|
+
TAP_AGENT_NAME: options.agentName,
|
|
4036
|
+
CODEX_TAP_AGENT_NAME: options.agentName
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4025
4039
|
function isAppServerUsedByOtherBridge(stateDir, excludeInstanceId, appServer) {
|
|
4026
4040
|
const pidDir = path21.join(stateDir, "pids");
|
|
4027
4041
|
if (!fs24.existsSync(pidDir)) return false;
|
|
@@ -4125,6 +4139,7 @@ Start the app-server manually:
|
|
|
4125
4139
|
const logPath = appServerLogFilePath(options.stateDir, options.instanceId);
|
|
4126
4140
|
fs24.mkdirSync(path21.dirname(logPath), { recursive: true });
|
|
4127
4141
|
rotateLog(logPath);
|
|
4142
|
+
const appServerEnv = buildCodexAppServerEnv(options);
|
|
4128
4143
|
if (options.noAuth) {
|
|
4129
4144
|
const manualCommand2 = formatCodexAppServerCommand("codex", effectiveUrl);
|
|
4130
4145
|
let pid2;
|
|
@@ -4134,7 +4149,8 @@ Start the app-server manually:
|
|
|
4134
4149
|
resolvedCommand,
|
|
4135
4150
|
effectiveUrl,
|
|
4136
4151
|
options.repoRoot,
|
|
4137
|
-
logPath
|
|
4152
|
+
logPath,
|
|
4153
|
+
appServerEnv
|
|
4138
4154
|
);
|
|
4139
4155
|
} catch (err) {
|
|
4140
4156
|
throw new Error(
|
|
@@ -4151,6 +4167,7 @@ Start it manually:
|
|
|
4151
4167
|
effectiveUrl,
|
|
4152
4168
|
options.repoRoot,
|
|
4153
4169
|
logPath,
|
|
4170
|
+
appServerEnv,
|
|
4154
4171
|
options.platform
|
|
4155
4172
|
);
|
|
4156
4173
|
} catch (err) {
|
|
@@ -4211,7 +4228,8 @@ Or start it manually:
|
|
|
4211
4228
|
resolvedCommand,
|
|
4212
4229
|
auth.upstreamUrl,
|
|
4213
4230
|
options.repoRoot,
|
|
4214
|
-
logPath
|
|
4231
|
+
logPath,
|
|
4232
|
+
appServerEnv
|
|
4215
4233
|
);
|
|
4216
4234
|
} catch (err) {
|
|
4217
4235
|
if (auth.gatewayPid != null) {
|
|
@@ -4232,6 +4250,7 @@ Start it manually:
|
|
|
4232
4250
|
auth.upstreamUrl,
|
|
4233
4251
|
options.repoRoot,
|
|
4234
4252
|
logPath,
|
|
4253
|
+
appServerEnv,
|
|
4235
4254
|
options.platform
|
|
4236
4255
|
);
|
|
4237
4256
|
} catch (err) {
|
|
@@ -4444,9 +4463,12 @@ async function startBridge(options) {
|
|
|
4444
4463
|
appServer = await ensureCodexAppServer({
|
|
4445
4464
|
instanceId,
|
|
4446
4465
|
stateDir,
|
|
4466
|
+
runtimeStateDir,
|
|
4467
|
+
commsDir,
|
|
4447
4468
|
repoRoot,
|
|
4448
4469
|
platform: options.platform,
|
|
4449
4470
|
appServerUrl: effectiveAppServerUrl,
|
|
4471
|
+
agentName: resolvedAgent,
|
|
4450
4472
|
existingAppServer: previousAppServer,
|
|
4451
4473
|
noAuth: options.noAuth
|
|
4452
4474
|
});
|
|
@@ -4467,7 +4489,9 @@ async function startBridge(options) {
|
|
|
4467
4489
|
const bridgeEnv = {
|
|
4468
4490
|
...runtimeEnv,
|
|
4469
4491
|
TAP_COMMS_DIR: commsDir,
|
|
4470
|
-
TAP_STATE_DIR:
|
|
4492
|
+
TAP_STATE_DIR: stateDir,
|
|
4493
|
+
TAP_RUNTIME_STATE_DIR: runtimeStateDir,
|
|
4494
|
+
TAP_REPO_ROOT: repoRoot,
|
|
4471
4495
|
TAP_BRIDGE_RUNTIME: runtime,
|
|
4472
4496
|
TAP_BRIDGE_INSTANCE_ID: instanceId,
|
|
4473
4497
|
TAP_AGENT_ID: instanceId,
|
|
@@ -5051,6 +5075,53 @@ async function addCommand(args) {
|
|
|
5051
5075
|
};
|
|
5052
5076
|
}
|
|
5053
5077
|
|
|
5078
|
+
// src/engine/health-monitor.ts
|
|
5079
|
+
import * as fs27 from "fs";
|
|
5080
|
+
import * as path24 from "path";
|
|
5081
|
+
var DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS = 2 * 60 * 1e3;
|
|
5082
|
+
function getHeartbeatActivityMs2(record) {
|
|
5083
|
+
const timestamp = new Date(record.lastActivity ?? record.timestamp ?? 0).getTime();
|
|
5084
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
5085
|
+
}
|
|
5086
|
+
function isSameInstanceHeartbeat2(key, heartbeat, instanceId) {
|
|
5087
|
+
if (heartbeat.instanceId === instanceId) return true;
|
|
5088
|
+
if (heartbeat.connectHash === `instance:${instanceId}`) return true;
|
|
5089
|
+
return key === instanceId || key.replace(/_/g, "-") === instanceId || key.replace(/-/g, "_") === instanceId;
|
|
5090
|
+
}
|
|
5091
|
+
function loadLiveDispatchEvidence(commsDir, instanceId) {
|
|
5092
|
+
const heartbeatsPath = path24.join(commsDir, "heartbeats.json");
|
|
5093
|
+
if (!fs27.existsSync(heartbeatsPath)) return null;
|
|
5094
|
+
try {
|
|
5095
|
+
const store = JSON.parse(
|
|
5096
|
+
fs27.readFileSync(heartbeatsPath, "utf-8")
|
|
5097
|
+
);
|
|
5098
|
+
let best = null;
|
|
5099
|
+
let bestActivityMs = -1;
|
|
5100
|
+
for (const [key, heartbeat] of Object.entries(store)) {
|
|
5101
|
+
if (!isSameInstanceHeartbeat2(key, heartbeat, instanceId)) continue;
|
|
5102
|
+
if (heartbeat.source !== "bridge-dispatch") continue;
|
|
5103
|
+
if (heartbeat.bridgePid == null || !isProcessAlive(heartbeat.bridgePid)) {
|
|
5104
|
+
continue;
|
|
5105
|
+
}
|
|
5106
|
+
const activityMs = getHeartbeatActivityMs2(heartbeat);
|
|
5107
|
+
if (activityMs == null || Date.now() - activityMs > DISPATCH_EVIDENCE_FRESH_THRESHOLD_MS) {
|
|
5108
|
+
continue;
|
|
5109
|
+
}
|
|
5110
|
+
if (activityMs > bestActivityMs) {
|
|
5111
|
+
bestActivityMs = activityMs;
|
|
5112
|
+
best = {
|
|
5113
|
+
bridgePid: heartbeat.bridgePid,
|
|
5114
|
+
lastActivity: heartbeat.lastActivity ?? heartbeat.timestamp ?? new Date(activityMs).toISOString()
|
|
5115
|
+
};
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
return best;
|
|
5119
|
+
} catch {
|
|
5120
|
+
return null;
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
var HEARTBEAT_FRESH_THRESHOLD_MS = 2 * 60 * 1e3;
|
|
5124
|
+
|
|
5054
5125
|
// src/commands/status.ts
|
|
5055
5126
|
init_config();
|
|
5056
5127
|
init_utils();
|
|
@@ -5064,12 +5135,13 @@ Description:
|
|
|
5064
5135
|
Examples:
|
|
5065
5136
|
npx @hua-labs/tap status
|
|
5066
5137
|
`.trim();
|
|
5067
|
-
function resolveStatus(inst, stateDir) {
|
|
5138
|
+
function resolveStatus(inst, stateDir, commsDir) {
|
|
5068
5139
|
if (!inst.installed) {
|
|
5069
5140
|
return {
|
|
5070
5141
|
status: "not installed",
|
|
5071
5142
|
lifecycle: null,
|
|
5072
|
-
session: null
|
|
5143
|
+
session: null,
|
|
5144
|
+
warnings: []
|
|
5073
5145
|
};
|
|
5074
5146
|
}
|
|
5075
5147
|
switch (inst.bridgeMode) {
|
|
@@ -5078,9 +5150,11 @@ function resolveStatus(inst, stateDir) {
|
|
|
5078
5150
|
return {
|
|
5079
5151
|
status: inst.lastVerifiedAt ? "active" : "configured",
|
|
5080
5152
|
lifecycle: null,
|
|
5081
|
-
session: null
|
|
5153
|
+
session: null,
|
|
5154
|
+
warnings: []
|
|
5082
5155
|
};
|
|
5083
5156
|
case "app-server": {
|
|
5157
|
+
let staleLifecycle = null;
|
|
5084
5158
|
if (inst.bridge) {
|
|
5085
5159
|
const lifecycle = resolveBridgeLifecycleSnapshot(
|
|
5086
5160
|
stateDir,
|
|
@@ -5088,21 +5162,40 @@ function resolveStatus(inst, stateDir) {
|
|
|
5088
5162
|
inst.bridge
|
|
5089
5163
|
);
|
|
5090
5164
|
if (lifecycle.status === "bridge-stale") {
|
|
5165
|
+
staleLifecycle = lifecycle;
|
|
5091
5166
|
inst.bridge = null;
|
|
5167
|
+
} else {
|
|
5168
|
+
const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(inst.bridge);
|
|
5092
5169
|
return {
|
|
5093
|
-
status:
|
|
5170
|
+
status: "active",
|
|
5094
5171
|
lifecycle,
|
|
5095
|
-
session:
|
|
5172
|
+
session: deriveCodexSessionState({
|
|
5173
|
+
runtimeHeartbeat,
|
|
5174
|
+
runtimeStateDir: inst.bridge.runtimeStateDir ?? null
|
|
5175
|
+
}),
|
|
5176
|
+
warnings: []
|
|
5096
5177
|
};
|
|
5097
5178
|
}
|
|
5098
|
-
|
|
5179
|
+
}
|
|
5180
|
+
const liveDispatch = loadLiveDispatchEvidence(commsDir, inst.instanceId);
|
|
5181
|
+
if (liveDispatch) {
|
|
5099
5182
|
return {
|
|
5100
|
-
status: "
|
|
5101
|
-
lifecycle
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5183
|
+
status: "dispatch-live",
|
|
5184
|
+
lifecycle: deriveBridgeLifecycleState({
|
|
5185
|
+
bridgeStatus: "stopped"
|
|
5186
|
+
}),
|
|
5187
|
+
session: deriveCodexSessionState({ runtimeHeartbeat: null }),
|
|
5188
|
+
warnings: [
|
|
5189
|
+
`fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
|
|
5190
|
+
]
|
|
5191
|
+
};
|
|
5192
|
+
}
|
|
5193
|
+
if (staleLifecycle) {
|
|
5194
|
+
return {
|
|
5195
|
+
status: inst.lastVerifiedAt ? "configured" : "installed",
|
|
5196
|
+
lifecycle: staleLifecycle,
|
|
5197
|
+
session: null,
|
|
5198
|
+
warnings: []
|
|
5106
5199
|
};
|
|
5107
5200
|
}
|
|
5108
5201
|
return {
|
|
@@ -5110,25 +5203,27 @@ function resolveStatus(inst, stateDir) {
|
|
|
5110
5203
|
lifecycle: deriveBridgeLifecycleState({
|
|
5111
5204
|
bridgeStatus: "stopped"
|
|
5112
5205
|
}),
|
|
5113
|
-
session: deriveCodexSessionState({ runtimeHeartbeat: null })
|
|
5206
|
+
session: deriveCodexSessionState({ runtimeHeartbeat: null }),
|
|
5207
|
+
warnings: []
|
|
5114
5208
|
};
|
|
5115
5209
|
}
|
|
5116
5210
|
default:
|
|
5117
5211
|
return {
|
|
5118
5212
|
status: "installed",
|
|
5119
5213
|
lifecycle: null,
|
|
5120
|
-
session: null
|
|
5214
|
+
session: null,
|
|
5215
|
+
warnings: []
|
|
5121
5216
|
};
|
|
5122
5217
|
}
|
|
5123
5218
|
}
|
|
5124
|
-
function instanceStatusLine(inst, status, lifecycle, session) {
|
|
5219
|
+
function instanceStatusLine(inst, status, lifecycle, session, warnings) {
|
|
5125
5220
|
const bridgeInfo = inst.bridge ? ` (pid: ${inst.bridge.pid})` : "";
|
|
5126
5221
|
const lifecycleStr = lifecycle?.status ?? "-";
|
|
5127
5222
|
const sessionStr = session?.status ?? "-";
|
|
5128
5223
|
const mode = inst.bridgeMode;
|
|
5129
5224
|
const portStr = inst.port ? ` port:${inst.port}` : "";
|
|
5130
5225
|
const restart = inst.restartRequired ? " [restart required]" : "";
|
|
5131
|
-
const warns =
|
|
5226
|
+
const warns = warnings.length > 0 ? ` [${warnings.length} warning(s)]` : "";
|
|
5132
5227
|
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
5228
|
}
|
|
5134
5229
|
async function statusCommand(args) {
|
|
@@ -5181,10 +5276,15 @@ async function statusCommand(args) {
|
|
|
5181
5276
|
for (const id of installed) {
|
|
5182
5277
|
const inst = state.instances[id];
|
|
5183
5278
|
if (inst) {
|
|
5184
|
-
const { status, lifecycle, session } = resolveStatus(
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5279
|
+
const { status, lifecycle, session, warnings } = resolveStatus(
|
|
5280
|
+
inst,
|
|
5281
|
+
stateDir,
|
|
5282
|
+
state.commsDir
|
|
5283
|
+
);
|
|
5284
|
+
const mergedWarnings = [...inst.warnings, ...warnings];
|
|
5285
|
+
log(instanceStatusLine(inst, status, lifecycle, session, mergedWarnings));
|
|
5286
|
+
if (mergedWarnings.length > 0) {
|
|
5287
|
+
for (const w of mergedWarnings) {
|
|
5188
5288
|
logWarn(` ${w}`);
|
|
5189
5289
|
}
|
|
5190
5290
|
}
|
|
@@ -5196,7 +5296,7 @@ async function statusCommand(args) {
|
|
|
5196
5296
|
bridgeMode: inst.bridgeMode,
|
|
5197
5297
|
bridge: inst.bridge,
|
|
5198
5298
|
port: inst.port,
|
|
5199
|
-
warnings:
|
|
5299
|
+
warnings: mergedWarnings
|
|
5200
5300
|
};
|
|
5201
5301
|
}
|
|
5202
5302
|
}
|
|
@@ -5227,7 +5327,7 @@ async function statusCommand(args) {
|
|
|
5227
5327
|
init_utils();
|
|
5228
5328
|
|
|
5229
5329
|
// src/engine/rollback.ts
|
|
5230
|
-
import * as
|
|
5330
|
+
import * as fs28 from "fs";
|
|
5231
5331
|
async function rollbackRuntime(_instanceId, runtimeState) {
|
|
5232
5332
|
const errors = [];
|
|
5233
5333
|
const restoredFiles = [];
|
|
@@ -5256,7 +5356,7 @@ async function rollbackRuntime(_instanceId, runtimeState) {
|
|
|
5256
5356
|
};
|
|
5257
5357
|
}
|
|
5258
5358
|
function rollbackArtifact(artifact) {
|
|
5259
|
-
if (!
|
|
5359
|
+
if (!fs28.existsSync(artifact.path)) {
|
|
5260
5360
|
return { restored: false, error: `File not found: ${artifact.path}` };
|
|
5261
5361
|
}
|
|
5262
5362
|
switch (artifact.kind) {
|
|
@@ -5274,7 +5374,7 @@ function rollbackArtifact(artifact) {
|
|
|
5274
5374
|
}
|
|
5275
5375
|
}
|
|
5276
5376
|
function rollbackJsonPath(artifact) {
|
|
5277
|
-
const raw =
|
|
5377
|
+
const raw = fs28.readFileSync(artifact.path, "utf-8");
|
|
5278
5378
|
let config;
|
|
5279
5379
|
try {
|
|
5280
5380
|
config = JSON.parse(raw);
|
|
@@ -5300,18 +5400,18 @@ function rollbackJsonPath(artifact) {
|
|
|
5300
5400
|
cleanEmptyParents(config, artifact.selector);
|
|
5301
5401
|
}
|
|
5302
5402
|
const tmp = `${artifact.path}.tmp.${process.pid}`;
|
|
5303
|
-
|
|
5304
|
-
|
|
5403
|
+
fs28.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5404
|
+
fs28.renameSync(tmp, artifact.path);
|
|
5305
5405
|
return { restored: true };
|
|
5306
5406
|
}
|
|
5307
5407
|
function rollbackTomlTable(artifact) {
|
|
5308
|
-
const content =
|
|
5408
|
+
const content = fs28.readFileSync(artifact.path, "utf-8");
|
|
5309
5409
|
const backup = artifact.backupPath ? readArtifactBackup(artifact.backupPath) : null;
|
|
5310
5410
|
if (backup?.kind === "toml-table" && backup.selector === artifact.selector) {
|
|
5311
5411
|
const nextContent = backup.existed ? replaceTomlTable(content, artifact.selector, backup.content ?? "") : removeTomlTable(content, artifact.selector);
|
|
5312
5412
|
const tmp2 = `${artifact.path}.tmp.${process.pid}`;
|
|
5313
|
-
|
|
5314
|
-
|
|
5413
|
+
fs28.writeFileSync(tmp2, nextContent, "utf-8");
|
|
5414
|
+
fs28.renameSync(tmp2, artifact.path);
|
|
5315
5415
|
return { restored: true };
|
|
5316
5416
|
}
|
|
5317
5417
|
if (!extractTomlTable(content, artifact.selector)) {
|
|
@@ -5321,13 +5421,13 @@ function rollbackTomlTable(artifact) {
|
|
|
5321
5421
|
};
|
|
5322
5422
|
}
|
|
5323
5423
|
const tmp = `${artifact.path}.tmp.${process.pid}`;
|
|
5324
|
-
|
|
5325
|
-
|
|
5424
|
+
fs28.writeFileSync(tmp, removeTomlTable(content, artifact.selector), "utf-8");
|
|
5425
|
+
fs28.renameSync(tmp, artifact.path);
|
|
5326
5426
|
return { restored: true };
|
|
5327
5427
|
}
|
|
5328
5428
|
function rollbackFile(artifact) {
|
|
5329
|
-
if (
|
|
5330
|
-
|
|
5429
|
+
if (fs28.existsSync(artifact.path)) {
|
|
5430
|
+
fs28.unlinkSync(artifact.path);
|
|
5331
5431
|
return { restored: true };
|
|
5332
5432
|
}
|
|
5333
5433
|
return { restored: false, error: `File not found: ${artifact.path}` };
|
|
@@ -5502,13 +5602,13 @@ async function removeCommand(args) {
|
|
|
5502
5602
|
init_utils();
|
|
5503
5603
|
|
|
5504
5604
|
// src/commands/bridge-start.ts
|
|
5505
|
-
import * as
|
|
5605
|
+
import * as path27 from "path";
|
|
5506
5606
|
init_instance_config();
|
|
5507
5607
|
init_config();
|
|
5508
5608
|
init_utils();
|
|
5509
5609
|
|
|
5510
5610
|
// src/commands/bridge-helpers.ts
|
|
5511
|
-
import * as
|
|
5611
|
+
import * as path25 from "path";
|
|
5512
5612
|
function formatAge(seconds) {
|
|
5513
5613
|
if (seconds < 60) return `${seconds}s ago`;
|
|
5514
5614
|
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
@@ -5538,8 +5638,24 @@ function resolveTuiConnectUrl(appServer) {
|
|
|
5538
5638
|
function quoteCliArg(value) {
|
|
5539
5639
|
return `"${value.replace(/"/g, '\\"')}"`;
|
|
5540
5640
|
}
|
|
5541
|
-
function
|
|
5542
|
-
|
|
5641
|
+
function quoteShellEnvValue(value) {
|
|
5642
|
+
if (process.platform === "win32") {
|
|
5643
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
5644
|
+
}
|
|
5645
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
5646
|
+
}
|
|
5647
|
+
function formatCodexTuiAttachCommand(tuiConnectUrl, cwd, env = {}) {
|
|
5648
|
+
const base = `codex --enable tui_app_server --remote ${quoteCliArg(tuiConnectUrl)} --cd ${quoteCliArg(cwd)}`;
|
|
5649
|
+
const entries = Object.entries(env).filter(([, value]) => value.length > 0);
|
|
5650
|
+
if (entries.length === 0) {
|
|
5651
|
+
return base;
|
|
5652
|
+
}
|
|
5653
|
+
if (process.platform === "win32") {
|
|
5654
|
+
const envPrefix2 = entries.map(([key, value]) => `$env:${key} = ${quoteShellEnvValue(value)}`).join("; ");
|
|
5655
|
+
return `${envPrefix2}; ${base}`;
|
|
5656
|
+
}
|
|
5657
|
+
const envPrefix = entries.map(([key, value]) => `${key}=${quoteShellEnvValue(value)}`).join(" ");
|
|
5658
|
+
return `${envPrefix} ${base}`;
|
|
5543
5659
|
}
|
|
5544
5660
|
function resolveTuiAttachCwd(repoRoot, stateRepoRoot, runtimeThreadCwd, savedThreadCwd) {
|
|
5545
5661
|
return runtimeThreadCwd ?? savedThreadCwd ?? stateRepoRoot ?? repoRoot;
|
|
@@ -5554,7 +5670,7 @@ function formatThreadSummary(threadId, cwd) {
|
|
|
5554
5670
|
return cwd ? `${threadId} (${cwd})` : threadId;
|
|
5555
5671
|
}
|
|
5556
5672
|
function normalizeComparablePath(value) {
|
|
5557
|
-
return
|
|
5673
|
+
return path25.resolve(value).replace(/\\/g, "/").toLowerCase();
|
|
5558
5674
|
}
|
|
5559
5675
|
function sameOptionalPath(left, right) {
|
|
5560
5676
|
if (!left || !right) {
|
|
@@ -5626,22 +5742,22 @@ function transferManagedAppServerOwnership(state, stateDir, recipientId, appServ
|
|
|
5626
5742
|
}
|
|
5627
5743
|
|
|
5628
5744
|
// src/commands/bridge-heartbeat.ts
|
|
5629
|
-
import { existsSync as
|
|
5630
|
-
import * as
|
|
5745
|
+
import { existsSync as existsSync26, readFileSync as readFileSync22, renameSync as renameSync13, writeFileSync as writeFileSync14 } from "fs";
|
|
5746
|
+
import * as path26 from "path";
|
|
5631
5747
|
var BRIDGE_UP_ACTIVE_HEARTBEAT_WINDOW_MS = 10 * 60 * 1e3;
|
|
5632
5748
|
var BRIDGE_UP_ORPHAN_HEARTBEAT_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
5633
5749
|
var BRIDGE_UP_SIGNING_OFF_HEARTBEAT_WINDOW_MS = 5 * 60 * 1e3;
|
|
5634
5750
|
function loadBridgeHeartbeatStore(commsDir) {
|
|
5635
|
-
const heartbeatsPath =
|
|
5636
|
-
if (!
|
|
5751
|
+
const heartbeatsPath = path26.join(commsDir, "heartbeats.json");
|
|
5752
|
+
if (!existsSync26(heartbeatsPath)) return {};
|
|
5637
5753
|
try {
|
|
5638
|
-
return JSON.parse(
|
|
5754
|
+
return JSON.parse(readFileSync22(heartbeatsPath, "utf-8"));
|
|
5639
5755
|
} catch {
|
|
5640
5756
|
return null;
|
|
5641
5757
|
}
|
|
5642
5758
|
}
|
|
5643
5759
|
function saveBridgeHeartbeatStore(commsDir, store) {
|
|
5644
|
-
const heartbeatsPath =
|
|
5760
|
+
const heartbeatsPath = path26.join(commsDir, "heartbeats.json");
|
|
5645
5761
|
const tmp = `${heartbeatsPath}.tmp.${process.pid}`;
|
|
5646
5762
|
writeFileSync14(tmp, JSON.stringify(store, null, 2), "utf-8");
|
|
5647
5763
|
renameSync13(tmp, heartbeatsPath);
|
|
@@ -5948,7 +6064,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
5948
6064
|
}
|
|
5949
6065
|
}
|
|
5950
6066
|
logSuccess(`Bridge started (PID: ${bridge.pid})`);
|
|
5951
|
-
log(`Log: ${
|
|
6067
|
+
log(`Log: ${path27.join(ctx.stateDir, "logs", `bridge-${instanceId}.log`)}`);
|
|
5952
6068
|
if (bridge.appServer) {
|
|
5953
6069
|
log(`App server: ${formatAppServerState(bridge.appServer)}`);
|
|
5954
6070
|
if (bridge.appServer.logPath) {
|
|
@@ -6073,7 +6189,7 @@ async function bridgeStartAll(flags = {}) {
|
|
|
6073
6189
|
warnings.push(msg);
|
|
6074
6190
|
continue;
|
|
6075
6191
|
}
|
|
6076
|
-
const stateDir =
|
|
6192
|
+
const stateDir = path27.join(repoRoot, ".tap-comms");
|
|
6077
6193
|
const currentBridgeState = loadBridgeState(stateDir, instanceId);
|
|
6078
6194
|
const { manageAppServer, noAuth } = inferRestartMode(
|
|
6079
6195
|
currentBridgeState,
|
|
@@ -6492,7 +6608,7 @@ async function bridgeWatch(_intervalSeconds, stuckThresholdSeconds) {
|
|
|
6492
6608
|
}
|
|
6493
6609
|
|
|
6494
6610
|
// src/commands/bridge-status.ts
|
|
6495
|
-
import * as
|
|
6611
|
+
import * as path28 from "path";
|
|
6496
6612
|
init_config();
|
|
6497
6613
|
init_utils();
|
|
6498
6614
|
function bridgeStatusAll() {
|
|
@@ -6543,23 +6659,26 @@ function bridgeStatusAll() {
|
|
|
6543
6659
|
};
|
|
6544
6660
|
continue;
|
|
6545
6661
|
}
|
|
6546
|
-
const
|
|
6662
|
+
const rawStatus = getBridgeStatus(stateDir, instanceId);
|
|
6547
6663
|
const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
|
|
6548
|
-
const
|
|
6549
|
-
const
|
|
6550
|
-
const
|
|
6551
|
-
|
|
6664
|
+
const liveDispatch = rawStatus === "running" ? null : loadLiveDispatchEvidence(state.commsDir, instanceId);
|
|
6665
|
+
const surfaceBridgeState = liveDispatch ? null : bridgeState;
|
|
6666
|
+
const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(surfaceBridgeState);
|
|
6667
|
+
const savedThread = loadRuntimeBridgeThreadState(surfaceBridgeState);
|
|
6668
|
+
const status = liveDispatch ? "dispatch-live" : rawStatus;
|
|
6669
|
+
const lifecycle = liveDispatch ? deriveBridgeLifecycleState({ bridgeStatus: "stopped" }) : deriveBridgeLifecycleState({
|
|
6670
|
+
bridgeStatus: rawStatus,
|
|
6552
6671
|
bridgeState,
|
|
6553
6672
|
runtimeHeartbeat,
|
|
6554
6673
|
savedThread,
|
|
6555
6674
|
persistedLifecycle: inst.bridgeLifecycle ?? bridgeState?.lifecycle ?? null
|
|
6556
6675
|
});
|
|
6557
|
-
const session =
|
|
6676
|
+
const session = rawStatus === "running" || liveDispatch ? deriveCodexSessionState({
|
|
6558
6677
|
runtimeHeartbeat,
|
|
6559
|
-
runtimeStateDir:
|
|
6678
|
+
runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
|
|
6560
6679
|
}) : null;
|
|
6561
|
-
const age = getHeartbeatAge(stateDir, instanceId);
|
|
6562
|
-
if (
|
|
6680
|
+
const age = liveDispatch ? null : getHeartbeatAge(stateDir, instanceId);
|
|
6681
|
+
if (rawStatus === "stale" && inst.bridge) {
|
|
6563
6682
|
state.instances[instanceId] = {
|
|
6564
6683
|
...inst,
|
|
6565
6684
|
bridge: null,
|
|
@@ -6571,22 +6690,22 @@ function bridgeStatusAll() {
|
|
|
6571
6690
|
};
|
|
6572
6691
|
stateChanged = true;
|
|
6573
6692
|
}
|
|
6574
|
-
const pid =
|
|
6575
|
-
const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
|
|
6693
|
+
const pid = surfaceBridgeState?.pid ?? null;
|
|
6694
|
+
const heartbeat = liveDispatch ? null : getBridgeHeartbeatTimestamp(stateDir, instanceId);
|
|
6576
6695
|
const pidStr = pid ? String(pid) : "-";
|
|
6577
6696
|
const portStr = inst.port ? String(inst.port) : "-";
|
|
6578
6697
|
const ageStr = age !== null ? formatAge(age) : "-";
|
|
6579
6698
|
log(
|
|
6580
6699
|
`${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
6700
|
);
|
|
6582
|
-
if (
|
|
6583
|
-
log(` App server: ${formatAppServerState(
|
|
6584
|
-
if (
|
|
6585
|
-
log(` Server log: ${
|
|
6701
|
+
if (surfaceBridgeState?.appServer) {
|
|
6702
|
+
log(` App server: ${formatAppServerState(surfaceBridgeState.appServer)}`);
|
|
6703
|
+
if (surfaceBridgeState.appServer.logPath) {
|
|
6704
|
+
log(` Server log: ${surfaceBridgeState.appServer.logPath}`);
|
|
6586
6705
|
}
|
|
6587
|
-
if (
|
|
6706
|
+
if (surfaceBridgeState.appServer.auth) {
|
|
6588
6707
|
log(
|
|
6589
|
-
` Protected: ${redactProtectedUrl(
|
|
6708
|
+
` Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
|
|
6590
6709
|
);
|
|
6591
6710
|
}
|
|
6592
6711
|
}
|
|
@@ -6604,6 +6723,11 @@ function bridgeStatusAll() {
|
|
|
6604
6723
|
if (transition) {
|
|
6605
6724
|
log(` Transition: ${transition}`);
|
|
6606
6725
|
}
|
|
6726
|
+
if (liveDispatch) {
|
|
6727
|
+
log(
|
|
6728
|
+
` Drift: fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
|
|
6729
|
+
);
|
|
6730
|
+
}
|
|
6607
6731
|
const turnInfo = getTurnInfo(stateDir, instanceId);
|
|
6608
6732
|
if (turnInfo?.activeTurnId) {
|
|
6609
6733
|
const ageStr2 = turnInfo.ageSeconds != null ? formatAge(turnInfo.ageSeconds) : "?";
|
|
@@ -6629,7 +6753,7 @@ function bridgeStatusAll() {
|
|
|
6629
6753
|
threadCwd: runtimeHeartbeat?.threadCwd ?? null,
|
|
6630
6754
|
savedThreadId: savedThread?.threadId ?? null,
|
|
6631
6755
|
savedThreadCwd: savedThread?.cwd ?? null,
|
|
6632
|
-
appServer:
|
|
6756
|
+
appServer: surfaceBridgeState?.appServer ?? null
|
|
6633
6757
|
};
|
|
6634
6758
|
}
|
|
6635
6759
|
if (instanceIds.length === 0) {
|
|
@@ -6726,14 +6850,17 @@ function bridgeStatusOne(identifier) {
|
|
|
6726
6850
|
}
|
|
6727
6851
|
const { config: resolvedCfg2 } = resolveConfig({}, repoRoot);
|
|
6728
6852
|
const stateDir = resolvedCfg2.stateDir;
|
|
6729
|
-
const
|
|
6853
|
+
const rawStatus = getBridgeStatus(stateDir, instanceId);
|
|
6730
6854
|
const bridgeState = loadBridgeState(stateDir, instanceId) ?? inst.bridge;
|
|
6731
|
-
const
|
|
6732
|
-
const
|
|
6733
|
-
const
|
|
6734
|
-
const
|
|
6735
|
-
const
|
|
6736
|
-
|
|
6855
|
+
const liveDispatch = rawStatus === "running" ? null : loadLiveDispatchEvidence(state.commsDir, instanceId);
|
|
6856
|
+
const surfaceBridgeState = liveDispatch ? null : bridgeState;
|
|
6857
|
+
const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(surfaceBridgeState);
|
|
6858
|
+
const savedThread = loadRuntimeBridgeThreadState(surfaceBridgeState);
|
|
6859
|
+
const age = liveDispatch ? null : getHeartbeatAge(stateDir, instanceId);
|
|
6860
|
+
const heartbeat = liveDispatch ? null : getBridgeHeartbeatTimestamp(stateDir, instanceId);
|
|
6861
|
+
const status = liveDispatch ? "dispatch-live" : rawStatus;
|
|
6862
|
+
const lifecycle = liveDispatch ? deriveBridgeLifecycleState({ bridgeStatus: "stopped" }) : deriveBridgeLifecycleState({
|
|
6863
|
+
bridgeStatus: rawStatus,
|
|
6737
6864
|
bridgeState,
|
|
6738
6865
|
runtimeHeartbeat,
|
|
6739
6866
|
savedThread,
|
|
@@ -6741,13 +6868,25 @@ function bridgeStatusOne(identifier) {
|
|
|
6741
6868
|
});
|
|
6742
6869
|
const session = deriveCodexSessionState({
|
|
6743
6870
|
runtimeHeartbeat,
|
|
6744
|
-
runtimeStateDir:
|
|
6871
|
+
runtimeStateDir: surfaceBridgeState?.runtimeStateDir ?? null
|
|
6745
6872
|
});
|
|
6746
6873
|
log(`Status: ${status}`);
|
|
6747
6874
|
log(`Lifecycle: ${lifecycle.summary}`);
|
|
6748
6875
|
log(`Session: ${session.summary}`);
|
|
6749
|
-
if (
|
|
6750
|
-
|
|
6876
|
+
if (rawStatus === "stale" && inst.bridge) {
|
|
6877
|
+
state.instances[instanceId] = {
|
|
6878
|
+
...inst,
|
|
6879
|
+
bridge: null,
|
|
6880
|
+
bridgeLifecycle: transitionBridgeLifecycle(
|
|
6881
|
+
inst.bridgeLifecycle ?? inst.bridge?.lifecycle ?? null,
|
|
6882
|
+
"crashed",
|
|
6883
|
+
"bridge pid not alive"
|
|
6884
|
+
)
|
|
6885
|
+
};
|
|
6886
|
+
saveState(repoRoot, state);
|
|
6887
|
+
}
|
|
6888
|
+
if (surfaceBridgeState) {
|
|
6889
|
+
log(`PID: ${surfaceBridgeState.pid}`);
|
|
6751
6890
|
log(
|
|
6752
6891
|
`Heartbeat: ${heartbeat ?? "-"}${age !== null ? ` (${formatAge(age)})` : ""}`
|
|
6753
6892
|
);
|
|
@@ -6762,35 +6901,35 @@ function bridgeStatusOne(identifier) {
|
|
|
6762
6901
|
);
|
|
6763
6902
|
}
|
|
6764
6903
|
log(
|
|
6765
|
-
`Log: ${
|
|
6904
|
+
`Log: ${path28.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
|
|
6766
6905
|
);
|
|
6767
|
-
if (
|
|
6768
|
-
log(`App server: ${
|
|
6769
|
-
log(`Server PID: ${
|
|
6906
|
+
if (surfaceBridgeState.appServer) {
|
|
6907
|
+
log(`App server: ${surfaceBridgeState.appServer.url}`);
|
|
6908
|
+
log(`Server PID: ${surfaceBridgeState.appServer.pid ?? "-"}`);
|
|
6770
6909
|
log(
|
|
6771
|
-
`Server mode: ${
|
|
6910
|
+
`Server mode: ${surfaceBridgeState.appServer.managed ? "managed" : "external"}`
|
|
6772
6911
|
);
|
|
6773
6912
|
log(
|
|
6774
|
-
`Health: ${
|
|
6913
|
+
`Health: ${surfaceBridgeState.appServer.healthy ? "healthy" : "unhealthy"}`
|
|
6775
6914
|
);
|
|
6776
|
-
log(`Checked: ${
|
|
6777
|
-
if (
|
|
6778
|
-
log(`Server log: ${
|
|
6915
|
+
log(`Checked: ${surfaceBridgeState.appServer.lastCheckedAt}`);
|
|
6916
|
+
if (surfaceBridgeState.appServer.logPath) {
|
|
6917
|
+
log(`Server log: ${surfaceBridgeState.appServer.logPath}`);
|
|
6779
6918
|
}
|
|
6780
|
-
if (
|
|
6781
|
-
log(`Auth: ${
|
|
6919
|
+
if (surfaceBridgeState.appServer.auth) {
|
|
6920
|
+
log(`Auth: ${surfaceBridgeState.appServer.auth.mode}`);
|
|
6782
6921
|
log(
|
|
6783
|
-
`Protected: ${redactProtectedUrl(
|
|
6922
|
+
`Protected: ${redactProtectedUrl(surfaceBridgeState.appServer.auth.protectedUrl)}`
|
|
6784
6923
|
);
|
|
6785
|
-
log(`Upstream: ${
|
|
6786
|
-
log(`TUI connect: ${
|
|
6787
|
-
log(`Gateway PID: ${
|
|
6788
|
-
if (
|
|
6789
|
-
log(`Gateway log: ${
|
|
6924
|
+
log(`Upstream: ${surfaceBridgeState.appServer.auth.upstreamUrl}`);
|
|
6925
|
+
log(`TUI connect: ${surfaceBridgeState.appServer.auth.upstreamUrl}`);
|
|
6926
|
+
log(`Gateway PID: ${surfaceBridgeState.appServer.auth.gatewayPid ?? "-"}`);
|
|
6927
|
+
if (surfaceBridgeState.appServer.auth.gatewayLogPath) {
|
|
6928
|
+
log(`Gateway log: ${surfaceBridgeState.appServer.auth.gatewayLogPath}`);
|
|
6790
6929
|
}
|
|
6791
|
-
} else if (
|
|
6930
|
+
} else if (surfaceBridgeState.appServer.managed) {
|
|
6792
6931
|
log(`Auth: none (--no-auth)`);
|
|
6793
|
-
log(`TUI connect: ${
|
|
6932
|
+
log(`TUI connect: ${surfaceBridgeState.appServer.url}`);
|
|
6794
6933
|
}
|
|
6795
6934
|
}
|
|
6796
6935
|
}
|
|
@@ -6798,6 +6937,11 @@ function bridgeStatusOne(identifier) {
|
|
|
6798
6937
|
if (transition) {
|
|
6799
6938
|
log(`Transition: ${transition}`);
|
|
6800
6939
|
}
|
|
6940
|
+
if (liveDispatch) {
|
|
6941
|
+
log(
|
|
6942
|
+
`Drift: fresh bridge-dispatch heartbeat from PID ${liveDispatch.bridgePid} without bridge pid state`
|
|
6943
|
+
);
|
|
6944
|
+
}
|
|
6801
6945
|
log("");
|
|
6802
6946
|
return {
|
|
6803
6947
|
ok: true,
|
|
@@ -6827,14 +6971,14 @@ function bridgeStatusOne(identifier) {
|
|
|
6827
6971
|
lastDispatchAt: session.lastDispatchAt
|
|
6828
6972
|
},
|
|
6829
6973
|
bridgeMode: inst.bridgeMode,
|
|
6830
|
-
pid:
|
|
6974
|
+
pid: surfaceBridgeState?.pid ?? null,
|
|
6831
6975
|
port: inst.port,
|
|
6832
6976
|
lastHeartbeat: heartbeat,
|
|
6833
6977
|
threadId: runtimeHeartbeat?.threadId ?? null,
|
|
6834
6978
|
threadCwd: runtimeHeartbeat?.threadCwd ?? null,
|
|
6835
6979
|
savedThreadId: savedThread?.threadId ?? null,
|
|
6836
6980
|
savedThreadCwd: savedThread?.cwd ?? null,
|
|
6837
|
-
appServer:
|
|
6981
|
+
appServer: surfaceBridgeState?.appServer ?? null
|
|
6838
6982
|
}
|
|
6839
6983
|
};
|
|
6840
6984
|
}
|
|
@@ -6929,7 +7073,23 @@ function bridgeTuiOne(identifier) {
|
|
|
6929
7073
|
runtimeHeartbeat?.threadCwd,
|
|
6930
7074
|
savedThread?.cwd
|
|
6931
7075
|
);
|
|
6932
|
-
const
|
|
7076
|
+
const attachEnv = {
|
|
7077
|
+
TAP_BRIDGE_INSTANCE_ID: instanceId,
|
|
7078
|
+
TAP_AGENT_ID: instanceId,
|
|
7079
|
+
TAP_COMMS_DIR: resolvedConfig.commsDir,
|
|
7080
|
+
TAP_STATE_DIR: stateDir,
|
|
7081
|
+
TAP_RUNTIME_STATE_DIR: bridgeState?.runtimeStateDir ?? getBridgeRuntimeStateDir(repoRoot, instanceId),
|
|
7082
|
+
TAP_REPO_ROOT: repoRoot
|
|
7083
|
+
};
|
|
7084
|
+
if (typeof inst.agentName === "string" && inst.agentName.trim()) {
|
|
7085
|
+
attachEnv.TAP_AGENT_NAME = inst.agentName;
|
|
7086
|
+
attachEnv.CODEX_TAP_AGENT_NAME = inst.agentName;
|
|
7087
|
+
}
|
|
7088
|
+
const attachCommand = formatCodexTuiAttachCommand(
|
|
7089
|
+
tuiConnectUrl,
|
|
7090
|
+
attachCwd,
|
|
7091
|
+
attachEnv
|
|
7092
|
+
);
|
|
6933
7093
|
const warnings = appServer.auth != null ? [
|
|
6934
7094
|
"Use the upstream TUI URL, not the protected gateway URL. The protected URL is bridge-only."
|
|
6935
7095
|
] : [];
|
|
@@ -6954,6 +7114,7 @@ function bridgeTuiOne(identifier) {
|
|
|
6954
7114
|
tuiConnectUrl,
|
|
6955
7115
|
attachCwd,
|
|
6956
7116
|
attachCommand,
|
|
7117
|
+
attachEnv,
|
|
6957
7118
|
appServer
|
|
6958
7119
|
}
|
|
6959
7120
|
};
|
|
@@ -7266,8 +7427,8 @@ async function bridgeCommand(args) {
|
|
|
7266
7427
|
|
|
7267
7428
|
// src/engine/dashboard.ts
|
|
7268
7429
|
init_config();
|
|
7269
|
-
import * as
|
|
7270
|
-
import * as
|
|
7430
|
+
import * as fs29 from "fs";
|
|
7431
|
+
import * as path29 from "path";
|
|
7271
7432
|
import { execSync as execSync4 } from "child_process";
|
|
7272
7433
|
function formatAgentLabel(agentIdOrName, displayName) {
|
|
7273
7434
|
const normalizedId = agentIdOrName.trim();
|
|
@@ -7300,10 +7461,10 @@ function resolveHeartbeatInstanceId(heartbeatId, displayName, state) {
|
|
|
7300
7461
|
return matches.length === 1 ? matches[0].instanceId : null;
|
|
7301
7462
|
}
|
|
7302
7463
|
function collectAgents(commsDir, state, bridges) {
|
|
7303
|
-
const heartbeatsPath =
|
|
7304
|
-
if (!
|
|
7464
|
+
const heartbeatsPath = path29.join(commsDir, "heartbeats.json");
|
|
7465
|
+
if (!fs29.existsSync(heartbeatsPath)) return [];
|
|
7305
7466
|
try {
|
|
7306
|
-
const raw =
|
|
7467
|
+
const raw = fs29.readFileSync(heartbeatsPath, "utf-8");
|
|
7307
7468
|
const data = JSON.parse(raw);
|
|
7308
7469
|
return Object.entries(data).map(([agentId, info]) => {
|
|
7309
7470
|
const instanceId = resolveHeartbeatInstanceId(
|
|
@@ -7363,22 +7524,22 @@ function collectBridges(repoRoot) {
|
|
|
7363
7524
|
});
|
|
7364
7525
|
}
|
|
7365
7526
|
}
|
|
7366
|
-
const tmpDir =
|
|
7367
|
-
if (
|
|
7527
|
+
const tmpDir = path29.join(repoRoot, ".tmp");
|
|
7528
|
+
if (fs29.existsSync(tmpDir)) {
|
|
7368
7529
|
try {
|
|
7369
|
-
const dirs =
|
|
7530
|
+
const dirs = fs29.readdirSync(tmpDir).filter((d) => d.startsWith("codex-app-server-bridge"));
|
|
7370
7531
|
for (const dir of dirs) {
|
|
7371
|
-
const daemonPath =
|
|
7372
|
-
if (!
|
|
7532
|
+
const daemonPath = path29.join(tmpDir, dir, "bridge-daemon.json");
|
|
7533
|
+
if (!fs29.existsSync(daemonPath)) continue;
|
|
7373
7534
|
try {
|
|
7374
|
-
const raw =
|
|
7535
|
+
const raw = fs29.readFileSync(daemonPath, "utf-8");
|
|
7375
7536
|
const daemon = JSON.parse(raw);
|
|
7376
7537
|
const alreadyCovered = bridges.some(
|
|
7377
7538
|
(b) => b.pid === daemon.pid && b.pid !== null
|
|
7378
7539
|
);
|
|
7379
7540
|
if (alreadyCovered) continue;
|
|
7380
|
-
const agentFile =
|
|
7381
|
-
const agentName =
|
|
7541
|
+
const agentFile = path29.join(tmpDir, dir, "agent-name.txt");
|
|
7542
|
+
const agentName = fs29.existsSync(agentFile) ? fs29.readFileSync(agentFile, "utf-8").trim() : dir;
|
|
7382
7543
|
const running = daemon.pid ? isProcessAlive(daemon.pid) : false;
|
|
7383
7544
|
const portMatch = daemon.appServerUrl?.match(/:(\d+)/);
|
|
7384
7545
|
const port = portMatch ? parseInt(portMatch[1], 10) : null;
|
|
@@ -7613,7 +7774,7 @@ async function downCommand(args) {
|
|
|
7613
7774
|
}
|
|
7614
7775
|
|
|
7615
7776
|
// src/commands/serve.ts
|
|
7616
|
-
import * as
|
|
7777
|
+
import * as path30 from "path";
|
|
7617
7778
|
import { spawn as spawn2 } from "child_process";
|
|
7618
7779
|
init_utils();
|
|
7619
7780
|
init_config();
|
|
@@ -7649,10 +7810,10 @@ async function serveCommand(args) {
|
|
|
7649
7810
|
let commsDir;
|
|
7650
7811
|
const commsDirIdx = args.indexOf("--comms-dir");
|
|
7651
7812
|
if (commsDirIdx !== -1 && args[commsDirIdx + 1]) {
|
|
7652
|
-
commsDir =
|
|
7813
|
+
commsDir = path30.resolve(normalizeTapPath(args[commsDirIdx + 1]));
|
|
7653
7814
|
}
|
|
7654
7815
|
if (!commsDir && process.env.TAP_COMMS_DIR) {
|
|
7655
|
-
commsDir =
|
|
7816
|
+
commsDir = path30.resolve(normalizeTapPath(process.env.TAP_COMMS_DIR));
|
|
7656
7817
|
}
|
|
7657
7818
|
if (!commsDir) {
|
|
7658
7819
|
const state = loadState(repoRoot);
|
|
@@ -7719,8 +7880,8 @@ async function serveCommand(args) {
|
|
|
7719
7880
|
// src/commands/init-worktree.ts
|
|
7720
7881
|
init_config();
|
|
7721
7882
|
init_utils();
|
|
7722
|
-
import * as
|
|
7723
|
-
import * as
|
|
7883
|
+
import * as fs30 from "fs";
|
|
7884
|
+
import * as path31 from "path";
|
|
7724
7885
|
import { execSync as execSync5 } from "child_process";
|
|
7725
7886
|
var INIT_WORKTREE_HELP = `
|
|
7726
7887
|
Usage:
|
|
@@ -7757,7 +7918,7 @@ function run(cmd, opts) {
|
|
|
7757
7918
|
}
|
|
7758
7919
|
}
|
|
7759
7920
|
function toAbsolute(p) {
|
|
7760
|
-
const resolved =
|
|
7921
|
+
const resolved = path31.resolve(p);
|
|
7761
7922
|
return resolved.replace(/\\/g, "/");
|
|
7762
7923
|
}
|
|
7763
7924
|
function probeBun(candidate) {
|
|
@@ -7788,18 +7949,18 @@ function findBun() {
|
|
|
7788
7949
|
}
|
|
7789
7950
|
}
|
|
7790
7951
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
7791
|
-
const bunHome =
|
|
7952
|
+
const bunHome = path31.join(
|
|
7792
7953
|
home,
|
|
7793
7954
|
".bun",
|
|
7794
7955
|
"bin",
|
|
7795
7956
|
process.platform === "win32" ? "bun.exe" : "bun"
|
|
7796
7957
|
);
|
|
7797
|
-
if (
|
|
7958
|
+
if (fs30.existsSync(bunHome) && probeBun(bunHome)) return bunHome;
|
|
7798
7959
|
return null;
|
|
7799
7960
|
}
|
|
7800
7961
|
function step1CreateWorktree(opts) {
|
|
7801
7962
|
log("Step 1/9: Creating worktree...");
|
|
7802
|
-
if (
|
|
7963
|
+
if (fs30.existsSync(opts.worktreePath)) {
|
|
7803
7964
|
logWarn(`Directory already exists: ${opts.worktreePath}`);
|
|
7804
7965
|
try {
|
|
7805
7966
|
run("git rev-parse --git-dir", { cwd: opts.worktreePath });
|
|
@@ -7861,22 +8022,22 @@ function step2MergeMain(opts, warnings) {
|
|
|
7861
8022
|
}
|
|
7862
8023
|
function step3CopyPermissions(opts, warnings) {
|
|
7863
8024
|
log("Step 3/9: Copying permissions...");
|
|
7864
|
-
const srcSettings =
|
|
8025
|
+
const srcSettings = path31.join(
|
|
7865
8026
|
opts.repoRoot,
|
|
7866
8027
|
".claude",
|
|
7867
8028
|
"settings.local.json"
|
|
7868
8029
|
);
|
|
7869
|
-
const destDir =
|
|
7870
|
-
const destSettings =
|
|
7871
|
-
if (!
|
|
8030
|
+
const destDir = path31.join(opts.worktreePath, ".claude");
|
|
8031
|
+
const destSettings = path31.join(destDir, "settings.local.json");
|
|
8032
|
+
if (!fs30.existsSync(srcSettings)) {
|
|
7872
8033
|
warn(
|
|
7873
8034
|
warnings,
|
|
7874
8035
|
"No .claude/settings.local.json found in main repo. Skipping."
|
|
7875
8036
|
);
|
|
7876
8037
|
return;
|
|
7877
8038
|
}
|
|
7878
|
-
|
|
7879
|
-
|
|
8039
|
+
fs30.mkdirSync(destDir, { recursive: true });
|
|
8040
|
+
fs30.copyFileSync(srcSettings, destSettings);
|
|
7880
8041
|
logSuccess("Copied settings.local.json");
|
|
7881
8042
|
try {
|
|
7882
8043
|
run("git update-index --skip-worktree .claude/settings.local.json", {
|
|
@@ -7901,7 +8062,7 @@ function step4GenerateMcpJson(opts, warnings) {
|
|
|
7901
8062
|
const wtAbs = toAbsolute(opts.worktreePath);
|
|
7902
8063
|
const bunAbs = toAbsolute(bunPath);
|
|
7903
8064
|
const commsAbs = toAbsolute(opts.commsDir);
|
|
7904
|
-
const channelEntry =
|
|
8065
|
+
const channelEntry = path31.join(
|
|
7905
8066
|
wtAbs,
|
|
7906
8067
|
"packages/tap-plugin/channels/tap-comms.ts"
|
|
7907
8068
|
);
|
|
@@ -7918,8 +8079,8 @@ function step4GenerateMcpJson(opts, warnings) {
|
|
|
7918
8079
|
}
|
|
7919
8080
|
}
|
|
7920
8081
|
};
|
|
7921
|
-
const mcpPath =
|
|
7922
|
-
|
|
8082
|
+
const mcpPath = path31.join(opts.worktreePath, ".mcp.json");
|
|
8083
|
+
fs30.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
7923
8084
|
logSuccess(`.mcp.json generated (absolute paths + cwd)`);
|
|
7924
8085
|
log(` bun: ${bunAbs}`);
|
|
7925
8086
|
log(` comms: ${commsAbs}`);
|
|
@@ -7957,16 +8118,16 @@ function step6BuildEslintPlugin(opts, warnings) {
|
|
|
7957
8118
|
}
|
|
7958
8119
|
function step7VerifyComms(opts, warnings) {
|
|
7959
8120
|
log("Step 7/9: Verifying comms directory...");
|
|
7960
|
-
if (!
|
|
8121
|
+
if (!fs30.existsSync(opts.commsDir)) {
|
|
7961
8122
|
warn(warnings, `Comms directory not found: ${opts.commsDir}`);
|
|
7962
8123
|
warn(warnings, "Create it or run: npx @hua-labs/tap init");
|
|
7963
8124
|
return;
|
|
7964
8125
|
}
|
|
7965
8126
|
const requiredDirs = ["inbox", "findings", "reviews", "letters"];
|
|
7966
8127
|
for (const dir of requiredDirs) {
|
|
7967
|
-
const dirPath =
|
|
7968
|
-
if (!
|
|
7969
|
-
|
|
8128
|
+
const dirPath = path31.join(opts.commsDir, dir);
|
|
8129
|
+
if (!fs30.existsSync(dirPath)) {
|
|
8130
|
+
fs30.mkdirSync(dirPath, { recursive: true });
|
|
7970
8131
|
logSuccess(`Created ${dir}/`);
|
|
7971
8132
|
}
|
|
7972
8133
|
}
|
|
@@ -8025,17 +8186,17 @@ async function initWorktreeCommand(args) {
|
|
|
8025
8186
|
}
|
|
8026
8187
|
const repoRoot = findRepoRoot();
|
|
8027
8188
|
const { config } = resolveConfig({}, repoRoot);
|
|
8028
|
-
const branch = typeof flags["branch"] === "string" ? flags["branch"] :
|
|
8189
|
+
const branch = typeof flags["branch"] === "string" ? flags["branch"] : path31.basename(path31.resolve(worktreePath));
|
|
8029
8190
|
const base = typeof flags["base"] === "string" ? flags["base"] : "origin/main";
|
|
8030
8191
|
const mission = typeof flags["mission"] === "string" ? flags["mission"] : void 0;
|
|
8031
8192
|
const commsDir = typeof flags["comms-dir"] === "string" ? flags["comms-dir"] : config.commsDir;
|
|
8032
8193
|
const skipInstall = flags["skip-install"] === true;
|
|
8033
8194
|
const opts = {
|
|
8034
|
-
worktreePath:
|
|
8195
|
+
worktreePath: path31.resolve(worktreePath),
|
|
8035
8196
|
branch,
|
|
8036
8197
|
base,
|
|
8037
8198
|
mission,
|
|
8038
|
-
commsDir:
|
|
8199
|
+
commsDir: path31.resolve(commsDir),
|
|
8039
8200
|
skipInstall,
|
|
8040
8201
|
repoRoot
|
|
8041
8202
|
};
|
|
@@ -8260,10 +8421,10 @@ async function dashboardCommand(args) {
|
|
|
8260
8421
|
|
|
8261
8422
|
// src/commands/doctor.ts
|
|
8262
8423
|
import {
|
|
8263
|
-
existsSync as
|
|
8424
|
+
existsSync as existsSync29,
|
|
8264
8425
|
mkdirSync as mkdirSync14,
|
|
8265
8426
|
readdirSync as readdirSync8,
|
|
8266
|
-
readFileSync as
|
|
8427
|
+
readFileSync as readFileSync24,
|
|
8267
8428
|
renameSync as renameSync14,
|
|
8268
8429
|
statSync as statSync3,
|
|
8269
8430
|
unlinkSync as unlinkSync8,
|
|
@@ -8271,7 +8432,7 @@ import {
|
|
|
8271
8432
|
} from "fs";
|
|
8272
8433
|
import { homedir as homedir3 } from "os";
|
|
8273
8434
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
8274
|
-
import { dirname as dirname15, join as
|
|
8435
|
+
import { dirname as dirname15, join as join28, resolve as resolve14 } from "path";
|
|
8275
8436
|
init_config();
|
|
8276
8437
|
init_drift_detector();
|
|
8277
8438
|
init_utils();
|
|
@@ -8306,7 +8467,7 @@ function appendWarningMessage(message, extra) {
|
|
|
8306
8467
|
return message.includes(extra) ? message : `${message}; ${extra}`;
|
|
8307
8468
|
}
|
|
8308
8469
|
function findCodexConfigPath3() {
|
|
8309
|
-
return
|
|
8470
|
+
return join28(homedir3(), ".codex", "config.toml");
|
|
8310
8471
|
}
|
|
8311
8472
|
function canonicalizeTrustPath3(targetPath) {
|
|
8312
8473
|
let resolved = resolve14(targetPath).replace(/\//g, "\\");
|
|
@@ -8376,7 +8537,7 @@ function repairCodexConfig(repoRoot, commsDir) {
|
|
|
8376
8537
|
spec.managed.issues[0] ?? "Unable to resolve the managed tap MCP server for Codex."
|
|
8377
8538
|
);
|
|
8378
8539
|
}
|
|
8379
|
-
const existingContent =
|
|
8540
|
+
const existingContent = existsSync29(spec.configPath) ? readFileSync24(spec.configPath, "utf-8") : "";
|
|
8380
8541
|
const existingTapEnvTable = extractTomlTable(
|
|
8381
8542
|
existingContent,
|
|
8382
8543
|
"mcp_servers.tap.env"
|
|
@@ -8443,7 +8604,7 @@ function repairCodexConfig(repoRoot, commsDir) {
|
|
|
8443
8604
|
return `Repaired Codex config at ${spec.configPath}. Restart Codex to reload MCP settings.`;
|
|
8444
8605
|
}
|
|
8445
8606
|
function countFiles(dir, ext = ".md") {
|
|
8446
|
-
if (!
|
|
8607
|
+
if (!existsSync29(dir)) return 0;
|
|
8447
8608
|
try {
|
|
8448
8609
|
return readdirSync8(dir).filter((f) => f.endsWith(ext)).length;
|
|
8449
8610
|
} catch {
|
|
@@ -8451,14 +8612,14 @@ function countFiles(dir, ext = ".md") {
|
|
|
8451
8612
|
}
|
|
8452
8613
|
}
|
|
8453
8614
|
function recentFileCount(dir, withinMs) {
|
|
8454
|
-
if (!
|
|
8615
|
+
if (!existsSync29(dir)) return 0;
|
|
8455
8616
|
const cutoff = Date.now() - withinMs;
|
|
8456
8617
|
let count = 0;
|
|
8457
8618
|
try {
|
|
8458
8619
|
for (const f of readdirSync8(dir)) {
|
|
8459
8620
|
if (!f.endsWith(".md")) continue;
|
|
8460
8621
|
try {
|
|
8461
|
-
if (statSync3(
|
|
8622
|
+
if (statSync3(join28(dir, f)).mtimeMs > cutoff) count++;
|
|
8462
8623
|
} catch {
|
|
8463
8624
|
}
|
|
8464
8625
|
}
|
|
@@ -8467,16 +8628,16 @@ function recentFileCount(dir, withinMs) {
|
|
|
8467
8628
|
return count;
|
|
8468
8629
|
}
|
|
8469
8630
|
function loadDoctorHeartbeatStore(commsDir) {
|
|
8470
|
-
const heartbeatsPath =
|
|
8471
|
-
if (!
|
|
8631
|
+
const heartbeatsPath = join28(commsDir, "heartbeats.json");
|
|
8632
|
+
if (!existsSync29(heartbeatsPath)) return null;
|
|
8472
8633
|
try {
|
|
8473
|
-
return JSON.parse(
|
|
8634
|
+
return JSON.parse(readFileSync24(heartbeatsPath, "utf-8"));
|
|
8474
8635
|
} catch {
|
|
8475
8636
|
return null;
|
|
8476
8637
|
}
|
|
8477
8638
|
}
|
|
8478
8639
|
function saveDoctorHeartbeatStore(commsDir, store) {
|
|
8479
|
-
const heartbeatsPath =
|
|
8640
|
+
const heartbeatsPath = join28(commsDir, "heartbeats.json");
|
|
8480
8641
|
const tmp = `${heartbeatsPath}.tmp.${process.pid}`;
|
|
8481
8642
|
writeFileSync16(tmp, JSON.stringify(store, null, 2), "utf-8");
|
|
8482
8643
|
renameSync14(tmp, heartbeatsPath);
|
|
@@ -8542,9 +8703,9 @@ function checkComms(commsDir) {
|
|
|
8542
8703
|
const checks = [];
|
|
8543
8704
|
checks.push({
|
|
8544
8705
|
name: "comms directory",
|
|
8545
|
-
status:
|
|
8546
|
-
message:
|
|
8547
|
-
fix:
|
|
8706
|
+
status: existsSync29(commsDir) ? PASS : FAIL,
|
|
8707
|
+
message: existsSync29(commsDir) ? commsDir : `Not found: ${commsDir}`,
|
|
8708
|
+
fix: existsSync29(commsDir) ? void 0 : () => {
|
|
8548
8709
|
mkdirSync14(commsDir, { recursive: true });
|
|
8549
8710
|
return `Created ${commsDir}`;
|
|
8550
8711
|
}
|
|
@@ -8554,8 +8715,8 @@ function checkComms(commsDir) {
|
|
|
8554
8715
|
["reviews", false],
|
|
8555
8716
|
["findings", false]
|
|
8556
8717
|
]) {
|
|
8557
|
-
const dir =
|
|
8558
|
-
const exists =
|
|
8718
|
+
const dir = join28(commsDir, subdir);
|
|
8719
|
+
const exists = existsSync29(dir);
|
|
8559
8720
|
checks.push({
|
|
8560
8721
|
name: `${subdir} directory`,
|
|
8561
8722
|
status: exists ? PASS : required ? FAIL : WARN,
|
|
@@ -8566,10 +8727,10 @@ function checkComms(commsDir) {
|
|
|
8566
8727
|
}
|
|
8567
8728
|
});
|
|
8568
8729
|
}
|
|
8569
|
-
const heartbeats =
|
|
8570
|
-
if (
|
|
8730
|
+
const heartbeats = join28(commsDir, "heartbeats.json");
|
|
8731
|
+
if (existsSync29(heartbeats)) {
|
|
8571
8732
|
try {
|
|
8572
|
-
const store = JSON.parse(
|
|
8733
|
+
const store = JSON.parse(readFileSync24(heartbeats, "utf-8"));
|
|
8573
8734
|
const agents = Object.keys(store);
|
|
8574
8735
|
const now = Date.now();
|
|
8575
8736
|
const active = agents.filter((a) => {
|
|
@@ -8683,7 +8844,7 @@ function checkInstances(repoRoot, stateDir, commsDir) {
|
|
|
8683
8844
|
}
|
|
8684
8845
|
}
|
|
8685
8846
|
}
|
|
8686
|
-
const pidPath =
|
|
8847
|
+
const pidPath = join28(stateDir, "pids", `bridge-${id}.json`);
|
|
8687
8848
|
try {
|
|
8688
8849
|
unlinkSync8(pidPath);
|
|
8689
8850
|
} catch {
|
|
@@ -8745,8 +8906,8 @@ function checkInstances(repoRoot, stateDir, commsDir) {
|
|
|
8745
8906
|
}
|
|
8746
8907
|
function checkMessageLifecycle(commsDir) {
|
|
8747
8908
|
const checks = [];
|
|
8748
|
-
const inbox =
|
|
8749
|
-
if (!
|
|
8909
|
+
const inbox = join28(commsDir, "inbox");
|
|
8910
|
+
if (!existsSync29(inbox)) {
|
|
8750
8911
|
checks.push({
|
|
8751
8912
|
name: "message flow",
|
|
8752
8913
|
status: FAIL,
|
|
@@ -8762,10 +8923,10 @@ function checkMessageLifecycle(commsDir) {
|
|
|
8762
8923
|
status: recent10m > 0 ? PASS : total > 0 ? WARN : FAIL,
|
|
8763
8924
|
message: `${total} total, ${recent1h} in last 1h, ${recent10m} in last 10m`
|
|
8764
8925
|
});
|
|
8765
|
-
const receiptsPath =
|
|
8766
|
-
if (
|
|
8926
|
+
const receiptsPath = join28(commsDir, "receipts", "receipts.json");
|
|
8927
|
+
if (existsSync29(receiptsPath)) {
|
|
8767
8928
|
try {
|
|
8768
|
-
const receipts = JSON.parse(
|
|
8929
|
+
const receipts = JSON.parse(readFileSync24(receiptsPath, "utf-8"));
|
|
8769
8930
|
const receiptCount = Object.keys(receipts).length;
|
|
8770
8931
|
checks.push({
|
|
8771
8932
|
name: "read receipts",
|
|
@@ -8784,8 +8945,8 @@ function checkMessageLifecycle(commsDir) {
|
|
|
8784
8945
|
}
|
|
8785
8946
|
function checkMcpServer(repoRoot) {
|
|
8786
8947
|
const checks = [];
|
|
8787
|
-
const mcpJson =
|
|
8788
|
-
if (!
|
|
8948
|
+
const mcpJson = join28(repoRoot, ".mcp.json");
|
|
8949
|
+
if (!existsSync29(mcpJson)) {
|
|
8789
8950
|
checks.push({
|
|
8790
8951
|
name: "MCP config (.mcp.json)",
|
|
8791
8952
|
status: WARN,
|
|
@@ -8795,7 +8956,7 @@ function checkMcpServer(repoRoot) {
|
|
|
8795
8956
|
}
|
|
8796
8957
|
let config;
|
|
8797
8958
|
try {
|
|
8798
|
-
config = JSON.parse(
|
|
8959
|
+
config = JSON.parse(readFileSync24(mcpJson, "utf-8"));
|
|
8799
8960
|
} catch {
|
|
8800
8961
|
checks.push({
|
|
8801
8962
|
name: "MCP config (.mcp.json)",
|
|
@@ -8838,7 +8999,7 @@ function checkMcpServer(repoRoot) {
|
|
|
8838
8999
|
});
|
|
8839
9000
|
if (hasTapComms.command) {
|
|
8840
9001
|
const cmd = hasTapComms.command;
|
|
8841
|
-
let cmdAvailable =
|
|
9002
|
+
let cmdAvailable = existsSync29(cmd);
|
|
8842
9003
|
if (!cmdAvailable) {
|
|
8843
9004
|
try {
|
|
8844
9005
|
const result = spawnSync6(cmd, ["--version"], {
|
|
@@ -8860,8 +9021,8 @@ function checkMcpServer(repoRoot) {
|
|
|
8860
9021
|
const mcpScript = hasTapComms.args[0];
|
|
8861
9022
|
checks.push({
|
|
8862
9023
|
name: "MCP server script",
|
|
8863
|
-
status:
|
|
8864
|
-
message:
|
|
9024
|
+
status: existsSync29(mcpScript) ? PASS : FAIL,
|
|
9025
|
+
message: existsSync29(mcpScript) ? mcpScript : `Not found: ${mcpScript}`
|
|
8865
9026
|
});
|
|
8866
9027
|
if (mcpScript.endsWith(".mjs") && hasTapComms.command && !hasTapComms.command.includes("bun")) {
|
|
8867
9028
|
checks.push({
|
|
@@ -8894,8 +9055,8 @@ function checkMcpServer(repoRoot) {
|
|
|
8894
9055
|
} else {
|
|
8895
9056
|
checks.push({
|
|
8896
9057
|
name: "MCP TAP_COMMS_DIR",
|
|
8897
|
-
status:
|
|
8898
|
-
message:
|
|
9058
|
+
status: existsSync29(envCommsDir) ? PASS : FAIL,
|
|
9059
|
+
message: existsSync29(envCommsDir) ? envCommsDir : `Directory not found: ${envCommsDir}`
|
|
8899
9060
|
});
|
|
8900
9061
|
}
|
|
8901
9062
|
checks.push({
|
|
@@ -8912,7 +9073,7 @@ function checkCodexConfig(repoRoot, commsDir) {
|
|
|
8912
9073
|
}
|
|
8913
9074
|
const checks = [];
|
|
8914
9075
|
const fixHint = 'Run "tap doctor --fix" or "tap add codex --force".';
|
|
8915
|
-
if (!
|
|
9076
|
+
if (!existsSync29(spec.configPath)) {
|
|
8916
9077
|
checks.push({
|
|
8917
9078
|
name: "MCP config (~/.codex/config.toml)",
|
|
8918
9079
|
status: WARN,
|
|
@@ -8921,7 +9082,7 @@ function checkCodexConfig(repoRoot, commsDir) {
|
|
|
8921
9082
|
});
|
|
8922
9083
|
return checks;
|
|
8923
9084
|
}
|
|
8924
|
-
const content =
|
|
9085
|
+
const content = readFileSync24(spec.configPath, "utf-8");
|
|
8925
9086
|
const tapTable = extractTomlTable(content, "mcp_servers.tap");
|
|
8926
9087
|
const tapEnvTable = extractTomlTable(content, "mcp_servers.tap.env");
|
|
8927
9088
|
const legacyTable = extractTomlTable(content, "mcp_servers.tap-comms");
|
|
@@ -9005,8 +9166,8 @@ function checkCodexConfig(repoRoot, commsDir) {
|
|
|
9005
9166
|
}
|
|
9006
9167
|
function checkBridgeTurnHealth(repoRoot) {
|
|
9007
9168
|
const checks = [];
|
|
9008
|
-
const tmpDir =
|
|
9009
|
-
if (!
|
|
9169
|
+
const tmpDir = join28(repoRoot, ".tmp");
|
|
9170
|
+
if (!existsSync29(tmpDir)) return checks;
|
|
9010
9171
|
const state = loadState(repoRoot);
|
|
9011
9172
|
const activeMatchers = /* @__PURE__ */ new Set();
|
|
9012
9173
|
if (state) {
|
|
@@ -9032,11 +9193,11 @@ function checkBridgeTurnHealth(repoRoot) {
|
|
|
9032
9193
|
return checks;
|
|
9033
9194
|
}
|
|
9034
9195
|
for (const dir of dirs) {
|
|
9035
|
-
const heartbeatPath =
|
|
9036
|
-
if (!
|
|
9196
|
+
const heartbeatPath = join28(tmpDir, dir, "heartbeat.json");
|
|
9197
|
+
if (!existsSync29(heartbeatPath)) continue;
|
|
9037
9198
|
let heartbeat;
|
|
9038
9199
|
try {
|
|
9039
|
-
heartbeat = JSON.parse(
|
|
9200
|
+
heartbeat = JSON.parse(readFileSync24(heartbeatPath, "utf-8"));
|
|
9040
9201
|
} catch {
|
|
9041
9202
|
checks.push({
|
|
9042
9203
|
name: `turn: ${dir}`,
|
|
@@ -9209,9 +9370,9 @@ async function doctorCommand(args) {
|
|
|
9209
9370
|
inst.agentName = instConfig.agentName;
|
|
9210
9371
|
inst.port = instConfig.port;
|
|
9211
9372
|
inst.configHash = instConfig.configHash;
|
|
9212
|
-
inst.configSourceFile = inst.configSourceFile ||
|
|
9373
|
+
inst.configSourceFile = inst.configSourceFile || join28(config.stateDir, "instances", `${result.instanceId}.json`);
|
|
9213
9374
|
saveState(repoRoot, state);
|
|
9214
|
-
if (inst.configPath &&
|
|
9375
|
+
if (inst.configPath && existsSync29(inst.configPath)) {
|
|
9215
9376
|
const currentHash = hashFile(inst.configPath);
|
|
9216
9377
|
if (instConfig.runtimeConfigHash !== currentHash) {
|
|
9217
9378
|
instConfig.runtimeConfigHash = currentHash;
|
|
@@ -9332,8 +9493,8 @@ async function doctorCommand(args) {
|
|
|
9332
9493
|
// src/commands/comms.ts
|
|
9333
9494
|
init_utils();
|
|
9334
9495
|
import { execSync as execSync6, spawnSync as spawnSync7 } from "child_process";
|
|
9335
|
-
import * as
|
|
9336
|
-
import * as
|
|
9496
|
+
import * as fs31 from "fs";
|
|
9497
|
+
import * as path32 from "path";
|
|
9337
9498
|
var COMMS_HELP = `
|
|
9338
9499
|
Usage:
|
|
9339
9500
|
tap comms <subcommand>
|
|
@@ -9347,7 +9508,7 @@ Examples:
|
|
|
9347
9508
|
npx @hua-labs/tap comms push
|
|
9348
9509
|
`.trim();
|
|
9349
9510
|
function isGitRepo(dir) {
|
|
9350
|
-
return
|
|
9511
|
+
return fs31.existsSync(path32.join(dir, ".git"));
|
|
9351
9512
|
}
|
|
9352
9513
|
function commsPull(commsDir) {
|
|
9353
9514
|
logHeader("tap comms pull");
|
|
@@ -9606,8 +9767,8 @@ async function watchCommand(args) {
|
|
|
9606
9767
|
import * as http from "http";
|
|
9607
9768
|
|
|
9608
9769
|
// src/engine/missions.ts
|
|
9609
|
-
import * as
|
|
9610
|
-
import * as
|
|
9770
|
+
import * as fs32 from "fs";
|
|
9771
|
+
import * as path33 from "path";
|
|
9611
9772
|
function parseStatus(raw) {
|
|
9612
9773
|
const trimmed = raw.trim();
|
|
9613
9774
|
if (trimmed.includes("active")) return "active";
|
|
@@ -9633,10 +9794,10 @@ function parseRow(line) {
|
|
|
9633
9794
|
return { id: id.toUpperCase(), title, branch, status, owner };
|
|
9634
9795
|
}
|
|
9635
9796
|
function parseMissionsFile(repoRoot) {
|
|
9636
|
-
const missionsPath =
|
|
9797
|
+
const missionsPath = path33.join(repoRoot, "docs", "missions", "MISSIONS.md");
|
|
9637
9798
|
let content;
|
|
9638
9799
|
try {
|
|
9639
|
-
content =
|
|
9800
|
+
content = fs32.readFileSync(missionsPath, "utf-8");
|
|
9640
9801
|
} catch {
|
|
9641
9802
|
return [];
|
|
9642
9803
|
}
|