agent-relay-server 0.4.11 → 0.4.12

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 CHANGED
@@ -314,7 +314,18 @@ agent-relay-codex uninstall
314
314
  ```
315
315
 
316
316
  This removes the Codex SessionStart hook, local plugin marketplace files, and
317
- launcher shims. It leaves shell profile PATH edits visible for manual cleanup.
317
+ launcher shims. It leaves shell profile PATH edits and runtime logs in place.
318
+
319
+ Full managed cleanup:
320
+
321
+ ```bash
322
+ agent-relay-codex uninstall --purge
323
+ ```
324
+
325
+ Purge also removes Agent Relay-managed shell profile PATH snippets, Codex runtime
326
+ logs, the launcher directory, and empty install directories. It only removes
327
+ profile snippets written by the installer marker; manual PATH edits are left
328
+ alone.
318
329
 
319
330
  ## What the Agent Sees
320
331
 
@@ -55,7 +55,7 @@ function usage(exitCode = 0): never {
55
55
  Usage:
56
56
  agent-relay-codex [--relay-url URL] [--listen ws://127.0.0.1:PORT] [-- <codex args...>]
57
57
  agent-relay-codex install [--alias|--no-alias]
58
- agent-relay-codex uninstall
58
+ agent-relay-codex uninstall [--purge]
59
59
  agent-relay-codex alias install
60
60
  agent-relay-codex alias remove
61
61
  agent-relay-codex doctor
@@ -66,6 +66,8 @@ With no subcommand, this launches Codex with live Agent Relay support.`);
66
66
  process.exit(exitCode);
67
67
  }
68
68
 
69
+ const pathMarker = "# Agent Relay Codex alias";
70
+
69
71
  function commandExists(command: string): boolean {
70
72
  return findOnPath(command) !== null;
71
73
  }
@@ -633,26 +635,110 @@ function installPathEntry(): boolean {
633
635
  }
634
636
 
635
637
  const shell = process.env.SHELL || "";
636
- const marker = "# Agent Relay Codex alias";
637
638
  const exportLine = `export PATH=${shellQuote(aliasBinDir)}:$PATH`;
638
639
  let profilePath = join(home, ".profile");
639
- let snippet = `\n${marker}\n${exportLine}\n`;
640
+ let snippet = `\n${pathMarker}\n${exportLine}\n`;
640
641
 
641
642
  if (shell.includes("zsh")) profilePath = join(home, ".zshrc");
642
643
  if (shell.includes("bash")) profilePath = join(home, ".bashrc");
643
644
  if (shell.includes("fish")) {
644
645
  profilePath = join(home, ".config", "fish", "config.fish");
645
- snippet = `\n${marker}\nfish_add_path ${shellQuote(aliasBinDir)}\n`;
646
+ snippet = `\n${pathMarker}\nfish_add_path ${shellQuote(aliasBinDir)}\n`;
646
647
  }
647
648
 
648
649
  mkdirSync(dirname(profilePath), { recursive: true });
649
650
  const current = existsSync(profilePath) ? readFileSync(profilePath, "utf8") : "";
650
- if (!current.includes(marker) && !current.includes(aliasBinDir)) {
651
+ if (!current.includes(pathMarker) && !current.includes(aliasBinDir)) {
651
652
  writeFileSync(profilePath, `${current.replace(/\s*$/, "")}${snippet}`);
652
653
  }
653
654
  return true;
654
655
  }
655
656
 
657
+ function managedProfilePaths(): string[] {
658
+ return [
659
+ join(home, ".profile"),
660
+ join(home, ".bashrc"),
661
+ join(home, ".zshrc"),
662
+ join(home, ".config", "fish", "config.fish"),
663
+ ];
664
+ }
665
+
666
+ function removeManagedPathSnippet(input: string): string {
667
+ const hadTrailingNewline = /\r?\n$/.test(input);
668
+ const lines = input.split(/\r?\n/);
669
+ const output: string[] = [];
670
+
671
+ for (let index = 0; index < lines.length; index += 1) {
672
+ const line = lines[index]!;
673
+ if (line === pathMarker) {
674
+ const next = lines[index + 1] || "";
675
+ if (next.includes(aliasBinDir)) index += 1;
676
+ continue;
677
+ }
678
+ output.push(line);
679
+ }
680
+
681
+ const cleaned = output.join("\n").replace(/\n{3,}/g, "\n\n").replace(/[ \t]+\n/g, "\n");
682
+ return hadTrailingNewline && cleaned ? `${cleaned.replace(/\n*$/, "")}\n` : cleaned.replace(/\n*$/, "");
683
+ }
684
+
685
+ function removeUnixPathEntries(): number {
686
+ let changed = 0;
687
+ for (const profilePath of managedProfilePaths()) {
688
+ if (!existsSync(profilePath)) continue;
689
+ const current = readFileSync(profilePath, "utf8");
690
+ if (!current.includes(pathMarker)) continue;
691
+ const cleaned = removeManagedPathSnippet(current);
692
+ if (cleaned !== current) {
693
+ writeFileSync(profilePath, cleaned);
694
+ changed += 1;
695
+ }
696
+ }
697
+ return changed;
698
+ }
699
+
700
+ function removeWindowsPathEntry(): boolean {
701
+ const script = [
702
+ `$target = ${JSON.stringify(aliasBinDir)}`,
703
+ "$path = [Environment]::GetEnvironmentVariable('Path', 'User')",
704
+ "if ($path) {",
705
+ " $parts = $path -split ';' | Where-Object { $_ -and ($_ -ne $target) }",
706
+ " [Environment]::SetEnvironmentVariable('Path', ($parts -join ';'), 'User')",
707
+ "}",
708
+ "$bin = [Environment]::GetEnvironmentVariable('AGENT_RELAY_CODEX_BIN', 'User')",
709
+ "if ($bin -eq $target) { [Environment]::SetEnvironmentVariable('AGENT_RELAY_CODEX_BIN', $null, 'User') }",
710
+ ].join("; ");
711
+ const result = Bun.spawnSync(["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], {
712
+ stdout: "pipe",
713
+ stderr: "pipe",
714
+ });
715
+ return result.exitCode === 0;
716
+ }
717
+
718
+ function removeManagedPathEntries(): string {
719
+ if (process.platform === "win32") {
720
+ return removeWindowsPathEntry() ? "Removed managed Windows user PATH entries." : "Could not update Windows user PATH entries.";
721
+ }
722
+ const changed = removeUnixPathEntries();
723
+ return changed > 0 ? `Removed managed PATH snippets from ${changed} shell profile(s).` : "No managed shell profile PATH snippets found.";
724
+ }
725
+
726
+ function removeEmptyDirectory(path: string): boolean {
727
+ if (!existsSync(path)) return false;
728
+ try {
729
+ if (readdirSync(path).length > 0) return false;
730
+ rmSync(path, { recursive: false, force: true });
731
+ return true;
732
+ } catch {
733
+ return false;
734
+ }
735
+ }
736
+
737
+ function removeEmptyInstallParents(): void {
738
+ removeEmptyDirectory(installRoot);
739
+ removeEmptyDirectory(dirname(installRoot));
740
+ }
741
+
656
742
  function installCodexAlias(): void {
657
743
  installLauncherShims(true);
658
744
  const updated = installPathEntry();
@@ -846,7 +932,11 @@ async function install(args: string[]): Promise<void> {
846
932
  }
847
933
  }
848
934
 
849
- function uninstall(): void {
935
+ function uninstall(args: string[] = []): void {
936
+ const purge = args.includes("--purge");
937
+ const unknown = args.filter((arg) => arg !== "--purge");
938
+ if (unknown.length > 0) throw new Error(`Unknown uninstall option: ${unknown.join(" ")}`);
939
+
850
940
  stopRuntimeSidecars();
851
941
  removeHook();
852
942
  removeLauncherShim("codex");
@@ -854,14 +944,24 @@ function uninstall(): void {
854
944
  rmSync(marketplaceRoot, { recursive: true, force: true });
855
945
  rmSync(installedPackageRoot, { recursive: true, force: true });
856
946
  console.log("Uninstalled Agent Relay Codex hook, plugin marketplace files, and launcher shims.");
857
- console.log(`PATH entries are left untouched; remove ${aliasBinDir} from your shell profile if you no longer want it there.`);
947
+
948
+ if (!purge) {
949
+ console.log(`PATH entries and runtime logs are left untouched. Run agent-relay-codex uninstall --purge for full managed cleanup.`);
950
+ return;
951
+ }
952
+
953
+ rmSync(runtimeRoot, { recursive: true, force: true });
954
+ rmSync(aliasBinDir, { recursive: true, force: true });
955
+ console.log(removeManagedPathEntries());
956
+ removeEmptyInstallParents();
957
+ console.log("Purged Agent Relay Codex runtime files, launcher directory, and empty install directories.");
858
958
  }
859
959
 
860
960
  async function main(): Promise<void> {
861
961
  const [command, ...args] = process.argv.slice(2);
862
962
  if (command === "help" || command === "--help" || command === "-h") usage(0);
863
963
  if (command === "install") return install(args);
864
- if (command === "uninstall") return uninstall();
964
+ if (command === "uninstall") return uninstall(args);
865
965
  if (command === "alias" && args[0] === "install") {
866
966
  installCodexSupport(false);
867
967
  return installCodexAlias();
package/codex/README.md CHANGED
@@ -52,6 +52,22 @@ starts Codex with
52
52
  `--remote`, lets the SessionStart hook attach a sidecar to the actual thread,
53
53
  and kills sidecars plus the app-server when Codex exits.
54
54
 
55
+ Uninstall the Codex hook, local plugin marketplace files, and launcher shims:
56
+
57
+ ```bash
58
+ agent-relay-codex uninstall
59
+ ```
60
+
61
+ Remove all Agent Relay-managed Codex install state, including installer-written
62
+ PATH snippets and runtime logs:
63
+
64
+ ```bash
65
+ agent-relay-codex uninstall --purge
66
+ ```
67
+
68
+ Purge only removes shell profile snippets written with the Agent Relay installer
69
+ marker. Manual PATH edits are left alone.
70
+
55
71
  ## Approval mode
56
72
 
57
73
  Relay replies are usually sent with a shell command (`curl` to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",