@hua-labs/tap 0.1.1 → 0.2.0
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 +123 -152
- package/dist/bridges/codex-app-server-auth-gateway.d.mts +8 -0
- package/dist/bridges/codex-app-server-auth-gateway.mjs +183 -0
- package/dist/bridges/codex-app-server-auth-gateway.mjs.map +1 -0
- package/dist/bridges/codex-bridge-runner.d.mts +10 -1
- package/dist/bridges/codex-bridge-runner.mjs +233 -121
- package/dist/bridges/codex-bridge-runner.mjs.map +1 -1
- package/dist/cli.mjs +2728 -991
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +139 -5
- package/dist/index.mjs +528 -69
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-server.d.mts +2 -0
- package/dist/mcp-server.mjs +22174 -0
- package/dist/mcp-server.mjs.map +1 -0
- package/package.json +7 -4
|
@@ -9,7 +9,7 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/engine/termination.ts
|
|
12
|
-
import * as
|
|
12
|
+
import * as fs5 from "fs";
|
|
13
13
|
import * as crypto from "crypto";
|
|
14
14
|
function isAtOrAbove(severity, floor) {
|
|
15
15
|
return SEVERITY_RANK[severity] >= SEVERITY_RANK[floor];
|
|
@@ -20,7 +20,7 @@ function computeFindingHash(findings) {
|
|
|
20
20
|
return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
21
21
|
}
|
|
22
22
|
function evalManualStop(ctx) {
|
|
23
|
-
if (
|
|
23
|
+
if (fs5.existsSync(ctx.stopSignalPath)) {
|
|
24
24
|
return {
|
|
25
25
|
verdict: "stop",
|
|
26
26
|
reason: `Manual stop signal found at ${ctx.stopSignalPath}`,
|
|
@@ -145,11 +145,11 @@ var init_termination = __esm({
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
// src/engine/review.ts
|
|
148
|
-
import * as
|
|
149
|
-
import * as
|
|
148
|
+
import * as fs6 from "fs";
|
|
149
|
+
import * as path5 from "path";
|
|
150
150
|
import * as crypto2 from "crypto";
|
|
151
151
|
function parseInboxFilename(filename) {
|
|
152
|
-
const base =
|
|
152
|
+
const base = path5.basename(filename, ".md");
|
|
153
153
|
const match = base.match(/^(\d{8})-([^-]+)-([^-]+)-(.+)$/);
|
|
154
154
|
if (!match) return null;
|
|
155
155
|
return {
|
|
@@ -201,7 +201,7 @@ function buildReviewPrompt(request, agentName, round) {
|
|
|
201
201
|
`4. Write structured findings`,
|
|
202
202
|
``,
|
|
203
203
|
`## Output`,
|
|
204
|
-
`Write review to: ${
|
|
204
|
+
`Write review to: ${path5.join("reviews", request.generation, `review-PR${request.prNumber}-${agentName}.md`)}`,
|
|
205
205
|
``,
|
|
206
206
|
`### Review File Format`,
|
|
207
207
|
`\`\`\`markdown`,
|
|
@@ -287,8 +287,8 @@ function extractFindings(content) {
|
|
|
287
287
|
return findings;
|
|
288
288
|
}
|
|
289
289
|
function parseReviewOutput(reviewFilePath2, round) {
|
|
290
|
-
if (!
|
|
291
|
-
const content =
|
|
290
|
+
if (!fs6.existsSync(reviewFilePath2)) return null;
|
|
291
|
+
const content = fs6.readFileSync(reviewFilePath2, "utf-8");
|
|
292
292
|
const findings = extractFindings(content);
|
|
293
293
|
const suggestedDiffLines = extractSuggestedDiffLines(content);
|
|
294
294
|
return {
|
|
@@ -301,7 +301,7 @@ function parseReviewOutput(reviewFilePath2, round) {
|
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
303
|
function reviewFilePath(commsDir, generation, prNumber, agentName) {
|
|
304
|
-
return
|
|
304
|
+
return path5.join(
|
|
305
305
|
commsDir,
|
|
306
306
|
"reviews",
|
|
307
307
|
generation,
|
|
@@ -315,42 +315,42 @@ function isStaleReviewRequest(request, commsDir, agentName) {
|
|
|
315
315
|
request.prNumber,
|
|
316
316
|
agentName
|
|
317
317
|
);
|
|
318
|
-
if (
|
|
319
|
-
const reviewStat =
|
|
320
|
-
const requestStat =
|
|
318
|
+
if (fs6.existsSync(revPath) && fs6.existsSync(request.sourcePath)) {
|
|
319
|
+
const reviewStat = fs6.statSync(revPath);
|
|
320
|
+
const requestStat = fs6.statSync(request.sourcePath);
|
|
321
321
|
if (reviewStat.mtimeMs > requestStat.mtimeMs) return true;
|
|
322
322
|
}
|
|
323
323
|
return false;
|
|
324
324
|
}
|
|
325
325
|
function computeRequestMarkerId(filePath) {
|
|
326
|
-
const stat =
|
|
326
|
+
const stat = fs6.statSync(filePath);
|
|
327
327
|
const input = `${filePath}|${stat.mtimeMs}`;
|
|
328
328
|
return crypto2.createHash("sha1").update(input).digest("hex");
|
|
329
329
|
}
|
|
330
330
|
function isAlreadyProcessed(stateDir, filePath) {
|
|
331
331
|
const markerId = computeRequestMarkerId(filePath);
|
|
332
|
-
return
|
|
332
|
+
return fs6.existsSync(path5.join(stateDir, "processed", `${markerId}.done`));
|
|
333
333
|
}
|
|
334
334
|
function unmarkProcessed(stateDir, request) {
|
|
335
335
|
const markerId = computeRequestMarkerId(request.sourcePath);
|
|
336
|
-
const markerPath =
|
|
337
|
-
if (
|
|
338
|
-
|
|
336
|
+
const markerPath = path5.join(stateDir, "processed", `${markerId}.done`);
|
|
337
|
+
if (fs6.existsSync(markerPath)) {
|
|
338
|
+
fs6.unlinkSync(markerPath);
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
function markAsProcessed(stateDir, request) {
|
|
342
342
|
const markerId = computeRequestMarkerId(request.sourcePath);
|
|
343
|
-
const markerDir =
|
|
344
|
-
|
|
345
|
-
const markerPath =
|
|
343
|
+
const markerDir = path5.join(stateDir, "processed");
|
|
344
|
+
fs6.mkdirSync(markerDir, { recursive: true });
|
|
345
|
+
const markerPath = path5.join(markerDir, `${markerId}.done`);
|
|
346
346
|
const payload = {
|
|
347
347
|
prNumber: request.prNumber,
|
|
348
348
|
sourcePath: request.sourcePath,
|
|
349
349
|
processedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
350
350
|
};
|
|
351
351
|
const tmp = `${markerPath}.tmp.${process.pid}`;
|
|
352
|
-
|
|
353
|
-
|
|
352
|
+
fs6.writeFileSync(tmp, JSON.stringify(payload, null, 2), "utf-8");
|
|
353
|
+
fs6.renameSync(tmp, markerPath);
|
|
354
354
|
}
|
|
355
355
|
function writeReviewReceipt(commsDir, request, agentName) {
|
|
356
356
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
|
|
@@ -360,14 +360,14 @@ function writeReviewReceipt(commsDir, request, agentName) {
|
|
|
360
360
|
``,
|
|
361
361
|
`- PR #${request.prNumber} review request received.`,
|
|
362
362
|
`- headless reviewer processing.`,
|
|
363
|
-
`- request: ${
|
|
363
|
+
`- request: ${path5.basename(request.sourcePath)}`
|
|
364
364
|
].join("\n");
|
|
365
|
-
const inboxDir =
|
|
366
|
-
|
|
367
|
-
const inboxPath =
|
|
365
|
+
const inboxDir = path5.join(commsDir, "inbox");
|
|
366
|
+
fs6.mkdirSync(inboxDir, { recursive: true });
|
|
367
|
+
const inboxPath = path5.join(inboxDir, filename);
|
|
368
368
|
const tmp = `${inboxPath}.tmp.${process.pid}`;
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
fs6.writeFileSync(tmp, content, "utf-8");
|
|
370
|
+
fs6.renameSync(tmp, inboxPath);
|
|
371
371
|
return inboxPath;
|
|
372
372
|
}
|
|
373
373
|
function isHeadlessReviewer() {
|
|
@@ -382,13 +382,13 @@ function getHeadlessEnvConfig() {
|
|
|
382
382
|
};
|
|
383
383
|
}
|
|
384
384
|
function scanInboxForReviews(commsDir, stateDir, generation, agentName) {
|
|
385
|
-
const inboxDir =
|
|
386
|
-
if (!
|
|
387
|
-
const files =
|
|
385
|
+
const inboxDir = path5.join(commsDir, "inbox");
|
|
386
|
+
if (!fs6.existsSync(inboxDir)) return [];
|
|
387
|
+
const files = fs6.readdirSync(inboxDir).filter((f) => f.endsWith(".md"));
|
|
388
388
|
const requests = [];
|
|
389
389
|
for (const file of files) {
|
|
390
|
-
const filePath =
|
|
391
|
-
const content =
|
|
390
|
+
const filePath = path5.join(inboxDir, file);
|
|
391
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
392
392
|
const request = detectReviewRequest(filePath, content, generation);
|
|
393
393
|
if (!request) continue;
|
|
394
394
|
const to = request.recipient.toLowerCase();
|
|
@@ -437,8 +437,8 @@ var headless_loop_exports = {};
|
|
|
437
437
|
__export(headless_loop_exports, {
|
|
438
438
|
createHeadlessLoop: () => createHeadlessLoop
|
|
439
439
|
});
|
|
440
|
-
import * as
|
|
441
|
-
import * as
|
|
440
|
+
import * as fs7 from "fs";
|
|
441
|
+
import * as path6 from "path";
|
|
442
442
|
function createHeadlessLoop(options) {
|
|
443
443
|
const envConfig = getHeadlessEnvConfig();
|
|
444
444
|
const terminationConfig = {
|
|
@@ -481,12 +481,12 @@ function createHeadlessLoop(options) {
|
|
|
481
481
|
const prompt = buildReviewPrompt(request, options.agentName, 1);
|
|
482
482
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
|
|
483
483
|
const dispatchFilename = `${date}-headless-${options.agentName}-review-PR${request.prNumber}.md`;
|
|
484
|
-
const inboxDir =
|
|
485
|
-
|
|
486
|
-
const dispatchFile =
|
|
484
|
+
const inboxDir = path6.join(options.commsDir, "inbox");
|
|
485
|
+
fs7.mkdirSync(inboxDir, { recursive: true });
|
|
486
|
+
const dispatchFile = path6.join(inboxDir, dispatchFilename);
|
|
487
487
|
const tmp = `${dispatchFile}.tmp.${process.pid}`;
|
|
488
|
-
|
|
489
|
-
|
|
488
|
+
fs7.writeFileSync(tmp, prompt, "utf-8");
|
|
489
|
+
fs7.renameSync(tmp, dispatchFile);
|
|
490
490
|
state.activeSession = {
|
|
491
491
|
request,
|
|
492
492
|
agentName: options.agentName,
|
|
@@ -512,8 +512,8 @@ function createHeadlessLoop(options) {
|
|
|
512
512
|
if (!state.activeSession) return;
|
|
513
513
|
const session = state.activeSession;
|
|
514
514
|
const revPath = session.reviewFilePath;
|
|
515
|
-
if (!
|
|
516
|
-
const stat =
|
|
515
|
+
if (!fs7.existsSync(revPath)) return;
|
|
516
|
+
const stat = fs7.statSync(revPath);
|
|
517
517
|
const lastRound = session.rounds[session.rounds.length - 1];
|
|
518
518
|
const lastCheck = lastRound?.timestamp ?? session.startedAt;
|
|
519
519
|
if (stat.mtime.toISOString() <= lastCheck) return;
|
|
@@ -524,7 +524,7 @@ function createHeadlessLoop(options) {
|
|
|
524
524
|
log(
|
|
525
525
|
`PR #${session.request.prNumber} round ${roundNum}: ${round.findingCount} findings, ${round.suggestedDiffLines} suggested diff lines`
|
|
526
526
|
);
|
|
527
|
-
const stopSignalPath =
|
|
527
|
+
const stopSignalPath = path6.join(options.stateDir, "stop-signal");
|
|
528
528
|
const ctx = {
|
|
529
529
|
round: roundNum,
|
|
530
530
|
rounds: session.rounds,
|
|
@@ -546,21 +546,21 @@ function createHeadlessLoop(options) {
|
|
|
546
546
|
const prompt = buildReviewPrompt(session.request, options.agentName, round);
|
|
547
547
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
|
|
548
548
|
const dispatchFilename = `${date}-headless-${options.agentName}-review-PR${session.request.prNumber}-r${round}.md`;
|
|
549
|
-
const inboxDir =
|
|
550
|
-
|
|
551
|
-
const dispatchFile =
|
|
549
|
+
const inboxDir = path6.join(options.commsDir, "inbox");
|
|
550
|
+
fs7.mkdirSync(inboxDir, { recursive: true });
|
|
551
|
+
const dispatchFile = path6.join(inboxDir, dispatchFilename);
|
|
552
552
|
const tmp = `${dispatchFile}.tmp.${process.pid}`;
|
|
553
|
-
|
|
554
|
-
|
|
553
|
+
fs7.writeFileSync(tmp, prompt, "utf-8");
|
|
554
|
+
fs7.renameSync(tmp, dispatchFile);
|
|
555
555
|
}
|
|
556
556
|
function completeSession(session) {
|
|
557
557
|
session.terminatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
558
|
-
const inboxDir =
|
|
559
|
-
if (
|
|
558
|
+
const inboxDir = path6.join(options.commsDir, "inbox");
|
|
559
|
+
if (fs7.existsSync(inboxDir)) {
|
|
560
560
|
const prefix = `headless-${options.agentName}-review-PR${session.request.prNumber}`;
|
|
561
|
-
const files =
|
|
561
|
+
const files = fs7.readdirSync(inboxDir).filter((f) => f.includes(prefix));
|
|
562
562
|
for (const f of files) {
|
|
563
|
-
|
|
563
|
+
fs7.unlinkSync(path6.join(inboxDir, f));
|
|
564
564
|
}
|
|
565
565
|
}
|
|
566
566
|
state.activeSession = null;
|
|
@@ -604,48 +604,90 @@ var init_headless_loop = __esm({
|
|
|
604
604
|
});
|
|
605
605
|
|
|
606
606
|
// src/bridges/codex-bridge-runner.ts
|
|
607
|
-
import * as
|
|
608
|
-
import * as
|
|
609
|
-
import { spawn } from "child_process";
|
|
610
|
-
import { fileURLToPath } from "url";
|
|
607
|
+
import * as fs8 from "fs";
|
|
608
|
+
import * as path7 from "path";
|
|
609
|
+
import { spawn as spawn2 } from "child_process";
|
|
610
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
611
611
|
|
|
612
612
|
// src/config/resolve.ts
|
|
613
|
+
import * as fs2 from "fs";
|
|
614
|
+
import * as path2 from "path";
|
|
615
|
+
|
|
616
|
+
// src/utils.ts
|
|
613
617
|
import * as fs from "fs";
|
|
614
618
|
import * as path from "path";
|
|
619
|
+
var _noGitWarned = false;
|
|
620
|
+
function _setNoGitWarned() {
|
|
621
|
+
_noGitWarned = true;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/config/resolve.ts
|
|
615
625
|
var SHARED_CONFIG_FILE = "tap-config.json";
|
|
616
626
|
var LOCAL_CONFIG_FILE = "tap-config.local.json";
|
|
627
|
+
var LEGACY_CONFIG_FILE = ".tap-config";
|
|
617
628
|
var DEFAULT_RUNTIME_COMMAND = "node";
|
|
618
629
|
var DEFAULT_APP_SERVER_URL = "ws://127.0.0.1:4501";
|
|
619
630
|
function findRepoRoot(startDir = process.cwd()) {
|
|
620
|
-
let dir =
|
|
631
|
+
let dir = path2.resolve(startDir);
|
|
621
632
|
while (true) {
|
|
622
|
-
if (
|
|
623
|
-
if (
|
|
624
|
-
|
|
633
|
+
if (fs2.existsSync(path2.join(dir, ".git"))) return dir;
|
|
634
|
+
if (fs2.existsSync(path2.join(dir, "package.json"))) {
|
|
635
|
+
if (!_noGitWarned) {
|
|
636
|
+
_setNoGitWarned();
|
|
637
|
+
console.error(
|
|
638
|
+
"[tap] warning: No .git directory found. Resolved via package.json. Use --comms-dir to specify explicitly."
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
return dir;
|
|
642
|
+
}
|
|
643
|
+
const parent = path2.dirname(dir);
|
|
625
644
|
if (parent === dir) break;
|
|
626
645
|
dir = parent;
|
|
627
646
|
}
|
|
647
|
+
if (!_noGitWarned) {
|
|
648
|
+
_setNoGitWarned();
|
|
649
|
+
console.error(
|
|
650
|
+
"[tap] warning: No git repository found. Using cwd as root. Run 'git init' or use --comms-dir."
|
|
651
|
+
);
|
|
652
|
+
}
|
|
628
653
|
return process.cwd();
|
|
629
654
|
}
|
|
630
655
|
function loadJsonFile(filePath) {
|
|
631
|
-
if (!
|
|
656
|
+
if (!fs2.existsSync(filePath)) return null;
|
|
632
657
|
try {
|
|
633
|
-
const raw =
|
|
658
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
634
659
|
return JSON.parse(raw);
|
|
635
660
|
} catch {
|
|
636
661
|
return null;
|
|
637
662
|
}
|
|
638
663
|
}
|
|
639
664
|
function loadSharedConfig(repoRoot) {
|
|
640
|
-
return loadJsonFile(
|
|
665
|
+
return loadJsonFile(path2.join(repoRoot, SHARED_CONFIG_FILE));
|
|
641
666
|
}
|
|
642
667
|
function loadLocalConfig(repoRoot) {
|
|
643
|
-
return loadJsonFile(
|
|
668
|
+
return loadJsonFile(path2.join(repoRoot, LOCAL_CONFIG_FILE));
|
|
669
|
+
}
|
|
670
|
+
function readLegacyShellValue(configText, key) {
|
|
671
|
+
const match = configText.match(new RegExp(`^${key}="?(.+?)"?$`, "m"));
|
|
672
|
+
return match?.[1]?.trim() || null;
|
|
673
|
+
}
|
|
674
|
+
function loadLegacyShellConfig(repoRoot) {
|
|
675
|
+
const filePath = path2.join(repoRoot, LEGACY_CONFIG_FILE);
|
|
676
|
+
if (!fs2.existsSync(filePath)) return null;
|
|
677
|
+
try {
|
|
678
|
+
const raw = fs2.readFileSync(filePath, "utf-8");
|
|
679
|
+
const commsDir = readLegacyShellValue(raw, "TAP_COMMS_DIR");
|
|
680
|
+
if (!commsDir) return null;
|
|
681
|
+
return { commsDir };
|
|
682
|
+
} catch {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
644
685
|
}
|
|
645
686
|
function resolveConfig(overrides = {}, startDir) {
|
|
646
687
|
const repoRoot = findRepoRoot(startDir);
|
|
647
688
|
const shared = loadSharedConfig(repoRoot) ?? {};
|
|
648
689
|
const local = loadLocalConfig(repoRoot) ?? {};
|
|
690
|
+
const legacy = loadLegacyShellConfig(repoRoot) ?? {};
|
|
649
691
|
const sources = {
|
|
650
692
|
repoRoot: "auto",
|
|
651
693
|
commsDir: "auto",
|
|
@@ -655,10 +697,10 @@ function resolveConfig(overrides = {}, startDir) {
|
|
|
655
697
|
};
|
|
656
698
|
let commsDir;
|
|
657
699
|
if (overrides.commsDir) {
|
|
658
|
-
commsDir =
|
|
700
|
+
commsDir = resolvePath(repoRoot, overrides.commsDir);
|
|
659
701
|
sources.commsDir = "cli-flag";
|
|
660
702
|
} else if (process.env.TAP_COMMS_DIR) {
|
|
661
|
-
commsDir =
|
|
703
|
+
commsDir = resolvePath(repoRoot, process.env.TAP_COMMS_DIR);
|
|
662
704
|
sources.commsDir = "env";
|
|
663
705
|
} else if (local.commsDir) {
|
|
664
706
|
commsDir = resolvePath(repoRoot, local.commsDir);
|
|
@@ -666,15 +708,18 @@ function resolveConfig(overrides = {}, startDir) {
|
|
|
666
708
|
} else if (shared.commsDir) {
|
|
667
709
|
commsDir = resolvePath(repoRoot, shared.commsDir);
|
|
668
710
|
sources.commsDir = "shared-config";
|
|
711
|
+
} else if (legacy.commsDir) {
|
|
712
|
+
commsDir = resolvePath(repoRoot, legacy.commsDir);
|
|
713
|
+
sources.commsDir = "legacy-shell-config";
|
|
669
714
|
} else {
|
|
670
|
-
commsDir =
|
|
715
|
+
commsDir = path2.join(repoRoot, "tap-comms");
|
|
671
716
|
}
|
|
672
717
|
let stateDir;
|
|
673
718
|
if (overrides.stateDir) {
|
|
674
|
-
stateDir =
|
|
719
|
+
stateDir = resolvePath(repoRoot, overrides.stateDir);
|
|
675
720
|
sources.stateDir = "cli-flag";
|
|
676
721
|
} else if (process.env.TAP_STATE_DIR) {
|
|
677
|
-
stateDir =
|
|
722
|
+
stateDir = resolvePath(repoRoot, process.env.TAP_STATE_DIR);
|
|
678
723
|
sources.stateDir = "env";
|
|
679
724
|
} else if (local.stateDir) {
|
|
680
725
|
stateDir = resolvePath(repoRoot, local.stateDir);
|
|
@@ -683,7 +728,7 @@ function resolveConfig(overrides = {}, startDir) {
|
|
|
683
728
|
stateDir = resolvePath(repoRoot, shared.stateDir);
|
|
684
729
|
sources.stateDir = "shared-config";
|
|
685
730
|
} else {
|
|
686
|
-
stateDir =
|
|
731
|
+
stateDir = path2.join(repoRoot, ".tap-comms");
|
|
687
732
|
}
|
|
688
733
|
let runtimeCommand;
|
|
689
734
|
if (overrides.runtimeCommand) {
|
|
@@ -723,18 +768,40 @@ function resolveConfig(overrides = {}, startDir) {
|
|
|
723
768
|
};
|
|
724
769
|
}
|
|
725
770
|
function resolvePath(repoRoot, p) {
|
|
726
|
-
|
|
771
|
+
const normalized = normalizeTapPath(p);
|
|
772
|
+
return path2.isAbsolute(normalized) ? normalized : path2.resolve(repoRoot, normalized);
|
|
773
|
+
}
|
|
774
|
+
function normalizeTapPath(input) {
|
|
775
|
+
const trimmed = input.trim().replace(/^["'`]+|["'`]+$/g, "");
|
|
776
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed)) {
|
|
777
|
+
return trimmed;
|
|
778
|
+
}
|
|
779
|
+
if (process.platform === "win32") {
|
|
780
|
+
const match = trimmed.match(/^\/([A-Za-z])\/(.*)$/);
|
|
781
|
+
if (match) {
|
|
782
|
+
return `${match[1].toUpperCase()}:\\${match[2].replace(/\//g, "\\")}`;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return trimmed;
|
|
727
786
|
}
|
|
728
787
|
|
|
788
|
+
// src/engine/bridge.ts
|
|
789
|
+
import * as fs4 from "fs";
|
|
790
|
+
import * as net from "net";
|
|
791
|
+
import * as path4 from "path";
|
|
792
|
+
import { randomBytes } from "crypto";
|
|
793
|
+
import { spawn, spawnSync, execSync as execSync2 } from "child_process";
|
|
794
|
+
import { fileURLToPath } from "url";
|
|
795
|
+
|
|
729
796
|
// src/runtime/resolve-node.ts
|
|
730
|
-
import * as
|
|
731
|
-
import * as
|
|
797
|
+
import * as fs3 from "fs";
|
|
798
|
+
import * as path3 from "path";
|
|
732
799
|
import { execSync } from "child_process";
|
|
733
800
|
function readNodeVersion(repoRoot) {
|
|
734
|
-
const nvFile =
|
|
735
|
-
if (!
|
|
801
|
+
const nvFile = path3.join(repoRoot, ".node-version");
|
|
802
|
+
if (!fs3.existsSync(nvFile)) return null;
|
|
736
803
|
try {
|
|
737
|
-
const raw =
|
|
804
|
+
const raw = fs3.readFileSync(nvFile, "utf-8").trim();
|
|
738
805
|
return raw.length > 0 ? raw.replace(/^v/, "") : null;
|
|
739
806
|
} catch {
|
|
740
807
|
return null;
|
|
@@ -744,16 +811,16 @@ function fnmCandidateDirs() {
|
|
|
744
811
|
if (process.platform === "win32") {
|
|
745
812
|
return [
|
|
746
813
|
process.env.FNM_DIR,
|
|
747
|
-
process.env.APPDATA ?
|
|
748
|
-
process.env.LOCALAPPDATA ?
|
|
749
|
-
process.env.USERPROFILE ?
|
|
814
|
+
process.env.APPDATA ? path3.join(process.env.APPDATA, "fnm") : null,
|
|
815
|
+
process.env.LOCALAPPDATA ? path3.join(process.env.LOCALAPPDATA, "fnm") : null,
|
|
816
|
+
process.env.USERPROFILE ? path3.join(process.env.USERPROFILE, "scoop", "persist", "fnm") : null
|
|
750
817
|
].filter(Boolean);
|
|
751
818
|
}
|
|
752
819
|
return [
|
|
753
820
|
process.env.FNM_DIR,
|
|
754
|
-
process.env.HOME ?
|
|
755
|
-
process.env.HOME ?
|
|
756
|
-
process.env.XDG_DATA_HOME ?
|
|
821
|
+
process.env.HOME ? path3.join(process.env.HOME, ".local", "share", "fnm") : null,
|
|
822
|
+
process.env.HOME ? path3.join(process.env.HOME, ".fnm") : null,
|
|
823
|
+
process.env.XDG_DATA_HOME ? path3.join(process.env.XDG_DATA_HOME, "fnm") : null
|
|
757
824
|
].filter(Boolean);
|
|
758
825
|
}
|
|
759
826
|
function nodeExecutableName() {
|
|
@@ -763,14 +830,14 @@ function probeFnmNode(desiredVersion) {
|
|
|
763
830
|
const dirs = fnmCandidateDirs();
|
|
764
831
|
const exe = nodeExecutableName();
|
|
765
832
|
for (const baseDir of dirs) {
|
|
766
|
-
const candidate =
|
|
833
|
+
const candidate = path3.join(
|
|
767
834
|
baseDir,
|
|
768
835
|
"node-versions",
|
|
769
836
|
`v${desiredVersion}`,
|
|
770
837
|
"installation",
|
|
771
838
|
exe
|
|
772
839
|
);
|
|
773
|
-
if (!
|
|
840
|
+
if (!fs3.existsSync(candidate)) continue;
|
|
774
841
|
try {
|
|
775
842
|
const v = execSync(`"${candidate}" --version`, {
|
|
776
843
|
encoding: "utf-8",
|
|
@@ -811,12 +878,12 @@ function checkStripTypesSupport(command) {
|
|
|
811
878
|
}
|
|
812
879
|
function findTsxFallback(repoRoot) {
|
|
813
880
|
const candidates = [
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
881
|
+
path3.join(repoRoot, "node_modules", ".bin", "tsx.exe"),
|
|
882
|
+
path3.join(repoRoot, "node_modules", ".bin", "tsx.CMD"),
|
|
883
|
+
path3.join(repoRoot, "node_modules", ".bin", "tsx")
|
|
817
884
|
];
|
|
818
885
|
for (const c of candidates) {
|
|
819
|
-
if (
|
|
886
|
+
if (fs3.existsSync(c)) return c;
|
|
820
887
|
}
|
|
821
888
|
return null;
|
|
822
889
|
}
|
|
@@ -825,7 +892,7 @@ function getFnmBinDir(repoRoot) {
|
|
|
825
892
|
if (!desiredVersion) return null;
|
|
826
893
|
const nodePath = probeFnmNode(desiredVersion);
|
|
827
894
|
if (!nodePath) return null;
|
|
828
|
-
return
|
|
895
|
+
return path3.dirname(nodePath);
|
|
829
896
|
}
|
|
830
897
|
function resolveNodeRuntime(configCommand, repoRoot) {
|
|
831
898
|
if (configCommand === "bun" || configCommand.endsWith("bun.exe")) {
|
|
@@ -881,19 +948,35 @@ function buildRuntimeEnv(repoRoot, baseEnv = process.env) {
|
|
|
881
948
|
const currentPath = baseEnv[pathKey] ?? baseEnv.PATH ?? "";
|
|
882
949
|
return {
|
|
883
950
|
...baseEnv,
|
|
884
|
-
[pathKey]: `${fnmBin}${
|
|
951
|
+
[pathKey]: `${fnmBin}${path3.delimiter}${currentPath}`
|
|
885
952
|
};
|
|
886
953
|
}
|
|
887
954
|
|
|
955
|
+
// src/engine/bridge.ts
|
|
956
|
+
var DEFAULT_APP_SERVER_URL2 = "ws://127.0.0.1:4501";
|
|
957
|
+
function resolveAppServerUrl(baseUrl, port) {
|
|
958
|
+
const resolvedBase = (baseUrl ?? DEFAULT_APP_SERVER_URL2).replace(/\/$/, "");
|
|
959
|
+
if (port == null) {
|
|
960
|
+
return resolvedBase;
|
|
961
|
+
}
|
|
962
|
+
try {
|
|
963
|
+
const parsed = new URL(resolvedBase);
|
|
964
|
+
parsed.port = String(port);
|
|
965
|
+
return parsed.toString().replace(/\/$/, "");
|
|
966
|
+
} catch {
|
|
967
|
+
return resolvedBase;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
888
971
|
// src/bridges/codex-bridge-runner.ts
|
|
889
972
|
function findRepoRootFromRunner() {
|
|
890
|
-
let dir =
|
|
973
|
+
let dir = path7.resolve(path7.dirname(fileURLToPath2(import.meta.url)));
|
|
891
974
|
while (true) {
|
|
892
|
-
if (
|
|
893
|
-
if (
|
|
894
|
-
if (
|
|
975
|
+
if (fs8.existsSync(path7.join(dir, SHARED_CONFIG_FILE))) return dir;
|
|
976
|
+
if (fs8.existsSync(path7.join(dir, LOCAL_CONFIG_FILE))) return dir;
|
|
977
|
+
if (fs8.existsSync(path7.join(dir, "scripts", "codex-app-server-bridge.ts")))
|
|
895
978
|
return dir;
|
|
896
|
-
const parent =
|
|
979
|
+
const parent = path7.dirname(dir);
|
|
897
980
|
if (parent === dir) return null;
|
|
898
981
|
dir = parent;
|
|
899
982
|
}
|
|
@@ -903,7 +986,7 @@ function maybeStartHeadlessLoop(repoRoot, commsDir, stateDir) {
|
|
|
903
986
|
Promise.resolve().then(() => (init_headless_loop(), headless_loop_exports)).then(({ createHeadlessLoop: createHeadlessLoop2 }) => {
|
|
904
987
|
const agentName = process.env.TAP_AGENT_NAME ?? process.env.CODEX_TAP_AGENT_NAME ?? "reviewer";
|
|
905
988
|
const generation = process.env.TAP_REVIEW_GENERATION ?? "gen11";
|
|
906
|
-
const resolvedStateDir = stateDir ??
|
|
989
|
+
const resolvedStateDir = stateDir ?? path7.join(repoRoot, ".tap-comms");
|
|
907
990
|
const loop = createHeadlessLoop2({
|
|
908
991
|
commsDir,
|
|
909
992
|
stateDir: resolvedStateDir,
|
|
@@ -920,23 +1003,40 @@ function maybeStartHeadlessLoop(repoRoot, commsDir, stateDir) {
|
|
|
920
1003
|
console.error("[headless-loop] Failed to start:", err);
|
|
921
1004
|
});
|
|
922
1005
|
}
|
|
1006
|
+
function buildBridgeScriptArgs(scriptPath, options) {
|
|
1007
|
+
const args = [
|
|
1008
|
+
scriptPath,
|
|
1009
|
+
`--repo-root=${options.repoRoot}`,
|
|
1010
|
+
`--comms-dir=${options.commsDir}`,
|
|
1011
|
+
`--app-server-url=${options.appServerUrl}`
|
|
1012
|
+
];
|
|
1013
|
+
if (options.agentName) {
|
|
1014
|
+
args.push(`--agent-name=${options.agentName}`);
|
|
1015
|
+
}
|
|
1016
|
+
if (options.gatewayTokenFile) {
|
|
1017
|
+
args.push(`--gateway-token-file=${options.gatewayTokenFile}`);
|
|
1018
|
+
}
|
|
1019
|
+
if (options.stateDir) {
|
|
1020
|
+
args.push(`--state-dir=${options.stateDir}`);
|
|
1021
|
+
}
|
|
1022
|
+
return args;
|
|
1023
|
+
}
|
|
923
1024
|
async function main() {
|
|
924
1025
|
const repoRootHint = findRepoRootFromRunner() ?? void 0;
|
|
925
1026
|
const { config } = resolveConfig({}, repoRootHint);
|
|
926
1027
|
const repoRoot = config.repoRoot;
|
|
927
1028
|
const commsDir = config.commsDir;
|
|
928
|
-
|
|
929
|
-
const instancePort =
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
}
|
|
1029
|
+
const instancePortRaw = process.env.TAP_BRIDGE_PORT;
|
|
1030
|
+
const instancePort = instancePortRaw ? Number.parseInt(instancePortRaw, 10) : void 0;
|
|
1031
|
+
const envAppServerUrl = process.env.CODEX_APP_SERVER_URL?.trim();
|
|
1032
|
+
const gatewayTokenFile = process.env.TAP_GATEWAY_TOKEN_FILE?.trim();
|
|
1033
|
+
const appServerUrl = envAppServerUrl || resolveAppServerUrl(
|
|
1034
|
+
config.appServerUrl,
|
|
1035
|
+
Number.isFinite(instancePort) ? instancePort : void 0
|
|
1036
|
+
);
|
|
938
1037
|
const instanceId = process.env.TAP_BRIDGE_INSTANCE_ID;
|
|
939
|
-
const
|
|
1038
|
+
const envStateDir = process.env.TAP_STATE_DIR;
|
|
1039
|
+
const stateDir = envStateDir ? envStateDir : instanceId ? path7.join(repoRoot, ".tmp", `codex-app-server-bridge-${instanceId}`) : void 0;
|
|
940
1040
|
const preResolved = process.env.TAP_RESOLVED_NODE;
|
|
941
1041
|
const resolved = preResolved ? {
|
|
942
1042
|
command: preResolved,
|
|
@@ -945,12 +1045,13 @@ async function main() {
|
|
|
945
1045
|
majorVersion: null
|
|
946
1046
|
} : resolveNodeRuntime(config.runtimeCommand, repoRoot);
|
|
947
1047
|
const command = resolved.command;
|
|
948
|
-
const
|
|
1048
|
+
const agentName = process.env.TAP_AGENT_NAME?.trim() || process.env.CODEX_TAP_AGENT_NAME?.trim() || void 0;
|
|
1049
|
+
const scriptPath = path7.join(
|
|
949
1050
|
repoRoot,
|
|
950
1051
|
"scripts",
|
|
951
1052
|
"codex-app-server-bridge.ts"
|
|
952
1053
|
);
|
|
953
|
-
if (!
|
|
1054
|
+
if (!fs8.existsSync(scriptPath)) {
|
|
954
1055
|
throw new Error(
|
|
955
1056
|
`Bridge script not found: ${scriptPath}
|
|
956
1057
|
Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
|
|
@@ -961,14 +1062,15 @@ Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
|
|
|
961
1062
|
args.push("--experimental-strip-types");
|
|
962
1063
|
}
|
|
963
1064
|
args.push(
|
|
964
|
-
scriptPath,
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1065
|
+
...buildBridgeScriptArgs(scriptPath, {
|
|
1066
|
+
repoRoot,
|
|
1067
|
+
commsDir,
|
|
1068
|
+
appServerUrl,
|
|
1069
|
+
gatewayTokenFile,
|
|
1070
|
+
stateDir,
|
|
1071
|
+
agentName
|
|
1072
|
+
})
|
|
968
1073
|
);
|
|
969
|
-
if (stateDir) {
|
|
970
|
-
args.push(`--state-dir=${stateDir}`);
|
|
971
|
-
}
|
|
972
1074
|
const busyMode = process.env.TAP_BUSY_MODE;
|
|
973
1075
|
if (busyMode) args.push(`--busy-mode=${busyMode}`);
|
|
974
1076
|
const pollSeconds = process.env.TAP_POLL_SECONDS;
|
|
@@ -984,7 +1086,7 @@ Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
|
|
|
984
1086
|
if (process.env.TAP_PROCESS_EXISTING === "true")
|
|
985
1087
|
args.push("--process-existing-messages");
|
|
986
1088
|
const runtimeEnv = buildRuntimeEnv(repoRoot);
|
|
987
|
-
const child =
|
|
1089
|
+
const child = spawn2(command, args, {
|
|
988
1090
|
cwd: repoRoot,
|
|
989
1091
|
env: runtimeEnv,
|
|
990
1092
|
stdio: "inherit"
|
|
@@ -1002,8 +1104,18 @@ Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
|
|
|
1002
1104
|
process.exit(1);
|
|
1003
1105
|
});
|
|
1004
1106
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1107
|
+
function isDirectExecution() {
|
|
1108
|
+
const entry = process.argv[1];
|
|
1109
|
+
if (!entry) return false;
|
|
1110
|
+
return import.meta.url === pathToFileURL(path7.resolve(entry)).href;
|
|
1111
|
+
}
|
|
1112
|
+
if (isDirectExecution()) {
|
|
1113
|
+
main().catch((error) => {
|
|
1114
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
1115
|
+
process.exit(1);
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
export {
|
|
1119
|
+
buildBridgeScriptArgs
|
|
1120
|
+
};
|
|
1009
1121
|
//# sourceMappingURL=codex-bridge-runner.mjs.map
|