@hua-labs/tap 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridges/codex-app-server-auth-gateway.mjs +11 -3
- package/dist/bridges/codex-app-server-auth-gateway.mjs.map +1 -1
- package/dist/bridges/codex-app-server-bridge.d.mts +1 -0
- package/dist/bridges/codex-app-server-bridge.mjs +16 -13
- package/dist/bridges/codex-app-server-bridge.mjs.map +1 -1
- package/dist/bridges/codex-bridge-runner.mjs +21 -17
- package/dist/bridges/codex-bridge-runner.mjs.map +1 -1
- package/dist/cli.mjs +483 -146
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +4 -1
- package/dist/index.mjs +172 -75
- 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 +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/commands/init.ts
|
|
2
2
|
import * as fs6 from "fs";
|
|
3
3
|
import * as path6 from "path";
|
|
4
|
-
import {
|
|
4
|
+
import { spawnSync } from "child_process";
|
|
5
5
|
|
|
6
6
|
// src/state.ts
|
|
7
7
|
import * as fs3 from "fs";
|
|
@@ -23,9 +23,16 @@ function detectPlatform() {
|
|
|
23
23
|
return process.platform;
|
|
24
24
|
}
|
|
25
25
|
var _noGitWarned = false;
|
|
26
|
+
var _loggedWarnings = /* @__PURE__ */ new Set();
|
|
26
27
|
function _setNoGitWarned() {
|
|
27
28
|
_noGitWarned = true;
|
|
28
29
|
}
|
|
30
|
+
function resetLoggedWarnings() {
|
|
31
|
+
_loggedWarnings.clear();
|
|
32
|
+
}
|
|
33
|
+
function wasWarningLogged(message) {
|
|
34
|
+
return _loggedWarnings.has(message);
|
|
35
|
+
}
|
|
29
36
|
function findRepoRoot(startDir = process.cwd()) {
|
|
30
37
|
let dir = path.resolve(startDir);
|
|
31
38
|
while (true) {
|
|
@@ -33,8 +40,8 @@ function findRepoRoot(startDir = process.cwd()) {
|
|
|
33
40
|
if (fs.existsSync(path.join(dir, "package.json"))) {
|
|
34
41
|
if (!_noGitWarned) {
|
|
35
42
|
_setNoGitWarned();
|
|
36
|
-
|
|
37
|
-
"No .git directory found. Resolved
|
|
43
|
+
log(
|
|
44
|
+
"No .git directory found. Resolved tap root via package.json. That's fine outside git; use --comms-dir to choose a different comms location."
|
|
38
45
|
);
|
|
39
46
|
}
|
|
40
47
|
return dir;
|
|
@@ -45,8 +52,8 @@ function findRepoRoot(startDir = process.cwd()) {
|
|
|
45
52
|
}
|
|
46
53
|
if (!_noGitWarned) {
|
|
47
54
|
_setNoGitWarned();
|
|
48
|
-
|
|
49
|
-
"No git repository or package.json found. Using current directory as root.
|
|
55
|
+
log(
|
|
56
|
+
"No git repository or package.json found. Using the current directory as tap root. That's fine outside git; use --comms-dir to choose a different comms location."
|
|
50
57
|
);
|
|
51
58
|
}
|
|
52
59
|
return process.cwd();
|
|
@@ -101,7 +108,9 @@ function logSuccess(message) {
|
|
|
101
108
|
if (!_jsonMode) console.log(` + ${message}`);
|
|
102
109
|
}
|
|
103
110
|
function logWarn(message) {
|
|
104
|
-
if (
|
|
111
|
+
if (_jsonMode) return;
|
|
112
|
+
_loggedWarnings.add(message);
|
|
113
|
+
console.log(` ! ${message}`);
|
|
105
114
|
}
|
|
106
115
|
function logError(message) {
|
|
107
116
|
if (!_jsonMode) console.error(` x ${message}`);
|
|
@@ -111,6 +120,16 @@ function logHeader(message) {
|
|
|
111
120
|
${message}
|
|
112
121
|
`);
|
|
113
122
|
}
|
|
123
|
+
function parseIntFlag(value, name, min, max) {
|
|
124
|
+
if (value === void 0) return void 0;
|
|
125
|
+
const parsed = Number(value);
|
|
126
|
+
if (!Number.isInteger(parsed) || parsed < min || parsed > max) {
|
|
127
|
+
throw new RangeError(
|
|
128
|
+
`Invalid ${name}: ${value}. Must be an integer between ${min} and ${max}.`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
114
133
|
function resolveInstanceId(identifier, state) {
|
|
115
134
|
if (state.instances[identifier]) {
|
|
116
135
|
return { ok: true, instanceId: identifier };
|
|
@@ -160,8 +179,8 @@ function findRepoRoot2(startDir = process.cwd()) {
|
|
|
160
179
|
if (fs2.existsSync(path2.join(dir, "package.json"))) {
|
|
161
180
|
if (!_noGitWarned) {
|
|
162
181
|
_setNoGitWarned();
|
|
163
|
-
|
|
164
|
-
"
|
|
182
|
+
log(
|
|
183
|
+
"No .git directory found. Resolved tap root via package.json. That's fine outside git; use --comms-dir to choose a different comms location."
|
|
165
184
|
);
|
|
166
185
|
}
|
|
167
186
|
return dir;
|
|
@@ -172,8 +191,8 @@ function findRepoRoot2(startDir = process.cwd()) {
|
|
|
172
191
|
}
|
|
173
192
|
if (!_noGitWarned) {
|
|
174
193
|
_setNoGitWarned();
|
|
175
|
-
|
|
176
|
-
"
|
|
194
|
+
log(
|
|
195
|
+
"No git repository or package.json found. Using the current directory as tap root. That's fine outside git; use --comms-dir to choose a different comms location."
|
|
177
196
|
);
|
|
178
197
|
}
|
|
179
198
|
return process.cwd();
|
|
@@ -187,7 +206,7 @@ function loadJsonFile(filePath) {
|
|
|
187
206
|
return null;
|
|
188
207
|
}
|
|
189
208
|
}
|
|
190
|
-
function
|
|
209
|
+
function loadSharedConfig(repoRoot) {
|
|
191
210
|
return loadJsonFile(path2.join(repoRoot, SHARED_CONFIG_FILE));
|
|
192
211
|
}
|
|
193
212
|
function loadLocalConfig(repoRoot) {
|
|
@@ -211,7 +230,7 @@ function loadLegacyShellConfig(repoRoot) {
|
|
|
211
230
|
}
|
|
212
231
|
function resolveConfig(overrides = {}, startDir) {
|
|
213
232
|
const repoRoot = findRepoRoot2(startDir);
|
|
214
|
-
const shared =
|
|
233
|
+
const shared = loadSharedConfig(repoRoot) ?? {};
|
|
215
234
|
const local = loadLocalConfig(repoRoot) ?? {};
|
|
216
235
|
const legacy = loadLegacyShellConfig(repoRoot) ?? {};
|
|
217
236
|
const sources = {
|
|
@@ -754,7 +773,39 @@ function parsePermissionMode(args) {
|
|
|
754
773
|
}
|
|
755
774
|
return "safe";
|
|
756
775
|
}
|
|
776
|
+
var INIT_HELP = `
|
|
777
|
+
Usage:
|
|
778
|
+
tap-comms init [options]
|
|
779
|
+
|
|
780
|
+
Description:
|
|
781
|
+
Initialize the tap-comms directory structure, state file, and permissions.
|
|
782
|
+
Optionally clone a shared comms repository.
|
|
783
|
+
|
|
784
|
+
Options:
|
|
785
|
+
--comms-dir <path> Override comms directory (default: tap-comms/)
|
|
786
|
+
--comms-repo <url> Clone a shared comms git repo into comms directory
|
|
787
|
+
--permissions <mode> Permission mode: safe (default) or full
|
|
788
|
+
--force Re-initialize even if already set up
|
|
789
|
+
--help, -h Show help
|
|
790
|
+
|
|
791
|
+
Examples:
|
|
792
|
+
npx @hua-labs/tap init
|
|
793
|
+
npx @hua-labs/tap init --permissions full
|
|
794
|
+
npx @hua-labs/tap init --comms-repo https://github.com/org/comms.git
|
|
795
|
+
npx @hua-labs/tap init --comms-dir /shared/comms --force
|
|
796
|
+
`.trim();
|
|
757
797
|
async function initCommand(args) {
|
|
798
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
799
|
+
log(INIT_HELP);
|
|
800
|
+
return {
|
|
801
|
+
ok: true,
|
|
802
|
+
command: "init",
|
|
803
|
+
code: "TAP_NO_OP",
|
|
804
|
+
message: INIT_HELP,
|
|
805
|
+
warnings: [],
|
|
806
|
+
data: {}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
758
809
|
const repoRoot = findRepoRoot();
|
|
759
810
|
const commsDir = resolveCommsDir(args, repoRoot);
|
|
760
811
|
const permMode = parsePermissionMode(args);
|
|
@@ -791,10 +842,19 @@ async function initCommand(args) {
|
|
|
791
842
|
} else {
|
|
792
843
|
log(`Cloning comms repo: ${commsRepoUrl}`);
|
|
793
844
|
try {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
845
|
+
const cloneResult = spawnSync(
|
|
846
|
+
"git",
|
|
847
|
+
["clone", commsRepoUrl, commsDir],
|
|
848
|
+
{
|
|
849
|
+
stdio: "pipe",
|
|
850
|
+
encoding: "utf-8"
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
if (cloneResult.status !== 0) {
|
|
854
|
+
throw new Error(
|
|
855
|
+
cloneResult.stderr || `git clone exited with code ${cloneResult.status}`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
798
858
|
logSuccess(`Cloned comms repo to ${commsDir}`);
|
|
799
859
|
} catch (err) {
|
|
800
860
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -811,7 +871,7 @@ async function initCommand(args) {
|
|
|
811
871
|
}
|
|
812
872
|
}
|
|
813
873
|
{
|
|
814
|
-
const sharedConfig =
|
|
874
|
+
const sharedConfig = loadSharedConfig(repoRoot) ?? {};
|
|
815
875
|
let configChanged = false;
|
|
816
876
|
if (commsRepoUrl) {
|
|
817
877
|
sharedConfig.commsRepoUrl = commsRepoUrl;
|
|
@@ -903,17 +963,17 @@ ${entry}
|
|
|
903
963
|
// src/adapters/claude.ts
|
|
904
964
|
import * as fs8 from "fs";
|
|
905
965
|
import * as path8 from "path";
|
|
906
|
-
import { execSync
|
|
966
|
+
import { execSync } from "child_process";
|
|
907
967
|
|
|
908
968
|
// src/adapters/common.ts
|
|
909
969
|
import * as fs7 from "fs";
|
|
910
970
|
import * as os2 from "os";
|
|
911
971
|
import * as path7 from "path";
|
|
912
|
-
import { spawnSync } from "child_process";
|
|
972
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
913
973
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
914
974
|
function probeCommand(candidates) {
|
|
915
975
|
for (const candidate of candidates) {
|
|
916
|
-
const result =
|
|
976
|
+
const result = spawnSync2(candidate, ["--version"], {
|
|
917
977
|
encoding: "utf-8",
|
|
918
978
|
shell: process.platform === "win32"
|
|
919
979
|
});
|
|
@@ -991,7 +1051,7 @@ function findPreferredBunCommand() {
|
|
|
991
1051
|
const candidates = process.platform === "win32" ? [path7.join(home, ".bun", "bin", "bun.exe"), "bun", "bun.cmd"] : [path7.join(home, ".bun", "bin", "bun"), "bun"];
|
|
992
1052
|
for (const candidate of candidates) {
|
|
993
1053
|
if (path7.isAbsolute(candidate) && !fs7.existsSync(candidate)) continue;
|
|
994
|
-
const result =
|
|
1054
|
+
const result = spawnSync2(candidate, ["--version"], {
|
|
995
1055
|
encoding: "utf-8",
|
|
996
1056
|
shell: process.platform === "win32"
|
|
997
1057
|
});
|
|
@@ -1068,7 +1128,7 @@ function findMcpJsonPath(ctx) {
|
|
|
1068
1128
|
}
|
|
1069
1129
|
function findClaudeCommand() {
|
|
1070
1130
|
try {
|
|
1071
|
-
|
|
1131
|
+
execSync("claude --version", { stdio: "pipe" });
|
|
1072
1132
|
return "claude";
|
|
1073
1133
|
} catch {
|
|
1074
1134
|
return null;
|
|
@@ -1929,13 +1989,13 @@ import * as fs13 from "fs";
|
|
|
1929
1989
|
import * as net from "net";
|
|
1930
1990
|
import * as path13 from "path";
|
|
1931
1991
|
import { randomBytes } from "crypto";
|
|
1932
|
-
import { spawn, spawnSync as
|
|
1992
|
+
import { spawn, spawnSync as spawnSync3, execSync as execSync3 } from "child_process";
|
|
1933
1993
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1934
1994
|
|
|
1935
1995
|
// src/runtime/resolve-node.ts
|
|
1936
1996
|
import * as fs12 from "fs";
|
|
1937
1997
|
import * as path12 from "path";
|
|
1938
|
-
import { execSync as
|
|
1998
|
+
import { execSync as execSync2 } from "child_process";
|
|
1939
1999
|
function readNodeVersion(repoRoot) {
|
|
1940
2000
|
const nvFile = path12.join(repoRoot, ".node-version");
|
|
1941
2001
|
if (!fs12.existsSync(nvFile)) return null;
|
|
@@ -1978,7 +2038,7 @@ function probeFnmNode(desiredVersion) {
|
|
|
1978
2038
|
);
|
|
1979
2039
|
if (!fs12.existsSync(candidate)) continue;
|
|
1980
2040
|
try {
|
|
1981
|
-
const v =
|
|
2041
|
+
const v = execSync2(`"${candidate}" --version`, {
|
|
1982
2042
|
encoding: "utf-8",
|
|
1983
2043
|
timeout: 5e3
|
|
1984
2044
|
}).trim();
|
|
@@ -1992,7 +2052,7 @@ function probeFnmNode(desiredVersion) {
|
|
|
1992
2052
|
}
|
|
1993
2053
|
function detectNodeMajorVersion(command) {
|
|
1994
2054
|
try {
|
|
1995
|
-
const version2 =
|
|
2055
|
+
const version2 = execSync2(`"${command}" --version`, {
|
|
1996
2056
|
encoding: "utf-8",
|
|
1997
2057
|
timeout: 5e3
|
|
1998
2058
|
}).trim();
|
|
@@ -2006,7 +2066,7 @@ function checkStripTypesSupport(command) {
|
|
|
2006
2066
|
const major = detectNodeMajorVersion(command);
|
|
2007
2067
|
if (major !== null && major >= 22) return true;
|
|
2008
2068
|
try {
|
|
2009
|
-
|
|
2069
|
+
execSync2(`"${command}" --experimental-strip-types -e ""`, {
|
|
2010
2070
|
timeout: 5e3,
|
|
2011
2071
|
stdio: "pipe"
|
|
2012
2072
|
});
|
|
@@ -2097,7 +2157,7 @@ var APP_SERVER_HEALTH_TIMEOUT_MS = 1500;
|
|
|
2097
2157
|
var APP_SERVER_START_TIMEOUT_MS = 2e4;
|
|
2098
2158
|
var APP_SERVER_GATEWAY_START_TIMEOUT_MS = 5e3;
|
|
2099
2159
|
var APP_SERVER_HEALTH_RETRY_MS = 250;
|
|
2100
|
-
var
|
|
2160
|
+
var AUTH_SUBPROTOCOL_PREFIX = "tap-auth-";
|
|
2101
2161
|
var APP_SERVER_AUTH_FILE_MODE = 384;
|
|
2102
2162
|
function appServerLogFilePath(stateDir, instanceId) {
|
|
2103
2163
|
return path13.join(stateDir, "logs", `app-server-${instanceId}.log`);
|
|
@@ -2158,8 +2218,11 @@ function resolvePowerShellCommand() {
|
|
|
2158
2218
|
function resolveAuthGatewayScript(repoRoot) {
|
|
2159
2219
|
const moduleDir = path13.dirname(fileURLToPath4(import.meta.url));
|
|
2160
2220
|
const candidates = [
|
|
2161
|
-
|
|
2162
|
-
path13.join(moduleDir, "
|
|
2221
|
+
// Bundled: dist/bridges/ sibling (npm install / built package)
|
|
2222
|
+
path13.join(moduleDir, "bridges", "codex-app-server-auth-gateway.mjs"),
|
|
2223
|
+
// Source: src/bridges/ sibling (monorepo dev with ts runner)
|
|
2224
|
+
path13.join(moduleDir, "bridges", "codex-app-server-auth-gateway.ts"),
|
|
2225
|
+
// Monorepo dist fallback
|
|
2163
2226
|
path13.join(
|
|
2164
2227
|
repoRoot,
|
|
2165
2228
|
"packages",
|
|
@@ -2212,10 +2275,8 @@ async function allocateLoopbackPort(hostname) {
|
|
|
2212
2275
|
});
|
|
2213
2276
|
});
|
|
2214
2277
|
}
|
|
2215
|
-
function buildProtectedAppServerUrl(publicUrl,
|
|
2216
|
-
|
|
2217
|
-
url.searchParams.set(APP_SERVER_AUTH_QUERY_PARAM, token);
|
|
2218
|
-
return url.toString().replace(/\/(?=\?|$)/, "");
|
|
2278
|
+
function buildProtectedAppServerUrl(publicUrl, _token) {
|
|
2279
|
+
return publicUrl;
|
|
2219
2280
|
}
|
|
2220
2281
|
function readGatewayTokenFromPath(tokenPath) {
|
|
2221
2282
|
return fs13.readFileSync(tokenPath, "utf8").trim();
|
|
@@ -2330,7 +2391,7 @@ async function createManagedAppServerAuth(options) {
|
|
|
2330
2391
|
throw new Error("Failed to spawn app-server auth gateway");
|
|
2331
2392
|
}
|
|
2332
2393
|
return {
|
|
2333
|
-
mode: "
|
|
2394
|
+
mode: "subprotocol",
|
|
2334
2395
|
protectedUrl,
|
|
2335
2396
|
upstreamUrl: upstreamUrl.toString().replace(/\/$/, ""),
|
|
2336
2397
|
tokenPath,
|
|
@@ -2444,7 +2505,7 @@ function findListeningProcessId(url, platform) {
|
|
|
2444
2505
|
if (port == null || !Number.isFinite(port)) {
|
|
2445
2506
|
return null;
|
|
2446
2507
|
}
|
|
2447
|
-
const result =
|
|
2508
|
+
const result = spawnSync3(
|
|
2448
2509
|
resolvePowerShellCommand(),
|
|
2449
2510
|
[
|
|
2450
2511
|
"-NoLogo",
|
|
@@ -2517,7 +2578,7 @@ async function findNextAvailableAppServerPort(state, baseUrl, basePort = 4501, e
|
|
|
2517
2578
|
`Failed to find a free app-server port starting at ${basePort}`
|
|
2518
2579
|
);
|
|
2519
2580
|
}
|
|
2520
|
-
async function checkAppServerHealth(url, timeoutMs = APP_SERVER_HEALTH_TIMEOUT_MS) {
|
|
2581
|
+
async function checkAppServerHealth(url, timeoutMs = APP_SERVER_HEALTH_TIMEOUT_MS, gatewayToken) {
|
|
2521
2582
|
const WebSocket = getWebSocketCtor();
|
|
2522
2583
|
if (!WebSocket) {
|
|
2523
2584
|
return false;
|
|
@@ -2539,7 +2600,8 @@ async function checkAppServerHealth(url, timeoutMs = APP_SERVER_HEALTH_TIMEOUT_M
|
|
|
2539
2600
|
};
|
|
2540
2601
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
2541
2602
|
try {
|
|
2542
|
-
|
|
2603
|
+
const protocols = gatewayToken ? [`${AUTH_SUBPROTOCOL_PREFIX}${gatewayToken}`] : void 0;
|
|
2604
|
+
socket = new WebSocket(url, protocols);
|
|
2543
2605
|
socket.addEventListener("open", () => finish(true), { once: true });
|
|
2544
2606
|
socket.addEventListener("error", () => finish(false), { once: true });
|
|
2545
2607
|
socket.addEventListener("close", () => finish(false), { once: true });
|
|
@@ -2548,10 +2610,14 @@ async function checkAppServerHealth(url, timeoutMs = APP_SERVER_HEALTH_TIMEOUT_M
|
|
|
2548
2610
|
}
|
|
2549
2611
|
});
|
|
2550
2612
|
}
|
|
2551
|
-
async function waitForAppServerHealth(url, timeoutMs) {
|
|
2613
|
+
async function waitForAppServerHealth(url, timeoutMs, gatewayToken) {
|
|
2552
2614
|
const deadline = Date.now() + timeoutMs;
|
|
2553
2615
|
while (Date.now() < deadline) {
|
|
2554
|
-
if (await checkAppServerHealth(
|
|
2616
|
+
if (await checkAppServerHealth(
|
|
2617
|
+
url,
|
|
2618
|
+
APP_SERVER_HEALTH_TIMEOUT_MS,
|
|
2619
|
+
gatewayToken
|
|
2620
|
+
)) {
|
|
2555
2621
|
return true;
|
|
2556
2622
|
}
|
|
2557
2623
|
await delay(APP_SERVER_HEALTH_RETRY_MS);
|
|
@@ -2564,7 +2630,7 @@ async function terminateProcess(pid, platform) {
|
|
|
2564
2630
|
}
|
|
2565
2631
|
try {
|
|
2566
2632
|
if (platform === "win32") {
|
|
2567
|
-
|
|
2633
|
+
execSync3(`taskkill /PID ${pid} /F /T`, { stdio: "pipe" });
|
|
2568
2634
|
} else {
|
|
2569
2635
|
process.kill(pid, "SIGTERM");
|
|
2570
2636
|
await delay(2e3);
|
|
@@ -2816,8 +2882,9 @@ Or start it manually:
|
|
|
2816
2882
|
throw new Error("Tap auth gateway token is missing after startup.");
|
|
2817
2883
|
}
|
|
2818
2884
|
const gatewayHealthy = await waitForAppServerHealth(
|
|
2819
|
-
|
|
2820
|
-
APP_SERVER_GATEWAY_START_TIMEOUT_MS
|
|
2885
|
+
effectiveUrl,
|
|
2886
|
+
APP_SERVER_GATEWAY_START_TIMEOUT_MS,
|
|
2887
|
+
gatewayToken
|
|
2821
2888
|
);
|
|
2822
2889
|
if (!gatewayHealthy) {
|
|
2823
2890
|
await terminateProcess(pid, options.platform);
|
|
@@ -3169,8 +3236,49 @@ function getBridgeStatus(stateDir, instanceId) {
|
|
|
3169
3236
|
}
|
|
3170
3237
|
|
|
3171
3238
|
// src/commands/add.ts
|
|
3239
|
+
var ADD_HELP = `
|
|
3240
|
+
Usage:
|
|
3241
|
+
tap-comms add <claude|codex|gemini> [options]
|
|
3242
|
+
|
|
3243
|
+
Description:
|
|
3244
|
+
Install a runtime instance and configure it to use tap-comms.
|
|
3245
|
+
|
|
3246
|
+
Options:
|
|
3247
|
+
--name <name> Instance name (default: runtime name)
|
|
3248
|
+
--port <port> Port for app-server bridge
|
|
3249
|
+
--agent-name <name> Agent display name for bridge identification
|
|
3250
|
+
--force Re-install even if already configured
|
|
3251
|
+
--headless Enable headless reviewer mode (requires --name)
|
|
3252
|
+
--role <role> Headless role: reviewer, validator, long-running
|
|
3253
|
+
--help, -h Show help
|
|
3254
|
+
|
|
3255
|
+
Examples:
|
|
3256
|
+
npx @hua-labs/tap add claude
|
|
3257
|
+
npx @hua-labs/tap add codex --name reviewer --port 4501 --headless --role reviewer
|
|
3258
|
+
`.trim();
|
|
3259
|
+
function normalizeAgentName(value) {
|
|
3260
|
+
if (typeof value !== "string") {
|
|
3261
|
+
return null;
|
|
3262
|
+
}
|
|
3263
|
+
const trimmed = value.trim();
|
|
3264
|
+
return trimmed ? trimmed : null;
|
|
3265
|
+
}
|
|
3266
|
+
function resolveAgentName2(options) {
|
|
3267
|
+
return normalizeAgentName(options.explicit) ?? normalizeAgentName(options.stored) ?? normalizeAgentName(options.env) ?? normalizeAgentName(options.fallback) ?? null;
|
|
3268
|
+
}
|
|
3172
3269
|
async function addCommand(args) {
|
|
3173
3270
|
const { positional, flags } = parseArgs(args);
|
|
3271
|
+
if (flags["help"] === true || flags["h"] === true) {
|
|
3272
|
+
log(ADD_HELP);
|
|
3273
|
+
return {
|
|
3274
|
+
ok: true,
|
|
3275
|
+
command: "add",
|
|
3276
|
+
code: "TAP_NO_OP",
|
|
3277
|
+
message: ADD_HELP,
|
|
3278
|
+
warnings: [],
|
|
3279
|
+
data: {}
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3174
3282
|
const runtimeArg = positional[0];
|
|
3175
3283
|
if (!runtimeArg) {
|
|
3176
3284
|
return {
|
|
@@ -3196,8 +3304,10 @@ async function addCommand(args) {
|
|
|
3196
3304
|
const instanceName = typeof flags["name"] === "string" ? flags["name"] : void 0;
|
|
3197
3305
|
const instanceId = buildInstanceId(runtime, instanceName);
|
|
3198
3306
|
const portStr = typeof flags["port"] === "string" ? flags["port"] : void 0;
|
|
3199
|
-
const port = portStr ?
|
|
3200
|
-
const agentNameFlag =
|
|
3307
|
+
const port = portStr ? Number(portStr) : null;
|
|
3308
|
+
const agentNameFlag = normalizeAgentName(
|
|
3309
|
+
typeof flags["agent-name"] === "string" ? flags["agent-name"] : null
|
|
3310
|
+
);
|
|
3201
3311
|
const force = flags["force"] === true;
|
|
3202
3312
|
const headlessFlag = flags["headless"] === true;
|
|
3203
3313
|
const roleArg = typeof flags["role"] === "string" ? flags["role"] : void 0;
|
|
@@ -3232,20 +3342,21 @@ async function addCommand(args) {
|
|
|
3232
3342
|
maxRounds: 5,
|
|
3233
3343
|
qualitySeverityFloor: "high"
|
|
3234
3344
|
} : null;
|
|
3235
|
-
if (portStr && (port === null || isNaN(port))) {
|
|
3345
|
+
if (portStr && (port === null || isNaN(port) || port < 1 || port > 65535)) {
|
|
3236
3346
|
return {
|
|
3237
3347
|
ok: false,
|
|
3238
3348
|
command: "add",
|
|
3239
3349
|
runtime,
|
|
3240
3350
|
instanceId,
|
|
3241
3351
|
code: "TAP_INVALID_ARGUMENT",
|
|
3242
|
-
message: `Invalid port: ${portStr}
|
|
3352
|
+
message: `Invalid port: ${portStr}. Must be between 1 and 65535.`,
|
|
3243
3353
|
warnings: [],
|
|
3244
3354
|
data: {}
|
|
3245
3355
|
};
|
|
3246
3356
|
}
|
|
3247
3357
|
const repoRoot = findRepoRoot();
|
|
3248
3358
|
const state = loadState(repoRoot);
|
|
3359
|
+
const adapter = getAdapter(runtime);
|
|
3249
3360
|
if (!state) {
|
|
3250
3361
|
return {
|
|
3251
3362
|
ok: false,
|
|
@@ -3258,7 +3369,39 @@ async function addCommand(args) {
|
|
|
3258
3369
|
data: {}
|
|
3259
3370
|
};
|
|
3260
3371
|
}
|
|
3261
|
-
|
|
3372
|
+
const existingInstance = state.instances[instanceId];
|
|
3373
|
+
const mode = adapter.bridgeMode();
|
|
3374
|
+
const envAgentName = normalizeAgentName(
|
|
3375
|
+
process.env.TAP_AGENT_NAME ?? process.env.CODEX_TAP_AGENT_NAME
|
|
3376
|
+
);
|
|
3377
|
+
const defaultAgentName = mode === "app-server" ? instanceId : null;
|
|
3378
|
+
const resolvedAgentName = resolveAgentName2({
|
|
3379
|
+
explicit: agentNameFlag,
|
|
3380
|
+
env: envAgentName,
|
|
3381
|
+
stored: existingInstance?.agentName ?? null,
|
|
3382
|
+
fallback: defaultAgentName
|
|
3383
|
+
});
|
|
3384
|
+
if (existingInstance?.installed && !force) {
|
|
3385
|
+
if (resolvedAgentName !== existingInstance.agentName) {
|
|
3386
|
+
const updatedState = updateInstanceState(state, instanceId, {
|
|
3387
|
+
...existingInstance,
|
|
3388
|
+
agentName: resolvedAgentName
|
|
3389
|
+
});
|
|
3390
|
+
saveState(repoRoot, updatedState);
|
|
3391
|
+
return {
|
|
3392
|
+
ok: true,
|
|
3393
|
+
command: "add",
|
|
3394
|
+
runtime,
|
|
3395
|
+
instanceId,
|
|
3396
|
+
code: "TAP_ADD_OK",
|
|
3397
|
+
message: resolvedAgentName === null ? `${instanceId} updated` : `${instanceId} agent name updated to "${resolvedAgentName}".`,
|
|
3398
|
+
warnings: [],
|
|
3399
|
+
data: {
|
|
3400
|
+
updatedFields: ["agentName"],
|
|
3401
|
+
agentName: resolvedAgentName
|
|
3402
|
+
}
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3262
3405
|
return {
|
|
3263
3406
|
ok: true,
|
|
3264
3407
|
command: "add",
|
|
@@ -3288,14 +3431,12 @@ async function addCommand(args) {
|
|
|
3288
3431
|
logHeader(`@hua-labs/tap add ${instanceId}`);
|
|
3289
3432
|
if (instanceName) log(`Instance name: ${instanceName}`);
|
|
3290
3433
|
if (port !== null) log(`Port: ${port}`);
|
|
3291
|
-
|
|
3292
|
-
const effectiveAgentName = agentNameFlag ?? existingAgentName ?? void 0;
|
|
3434
|
+
if (resolvedAgentName) log(`Agent name: ${resolvedAgentName}`);
|
|
3293
3435
|
const ctx = {
|
|
3294
3436
|
...createAdapterContext(state.commsDir, repoRoot),
|
|
3295
3437
|
instanceId,
|
|
3296
|
-
agentName:
|
|
3438
|
+
agentName: resolvedAgentName ?? void 0
|
|
3297
3439
|
};
|
|
3298
|
-
const adapter = getAdapter(runtime);
|
|
3299
3440
|
const warnings = [];
|
|
3300
3441
|
log("Probing runtime...");
|
|
3301
3442
|
const probe = await adapter.probe(ctx);
|
|
@@ -3375,7 +3516,6 @@ async function addCommand(args) {
|
|
|
3375
3516
|
);
|
|
3376
3517
|
}
|
|
3377
3518
|
let bridge = null;
|
|
3378
|
-
const mode = adapter.bridgeMode();
|
|
3379
3519
|
if (mode === "app-server") {
|
|
3380
3520
|
const bridgeScript = adapter.resolveBridgeScript?.(ctx);
|
|
3381
3521
|
if (!bridgeScript) {
|
|
@@ -3383,36 +3523,34 @@ async function addCommand(args) {
|
|
|
3383
3523
|
warnings.push("Bridge script not found. Run bridge manually.");
|
|
3384
3524
|
} else {
|
|
3385
3525
|
const { config: resolvedCfg } = resolveConfig({}, repoRoot);
|
|
3386
|
-
{
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
warnings.push(`Bridge not started: ${msg}`);
|
|
3408
|
-
}
|
|
3526
|
+
log(`Starting bridge: ${bridgeScript}`);
|
|
3527
|
+
try {
|
|
3528
|
+
bridge = await startBridge({
|
|
3529
|
+
instanceId,
|
|
3530
|
+
runtime,
|
|
3531
|
+
stateDir: ctx.stateDir,
|
|
3532
|
+
commsDir: ctx.commsDir,
|
|
3533
|
+
bridgeScript,
|
|
3534
|
+
platform: ctx.platform,
|
|
3535
|
+
agentName: resolvedAgentName ?? void 0,
|
|
3536
|
+
runtimeCommand: resolvedCfg.runtimeCommand,
|
|
3537
|
+
appServerUrl: resolvedCfg.appServerUrl,
|
|
3538
|
+
repoRoot,
|
|
3539
|
+
port: port ?? void 0,
|
|
3540
|
+
headless
|
|
3541
|
+
});
|
|
3542
|
+
logSuccess(`Bridge started (PID: ${bridge.pid})`);
|
|
3543
|
+
} catch (err) {
|
|
3544
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3545
|
+
logWarn(`Bridge not started: ${msg}`);
|
|
3546
|
+
warnings.push(`Bridge not started: ${msg}`);
|
|
3409
3547
|
}
|
|
3410
3548
|
}
|
|
3411
3549
|
}
|
|
3412
3550
|
const instanceState = {
|
|
3413
3551
|
instanceId,
|
|
3414
3552
|
runtime,
|
|
3415
|
-
agentName:
|
|
3553
|
+
agentName: resolvedAgentName,
|
|
3416
3554
|
port,
|
|
3417
3555
|
installed: true,
|
|
3418
3556
|
configPath: probe.configPath ?? "",
|
|
@@ -3424,7 +3562,7 @@ async function addCommand(args) {
|
|
|
3424
3562
|
lastVerifiedAt: verify.ok ? (/* @__PURE__ */ new Date()).toISOString() : null,
|
|
3425
3563
|
bridge,
|
|
3426
3564
|
headless,
|
|
3427
|
-
warnings: [...result.warnings, ...verify.warnings]
|
|
3565
|
+
warnings: Array.from(/* @__PURE__ */ new Set([...result.warnings, ...verify.warnings]))
|
|
3428
3566
|
};
|
|
3429
3567
|
const newState = updateInstanceState(state, instanceId, instanceState);
|
|
3430
3568
|
saveState(repoRoot, newState);
|
|
@@ -3432,6 +3570,13 @@ async function addCommand(args) {
|
|
|
3432
3570
|
if (result.restartRequired) {
|
|
3433
3571
|
logWarn(`Restart ${runtime} to pick up the new configuration.`);
|
|
3434
3572
|
}
|
|
3573
|
+
if (runtime === "claude") {
|
|
3574
|
+
log("");
|
|
3575
|
+
log("For real-time notifications:");
|
|
3576
|
+
log(" claude --dangerously-load-development-channels server:tap-comms");
|
|
3577
|
+
log("Or polling mode (tools still work):");
|
|
3578
|
+
log(" claude");
|
|
3579
|
+
}
|
|
3435
3580
|
logHeader("Done!");
|
|
3436
3581
|
return {
|
|
3437
3582
|
ok: true,
|
|
@@ -3451,6 +3596,16 @@ async function addCommand(args) {
|
|
|
3451
3596
|
}
|
|
3452
3597
|
|
|
3453
3598
|
// src/commands/status.ts
|
|
3599
|
+
var STATUS_HELP = `
|
|
3600
|
+
Usage:
|
|
3601
|
+
tap-comms status
|
|
3602
|
+
|
|
3603
|
+
Description:
|
|
3604
|
+
Show all installed instances, their bridge status, and configuration info.
|
|
3605
|
+
|
|
3606
|
+
Examples:
|
|
3607
|
+
npx @hua-labs/tap status
|
|
3608
|
+
`.trim();
|
|
3454
3609
|
function resolveStatus(inst, stateDir) {
|
|
3455
3610
|
if (!inst.installed) return "not installed";
|
|
3456
3611
|
switch (inst.bridgeMode) {
|
|
@@ -3477,7 +3632,18 @@ function instanceStatusLine(inst, status) {
|
|
|
3477
3632
|
const warns = inst.warnings.length > 0 ? ` [${inst.warnings.length} warning(s)]` : "";
|
|
3478
3633
|
return `${inst.instanceId.padEnd(20)} ${inst.runtime.padEnd(8)} ${status.padEnd(14)} ${mode.padEnd(14)}${bridgeInfo}${portStr}${restart}${warns}`;
|
|
3479
3634
|
}
|
|
3480
|
-
async function statusCommand(
|
|
3635
|
+
async function statusCommand(args) {
|
|
3636
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3637
|
+
log(STATUS_HELP);
|
|
3638
|
+
return {
|
|
3639
|
+
ok: true,
|
|
3640
|
+
command: "status",
|
|
3641
|
+
code: "TAP_NO_OP",
|
|
3642
|
+
message: STATUS_HELP,
|
|
3643
|
+
warnings: [],
|
|
3644
|
+
data: {}
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3481
3647
|
const repoRoot = findRepoRoot();
|
|
3482
3648
|
const state = loadState(repoRoot);
|
|
3483
3649
|
if (!state) {
|
|
@@ -3706,7 +3872,32 @@ function cleanEmptyParents(obj, keyPath) {
|
|
|
3706
3872
|
}
|
|
3707
3873
|
|
|
3708
3874
|
// src/commands/remove.ts
|
|
3875
|
+
var REMOVE_HELP = `
|
|
3876
|
+
Usage:
|
|
3877
|
+
tap-comms remove <instance>
|
|
3878
|
+
|
|
3879
|
+
Description:
|
|
3880
|
+
Remove a registered instance, stop its bridge, and rollback config changes.
|
|
3881
|
+
|
|
3882
|
+
Arguments:
|
|
3883
|
+
<instance> Instance ID or runtime name (e.g. claude, codex-reviewer)
|
|
3884
|
+
|
|
3885
|
+
Examples:
|
|
3886
|
+
npx @hua-labs/tap remove claude
|
|
3887
|
+
npx @hua-labs/tap remove codex-reviewer
|
|
3888
|
+
`.trim();
|
|
3709
3889
|
async function removeCommand(args) {
|
|
3890
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3891
|
+
log(REMOVE_HELP);
|
|
3892
|
+
return {
|
|
3893
|
+
ok: true,
|
|
3894
|
+
command: "remove",
|
|
3895
|
+
code: "TAP_NO_OP",
|
|
3896
|
+
message: REMOVE_HELP,
|
|
3897
|
+
warnings: [],
|
|
3898
|
+
data: {}
|
|
3899
|
+
};
|
|
3900
|
+
}
|
|
3710
3901
|
const identifier = args.find((a) => !a.startsWith("-"));
|
|
3711
3902
|
if (!identifier) {
|
|
3712
3903
|
return {
|
|
@@ -3742,8 +3933,8 @@ async function removeCommand(args) {
|
|
|
3742
3933
|
};
|
|
3743
3934
|
}
|
|
3744
3935
|
const instanceId = resolved.instanceId;
|
|
3745
|
-
const
|
|
3746
|
-
if (!
|
|
3936
|
+
const instance2 = state.instances[instanceId];
|
|
3937
|
+
if (!instance2?.installed) {
|
|
3747
3938
|
return {
|
|
3748
3939
|
ok: true,
|
|
3749
3940
|
command: "remove",
|
|
@@ -3755,7 +3946,7 @@ async function removeCommand(args) {
|
|
|
3755
3946
|
};
|
|
3756
3947
|
}
|
|
3757
3948
|
logHeader(`@hua-labs/tap remove ${instanceId}`);
|
|
3758
|
-
if (
|
|
3949
|
+
if (instance2.bridge) {
|
|
3759
3950
|
const ctx = createAdapterContext(state.commsDir, repoRoot);
|
|
3760
3951
|
const stopped = await stopBridge({
|
|
3761
3952
|
instanceId,
|
|
@@ -3768,7 +3959,7 @@ async function removeCommand(args) {
|
|
|
3768
3959
|
log(`No running bridge for ${instanceId}`);
|
|
3769
3960
|
}
|
|
3770
3961
|
}
|
|
3771
|
-
const result = await rollbackRuntime(instanceId,
|
|
3962
|
+
const result = await rollbackRuntime(instanceId, instance2);
|
|
3772
3963
|
if (result.success) {
|
|
3773
3964
|
logSuccess(`Rolled back ${result.restoredCount} artifact(s)`);
|
|
3774
3965
|
for (const f of result.restoredFiles) logSuccess(`Restored: ${f}`);
|
|
@@ -3780,7 +3971,7 @@ async function removeCommand(args) {
|
|
|
3780
3971
|
ok: true,
|
|
3781
3972
|
command: "remove",
|
|
3782
3973
|
instanceId,
|
|
3783
|
-
runtime:
|
|
3974
|
+
runtime: instance2.runtime,
|
|
3784
3975
|
code: "TAP_REMOVE_OK",
|
|
3785
3976
|
message: `${instanceId} removed successfully`,
|
|
3786
3977
|
warnings: [],
|
|
@@ -3795,7 +3986,7 @@ async function removeCommand(args) {
|
|
|
3795
3986
|
ok: false,
|
|
3796
3987
|
command: "remove",
|
|
3797
3988
|
instanceId,
|
|
3798
|
-
runtime:
|
|
3989
|
+
runtime: instance2.runtime,
|
|
3799
3990
|
code: "TAP_ROLLBACK_FAILED",
|
|
3800
3991
|
message: "Rollback had errors. State preserved for retry.",
|
|
3801
3992
|
warnings: result.errors,
|
|
@@ -3824,7 +4015,7 @@ Subcommands:
|
|
|
3824
4015
|
|
|
3825
4016
|
Options:
|
|
3826
4017
|
--agent-name <name> Agent identity for bridge (or set TAP_AGENT_NAME env)
|
|
3827
|
-
|
|
4018
|
+
Overrides the stored name from 'tap add' when needed
|
|
3828
4019
|
--all Start all registered app-server instances
|
|
3829
4020
|
--busy-mode <steer|wait> How to handle active turns (default: steer)
|
|
3830
4021
|
--poll-seconds <n> Inbox poll interval (default: 5)
|
|
@@ -3860,11 +4051,11 @@ function redactProtectedUrl(url) {
|
|
|
3860
4051
|
try {
|
|
3861
4052
|
const parsed = new URL(url);
|
|
3862
4053
|
if (parsed.searchParams.has("tap_token")) {
|
|
3863
|
-
parsed.searchParams.
|
|
4054
|
+
parsed.searchParams.delete("tap_token");
|
|
3864
4055
|
}
|
|
3865
4056
|
return parsed.toString().replace(/\/$/, "");
|
|
3866
4057
|
} catch {
|
|
3867
|
-
return url.replace(/tap_token=[^&]+/g, "
|
|
4058
|
+
return url.replace(/[?&]tap_token=[^&]+/g, "");
|
|
3868
4059
|
}
|
|
3869
4060
|
}
|
|
3870
4061
|
function loadCurrentBridgeState(stateDir, instanceId, fallback) {
|
|
@@ -3947,37 +4138,37 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
3947
4138
|
};
|
|
3948
4139
|
}
|
|
3949
4140
|
const instanceId = resolved.instanceId;
|
|
3950
|
-
let
|
|
3951
|
-
if (!
|
|
4141
|
+
let instance2 = state.instances[instanceId];
|
|
4142
|
+
if (!instance2?.installed) {
|
|
3952
4143
|
return {
|
|
3953
4144
|
ok: false,
|
|
3954
4145
|
command: "bridge",
|
|
3955
4146
|
instanceId,
|
|
3956
|
-
runtime:
|
|
4147
|
+
runtime: instance2?.runtime,
|
|
3957
4148
|
code: "TAP_INSTANCE_NOT_FOUND",
|
|
3958
|
-
message: `${instanceId} is not installed. Run: npx @hua-labs/tap add ${
|
|
4149
|
+
message: `${instanceId} is not installed. Run: npx @hua-labs/tap add ${instance2?.runtime ?? identifier}`,
|
|
3959
4150
|
warnings: [],
|
|
3960
4151
|
data: {}
|
|
3961
4152
|
};
|
|
3962
4153
|
}
|
|
3963
|
-
const adapter = getAdapter(
|
|
4154
|
+
const adapter = getAdapter(instance2.runtime);
|
|
3964
4155
|
const mode = adapter.bridgeMode();
|
|
3965
4156
|
if (mode !== "app-server") {
|
|
3966
4157
|
return {
|
|
3967
4158
|
ok: true,
|
|
3968
4159
|
command: "bridge",
|
|
3969
4160
|
instanceId,
|
|
3970
|
-
runtime:
|
|
4161
|
+
runtime: instance2.runtime,
|
|
3971
4162
|
code: "TAP_NO_OP",
|
|
3972
4163
|
message: `${instanceId} uses ${mode} mode \u2014 no bridge needed.`,
|
|
3973
4164
|
warnings: [],
|
|
3974
4165
|
data: { bridgeMode: mode }
|
|
3975
4166
|
};
|
|
3976
4167
|
}
|
|
3977
|
-
const resolvedAgentName = agentName ??
|
|
3978
|
-
if (agentName && agentName !==
|
|
3979
|
-
|
|
3980
|
-
const updatedState = updateInstanceState(state, instanceId,
|
|
4168
|
+
const resolvedAgentName = agentName ?? instance2.agentName ?? void 0;
|
|
4169
|
+
if (agentName && agentName !== instance2.agentName) {
|
|
4170
|
+
instance2 = { ...instance2, agentName };
|
|
4171
|
+
const updatedState = updateInstanceState(state, instanceId, instance2);
|
|
3981
4172
|
saveState(repoRoot, updatedState);
|
|
3982
4173
|
state = updatedState;
|
|
3983
4174
|
}
|
|
@@ -3988,7 +4179,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
3988
4179
|
ok: false,
|
|
3989
4180
|
command: "bridge",
|
|
3990
4181
|
instanceId,
|
|
3991
|
-
runtime:
|
|
4182
|
+
runtime: instance2.runtime,
|
|
3992
4183
|
code: "TAP_BRIDGE_SCRIPT_MISSING",
|
|
3993
4184
|
message: `Bridge script not found for ${instanceId}. Ensure the runtime is properly configured.`,
|
|
3994
4185
|
warnings: [],
|
|
@@ -3997,8 +4188,8 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
3997
4188
|
}
|
|
3998
4189
|
const { config: resolvedConfig } = resolveConfig({}, repoRoot);
|
|
3999
4190
|
const runtimeCommand = resolvedConfig.runtimeCommand;
|
|
4000
|
-
const manageAppServer =
|
|
4001
|
-
let effectivePort =
|
|
4191
|
+
const manageAppServer = instance2.runtime === "codex" && flags["no-server"] !== true;
|
|
4192
|
+
let effectivePort = instance2.port;
|
|
4002
4193
|
if (effectivePort == null && manageAppServer) {
|
|
4003
4194
|
effectivePort = await findNextAvailableAppServerPort(
|
|
4004
4195
|
state,
|
|
@@ -4006,8 +4197,8 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4006
4197
|
4501,
|
|
4007
4198
|
instanceId
|
|
4008
4199
|
);
|
|
4009
|
-
|
|
4010
|
-
const updatedState = updateInstanceState(state, instanceId,
|
|
4200
|
+
instance2 = { ...instance2, port: effectivePort };
|
|
4201
|
+
const updatedState = updateInstanceState(state, instanceId, instance2);
|
|
4011
4202
|
saveState(repoRoot, updatedState);
|
|
4012
4203
|
state = updatedState;
|
|
4013
4204
|
}
|
|
@@ -4023,19 +4214,19 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4023
4214
|
if (effectivePort != null) log(`Port: ${effectivePort}`);
|
|
4024
4215
|
if (resolvedAgentName) log(`Agent name: ${resolvedAgentName}`);
|
|
4025
4216
|
const noAuth = flags["no-auth"] === true;
|
|
4026
|
-
if (!manageAppServer &&
|
|
4217
|
+
if (!manageAppServer && instance2.runtime === "codex") {
|
|
4027
4218
|
log("Auto server: disabled (--no-server)");
|
|
4028
4219
|
}
|
|
4029
4220
|
if (noAuth && manageAppServer) {
|
|
4030
4221
|
log("Auth gateway: disabled (--no-auth)");
|
|
4031
4222
|
}
|
|
4032
|
-
const willBeHeadless = flags["headless"] === true ||
|
|
4223
|
+
const willBeHeadless = flags["headless"] === true || instance2.headless?.enabled;
|
|
4033
4224
|
if (willBeHeadless) {
|
|
4034
|
-
const role = (typeof flags["role"] === "string" ? flags["role"] : null) ??
|
|
4225
|
+
const role = (typeof flags["role"] === "string" ? flags["role"] : null) ?? instance2.headless?.role ?? "reviewer";
|
|
4035
4226
|
log(`Headless: ${role}`);
|
|
4036
4227
|
}
|
|
4037
4228
|
try {
|
|
4038
|
-
if (!manageAppServer &&
|
|
4229
|
+
if (!manageAppServer && instance2.runtime === "codex") {
|
|
4039
4230
|
log("Checking app-server health...");
|
|
4040
4231
|
const healthy = await checkAppServerHealth(appServerUrl);
|
|
4041
4232
|
if (healthy) {
|
|
@@ -4046,7 +4237,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4046
4237
|
ok: false,
|
|
4047
4238
|
command: "bridge",
|
|
4048
4239
|
instanceId,
|
|
4049
|
-
runtime:
|
|
4240
|
+
runtime: instance2.runtime,
|
|
4050
4241
|
code: "TAP_BRIDGE_START_FAILED",
|
|
4051
4242
|
message: `App server not reachable at ${appServerUrl}. Start it first: codex app-server --listen ${appServerUrl}`,
|
|
4052
4243
|
warnings: [],
|
|
@@ -4060,7 +4251,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4060
4251
|
ok: false,
|
|
4061
4252
|
command: "bridge",
|
|
4062
4253
|
instanceId,
|
|
4063
|
-
runtime:
|
|
4254
|
+
runtime: instance2.runtime,
|
|
4064
4255
|
code: "TAP_INVALID_ARGUMENT",
|
|
4065
4256
|
message: `Invalid --busy-mode: ${String(busyModeRaw)}. Must be "steer" or "wait".`,
|
|
4066
4257
|
warnings: [],
|
|
@@ -4068,9 +4259,38 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4068
4259
|
};
|
|
4069
4260
|
}
|
|
4070
4261
|
const busyMode = busyModeRaw;
|
|
4071
|
-
const
|
|
4072
|
-
const
|
|
4073
|
-
const
|
|
4262
|
+
const pollSecondsRaw = typeof flags["poll-seconds"] === "string" ? flags["poll-seconds"] : void 0;
|
|
4263
|
+
const reconnectSecondsRaw = typeof flags["reconnect-seconds"] === "string" ? flags["reconnect-seconds"] : void 0;
|
|
4264
|
+
const lookbackRaw = typeof flags["message-lookback-minutes"] === "string" ? flags["message-lookback-minutes"] : void 0;
|
|
4265
|
+
let pollSeconds;
|
|
4266
|
+
let reconnectSeconds;
|
|
4267
|
+
let messageLookbackMinutes;
|
|
4268
|
+
try {
|
|
4269
|
+
pollSeconds = parseIntFlag(pollSecondsRaw, "--poll-seconds", 1, 3600);
|
|
4270
|
+
reconnectSeconds = parseIntFlag(
|
|
4271
|
+
reconnectSecondsRaw,
|
|
4272
|
+
"--reconnect-seconds",
|
|
4273
|
+
1,
|
|
4274
|
+
3600
|
|
4275
|
+
);
|
|
4276
|
+
messageLookbackMinutes = parseIntFlag(
|
|
4277
|
+
lookbackRaw,
|
|
4278
|
+
"--message-lookback-minutes",
|
|
4279
|
+
1,
|
|
4280
|
+
10080
|
|
4281
|
+
);
|
|
4282
|
+
} catch (err) {
|
|
4283
|
+
return {
|
|
4284
|
+
ok: false,
|
|
4285
|
+
command: "bridge",
|
|
4286
|
+
instanceId,
|
|
4287
|
+
runtime: instance2.runtime,
|
|
4288
|
+
code: "TAP_INVALID_ARGUMENT",
|
|
4289
|
+
message: err instanceof Error ? err.message : String(err),
|
|
4290
|
+
warnings: [],
|
|
4291
|
+
data: {}
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4074
4294
|
const threadId = typeof flags["thread-id"] === "string" ? flags["thread-id"] : void 0;
|
|
4075
4295
|
const ephemeral = flags["ephemeral"] === true;
|
|
4076
4296
|
const processExistingMessages = flags["process-existing-messages"] === true;
|
|
@@ -4082,7 +4302,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4082
4302
|
ok: false,
|
|
4083
4303
|
command: "bridge",
|
|
4084
4304
|
instanceId,
|
|
4085
|
-
runtime:
|
|
4305
|
+
runtime: instance2.runtime,
|
|
4086
4306
|
code: "TAP_INVALID_ARGUMENT",
|
|
4087
4307
|
message: `Invalid --role: ${roleArg}. Must be: ${validRoles.join(", ")}`,
|
|
4088
4308
|
warnings: [],
|
|
@@ -4094,10 +4314,10 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4094
4314
|
role: roleArg ?? "reviewer",
|
|
4095
4315
|
maxRounds: 5,
|
|
4096
4316
|
qualitySeverityFloor: "high"
|
|
4097
|
-
} :
|
|
4317
|
+
} : instance2.headless;
|
|
4098
4318
|
const bridge = await startBridge({
|
|
4099
4319
|
instanceId,
|
|
4100
|
-
runtime:
|
|
4320
|
+
runtime: instance2.runtime,
|
|
4101
4321
|
stateDir: ctx.stateDir,
|
|
4102
4322
|
commsDir: ctx.commsDir,
|
|
4103
4323
|
bridgeScript,
|
|
@@ -4138,14 +4358,14 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4138
4358
|
log(`TUI connect: ${bridge.appServer.url}`);
|
|
4139
4359
|
}
|
|
4140
4360
|
}
|
|
4141
|
-
const updated = { ...
|
|
4361
|
+
const updated = { ...instance2, bridge, manageAppServer, noAuth };
|
|
4142
4362
|
const newState = updateInstanceState(state, instanceId, updated);
|
|
4143
4363
|
saveState(repoRoot, newState);
|
|
4144
4364
|
return {
|
|
4145
4365
|
ok: true,
|
|
4146
4366
|
command: "bridge",
|
|
4147
4367
|
instanceId,
|
|
4148
|
-
runtime:
|
|
4368
|
+
runtime: instance2.runtime,
|
|
4149
4369
|
code: "TAP_BRIDGE_START_OK",
|
|
4150
4370
|
message: `Bridge for ${instanceId} started (PID: ${bridge.pid})`,
|
|
4151
4371
|
warnings: [],
|
|
@@ -4158,7 +4378,7 @@ async function bridgeStart(identifier, agentName, flags = {}) {
|
|
|
4158
4378
|
ok: false,
|
|
4159
4379
|
command: "bridge",
|
|
4160
4380
|
instanceId,
|
|
4161
|
-
runtime:
|
|
4381
|
+
runtime: instance2.runtime,
|
|
4162
4382
|
code: "TAP_BRIDGE_START_FAILED",
|
|
4163
4383
|
message: msg,
|
|
4164
4384
|
warnings: [],
|
|
@@ -4260,11 +4480,11 @@ async function bridgeStopOne(identifier) {
|
|
|
4260
4480
|
}
|
|
4261
4481
|
const instanceId = resolved.instanceId;
|
|
4262
4482
|
const ctx = createAdapterContext(state.commsDir, repoRoot);
|
|
4263
|
-
const
|
|
4483
|
+
const instance2 = state.instances[instanceId];
|
|
4264
4484
|
const bridgeState = loadCurrentBridgeState(
|
|
4265
4485
|
ctx.stateDir,
|
|
4266
4486
|
instanceId,
|
|
4267
|
-
|
|
4487
|
+
instance2?.bridge
|
|
4268
4488
|
);
|
|
4269
4489
|
const appServer = bridgeState?.appServer ?? null;
|
|
4270
4490
|
logHeader(`@hua-labs/tap bridge stop ${instanceId}`);
|
|
@@ -4312,8 +4532,8 @@ async function bridgeStopOne(identifier) {
|
|
|
4312
4532
|
}
|
|
4313
4533
|
}
|
|
4314
4534
|
}
|
|
4315
|
-
if (
|
|
4316
|
-
const updated = { ...
|
|
4535
|
+
if (instance2) {
|
|
4536
|
+
const updated = { ...instance2, bridge: null };
|
|
4317
4537
|
const newState = updateInstanceState(state, instanceId, updated);
|
|
4318
4538
|
saveState(repoRoot, newState);
|
|
4319
4539
|
}
|
|
@@ -4385,9 +4605,9 @@ async function bridgeStopAll() {
|
|
|
4385
4605
|
logSuccess(`Stopped bridge for ${instanceId}`);
|
|
4386
4606
|
stopped.push(instanceId);
|
|
4387
4607
|
}
|
|
4388
|
-
const
|
|
4389
|
-
if (
|
|
4390
|
-
state.instances[instanceId] = { ...
|
|
4608
|
+
const instance2 = state.instances[instanceId];
|
|
4609
|
+
if (instance2?.bridge) {
|
|
4610
|
+
state.instances[instanceId] = { ...instance2, bridge: null };
|
|
4391
4611
|
stateChanged = true;
|
|
4392
4612
|
}
|
|
4393
4613
|
}
|
|
@@ -4683,8 +4903,22 @@ async function bridgeRestart(identifier, flags) {
|
|
|
4683
4903
|
};
|
|
4684
4904
|
}
|
|
4685
4905
|
const { config: resolvedConfig } = resolveConfig({}, repoRoot);
|
|
4686
|
-
const drainStr = typeof flags["drain-timeout"] === "string" ? flags["drain-timeout"] :
|
|
4687
|
-
|
|
4906
|
+
const drainStr = typeof flags["drain-timeout"] === "string" ? flags["drain-timeout"] : void 0;
|
|
4907
|
+
let drainTimeout;
|
|
4908
|
+
try {
|
|
4909
|
+
drainTimeout = parseIntFlag(drainStr, "--drain-timeout", 1, 300) ?? 30;
|
|
4910
|
+
} catch (err) {
|
|
4911
|
+
return {
|
|
4912
|
+
ok: false,
|
|
4913
|
+
command: "bridge",
|
|
4914
|
+
instanceId,
|
|
4915
|
+
runtime: instance.runtime,
|
|
4916
|
+
code: "TAP_INVALID_ARGUMENT",
|
|
4917
|
+
message: err instanceof Error ? err.message : String(err),
|
|
4918
|
+
warnings: [],
|
|
4919
|
+
data: {}
|
|
4920
|
+
};
|
|
4921
|
+
}
|
|
4688
4922
|
logHeader(`@hua-labs/tap bridge restart ${instanceId}`);
|
|
4689
4923
|
log(`Drain timeout: ${drainTimeout}s`);
|
|
4690
4924
|
try {
|
|
@@ -4831,7 +5065,7 @@ async function bridgeCommand(args) {
|
|
|
4831
5065
|
// src/engine/dashboard.ts
|
|
4832
5066
|
import * as fs15 from "fs";
|
|
4833
5067
|
import * as path15 from "path";
|
|
4834
|
-
import { execSync as
|
|
5068
|
+
import { execSync as execSync4 } from "child_process";
|
|
4835
5069
|
function collectAgents(commsDir) {
|
|
4836
5070
|
const heartbeatsPath = path15.join(commsDir, "heartbeats.json");
|
|
4837
5071
|
if (!fs15.existsSync(heartbeatsPath)) return [];
|
|
@@ -4910,7 +5144,7 @@ function collectBridges(repoRoot) {
|
|
|
4910
5144
|
}
|
|
4911
5145
|
function collectPRs() {
|
|
4912
5146
|
try {
|
|
4913
|
-
const output =
|
|
5147
|
+
const output = execSync4(
|
|
4914
5148
|
"gh pr list --state all --limit 10 --json number,title,author,state,url",
|
|
4915
5149
|
{ encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
|
|
4916
5150
|
);
|
|
@@ -5084,7 +5318,34 @@ async function downCommand(args) {
|
|
|
5084
5318
|
// src/commands/serve.ts
|
|
5085
5319
|
import * as path16 from "path";
|
|
5086
5320
|
import { spawn as spawn2 } from "child_process";
|
|
5321
|
+
var SERVE_HELP = `
|
|
5322
|
+
Usage:
|
|
5323
|
+
tap-comms serve [options]
|
|
5324
|
+
|
|
5325
|
+
Description:
|
|
5326
|
+
Start the tap-comms MCP server over stdio. This command takes over the
|
|
5327
|
+
process \u2014 it is intended to be launched by an MCP host (e.g. Claude Code).
|
|
5328
|
+
|
|
5329
|
+
Options:
|
|
5330
|
+
--comms-dir <path> Override comms directory (also reads TAP_COMMS_DIR env)
|
|
5331
|
+
--help, -h Show help
|
|
5332
|
+
|
|
5333
|
+
Examples:
|
|
5334
|
+
npx @hua-labs/tap serve
|
|
5335
|
+
npx @hua-labs/tap serve --comms-dir /shared/comms
|
|
5336
|
+
`.trim();
|
|
5087
5337
|
async function serveCommand(args) {
|
|
5338
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
5339
|
+
log(SERVE_HELP);
|
|
5340
|
+
return {
|
|
5341
|
+
ok: true,
|
|
5342
|
+
command: "serve",
|
|
5343
|
+
code: "TAP_NO_OP",
|
|
5344
|
+
message: SERVE_HELP,
|
|
5345
|
+
warnings: [],
|
|
5346
|
+
data: {}
|
|
5347
|
+
};
|
|
5348
|
+
}
|
|
5088
5349
|
const repoRoot = findRepoRoot();
|
|
5089
5350
|
let commsDir;
|
|
5090
5351
|
const commsDirIdx = args.indexOf("--comms-dir");
|
|
@@ -5159,7 +5420,7 @@ async function serveCommand(args) {
|
|
|
5159
5420
|
// src/commands/init-worktree.ts
|
|
5160
5421
|
import * as fs16 from "fs";
|
|
5161
5422
|
import * as path17 from "path";
|
|
5162
|
-
import { execSync as
|
|
5423
|
+
import { execSync as execSync5 } from "child_process";
|
|
5163
5424
|
var INIT_WORKTREE_HELP = `
|
|
5164
5425
|
Usage:
|
|
5165
5426
|
tap-comms init-worktree [options]
|
|
@@ -5183,7 +5444,7 @@ function warn(warnings, message) {
|
|
|
5183
5444
|
}
|
|
5184
5445
|
function run(cmd, opts) {
|
|
5185
5446
|
try {
|
|
5186
|
-
return
|
|
5447
|
+
return execSync5(cmd, {
|
|
5187
5448
|
cwd: opts?.cwd,
|
|
5188
5449
|
encoding: "utf-8",
|
|
5189
5450
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -5200,7 +5461,7 @@ function toAbsolute(p) {
|
|
|
5200
5461
|
}
|
|
5201
5462
|
function probeBun(candidate) {
|
|
5202
5463
|
try {
|
|
5203
|
-
const out =
|
|
5464
|
+
const out = execSync5(`"${candidate}" --version`, {
|
|
5204
5465
|
encoding: "utf-8",
|
|
5205
5466
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5206
5467
|
timeout: 5e3
|
|
@@ -5214,7 +5475,7 @@ function findBun() {
|
|
|
5214
5475
|
const candidates = process.platform === "win32" ? ["bun.exe", "bun"] : ["bun"];
|
|
5215
5476
|
for (const name of candidates) {
|
|
5216
5477
|
try {
|
|
5217
|
-
const out =
|
|
5478
|
+
const out = execSync5(
|
|
5218
5479
|
process.platform === "win32" ? `where ${name}` : `which ${name}`,
|
|
5219
5480
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
|
|
5220
5481
|
).trim();
|
|
@@ -5445,7 +5706,7 @@ async function initWorktreeCommand(args) {
|
|
|
5445
5706
|
ok: true,
|
|
5446
5707
|
command: "init-worktree",
|
|
5447
5708
|
code: "TAP_NO_OP",
|
|
5448
|
-
message:
|
|
5709
|
+
message: INIT_WORKTREE_HELP,
|
|
5449
5710
|
warnings: [],
|
|
5450
5711
|
data: {}
|
|
5451
5712
|
};
|
|
@@ -5607,8 +5868,38 @@ function renderSnapshot(snapshot) {
|
|
|
5607
5868
|
}
|
|
5608
5869
|
}
|
|
5609
5870
|
}
|
|
5871
|
+
var DASHBOARD_HELP = `
|
|
5872
|
+
Usage:
|
|
5873
|
+
tap-comms dashboard [options]
|
|
5874
|
+
|
|
5875
|
+
Description:
|
|
5876
|
+
Display a unified ops dashboard: agents, bridges, PRs, and warnings.
|
|
5877
|
+
|
|
5878
|
+
Options:
|
|
5879
|
+
--json Output snapshot as JSON
|
|
5880
|
+
--watch Refresh dashboard on an interval
|
|
5881
|
+
--interval <seconds> Refresh interval in seconds (default: 5, min: 2)
|
|
5882
|
+
--comms-dir <path> Override comms directory
|
|
5883
|
+
--help, -h Show help
|
|
5884
|
+
|
|
5885
|
+
Examples:
|
|
5886
|
+
npx @hua-labs/tap dashboard
|
|
5887
|
+
npx @hua-labs/tap dashboard --watch --interval 10
|
|
5888
|
+
npx @hua-labs/tap dashboard --json
|
|
5889
|
+
`.trim();
|
|
5610
5890
|
async function dashboardCommand(args) {
|
|
5611
5891
|
const { flags } = parseArgs(args);
|
|
5892
|
+
if (flags["help"] === true || flags["h"] === true) {
|
|
5893
|
+
log(DASHBOARD_HELP);
|
|
5894
|
+
return {
|
|
5895
|
+
ok: true,
|
|
5896
|
+
command: "dashboard",
|
|
5897
|
+
code: "TAP_NO_OP",
|
|
5898
|
+
message: DASHBOARD_HELP,
|
|
5899
|
+
warnings: [],
|
|
5900
|
+
data: {}
|
|
5901
|
+
};
|
|
5902
|
+
}
|
|
5612
5903
|
const jsonMode = flags["json"] === true;
|
|
5613
5904
|
const watchMode = flags["watch"] === true;
|
|
5614
5905
|
const intervalStr = typeof flags["interval"] === "string" ? flags["interval"] : "5";
|
|
@@ -5671,7 +5962,7 @@ import {
|
|
|
5671
5962
|
statSync as statSync2,
|
|
5672
5963
|
unlinkSync as unlinkSync3
|
|
5673
5964
|
} from "fs";
|
|
5674
|
-
import { execSync as
|
|
5965
|
+
import { execSync as execSync6 } from "child_process";
|
|
5675
5966
|
import { join as join17 } from "path";
|
|
5676
5967
|
var PASS = "pass";
|
|
5677
5968
|
var WARN = "warn";
|
|
@@ -5938,7 +6229,7 @@ function checkMcpServer(repoRoot) {
|
|
|
5938
6229
|
let cmdAvailable = existsSync16(cmd);
|
|
5939
6230
|
if (!cmdAvailable) {
|
|
5940
6231
|
try {
|
|
5941
|
-
|
|
6232
|
+
execSync6(`"${cmd}" --version`, {
|
|
5942
6233
|
stdio: "pipe",
|
|
5943
6234
|
timeout: 5e3
|
|
5944
6235
|
});
|
|
@@ -6115,7 +6406,35 @@ function renderCheck(check, fixMode) {
|
|
|
6115
6406
|
const msg = check.message ? ` \u2014 ${check.message}${fixable}` : "";
|
|
6116
6407
|
return ` ${icon} ${check.name}${msg}`;
|
|
6117
6408
|
}
|
|
6409
|
+
var DOCTOR_HELP = `
|
|
6410
|
+
Usage:
|
|
6411
|
+
tap-comms doctor [options]
|
|
6412
|
+
|
|
6413
|
+
Description:
|
|
6414
|
+
Diagnose tap infrastructure health: comms directory, instances, bridges,
|
|
6415
|
+
message lifecycle, and MCP server configuration.
|
|
6416
|
+
|
|
6417
|
+
Options:
|
|
6418
|
+
--fix Auto-repair detected issues where possible
|
|
6419
|
+
--comms-dir <path> Override comms directory
|
|
6420
|
+
--help, -h Show help
|
|
6421
|
+
|
|
6422
|
+
Examples:
|
|
6423
|
+
npx @hua-labs/tap doctor
|
|
6424
|
+
npx @hua-labs/tap doctor --fix
|
|
6425
|
+
`.trim();
|
|
6118
6426
|
async function doctorCommand(args) {
|
|
6427
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
6428
|
+
log(DOCTOR_HELP);
|
|
6429
|
+
return {
|
|
6430
|
+
ok: true,
|
|
6431
|
+
command: "doctor",
|
|
6432
|
+
code: "TAP_NO_OP",
|
|
6433
|
+
message: DOCTOR_HELP,
|
|
6434
|
+
warnings: [],
|
|
6435
|
+
data: {}
|
|
6436
|
+
};
|
|
6437
|
+
}
|
|
6119
6438
|
const repoRoot = findRepoRoot();
|
|
6120
6439
|
const overrides = {};
|
|
6121
6440
|
let fixMode = false;
|
|
@@ -6228,7 +6547,7 @@ async function doctorCommand(args) {
|
|
|
6228
6547
|
}
|
|
6229
6548
|
|
|
6230
6549
|
// src/commands/comms.ts
|
|
6231
|
-
import { execSync as
|
|
6550
|
+
import { execSync as execSync7, spawnSync as spawnSync4 } from "child_process";
|
|
6232
6551
|
import * as fs17 from "fs";
|
|
6233
6552
|
import * as path18 from "path";
|
|
6234
6553
|
var COMMS_HELP = `
|
|
@@ -6260,7 +6579,7 @@ function commsPull(commsDir) {
|
|
|
6260
6579
|
};
|
|
6261
6580
|
}
|
|
6262
6581
|
try {
|
|
6263
|
-
const output =
|
|
6582
|
+
const output = execSync7("git pull --rebase", {
|
|
6264
6583
|
cwd: commsDir,
|
|
6265
6584
|
encoding: "utf-8",
|
|
6266
6585
|
stdio: "pipe"
|
|
@@ -6302,8 +6621,8 @@ function commsPush(commsDir) {
|
|
|
6302
6621
|
};
|
|
6303
6622
|
}
|
|
6304
6623
|
try {
|
|
6305
|
-
|
|
6306
|
-
const status =
|
|
6624
|
+
execSync7("git add -A", { cwd: commsDir, stdio: "pipe" });
|
|
6625
|
+
const status = execSync7("git status --porcelain", {
|
|
6307
6626
|
cwd: commsDir,
|
|
6308
6627
|
encoding: "utf-8",
|
|
6309
6628
|
stdio: "pipe"
|
|
@@ -6320,11 +6639,23 @@ function commsPush(commsDir) {
|
|
|
6320
6639
|
};
|
|
6321
6640
|
}
|
|
6322
6641
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6642
|
+
const commitResult = spawnSync4(
|
|
6643
|
+
"git",
|
|
6644
|
+
["commit", "-m", `chore(comms): sync ${timestamp}`],
|
|
6645
|
+
{ cwd: commsDir, stdio: "pipe", encoding: "utf-8" }
|
|
6646
|
+
);
|
|
6647
|
+
if (commitResult.status !== 0) {
|
|
6648
|
+
const msg = commitResult.stderr || `git commit exited with code ${commitResult.status}`;
|
|
6649
|
+
return {
|
|
6650
|
+
ok: false,
|
|
6651
|
+
command: "comms",
|
|
6652
|
+
code: "TAP_COMMS_PUSH_FAILED",
|
|
6653
|
+
message: `Commit failed: ${msg}`,
|
|
6654
|
+
warnings: [],
|
|
6655
|
+
data: { commsDir }
|
|
6656
|
+
};
|
|
6657
|
+
}
|
|
6658
|
+
execSync7("git push", { cwd: commsDir, stdio: "pipe" });
|
|
6328
6659
|
logSuccess("Comms push complete");
|
|
6329
6660
|
return {
|
|
6330
6661
|
ok: true,
|
|
@@ -6390,7 +6721,12 @@ function emitResult(result, jsonMode) {
|
|
|
6390
6721
|
} else {
|
|
6391
6722
|
logError(result.message);
|
|
6392
6723
|
}
|
|
6724
|
+
const emittedWarnings = /* @__PURE__ */ new Set();
|
|
6393
6725
|
for (const w of result.warnings) {
|
|
6726
|
+
if (emittedWarnings.has(w) || wasWarningLogged(w)) {
|
|
6727
|
+
continue;
|
|
6728
|
+
}
|
|
6729
|
+
emittedWarnings.add(w);
|
|
6394
6730
|
logWarn(w);
|
|
6395
6731
|
}
|
|
6396
6732
|
}
|
|
@@ -6459,6 +6795,7 @@ function normalizeCommandName(command) {
|
|
|
6459
6795
|
async function main() {
|
|
6460
6796
|
const rawArgs = process.argv.slice(2);
|
|
6461
6797
|
const { jsonMode, cleanArgs } = extractJsonFlag(rawArgs);
|
|
6798
|
+
resetLoggedWarnings();
|
|
6462
6799
|
setJsonMode(jsonMode);
|
|
6463
6800
|
const command = cleanArgs[0];
|
|
6464
6801
|
if (!command || command === "--help" || command === "-h") {
|
|
@@ -6516,7 +6853,7 @@ async function main() {
|
|
|
6516
6853
|
break;
|
|
6517
6854
|
case "serve": {
|
|
6518
6855
|
const serveResult = await serveCommand(commandArgs);
|
|
6519
|
-
if (!serveResult.ok) {
|
|
6856
|
+
if (!serveResult.ok || serveResult.code === "TAP_NO_OP") {
|
|
6520
6857
|
emitResult(serveResult, jsonMode);
|
|
6521
6858
|
}
|
|
6522
6859
|
process.exit(exitCode(serveResult));
|