@hua-labs/tap 0.2.4 → 0.2.5
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 +194 -194
- package/dist/bridges/codex-app-server-bridge.d.mts +10 -1
- package/dist/bridges/codex-app-server-bridge.mjs +93 -37
- package/dist/bridges/codex-app-server-bridge.mjs.map +1 -1
- package/dist/bridges/codex-bridge-runner.mjs +2 -0
- package/dist/bridges/codex-bridge-runner.mjs.map +1 -1
- package/dist/cli.mjs +598 -109
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +275 -66
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-server.mjs +185 -185
- package/dist/mcp-server.mjs.map +1 -1
- package/package.json +65 -65
package/dist/index.mjs
CHANGED
|
@@ -558,7 +558,7 @@ function buildManagedMcpServerSpec(ctx, instanceId) {
|
|
|
558
558
|
}
|
|
559
559
|
if (!sourcePath) {
|
|
560
560
|
issues.push(
|
|
561
|
-
"tap
|
|
561
|
+
"tap MCP server entry not found. Reinstall @hua-labs/tap or run from a repo with packages/tap-plugin/channels/ available."
|
|
562
562
|
);
|
|
563
563
|
return { command: null, args: [], env, sourcePath, warnings, issues };
|
|
564
564
|
}
|
|
@@ -588,7 +588,7 @@ function buildManagedMcpServerSpec(ctx, instanceId) {
|
|
|
588
588
|
}
|
|
589
589
|
if (!command) {
|
|
590
590
|
issues.push(
|
|
591
|
-
"bun is required to run the repo-local tap
|
|
591
|
+
"bun is required to run the repo-local tap MCP server (.ts source). Install bun: https://bun.sh"
|
|
592
592
|
);
|
|
593
593
|
return { command: null, args: [], env, sourcePath, warnings, issues };
|
|
594
594
|
}
|
|
@@ -782,6 +782,7 @@ var init_runtime = __esm({
|
|
|
782
782
|
// src/engine/bridge.ts
|
|
783
783
|
import * as fs7 from "fs";
|
|
784
784
|
import * as net from "net";
|
|
785
|
+
import * as os2 from "os";
|
|
785
786
|
import * as path7 from "path";
|
|
786
787
|
import { randomBytes } from "crypto";
|
|
787
788
|
import { spawn, spawnSync as spawnSync2, execSync as execSync2 } from "child_process";
|
|
@@ -822,12 +823,66 @@ function removeFileIfExists(filePath) {
|
|
|
822
823
|
} catch {
|
|
823
824
|
}
|
|
824
825
|
}
|
|
826
|
+
function toPowerShellSingleQuotedString(value) {
|
|
827
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
828
|
+
}
|
|
829
|
+
function toPowerShellStringArrayLiteral(values) {
|
|
830
|
+
return `@(${values.map(toPowerShellSingleQuotedString).join(", ")})`;
|
|
831
|
+
}
|
|
832
|
+
function cleanupStaleWindowsSpawnWrappers(now = Date.now()) {
|
|
833
|
+
let entries;
|
|
834
|
+
try {
|
|
835
|
+
entries = fs7.readdirSync(os2.tmpdir());
|
|
836
|
+
} catch {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
for (const entry of entries) {
|
|
840
|
+
if (!entry.startsWith(WINDOWS_SPAWN_WRAPPER_PREFIX) || !/\.(cmd|ps1)$/i.test(entry)) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
const wrapperPath = path7.join(os2.tmpdir(), entry);
|
|
844
|
+
try {
|
|
845
|
+
const stats = fs7.statSync(wrapperPath);
|
|
846
|
+
if (now - stats.mtimeMs < WINDOWS_SPAWN_WRAPPER_STALE_MS) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
fs7.unlinkSync(wrapperPath);
|
|
850
|
+
} catch {
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
function buildWindowsDetachedWrapperScript(command, args, logPath, stderrLogPath, env) {
|
|
855
|
+
const lines = ["$ErrorActionPreference = 'Stop'"];
|
|
856
|
+
for (const [key, value] of Object.entries(env)) {
|
|
857
|
+
if (value !== void 0 && value !== process.env[key]) {
|
|
858
|
+
lines.push(
|
|
859
|
+
`[Environment]::SetEnvironmentVariable(${toPowerShellSingleQuotedString(key)}, ${toPowerShellSingleQuotedString(value)}, 'Process')`
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
lines.push(
|
|
864
|
+
`$logPath = ${toPowerShellSingleQuotedString(logPath)}`,
|
|
865
|
+
`$stderrLogPath = ${toPowerShellSingleQuotedString(stderrLogPath)}`,
|
|
866
|
+
`$commandPath = ${toPowerShellSingleQuotedString(command)}`,
|
|
867
|
+
`$commandArgs = ${toPowerShellStringArrayLiteral(args)}`,
|
|
868
|
+
"$exitCode = 1",
|
|
869
|
+
"try {",
|
|
870
|
+
" & $commandPath @commandArgs >> $logPath 2>> $stderrLogPath",
|
|
871
|
+
" $exitCode = if ($null -ne $LASTEXITCODE) { $LASTEXITCODE } else { 0 }",
|
|
872
|
+
"} finally {",
|
|
873
|
+
" Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue",
|
|
874
|
+
"}",
|
|
875
|
+
"exit $exitCode"
|
|
876
|
+
);
|
|
877
|
+
return `${lines.join("\r\n")}\r
|
|
878
|
+
`;
|
|
879
|
+
}
|
|
825
880
|
function getWebSocketCtor() {
|
|
826
881
|
const candidate = globalThis.WebSocket;
|
|
827
882
|
return typeof candidate === "function" ? candidate : null;
|
|
828
883
|
}
|
|
829
884
|
function delay(ms) {
|
|
830
|
-
return new Promise((
|
|
885
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
831
886
|
}
|
|
832
887
|
function isLoopbackHost(hostname) {
|
|
833
888
|
return hostname === "127.0.0.1" || hostname === "localhost";
|
|
@@ -879,7 +934,7 @@ function getBridgeRuntimeStateDir(repoRoot, instanceId) {
|
|
|
879
934
|
}
|
|
880
935
|
async function allocateLoopbackPort(hostname) {
|
|
881
936
|
const bindHost = hostname === "localhost" ? "127.0.0.1" : hostname;
|
|
882
|
-
return await new Promise((
|
|
937
|
+
return await new Promise((resolve9, reject) => {
|
|
883
938
|
const server = net.createServer();
|
|
884
939
|
server.unref();
|
|
885
940
|
server.once("error", reject);
|
|
@@ -897,7 +952,7 @@ async function allocateLoopbackPort(hostname) {
|
|
|
897
952
|
reject(error);
|
|
898
953
|
return;
|
|
899
954
|
}
|
|
900
|
-
|
|
955
|
+
resolve9(port);
|
|
901
956
|
});
|
|
902
957
|
});
|
|
903
958
|
});
|
|
@@ -1080,35 +1135,50 @@ function findReusableManagedAppServer(stateDir, publicUrl) {
|
|
|
1080
1135
|
return null;
|
|
1081
1136
|
}
|
|
1082
1137
|
function startWindowsDetachedProcess(command, args, repoRoot, logPath, env = process.env) {
|
|
1083
|
-
const ext = path7.extname(command).toLowerCase();
|
|
1084
1138
|
const stderrLogPath = stderrLogFilePath(logPath);
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1139
|
+
const powerShellCommand = resolvePowerShellCommand();
|
|
1140
|
+
cleanupStaleWindowsSpawnWrappers();
|
|
1141
|
+
const wrapperPath = path7.join(
|
|
1142
|
+
os2.tmpdir(),
|
|
1143
|
+
`${WINDOWS_SPAWN_WRAPPER_PREFIX}${randomBytes(4).toString("hex")}.ps1`
|
|
1144
|
+
);
|
|
1145
|
+
fs7.writeFileSync(
|
|
1146
|
+
wrapperPath,
|
|
1147
|
+
buildWindowsDetachedWrapperScript(
|
|
1148
|
+
command,
|
|
1149
|
+
args,
|
|
1150
|
+
logPath,
|
|
1151
|
+
stderrLogPath,
|
|
1152
|
+
env
|
|
1153
|
+
)
|
|
1154
|
+
);
|
|
1155
|
+
const psCommand = [
|
|
1156
|
+
"$p = Start-Process",
|
|
1157
|
+
`-FilePath ${toPowerShellSingleQuotedString(powerShellCommand)}`,
|
|
1158
|
+
`-ArgumentList ${toPowerShellStringArrayLiteral(["-NoLogo", "-NoProfile", "-File", wrapperPath])}`,
|
|
1159
|
+
`-WorkingDirectory ${toPowerShellSingleQuotedString(repoRoot)}`,
|
|
1160
|
+
"-WindowStyle Hidden",
|
|
1161
|
+
"-PassThru",
|
|
1162
|
+
"; Write-Output $p.Id"
|
|
1163
|
+
].join(" ");
|
|
1164
|
+
const result = spawnSync2(
|
|
1165
|
+
powerShellCommand,
|
|
1166
|
+
["-NoLogo", "-NoProfile", "-Command", psCommand],
|
|
1167
|
+
{
|
|
1168
|
+
encoding: "utf-8",
|
|
1169
|
+
windowsHide: true
|
|
1170
|
+
}
|
|
1171
|
+
);
|
|
1172
|
+
if (result.status !== 0) {
|
|
1173
|
+
removeFileIfExists(wrapperPath);
|
|
1174
|
+
return null;
|
|
1111
1175
|
}
|
|
1176
|
+
const pid = parseInt(result.stdout.trim(), 10);
|
|
1177
|
+
if (!Number.isFinite(pid)) {
|
|
1178
|
+
removeFileIfExists(wrapperPath);
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
return pid;
|
|
1112
1182
|
}
|
|
1113
1183
|
function startWindowsCodexAppServer(command, url, repoRoot, logPath) {
|
|
1114
1184
|
return startWindowsDetachedProcess(
|
|
@@ -1170,12 +1240,12 @@ function resolveAppServerUrl(baseUrl, port) {
|
|
|
1170
1240
|
}
|
|
1171
1241
|
async function isTcpPortAvailable(hostname, port) {
|
|
1172
1242
|
const bindHost = hostname === "localhost" ? "127.0.0.1" : hostname;
|
|
1173
|
-
return await new Promise((
|
|
1243
|
+
return await new Promise((resolve9) => {
|
|
1174
1244
|
const server = net.createServer();
|
|
1175
1245
|
server.unref();
|
|
1176
|
-
server.once("error", () =>
|
|
1246
|
+
server.once("error", () => resolve9(false));
|
|
1177
1247
|
server.listen(port, bindHost, () => {
|
|
1178
|
-
server.close((error) =>
|
|
1248
|
+
server.close((error) => resolve9(!error));
|
|
1179
1249
|
});
|
|
1180
1250
|
});
|
|
1181
1251
|
}
|
|
@@ -1210,7 +1280,7 @@ async function checkAppServerHealth(url, timeoutMs = APP_SERVER_HEALTH_TIMEOUT_M
|
|
|
1210
1280
|
if (!WebSocket) {
|
|
1211
1281
|
return false;
|
|
1212
1282
|
}
|
|
1213
|
-
return new Promise((
|
|
1283
|
+
return new Promise((resolve9) => {
|
|
1214
1284
|
let settled = false;
|
|
1215
1285
|
let socket = null;
|
|
1216
1286
|
const finish = (healthy) => {
|
|
@@ -1223,7 +1293,7 @@ async function checkAppServerHealth(url, timeoutMs = APP_SERVER_HEALTH_TIMEOUT_M
|
|
|
1223
1293
|
socket?.close();
|
|
1224
1294
|
} catch {
|
|
1225
1295
|
}
|
|
1226
|
-
|
|
1296
|
+
resolve9(healthy);
|
|
1227
1297
|
};
|
|
1228
1298
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
1229
1299
|
try {
|
|
@@ -1547,7 +1617,11 @@ function logFilePath(stateDir, instanceId) {
|
|
|
1547
1617
|
function runtimeHeartbeatFilePath(runtimeStateDir) {
|
|
1548
1618
|
return path7.join(runtimeStateDir, "heartbeat.json");
|
|
1549
1619
|
}
|
|
1550
|
-
function
|
|
1620
|
+
function runtimeThreadStateFilePath(runtimeStateDir) {
|
|
1621
|
+
return path7.join(runtimeStateDir, "thread.json");
|
|
1622
|
+
}
|
|
1623
|
+
function loadRuntimeBridgeHeartbeat(bridgeState) {
|
|
1624
|
+
const runtimeStateDir = bridgeState?.runtimeStateDir;
|
|
1551
1625
|
if (!runtimeStateDir) {
|
|
1552
1626
|
return null;
|
|
1553
1627
|
}
|
|
@@ -1556,13 +1630,35 @@ function loadRuntimeHeartbeatTimestamp(runtimeStateDir) {
|
|
|
1556
1630
|
return null;
|
|
1557
1631
|
}
|
|
1558
1632
|
try {
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1633
|
+
return JSON.parse(
|
|
1634
|
+
fs7.readFileSync(heartbeatPath, "utf-8")
|
|
1635
|
+
);
|
|
1636
|
+
} catch {
|
|
1637
|
+
return null;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
function loadRuntimeBridgeThreadState(bridgeState) {
|
|
1641
|
+
const runtimeStateDir = bridgeState?.runtimeStateDir;
|
|
1642
|
+
if (!runtimeStateDir) {
|
|
1643
|
+
return null;
|
|
1644
|
+
}
|
|
1645
|
+
const threadPath = runtimeThreadStateFilePath(runtimeStateDir);
|
|
1646
|
+
if (!fs7.existsSync(threadPath)) {
|
|
1647
|
+
return null;
|
|
1648
|
+
}
|
|
1649
|
+
try {
|
|
1650
|
+
const parsed = JSON.parse(
|
|
1651
|
+
fs7.readFileSync(threadPath, "utf-8")
|
|
1652
|
+
);
|
|
1653
|
+
return parsed.threadId ? parsed : null;
|
|
1562
1654
|
} catch {
|
|
1563
1655
|
return null;
|
|
1564
1656
|
}
|
|
1565
1657
|
}
|
|
1658
|
+
function loadRuntimeHeartbeatTimestamp(runtimeStateDir) {
|
|
1659
|
+
const heartbeat = loadRuntimeBridgeHeartbeat({ runtimeStateDir });
|
|
1660
|
+
return typeof heartbeat?.updatedAt === "string" ? heartbeat.updatedAt : null;
|
|
1661
|
+
}
|
|
1566
1662
|
function resolveHeartbeatTimestamp(state) {
|
|
1567
1663
|
return loadRuntimeHeartbeatTimestamp(state?.runtimeStateDir) ?? state?.lastHeartbeat ?? null;
|
|
1568
1664
|
}
|
|
@@ -1732,6 +1828,7 @@ async function startBridge(options) {
|
|
|
1732
1828
|
options.messageLookbackMinutes
|
|
1733
1829
|
)
|
|
1734
1830
|
} : {},
|
|
1831
|
+
...process.env.TAP_COLD_START_WARMUP === "true" ? { TAP_COLD_START_WARMUP: "true" } : {},
|
|
1735
1832
|
...options.threadId ? { TAP_THREAD_ID: options.threadId } : {},
|
|
1736
1833
|
...options.ephemeral ? { TAP_EPHEMERAL: "true" } : {},
|
|
1737
1834
|
...options.processExistingMessages ? { TAP_PROCESS_EXISTING: "true" } : {}
|
|
@@ -1868,7 +1965,7 @@ function getBridgeStatus(stateDir, instanceId) {
|
|
|
1868
1965
|
}
|
|
1869
1966
|
return "running";
|
|
1870
1967
|
}
|
|
1871
|
-
var DEFAULT_APP_SERVER_URL2, APP_SERVER_HEALTH_TIMEOUT_MS, APP_SERVER_START_TIMEOUT_MS, APP_SERVER_GATEWAY_START_TIMEOUT_MS, APP_SERVER_HEALTH_RETRY_MS, AUTH_SUBPROTOCOL_PREFIX, APP_SERVER_AUTH_FILE_MODE;
|
|
1968
|
+
var DEFAULT_APP_SERVER_URL2, APP_SERVER_HEALTH_TIMEOUT_MS, APP_SERVER_START_TIMEOUT_MS, APP_SERVER_GATEWAY_START_TIMEOUT_MS, APP_SERVER_HEALTH_RETRY_MS, AUTH_SUBPROTOCOL_PREFIX, APP_SERVER_AUTH_FILE_MODE, WINDOWS_SPAWN_WRAPPER_PREFIX, WINDOWS_SPAWN_WRAPPER_STALE_MS;
|
|
1872
1969
|
var init_bridge = __esm({
|
|
1873
1970
|
"src/engine/bridge.ts"() {
|
|
1874
1971
|
"use strict";
|
|
@@ -1882,6 +1979,8 @@ var init_bridge = __esm({
|
|
|
1882
1979
|
APP_SERVER_HEALTH_RETRY_MS = 250;
|
|
1883
1980
|
AUTH_SUBPROTOCOL_PREFIX = "tap-auth-";
|
|
1884
1981
|
APP_SERVER_AUTH_FILE_MODE = 384;
|
|
1982
|
+
WINDOWS_SPAWN_WRAPPER_PREFIX = "tap-spawn-";
|
|
1983
|
+
WINDOWS_SPAWN_WRAPPER_STALE_MS = 60 * 60 * 1e3;
|
|
1885
1984
|
}
|
|
1886
1985
|
});
|
|
1887
1986
|
|
|
@@ -2082,13 +2181,14 @@ function setNestedKey(obj, keyPath, value) {
|
|
|
2082
2181
|
function normalizeTapCommsDir(value) {
|
|
2083
2182
|
return typeof value === "string" ? path9.resolve(value).replace(/\\/g, "/") : "";
|
|
2084
2183
|
}
|
|
2085
|
-
var MCP_SERVER_KEY, claudeAdapter;
|
|
2184
|
+
var MCP_SERVER_KEY, OLD_MCP_SERVER_KEY, claudeAdapter;
|
|
2086
2185
|
var init_claude = __esm({
|
|
2087
2186
|
"src/adapters/claude.ts"() {
|
|
2088
2187
|
"use strict";
|
|
2089
2188
|
init_state();
|
|
2090
2189
|
init_common();
|
|
2091
|
-
MCP_SERVER_KEY = "tap
|
|
2190
|
+
MCP_SERVER_KEY = "tap";
|
|
2191
|
+
OLD_MCP_SERVER_KEY = "tap-comms";
|
|
2092
2192
|
claudeAdapter = {
|
|
2093
2193
|
runtime: "claude",
|
|
2094
2194
|
async probe(ctx) {
|
|
@@ -2145,6 +2245,11 @@ var init_claude = __esm({
|
|
|
2145
2245
|
`Existing "${MCP_SERVER_KEY}" entry in .mcp.json will be overwritten.`
|
|
2146
2246
|
);
|
|
2147
2247
|
}
|
|
2248
|
+
if (config.mcpServers?.[OLD_MCP_SERVER_KEY]) {
|
|
2249
|
+
conflicts.push(
|
|
2250
|
+
`Legacy "${OLD_MCP_SERVER_KEY}" entry will be migrated to "${MCP_SERVER_KEY}".`
|
|
2251
|
+
);
|
|
2252
|
+
}
|
|
2148
2253
|
} catch {
|
|
2149
2254
|
warnings.push(
|
|
2150
2255
|
".mcp.json exists but is not valid JSON. Will be overwritten."
|
|
@@ -2154,7 +2259,7 @@ var init_claude = __esm({
|
|
|
2154
2259
|
const serverEntry = buildMcpServerEntry(ctx);
|
|
2155
2260
|
if (!serverEntry) {
|
|
2156
2261
|
warnings.push(
|
|
2157
|
-
"tap
|
|
2262
|
+
"tap MCP server entry not found. Skipping .mcp.json patch. Reinstall @hua-labs/tap or run from a repo with packages/tap-plugin/channels/ available."
|
|
2158
2263
|
);
|
|
2159
2264
|
return {
|
|
2160
2265
|
runtime: "claude",
|
|
@@ -2207,6 +2312,10 @@ var init_claude = __esm({
|
|
|
2207
2312
|
);
|
|
2208
2313
|
}
|
|
2209
2314
|
}
|
|
2315
|
+
const servers = config.mcpServers;
|
|
2316
|
+
if (servers?.[OLD_MCP_SERVER_KEY]) {
|
|
2317
|
+
delete servers[OLD_MCP_SERVER_KEY];
|
|
2318
|
+
}
|
|
2210
2319
|
if (op.key) {
|
|
2211
2320
|
setNestedKey(config, op.key, op.value);
|
|
2212
2321
|
}
|
|
@@ -2255,7 +2364,7 @@ var init_claude = __esm({
|
|
|
2255
2364
|
checks.push({ name: "Config is valid JSON", passed: true });
|
|
2256
2365
|
const entry = config.mcpServers?.[MCP_SERVER_KEY];
|
|
2257
2366
|
checks.push({
|
|
2258
|
-
name: "tap
|
|
2367
|
+
name: "tap entry present",
|
|
2259
2368
|
passed: !!entry,
|
|
2260
2369
|
message: entry ? void 0 : `mcpServers.${MCP_SERVER_KEY} not found`
|
|
2261
2370
|
});
|
|
@@ -2364,6 +2473,14 @@ function extractTomlTable(content, selector) {
|
|
|
2364
2473
|
return `${lines.slice(range.start, range.end).join("\n")}
|
|
2365
2474
|
`;
|
|
2366
2475
|
}
|
|
2476
|
+
function removeTomlTable(content, selector) {
|
|
2477
|
+
const lines = splitLines(content);
|
|
2478
|
+
const range = findTableRange(lines, selector);
|
|
2479
|
+
if (!range) return content;
|
|
2480
|
+
const next = [...lines.slice(0, range.start), ...lines.slice(range.end)];
|
|
2481
|
+
return `${trimTomlDocument(next.join("\n"))}
|
|
2482
|
+
`;
|
|
2483
|
+
}
|
|
2367
2484
|
function replaceTomlTable(content, selector, replacement) {
|
|
2368
2485
|
const lines = splitLines(content);
|
|
2369
2486
|
const range = findTableRange(lines, selector);
|
|
@@ -2489,12 +2606,12 @@ function verifyManagedToml(content, ctx, configPath) {
|
|
|
2489
2606
|
message: fs11.existsSync(configPath) ? void 0 : `${configPath} not found`
|
|
2490
2607
|
});
|
|
2491
2608
|
checks.push({
|
|
2492
|
-
name: "tap
|
|
2609
|
+
name: "tap MCP table present",
|
|
2493
2610
|
passed: !!mainTable,
|
|
2494
2611
|
message: mainTable ? void 0 : `${MCP_SELECTOR} not found`
|
|
2495
2612
|
});
|
|
2496
2613
|
checks.push({
|
|
2497
|
-
name: "tap
|
|
2614
|
+
name: "tap env table present",
|
|
2498
2615
|
passed: !!envTable,
|
|
2499
2616
|
message: envTable ? void 0 : `${ENV_SELECTOR} not found`
|
|
2500
2617
|
});
|
|
@@ -2514,12 +2631,12 @@ function verifyManagedToml(content, ctx, configPath) {
|
|
|
2514
2631
|
passed: mainTable.includes(
|
|
2515
2632
|
`command = "${managed.command.replace(/\\/g, "\\\\")}"`
|
|
2516
2633
|
) && mainTable.includes(`args = [${expectedArgs}]`),
|
|
2517
|
-
message: "Managed tap
|
|
2634
|
+
message: "Managed tap command/args do not match expected values"
|
|
2518
2635
|
});
|
|
2519
2636
|
}
|
|
2520
2637
|
return checks;
|
|
2521
2638
|
}
|
|
2522
|
-
var MCP_SELECTOR, ENV_SELECTOR, codexAdapter;
|
|
2639
|
+
var MCP_SELECTOR, ENV_SELECTOR, OLD_MCP_SELECTOR, OLD_ENV_SELECTOR, codexAdapter;
|
|
2523
2640
|
var init_codex = __esm({
|
|
2524
2641
|
"src/adapters/codex.ts"() {
|
|
2525
2642
|
"use strict";
|
|
@@ -2527,8 +2644,10 @@ var init_codex = __esm({
|
|
|
2527
2644
|
init_artifact_backups();
|
|
2528
2645
|
init_toml();
|
|
2529
2646
|
init_common();
|
|
2530
|
-
MCP_SELECTOR = "mcp_servers.tap
|
|
2531
|
-
ENV_SELECTOR = "mcp_servers.tap
|
|
2647
|
+
MCP_SELECTOR = "mcp_servers.tap";
|
|
2648
|
+
ENV_SELECTOR = "mcp_servers.tap.env";
|
|
2649
|
+
OLD_MCP_SELECTOR = "mcp_servers.tap-comms";
|
|
2650
|
+
OLD_ENV_SELECTOR = "mcp_servers.tap-comms.env";
|
|
2532
2651
|
codexAdapter = {
|
|
2533
2652
|
runtime: "codex",
|
|
2534
2653
|
async probe(ctx) {
|
|
@@ -2574,6 +2693,11 @@ var init_codex = __esm({
|
|
|
2574
2693
|
if (extractTomlTable(content, MCP_SELECTOR)) {
|
|
2575
2694
|
conflicts.push(`Existing ${MCP_SELECTOR} table will be updated.`);
|
|
2576
2695
|
}
|
|
2696
|
+
if (extractTomlTable(content, OLD_MCP_SELECTOR)) {
|
|
2697
|
+
conflicts.push(
|
|
2698
|
+
`Legacy ${OLD_MCP_SELECTOR} table will be migrated to ${MCP_SELECTOR}.`
|
|
2699
|
+
);
|
|
2700
|
+
}
|
|
2577
2701
|
if (extractTomlTable(content, ENV_SELECTOR)) {
|
|
2578
2702
|
conflicts.push(`Existing ${ENV_SELECTOR} table will be updated.`);
|
|
2579
2703
|
}
|
|
@@ -2639,6 +2763,12 @@ var init_codex = __esm({
|
|
|
2639
2763
|
return { ...artifact, backupPath };
|
|
2640
2764
|
});
|
|
2641
2765
|
let nextContent = existingContent;
|
|
2766
|
+
if (extractTomlTable(nextContent, OLD_ENV_SELECTOR)) {
|
|
2767
|
+
nextContent = removeTomlTable(nextContent, OLD_ENV_SELECTOR);
|
|
2768
|
+
}
|
|
2769
|
+
if (extractTomlTable(nextContent, OLD_MCP_SELECTOR)) {
|
|
2770
|
+
nextContent = removeTomlTable(nextContent, OLD_MCP_SELECTOR);
|
|
2771
|
+
}
|
|
2642
2772
|
nextContent = replaceTomlTable(
|
|
2643
2773
|
nextContent,
|
|
2644
2774
|
MCP_SELECTOR,
|
|
@@ -2815,7 +2945,7 @@ function verifyGeminiConfig(config, configPath, ctx) {
|
|
|
2815
2945
|
message: fs12.existsSync(configPath) ? void 0 : `${configPath} not found`
|
|
2816
2946
|
});
|
|
2817
2947
|
checks.push({
|
|
2818
|
-
name: "tap
|
|
2948
|
+
name: "tap entry present",
|
|
2819
2949
|
passed: !!entry,
|
|
2820
2950
|
message: entry ? void 0 : `${GEMINI_SELECTOR} not found`
|
|
2821
2951
|
});
|
|
@@ -2833,14 +2963,15 @@ function verifyGeminiConfig(config, configPath, ctx) {
|
|
|
2833
2963
|
}
|
|
2834
2964
|
return checks;
|
|
2835
2965
|
}
|
|
2836
|
-
var GEMINI_SELECTOR, geminiAdapter;
|
|
2966
|
+
var GEMINI_SELECTOR, OLD_GEMINI_SELECTOR, geminiAdapter;
|
|
2837
2967
|
var init_gemini = __esm({
|
|
2838
2968
|
"src/adapters/gemini.ts"() {
|
|
2839
2969
|
"use strict";
|
|
2840
2970
|
init_state();
|
|
2841
2971
|
init_artifact_backups();
|
|
2842
2972
|
init_common();
|
|
2843
|
-
GEMINI_SELECTOR = "mcpServers.tap
|
|
2973
|
+
GEMINI_SELECTOR = "mcpServers.tap";
|
|
2974
|
+
OLD_GEMINI_SELECTOR = "mcpServers.tap-comms";
|
|
2844
2975
|
geminiAdapter = {
|
|
2845
2976
|
runtime: "gemini",
|
|
2846
2977
|
async probe(ctx) {
|
|
@@ -2889,6 +3020,11 @@ var init_gemini = __esm({
|
|
|
2889
3020
|
if (readNestedKey(config, GEMINI_SELECTOR) !== void 0) {
|
|
2890
3021
|
conflicts.push(`Existing ${GEMINI_SELECTOR} entry will be updated.`);
|
|
2891
3022
|
}
|
|
3023
|
+
if (readNestedKey(config, OLD_GEMINI_SELECTOR) !== void 0) {
|
|
3024
|
+
conflicts.push(
|
|
3025
|
+
`Legacy ${OLD_GEMINI_SELECTOR} entry will be migrated to ${GEMINI_SELECTOR}.`
|
|
3026
|
+
);
|
|
3027
|
+
}
|
|
2892
3028
|
} catch {
|
|
2893
3029
|
warnings.push(
|
|
2894
3030
|
`${configPath} exists but is not valid JSON. It will be replaced.`
|
|
@@ -2956,6 +3092,13 @@ var init_gemini = __esm({
|
|
|
2956
3092
|
existed: previousValue !== void 0,
|
|
2957
3093
|
value: previousValue
|
|
2958
3094
|
});
|
|
3095
|
+
const oldValue = readNestedKey(config, OLD_GEMINI_SELECTOR);
|
|
3096
|
+
if (oldValue !== void 0) {
|
|
3097
|
+
const servers = config.mcpServers;
|
|
3098
|
+
if (servers) {
|
|
3099
|
+
delete servers["tap-comms"];
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
2959
3102
|
setNestedKey2(config, GEMINI_SELECTOR, {
|
|
2960
3103
|
command: managed.command,
|
|
2961
3104
|
args: managed.args,
|
|
@@ -3073,6 +3216,21 @@ function redactProtectedUrl(url) {
|
|
|
3073
3216
|
function loadCurrentBridgeState(stateDir, instanceId, fallback) {
|
|
3074
3217
|
return loadBridgeState(stateDir, instanceId) ?? fallback ?? null;
|
|
3075
3218
|
}
|
|
3219
|
+
function formatThreadSummary(threadId, cwd) {
|
|
3220
|
+
if (!threadId) {
|
|
3221
|
+
return "-";
|
|
3222
|
+
}
|
|
3223
|
+
return cwd ? `${threadId} (${cwd})` : threadId;
|
|
3224
|
+
}
|
|
3225
|
+
function normalizeComparablePath(value) {
|
|
3226
|
+
return path13.resolve(value).replace(/\\/g, "/").toLowerCase();
|
|
3227
|
+
}
|
|
3228
|
+
function sameOptionalPath(left, right) {
|
|
3229
|
+
if (!left || !right) {
|
|
3230
|
+
return left === right;
|
|
3231
|
+
}
|
|
3232
|
+
return normalizeComparablePath(left) === normalizeComparablePath(right);
|
|
3233
|
+
}
|
|
3076
3234
|
function getSharedAppServerUsers(state, stateDir, currentInstanceId, appServerUrl) {
|
|
3077
3235
|
const shared = [];
|
|
3078
3236
|
for (const [id, inst] of Object.entries(state.instances)) {
|
|
@@ -3685,12 +3843,18 @@ function bridgeStatusAll() {
|
|
|
3685
3843
|
pid: null,
|
|
3686
3844
|
port: inst.port,
|
|
3687
3845
|
lastHeartbeat: null,
|
|
3846
|
+
threadId: null,
|
|
3847
|
+
threadCwd: null,
|
|
3848
|
+
savedThreadId: null,
|
|
3849
|
+
savedThreadCwd: null,
|
|
3688
3850
|
appServer: null
|
|
3689
3851
|
};
|
|
3690
3852
|
continue;
|
|
3691
3853
|
}
|
|
3692
3854
|
const status = getBridgeStatus(stateDir, instanceId);
|
|
3693
3855
|
const bridgeState = loadBridgeState(stateDir, instanceId);
|
|
3856
|
+
const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(bridgeState);
|
|
3857
|
+
const savedThread = loadRuntimeBridgeThreadState(bridgeState);
|
|
3694
3858
|
const age = getHeartbeatAge(stateDir, instanceId);
|
|
3695
3859
|
const pid = bridgeState?.pid ?? null;
|
|
3696
3860
|
const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
|
|
@@ -3712,12 +3876,26 @@ function bridgeStatusAll() {
|
|
|
3712
3876
|
);
|
|
3713
3877
|
}
|
|
3714
3878
|
}
|
|
3879
|
+
if (runtimeHeartbeat?.threadId) {
|
|
3880
|
+
log(
|
|
3881
|
+
` Thread: ${formatThreadSummary(runtimeHeartbeat.threadId, runtimeHeartbeat.threadCwd)}`
|
|
3882
|
+
);
|
|
3883
|
+
}
|
|
3884
|
+
if (savedThread?.threadId && (savedThread.threadId !== runtimeHeartbeat?.threadId || !sameOptionalPath(savedThread.cwd, runtimeHeartbeat?.threadCwd))) {
|
|
3885
|
+
log(
|
|
3886
|
+
` Saved: ${formatThreadSummary(savedThread.threadId, savedThread.cwd)}`
|
|
3887
|
+
);
|
|
3888
|
+
}
|
|
3715
3889
|
bridges[instanceId] = {
|
|
3716
3890
|
status,
|
|
3717
3891
|
runtime: inst.runtime,
|
|
3718
3892
|
pid,
|
|
3719
3893
|
port: inst.port,
|
|
3720
3894
|
lastHeartbeat: heartbeat,
|
|
3895
|
+
threadId: runtimeHeartbeat?.threadId ?? null,
|
|
3896
|
+
threadCwd: runtimeHeartbeat?.threadCwd ?? null,
|
|
3897
|
+
savedThreadId: savedThread?.threadId ?? null,
|
|
3898
|
+
savedThreadCwd: savedThread?.cwd ?? null,
|
|
3721
3899
|
appServer: bridgeState?.appServer ?? null
|
|
3722
3900
|
};
|
|
3723
3901
|
}
|
|
@@ -3793,6 +3971,10 @@ function bridgeStatusOne(identifier) {
|
|
|
3793
3971
|
pid: null,
|
|
3794
3972
|
port: inst.port,
|
|
3795
3973
|
lastHeartbeat: null,
|
|
3974
|
+
threadId: null,
|
|
3975
|
+
threadCwd: null,
|
|
3976
|
+
savedThreadId: null,
|
|
3977
|
+
savedThreadCwd: null,
|
|
3796
3978
|
appServer: null
|
|
3797
3979
|
}
|
|
3798
3980
|
};
|
|
@@ -3801,6 +3983,8 @@ function bridgeStatusOne(identifier) {
|
|
|
3801
3983
|
const stateDir = resolvedCfg2.stateDir;
|
|
3802
3984
|
const status = getBridgeStatus(stateDir, instanceId);
|
|
3803
3985
|
const bridgeState = loadBridgeState(stateDir, instanceId);
|
|
3986
|
+
const runtimeHeartbeat = loadRuntimeBridgeHeartbeat(bridgeState);
|
|
3987
|
+
const savedThread = loadRuntimeBridgeThreadState(bridgeState);
|
|
3804
3988
|
const age = getHeartbeatAge(stateDir, instanceId);
|
|
3805
3989
|
const heartbeat = getBridgeHeartbeatTimestamp(stateDir, instanceId);
|
|
3806
3990
|
log(`Status: ${status}`);
|
|
@@ -3809,6 +3993,16 @@ function bridgeStatusOne(identifier) {
|
|
|
3809
3993
|
log(
|
|
3810
3994
|
`Heartbeat: ${heartbeat ?? "-"}${age !== null ? ` (${formatAge(age)})` : ""}`
|
|
3811
3995
|
);
|
|
3996
|
+
if (runtimeHeartbeat?.threadId) {
|
|
3997
|
+
log(
|
|
3998
|
+
`Thread: ${formatThreadSummary(runtimeHeartbeat.threadId, runtimeHeartbeat.threadCwd)}`
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
if (savedThread?.threadId && (savedThread.threadId !== runtimeHeartbeat?.threadId || !sameOptionalPath(savedThread.cwd, runtimeHeartbeat?.threadCwd))) {
|
|
4002
|
+
log(
|
|
4003
|
+
`Saved: ${formatThreadSummary(savedThread.threadId, savedThread.cwd)}`
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
3812
4006
|
log(
|
|
3813
4007
|
`Log: ${path13.join(stateDir, "logs", `bridge-${instanceId}.log`)}`
|
|
3814
4008
|
);
|
|
@@ -3857,6 +4051,10 @@ function bridgeStatusOne(identifier) {
|
|
|
3857
4051
|
pid: bridgeState?.pid ?? null,
|
|
3858
4052
|
port: inst.port,
|
|
3859
4053
|
lastHeartbeat: heartbeat,
|
|
4054
|
+
threadId: runtimeHeartbeat?.threadId ?? null,
|
|
4055
|
+
threadCwd: runtimeHeartbeat?.threadCwd ?? null,
|
|
4056
|
+
savedThreadId: savedThread?.threadId ?? null,
|
|
4057
|
+
savedThreadCwd: savedThread?.cwd ?? null,
|
|
3860
4058
|
appServer: bridgeState?.appServer ?? null
|
|
3861
4059
|
}
|
|
3862
4060
|
};
|
|
@@ -4084,7 +4282,7 @@ var init_bridge2 = __esm({
|
|
|
4084
4282
|
init_utils();
|
|
4085
4283
|
BRIDGE_HELP = `
|
|
4086
4284
|
Usage:
|
|
4087
|
-
tap
|
|
4285
|
+
tap bridge <subcommand> [instance] [options]
|
|
4088
4286
|
|
|
4089
4287
|
Subcommands:
|
|
4090
4288
|
start <instance> Start bridge for an instance (e.g. codex, codex-reviewer)
|
|
@@ -4142,7 +4340,18 @@ async function upCommand(args) {
|
|
|
4142
4340
|
};
|
|
4143
4341
|
}
|
|
4144
4342
|
const repoRoot = findRepoRoot();
|
|
4145
|
-
const
|
|
4343
|
+
const previousColdStartWarmup = process.env.TAP_COLD_START_WARMUP;
|
|
4344
|
+
process.env.TAP_COLD_START_WARMUP = "true";
|
|
4345
|
+
let result;
|
|
4346
|
+
try {
|
|
4347
|
+
result = await bridgeCommand(["start", "--all", ...args]);
|
|
4348
|
+
} finally {
|
|
4349
|
+
if (previousColdStartWarmup === void 0) {
|
|
4350
|
+
delete process.env.TAP_COLD_START_WARMUP;
|
|
4351
|
+
} else {
|
|
4352
|
+
process.env.TAP_COLD_START_WARMUP = previousColdStartWarmup;
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4146
4355
|
const snapshot = collectDashboardSnapshot(repoRoot);
|
|
4147
4356
|
const activeBridges = snapshot.bridges.filter(
|
|
4148
4357
|
(bridge) => bridge.status === "running"
|
|
@@ -4178,7 +4387,7 @@ var init_up = __esm({
|
|
|
4178
4387
|
init_utils();
|
|
4179
4388
|
UP_HELP = `
|
|
4180
4389
|
Usage:
|
|
4181
|
-
tap
|
|
4390
|
+
tap up [bridge-start options]
|
|
4182
4391
|
|
|
4183
4392
|
Description:
|
|
4184
4393
|
Start all registered app-server bridge daemons with one command.
|
|
@@ -4243,7 +4452,7 @@ var init_down = __esm({
|
|
|
4243
4452
|
init_utils();
|
|
4244
4453
|
DOWN_HELP = `
|
|
4245
4454
|
Usage:
|
|
4246
|
-
tap
|
|
4455
|
+
tap down
|
|
4247
4456
|
|
|
4248
4457
|
Description:
|
|
4249
4458
|
Stop all running bridge daemons and managed app-servers.
|
|
@@ -4297,14 +4506,14 @@ async function* streamEvents(options) {
|
|
|
4297
4506
|
const repoRoot = options?.repoRoot ?? findRepoRoot();
|
|
4298
4507
|
while (!options?.signal?.aborted) {
|
|
4299
4508
|
yield collectDashboardSnapshot(repoRoot, options?.commsDir);
|
|
4300
|
-
await new Promise((
|
|
4509
|
+
await new Promise((resolve9) => {
|
|
4301
4510
|
const onAbort = () => {
|
|
4302
4511
|
clearTimeout(timer);
|
|
4303
|
-
|
|
4512
|
+
resolve9();
|
|
4304
4513
|
};
|
|
4305
4514
|
const timer = setTimeout(() => {
|
|
4306
4515
|
options?.signal?.removeEventListener("abort", onAbort);
|
|
4307
|
-
|
|
4516
|
+
resolve9();
|
|
4308
4517
|
}, intervalMs);
|
|
4309
4518
|
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
4310
4519
|
});
|
|
@@ -4545,18 +4754,18 @@ async function startHttpServer(options) {
|
|
|
4545
4754
|
}
|
|
4546
4755
|
}
|
|
4547
4756
|
);
|
|
4548
|
-
await new Promise((
|
|
4757
|
+
await new Promise((resolve9, reject) => {
|
|
4549
4758
|
server.once("error", reject);
|
|
4550
4759
|
server.listen(port, host, () => {
|
|
4551
4760
|
server.removeListener("error", reject);
|
|
4552
|
-
|
|
4761
|
+
resolve9();
|
|
4553
4762
|
});
|
|
4554
4763
|
});
|
|
4555
4764
|
return {
|
|
4556
4765
|
port,
|
|
4557
4766
|
token,
|
|
4558
|
-
close: () => new Promise((
|
|
4559
|
-
server.close((err) => err ? reject(err) :
|
|
4767
|
+
close: () => new Promise((resolve9, reject) => {
|
|
4768
|
+
server.close((err) => err ? reject(err) : resolve9());
|
|
4560
4769
|
})
|
|
4561
4770
|
};
|
|
4562
4771
|
}
|