@hua-labs/tap 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ var __export = (target, all) => {
9
9
  };
10
10
 
11
11
  // src/engine/termination.ts
12
- import * as fs3 from "fs";
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 (fs3.existsSync(ctx.stopSignalPath)) {
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 fs4 from "fs";
149
- import * as path3 from "path";
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 = path3.basename(filename, ".md");
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: ${path3.join("reviews", request.generation, `review-PR${request.prNumber}-${agentName}.md`)}`,
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 (!fs4.existsSync(reviewFilePath2)) return null;
291
- const content = fs4.readFileSync(reviewFilePath2, "utf-8");
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 path3.join(
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 (fs4.existsSync(revPath) && fs4.existsSync(request.sourcePath)) {
319
- const reviewStat = fs4.statSync(revPath);
320
- const requestStat = fs4.statSync(request.sourcePath);
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 = fs4.statSync(filePath);
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 fs4.existsSync(path3.join(stateDir, "processed", `${markerId}.done`));
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 = path3.join(stateDir, "processed", `${markerId}.done`);
337
- if (fs4.existsSync(markerPath)) {
338
- fs4.unlinkSync(markerPath);
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 = path3.join(stateDir, "processed");
344
- fs4.mkdirSync(markerDir, { recursive: true });
345
- const markerPath = path3.join(markerDir, `${markerId}.done`);
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
- fs4.writeFileSync(tmp, JSON.stringify(payload, null, 2), "utf-8");
353
- fs4.renameSync(tmp, markerPath);
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: ${path3.basename(request.sourcePath)}`
363
+ `- request: ${path5.basename(request.sourcePath)}`
364
364
  ].join("\n");
365
- const inboxDir = path3.join(commsDir, "inbox");
366
- fs4.mkdirSync(inboxDir, { recursive: true });
367
- const inboxPath = path3.join(inboxDir, filename);
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
- fs4.writeFileSync(tmp, content, "utf-8");
370
- fs4.renameSync(tmp, inboxPath);
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 = path3.join(commsDir, "inbox");
386
- if (!fs4.existsSync(inboxDir)) return [];
387
- const files = fs4.readdirSync(inboxDir).filter((f) => f.endsWith(".md"));
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 = path3.join(inboxDir, file);
391
- const content = fs4.readFileSync(filePath, "utf-8");
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 fs5 from "fs";
441
- import * as path4 from "path";
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 = path4.join(options.commsDir, "inbox");
485
- fs5.mkdirSync(inboxDir, { recursive: true });
486
- const dispatchFile = path4.join(inboxDir, dispatchFilename);
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
- fs5.writeFileSync(tmp, prompt, "utf-8");
489
- fs5.renameSync(tmp, dispatchFile);
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 (!fs5.existsSync(revPath)) return;
516
- const stat = fs5.statSync(revPath);
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 = path4.join(options.stateDir, "stop-signal");
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 = path4.join(options.commsDir, "inbox");
550
- fs5.mkdirSync(inboxDir, { recursive: true });
551
- const dispatchFile = path4.join(inboxDir, dispatchFilename);
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
- fs5.writeFileSync(tmp, prompt, "utf-8");
554
- fs5.renameSync(tmp, dispatchFile);
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 = path4.join(options.commsDir, "inbox");
559
- if (fs5.existsSync(inboxDir)) {
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 = fs5.readdirSync(inboxDir).filter((f) => f.includes(prefix));
561
+ const files = fs7.readdirSync(inboxDir).filter((f) => f.includes(prefix));
562
562
  for (const f of files) {
563
- fs5.unlinkSync(path4.join(inboxDir, f));
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 fs6 from "fs";
608
- import * as path5 from "path";
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 = path.resolve(startDir);
631
+ let dir = path2.resolve(startDir);
621
632
  while (true) {
622
- if (fs.existsSync(path.join(dir, ".git"))) return dir;
623
- if (fs.existsSync(path.join(dir, "package.json"))) return dir;
624
- const parent = path.dirname(dir);
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 (!fs.existsSync(filePath)) return null;
656
+ if (!fs2.existsSync(filePath)) return null;
632
657
  try {
633
- const raw = fs.readFileSync(filePath, "utf-8");
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(path.join(repoRoot, SHARED_CONFIG_FILE));
665
+ return loadJsonFile(path2.join(repoRoot, SHARED_CONFIG_FILE));
641
666
  }
642
667
  function loadLocalConfig(repoRoot) {
643
- return loadJsonFile(path.join(repoRoot, LOCAL_CONFIG_FILE));
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 = path.resolve(overrides.commsDir);
700
+ commsDir = resolvePath(repoRoot, overrides.commsDir);
659
701
  sources.commsDir = "cli-flag";
660
702
  } else if (process.env.TAP_COMMS_DIR) {
661
- commsDir = path.resolve(process.env.TAP_COMMS_DIR);
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 = path.join(path.dirname(repoRoot), "tap-comms");
715
+ commsDir = path2.join(repoRoot, "tap-comms");
671
716
  }
672
717
  let stateDir;
673
718
  if (overrides.stateDir) {
674
- stateDir = path.resolve(overrides.stateDir);
719
+ stateDir = resolvePath(repoRoot, overrides.stateDir);
675
720
  sources.stateDir = "cli-flag";
676
721
  } else if (process.env.TAP_STATE_DIR) {
677
- stateDir = path.resolve(process.env.TAP_STATE_DIR);
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 = path.join(repoRoot, ".tap-comms");
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
- return path.isAbsolute(p) ? p : path.resolve(repoRoot, p);
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 fs2 from "fs";
731
- import * as path2 from "path";
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 = path2.join(repoRoot, ".node-version");
735
- if (!fs2.existsSync(nvFile)) return null;
801
+ const nvFile = path3.join(repoRoot, ".node-version");
802
+ if (!fs3.existsSync(nvFile)) return null;
736
803
  try {
737
- const raw = fs2.readFileSync(nvFile, "utf-8").trim();
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 ? path2.join(process.env.APPDATA, "fnm") : null,
748
- process.env.LOCALAPPDATA ? path2.join(process.env.LOCALAPPDATA, "fnm") : null,
749
- process.env.USERPROFILE ? path2.join(process.env.USERPROFILE, "scoop", "persist", "fnm") : null
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 ? path2.join(process.env.HOME, ".local", "share", "fnm") : null,
755
- process.env.HOME ? path2.join(process.env.HOME, ".fnm") : null,
756
- process.env.XDG_DATA_HOME ? path2.join(process.env.XDG_DATA_HOME, "fnm") : null
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 = path2.join(
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 (!fs2.existsSync(candidate)) continue;
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
- path2.join(repoRoot, "node_modules", ".bin", "tsx.exe"),
815
- path2.join(repoRoot, "node_modules", ".bin", "tsx.CMD"),
816
- path2.join(repoRoot, "node_modules", ".bin", "tsx")
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 (fs2.existsSync(c)) return c;
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 path2.dirname(nodePath);
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}${path2.delimiter}${currentPath}`
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 = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)));
973
+ let dir = path7.resolve(path7.dirname(fileURLToPath2(import.meta.url)));
891
974
  while (true) {
892
- if (fs6.existsSync(path5.join(dir, SHARED_CONFIG_FILE))) return dir;
893
- if (fs6.existsSync(path5.join(dir, LOCAL_CONFIG_FILE))) return dir;
894
- if (fs6.existsSync(path5.join(dir, "scripts", "codex-app-server-bridge.ts")))
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 = path5.dirname(dir);
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 ?? path5.join(repoRoot, ".tap-comms");
989
+ const resolvedStateDir = stateDir ?? path7.join(repoRoot, ".tap-comms");
907
990
  const loop = createHeadlessLoop2({
908
991
  commsDir,
909
992
  stateDir: resolvedStateDir,
@@ -920,23 +1003,75 @@ function maybeStartHeadlessLoop(repoRoot, commsDir, stateDir) {
920
1003
  console.error("[headless-loop] Failed to start:", err);
921
1004
  });
922
1005
  }
1006
+ function resolveBridgeDaemonScript(repoRoot, runnerUrl = import.meta.url, fileExists = fs8.existsSync) {
1007
+ const moduleDir = path7.dirname(fileURLToPath2(runnerUrl));
1008
+ const candidates = [
1009
+ // 1. Bundled standalone/npm install
1010
+ path7.join(moduleDir, "codex-app-server-bridge.mjs"),
1011
+ // 2. Source run from monorepo package
1012
+ path7.join(moduleDir, "codex-app-server-bridge.ts"),
1013
+ // 3. Built monorepo package dist
1014
+ path7.join(
1015
+ repoRoot,
1016
+ "packages",
1017
+ "tap-comms",
1018
+ "dist",
1019
+ "bridges",
1020
+ "codex-app-server-bridge.mjs"
1021
+ ),
1022
+ // 4. Monorepo source wrapper
1023
+ path7.join(
1024
+ repoRoot,
1025
+ "packages",
1026
+ "tap-comms",
1027
+ "src",
1028
+ "bridges",
1029
+ "codex-app-server-bridge.ts"
1030
+ ),
1031
+ // 5. Legacy monorepo root script
1032
+ path7.join(repoRoot, "scripts", "codex-app-server-bridge.ts")
1033
+ ];
1034
+ for (const candidate of candidates) {
1035
+ if (fileExists(candidate)) {
1036
+ return candidate;
1037
+ }
1038
+ }
1039
+ return null;
1040
+ }
1041
+ function buildBridgeScriptArgs(scriptPath, options) {
1042
+ const args = [
1043
+ scriptPath,
1044
+ `--repo-root=${options.repoRoot}`,
1045
+ `--comms-dir=${options.commsDir}`,
1046
+ `--app-server-url=${options.appServerUrl}`
1047
+ ];
1048
+ if (options.agentName) {
1049
+ args.push(`--agent-name=${options.agentName}`);
1050
+ }
1051
+ if (options.gatewayTokenFile) {
1052
+ args.push(`--gateway-token-file=${options.gatewayTokenFile}`);
1053
+ }
1054
+ if (options.stateDir) {
1055
+ args.push(`--state-dir=${options.stateDir}`);
1056
+ }
1057
+ return args;
1058
+ }
923
1059
  async function main() {
924
1060
  const repoRootHint = findRepoRootFromRunner() ?? void 0;
925
1061
  const { config } = resolveConfig({}, repoRootHint);
926
1062
  const repoRoot = config.repoRoot;
927
1063
  const commsDir = config.commsDir;
928
- let appServerUrl = config.appServerUrl;
929
- const instancePort = process.env.TAP_BRIDGE_PORT;
930
- if (instancePort) {
931
- try {
932
- const url = new URL(appServerUrl);
933
- url.port = instancePort;
934
- appServerUrl = url.toString().replace(/\/$/, "");
935
- } catch {
936
- }
937
- }
1064
+ const instancePortRaw = process.env.TAP_BRIDGE_PORT;
1065
+ const instancePort = instancePortRaw ? Number.parseInt(instancePortRaw, 10) : void 0;
1066
+ const envAppServerUrl = process.env.CODEX_APP_SERVER_URL?.trim();
1067
+ const gatewayTokenFile = process.env.TAP_GATEWAY_TOKEN_FILE?.trim();
1068
+ const appServerUrl = envAppServerUrl || resolveAppServerUrl(
1069
+ config.appServerUrl,
1070
+ Number.isFinite(instancePort) ? instancePort : void 0
1071
+ );
938
1072
  const instanceId = process.env.TAP_BRIDGE_INSTANCE_ID;
939
- const stateDir = instanceId ? path5.join(repoRoot, ".tmp", `codex-app-server-bridge-${instanceId}`) : void 0;
1073
+ const envStateDir = process.env.TAP_STATE_DIR;
1074
+ const stateDir = envStateDir ? envStateDir : instanceId ? path7.join(repoRoot, ".tmp", `codex-app-server-bridge-${instanceId}`) : void 0;
940
1075
  const preResolved = process.env.TAP_RESOLVED_NODE;
941
1076
  const resolved = preResolved ? {
942
1077
  command: preResolved,
@@ -945,15 +1080,12 @@ async function main() {
945
1080
  majorVersion: null
946
1081
  } : resolveNodeRuntime(config.runtimeCommand, repoRoot);
947
1082
  const command = resolved.command;
948
- const scriptPath = path5.join(
949
- repoRoot,
950
- "scripts",
951
- "codex-app-server-bridge.ts"
952
- );
953
- if (!fs6.existsSync(scriptPath)) {
1083
+ const agentName = process.env.TAP_AGENT_NAME?.trim() || process.env.CODEX_TAP_AGENT_NAME?.trim() || void 0;
1084
+ const scriptPath = resolveBridgeDaemonScript(repoRoot);
1085
+ if (!scriptPath) {
954
1086
  throw new Error(
955
- `Bridge script not found: ${scriptPath}
956
- Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
1087
+ `Bridge script not found for repo root ${repoRoot}.
1088
+ Expected a packaged dist/bridges/codex-app-server-bridge.mjs or monorepo bridge script.`
957
1089
  );
958
1090
  }
959
1091
  const args = [];
@@ -961,14 +1093,15 @@ Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
961
1093
  args.push("--experimental-strip-types");
962
1094
  }
963
1095
  args.push(
964
- scriptPath,
965
- `--repo-root=${repoRoot}`,
966
- `--comms-dir=${commsDir}`,
967
- `--app-server-url=${appServerUrl}`
1096
+ ...buildBridgeScriptArgs(scriptPath, {
1097
+ repoRoot,
1098
+ commsDir,
1099
+ appServerUrl,
1100
+ gatewayTokenFile,
1101
+ stateDir,
1102
+ agentName
1103
+ })
968
1104
  );
969
- if (stateDir) {
970
- args.push(`--state-dir=${stateDir}`);
971
- }
972
1105
  const busyMode = process.env.TAP_BUSY_MODE;
973
1106
  if (busyMode) args.push(`--busy-mode=${busyMode}`);
974
1107
  const pollSeconds = process.env.TAP_POLL_SECONDS;
@@ -984,7 +1117,7 @@ Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
984
1117
  if (process.env.TAP_PROCESS_EXISTING === "true")
985
1118
  args.push("--process-existing-messages");
986
1119
  const runtimeEnv = buildRuntimeEnv(repoRoot);
987
- const child = spawn(command, args, {
1120
+ const child = spawn2(command, args, {
988
1121
  cwd: repoRoot,
989
1122
  env: runtimeEnv,
990
1123
  stdio: "inherit"
@@ -1002,8 +1135,19 @@ Ensure scripts/codex-app-server-bridge.ts exists in repo root.`
1002
1135
  process.exit(1);
1003
1136
  });
1004
1137
  }
1005
- main().catch((error) => {
1006
- console.error(error instanceof Error ? error.message : String(error));
1007
- process.exit(1);
1008
- });
1138
+ function isDirectExecution() {
1139
+ const entry = process.argv[1];
1140
+ if (!entry) return false;
1141
+ return import.meta.url === pathToFileURL(path7.resolve(entry)).href;
1142
+ }
1143
+ if (isDirectExecution()) {
1144
+ main().catch((error) => {
1145
+ console.error(error instanceof Error ? error.message : String(error));
1146
+ process.exit(1);
1147
+ });
1148
+ }
1149
+ export {
1150
+ buildBridgeScriptArgs,
1151
+ resolveBridgeDaemonScript
1152
+ };
1009
1153
  //# sourceMappingURL=codex-bridge-runner.mjs.map