xmux-bridge 1.0.40 → 1.0.41
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 +19 -56
- package/{bridge-mcp-server.js → mcp/servers/bridge.js} +6 -2
- package/{xmux-lead-mcp-server.js → mcp/servers/lead.js} +6 -2
- package/{scripts/setup_claude_mcp.js → mcp/setup/claude.js} +10 -9
- package/{scripts/setup_xmux_codex_mcp.js → mcp/setup/codex.js} +32 -16
- package/{scripts/setup_copilot_mcp.js → mcp/setup/copilot.js} +7 -8
- package/{scripts/setup_gemini_mcp.js → mcp/setup/gemini.js} +7 -8
- package/package.json +7 -13
- package/bin/xmux +0 -9
- package/scripts/trust_codex_project.js +0 -44
- package/scripts/trust_copilot_project.js +0 -49
- package/scripts/trust_gemini_project.js +0 -47
- package/xmux-bridge.zsh +0 -481
- package/xmux.zsh +0 -5146
package/README.md
CHANGED
|
@@ -24,11 +24,6 @@ brew tap DwvN-Lee/xmux
|
|
|
24
24
|
brew install xmux
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Homebrew owns the stable runtime under `$(brew --prefix)/opt/xmux/libexec`.
|
|
28
|
-
The installed `xmux` command exports that path as `XMUX_INSTALL_DIR` and then
|
|
29
|
-
execs the runtime wrapper in `libexec/bin/xmux`. Ad hoc local directories, npx
|
|
30
|
-
caches, and zsh plugin directories are not part of the normal runtime path.
|
|
31
|
-
|
|
32
27
|
Configure Codex integration explicitly:
|
|
33
28
|
|
|
34
29
|
```bash
|
|
@@ -36,21 +31,8 @@ xmux setup-codex
|
|
|
36
31
|
xmux doctor-codex
|
|
37
32
|
```
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
that
|
|
41
|
-
versioned npm package, points that MCP runtime back at Homebrew with
|
|
42
|
-
`XMUX_INSTALL_DIR`, adds the installed `xmux` path to Codex shell policy,
|
|
43
|
-
installs the scoped XMux command rule, and refreshes available XMux skills
|
|
44
|
-
under `~/.codex/skills`. Runtime-only installs do not include skill source
|
|
45
|
-
files, so pass an external skill source when refreshing skills:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
xmux setup-codex --skills-dir /path/to/xmux-skills
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
`XMUX_CODEX_SKILLS_DIR` provides the same source path for automation. Without
|
|
52
|
-
`--skills-dir` or `XMUX_CODEX_SKILLS_DIR`, `setup-codex` skips skill refresh
|
|
53
|
-
and leaves existing user-owned skills untouched.
|
|
34
|
+
`xmux setup-codex` registers XMux with Codex, and `xmux doctor-codex` checks
|
|
35
|
+
that the integration is ready.
|
|
54
36
|
|
|
55
37
|
Start the Codex lead from the target project directory:
|
|
56
38
|
|
|
@@ -148,7 +130,7 @@ Runtime state is project-local:
|
|
|
148
130
|
Runtime path environment names are now split by responsibility:
|
|
149
131
|
|
|
150
132
|
```text
|
|
151
|
-
XMUX_INSTALL_DIR # XMux
|
|
133
|
+
XMUX_INSTALL_DIR # XMux install root
|
|
152
134
|
XMUX_PROJECT_DIR # project root where Codex is working
|
|
153
135
|
XMUX_STATE_DIR # project-local runtime state, usually $XMUX_PROJECT_DIR/.codex/xmux
|
|
154
136
|
```
|
|
@@ -156,36 +138,25 @@ XMUX_STATE_DIR # project-local runtime state, usually $XMUX_PROJECT_DIR/.code
|
|
|
156
138
|
Codex uses the normal user runtime under `~/.codex`. XMux does not create an
|
|
157
139
|
isolated Codex home for a team, and Codex teammate mode is unsupported.
|
|
158
140
|
|
|
159
|
-
Agent automation uses `xmux`
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
bootstrap command remains `xmux -n <session>` after setup; ad hoc local paths
|
|
163
|
-
and shell-loading details are not part of the agent contract.
|
|
141
|
+
Agent automation uses the installed `xmux` command that `xmux setup-codex`
|
|
142
|
+
makes available to Codex. The user-facing bootstrap command remains
|
|
143
|
+
`xmux -n <session>` after setup.
|
|
164
144
|
|
|
165
145
|
The Codex lead MCP server is `xmux_lead`. `xmux setup-codex` configures it so
|
|
166
146
|
Codex can route requests, wait for teammate responses, read events, and inspect
|
|
167
147
|
team status.
|
|
168
|
-
The
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
team runtime environment prepared by XMux. The bridge and mailbox paths are
|
|
148
|
+
The installed `xmux` command owns the tmux runtime. The `xmux_lead` MCP server
|
|
149
|
+
is delivered as a versioned npm entrypoint, and Codex skills are optional
|
|
150
|
+
shortcuts for orchestrating that runtime. The MCP command is install-scoped and
|
|
151
|
+
does not pin `XMUX_PROJECT_DIR`/`XMUX_STATE_DIR`; those values come from the
|
|
152
|
+
active `xmux -n <session>` lead runtime.
|
|
153
|
+
|
|
154
|
+
Provider teammates write responses through `mcp/servers/bridge.js`, using the
|
|
155
|
+
team runtime environment prepared by XMux. The MCP and mailbox paths are
|
|
177
156
|
implementation details behind Codex-led teammate orchestration.
|
|
178
157
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Homebrew does not install Codex skills or repo-local plugin files; normal
|
|
182
|
-
runtime operation depends on the installed `xmux` command and
|
|
183
|
-
`XMUX_INSTALL_DIR`, not a checkout path.
|
|
184
|
-
|
|
185
|
-
The plugin skill source of truth is `plugins/xmux/skills`; the top-level
|
|
186
|
-
`skills/` directory is a mirrored distribution copy for explicit skill refresh
|
|
187
|
-
workflows. Users explicitly invoke Codex skills with `$`, for example
|
|
188
|
-
`$xmux-teams`. The official XMux skills cover agent-facing orchestration flows:
|
|
158
|
+
Users can ask for teammate work in natural language. When XMux skills are
|
|
159
|
+
available in Codex, the official skill shortcuts are:
|
|
189
160
|
|
|
190
161
|
```text
|
|
191
162
|
$xmux-teams
|
|
@@ -196,22 +167,14 @@ $xmux-diagnosis
|
|
|
196
167
|
$xmux-send-pane
|
|
197
168
|
```
|
|
198
169
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
zsh -n xmux.zsh
|
|
203
|
-
zsh -n xmux-bridge.zsh
|
|
204
|
-
node --check scripts/setup_xmux_codex_mcp.js
|
|
205
|
-
git diff --check
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Homebrew distribution notes live in [Homebrew distribution](docs/operations/homebrew.md).
|
|
170
|
+
Homebrew installation details live in [Homebrew installation](docs/operations/homebrew.md).
|
|
209
171
|
|
|
210
172
|
## Docs
|
|
211
173
|
|
|
212
174
|
- [Documentation index](docs/README.md)
|
|
175
|
+
- [Repository layout](docs/runtime/repository-layout.md)
|
|
213
176
|
- [Codex lead runtime](docs/runtime/codex-lead.md)
|
|
214
|
-
- [Homebrew
|
|
177
|
+
- [Homebrew installation](docs/operations/homebrew.md)
|
|
215
178
|
- [Wrapper-first debugging](docs/operations/debugging.md)
|
|
216
179
|
- [Claude teammate](docs/teammates/claude.md)
|
|
217
180
|
- [Gemini teammate](docs/teammates/gemini.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* bridge
|
|
3
|
+
* mcp/servers/bridge.js
|
|
4
4
|
* Minimal MCP server for xmux.
|
|
5
5
|
* Exposes write_to_lead(text, summary?) so Claude, Gemini, and Copilot
|
|
6
6
|
* teammates can write directly to the XMux lead inbox.
|
|
@@ -140,7 +140,11 @@ function trimToCap(msgs, cap) {
|
|
|
140
140
|
function mailboxInstallBases() {
|
|
141
141
|
const seen = new Set();
|
|
142
142
|
const bases = [];
|
|
143
|
-
|
|
143
|
+
const packageRoot = path.basename(__dirname) === 'servers'
|
|
144
|
+
&& path.basename(path.dirname(__dirname)) === 'mcp'
|
|
145
|
+
? path.dirname(path.dirname(__dirname))
|
|
146
|
+
: __dirname;
|
|
147
|
+
for (const candidate of [XMUX_INSTALL_DIR, packageRoot, __dirname]) {
|
|
144
148
|
if (!candidate) continue;
|
|
145
149
|
const resolved = path.resolve(candidate);
|
|
146
150
|
if (seen.has(resolved)) continue;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* mcp/servers/lead.js
|
|
4
4
|
* Stdio-only MCP server exposing XMux lead/team mailbox tools.
|
|
5
5
|
*
|
|
6
6
|
* Mailbox persistence is delegated to the Node mailbox CLI.
|
|
@@ -138,7 +138,11 @@ function parseJsonOutput(stdout) {
|
|
|
138
138
|
function mailboxInstallBases() {
|
|
139
139
|
const seen = new Set();
|
|
140
140
|
const bases = [];
|
|
141
|
-
|
|
141
|
+
const packageRoot = path.basename(__dirname) === 'servers'
|
|
142
|
+
&& path.basename(path.dirname(__dirname)) === 'mcp'
|
|
143
|
+
? path.dirname(path.dirname(__dirname))
|
|
144
|
+
: __dirname;
|
|
145
|
+
for (const candidate of [XMUX_INSTALL_DIR, packageRoot, __dirname]) {
|
|
142
146
|
if (!candidate) continue;
|
|
143
147
|
const resolved = path.resolve(candidate);
|
|
144
148
|
if (seen.has(resolved)) continue;
|
|
@@ -28,7 +28,7 @@ function stableHomebrewXmuxInstallDir(installDir) {
|
|
|
28
28
|
|
|
29
29
|
const prefix = resolved.split(marker, 1)[0];
|
|
30
30
|
const candidate = path.join(prefix, "opt", "xmux", "libexec");
|
|
31
|
-
if (fs.existsSync(path.join(candidate, "xmux.zsh"))) {
|
|
31
|
+
if (fs.existsSync(path.join(candidate, "runtime", "shell", "xmux.zsh")) || fs.existsSync(path.join(candidate, "xmux.zsh"))) {
|
|
32
32
|
return candidate;
|
|
33
33
|
}
|
|
34
34
|
return resolved;
|
|
@@ -36,13 +36,14 @@ function stableHomebrewXmuxInstallDir(installDir) {
|
|
|
36
36
|
|
|
37
37
|
function stableHomebrewXmuxFilePath(filePath) {
|
|
38
38
|
const resolved = absolute(filePath);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
39
|
+
const marker = `${path.sep}Cellar${path.sep}xmux${path.sep}`;
|
|
40
|
+
const libexecSegment = `${path.sep}libexec${path.sep}`;
|
|
41
|
+
const libexecIndex = resolved.indexOf(libexecSegment);
|
|
42
|
+
if (!resolved.includes(marker) || libexecIndex < 0) return resolved;
|
|
43
|
+
const prefix = resolved.split(marker, 1)[0];
|
|
44
|
+
const optDir = path.join(prefix, "opt", "xmux", "libexec");
|
|
45
|
+
const relativePath = resolved.slice(libexecIndex + libexecSegment.length);
|
|
46
|
+
const candidate = path.join(optDir, relativePath);
|
|
46
47
|
if (fs.existsSync(candidate)) {
|
|
47
48
|
return candidate;
|
|
48
49
|
}
|
|
@@ -72,7 +73,7 @@ function atomicWriteJson(filePath, data) {
|
|
|
72
73
|
|
|
73
74
|
function usage() {
|
|
74
75
|
process.stderr.write(
|
|
75
|
-
"usage:
|
|
76
|
+
"usage: claude.js <bridge_js> <project_dir> <outbox> <agent> <team> <state_dir> <install_dir>\n",
|
|
76
77
|
);
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -32,26 +32,39 @@ function abs(value) {
|
|
|
32
32
|
return path.resolve(expandUser(value));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function xmux_runtime_shell_path(installDir) {
|
|
36
|
+
return path.join(abs(installDir), "runtime", "shell", "xmux.zsh");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function has_xmux_runtime(installDir) {
|
|
40
|
+
const root = abs(installDir);
|
|
41
|
+
return fs.existsSync(xmux_runtime_shell_path(root)) || fs.existsSync(path.join(root, "xmux.zsh"));
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
function stable_homebrew_xmux_install_dir(xmuxInstallDir) {
|
|
36
45
|
const installDir = abs(xmuxInstallDir);
|
|
37
46
|
const marker = `${path.sep}Cellar${path.sep}xmux${path.sep}`;
|
|
38
47
|
if (!installDir.includes(marker) || !installDir.endsWith(`${path.sep}libexec`)) {
|
|
39
48
|
return installDir;
|
|
40
49
|
}
|
|
41
|
-
if (!
|
|
50
|
+
if (!has_xmux_runtime(installDir)) {
|
|
42
51
|
return installDir;
|
|
43
52
|
}
|
|
44
53
|
const prefix = installDir.split(marker, 1)[0];
|
|
45
54
|
const candidate = path.join(prefix, "opt", "xmux", "libexec");
|
|
46
|
-
return
|
|
55
|
+
return has_xmux_runtime(candidate) ? candidate : installDir;
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
function stable_homebrew_xmux_file_path(inputPath) {
|
|
50
59
|
const resolved = abs(inputPath);
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
const marker = `${path.sep}Cellar${path.sep}xmux${path.sep}`;
|
|
61
|
+
const libexecSegment = `${path.sep}libexec${path.sep}`;
|
|
62
|
+
const libexecIndex = resolved.indexOf(libexecSegment);
|
|
63
|
+
if (!resolved.includes(marker) || libexecIndex < 0) return resolved;
|
|
64
|
+
const prefix = resolved.split(marker, 1)[0];
|
|
65
|
+
const optDir = path.join(prefix, "opt", "xmux", "libexec");
|
|
66
|
+
const relativePath = resolved.slice(libexecIndex + libexecSegment.length);
|
|
67
|
+
const candidate = path.join(optDir, relativePath);
|
|
55
68
|
return fs.existsSync(candidate) ? candidate : resolved;
|
|
56
69
|
}
|
|
57
70
|
|
|
@@ -139,14 +152,15 @@ function package_spec_has_version(packageSpec) {
|
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
function xmux_version_from_install_dir(xmuxInstallDir) {
|
|
142
|
-
const
|
|
155
|
+
const root = abs(xmuxInstallDir);
|
|
156
|
+
const content = read_text(xmux_runtime_shell_path(root)) || read_text(path.join(root, "xmux.zsh"));
|
|
143
157
|
const match = content.match(/^XMUX_VERSION=["']([^"']+)["']/m);
|
|
144
158
|
return match ? match[1] : "";
|
|
145
159
|
}
|
|
146
160
|
|
|
147
161
|
function default_mcp_package_spec(xmuxInstallDir, packageName = "", packageVersion = "") {
|
|
148
162
|
const installPackage = read_json(path.join(abs(xmuxInstallDir), "package.json")) || {};
|
|
149
|
-
const scriptPackage = read_json(path.join(path.dirname(path.dirname(abs(__filename))), "package.json")) || {};
|
|
163
|
+
const scriptPackage = read_json(path.join(path.dirname(path.dirname(path.dirname(abs(__filename)))), "package.json")) || {};
|
|
150
164
|
const name = packageName
|
|
151
165
|
|| process.env.XMUX_MCP_NPM_PACKAGE
|
|
152
166
|
|| installPackage.name
|
|
@@ -272,7 +286,7 @@ function is_xmux_runtime_bin_path(candidatePath, currentXmuxBin) {
|
|
|
272
286
|
if (expanded === abs(currentXmuxBin)) return true;
|
|
273
287
|
if (path.basename(expanded) !== "bin") return false;
|
|
274
288
|
const installDir = path.dirname(expanded);
|
|
275
|
-
if (
|
|
289
|
+
if (has_xmux_runtime(installDir) && fs.existsSync(path.join(expanded, "xmux"))) {
|
|
276
290
|
return true;
|
|
277
291
|
}
|
|
278
292
|
if (path.basename(installDir) !== "libexec") return false;
|
|
@@ -419,7 +433,7 @@ function install_xmux_command_rule(configPath) {
|
|
|
419
433
|
let content = remove_marker_block(read_text(filePath), RULE_BEGIN, RULE_END);
|
|
420
434
|
const block = [
|
|
421
435
|
RULE_BEGIN,
|
|
422
|
-
"# Allow the scoped XMux wrapper command; XMux
|
|
436
|
+
"# Allow the scoped XMux wrapper command; user intent and XMux wrappers control operation scope.",
|
|
423
437
|
'prefix_rule(pattern=["xmux"], decision="allow")',
|
|
424
438
|
RULE_END,
|
|
425
439
|
].join("\n");
|
|
@@ -568,12 +582,12 @@ function _xmux_lead_mcp_processes_from_ps(psOutput) {
|
|
|
568
582
|
const processes = [];
|
|
569
583
|
for (const rawLine of String(psOutput || "").split(/\r?\n/)) {
|
|
570
584
|
const stripped = rawLine.trim();
|
|
571
|
-
if (!stripped.includes("xmux-lead-mcp-server.js")) continue;
|
|
585
|
+
if (!stripped.includes("mcp/servers/lead.js") && !stripped.includes("xmux-lead-mcp-server.js")) continue;
|
|
572
586
|
const match = stripped.match(/^(\d+)\s+(.*)$/);
|
|
573
587
|
if (!match) continue;
|
|
574
588
|
const [, pid, command] = match;
|
|
575
589
|
const tokens = splitShellWords(command.trim());
|
|
576
|
-
const serverPath = tokens.find((token) => token.endsWith("xmux-lead-mcp-server.js")) || "";
|
|
590
|
+
const serverPath = tokens.find((token) => token.endsWith("mcp/servers/lead.js") || token.endsWith("xmux-lead-mcp-server.js")) || "";
|
|
577
591
|
if (!serverPath) continue;
|
|
578
592
|
processes.push({ pid, command: command.trim(), server_path: abs(serverPath) });
|
|
579
593
|
}
|
|
@@ -591,7 +605,7 @@ function running_xmux_lead_mcp_processes() {
|
|
|
591
605
|
|
|
592
606
|
function _is_homebrew_xmux_mcp_server(serverPath) {
|
|
593
607
|
const normalized = abs(serverPath);
|
|
594
|
-
return normalized.endsWith("xmux-lead-mcp-server.js")
|
|
608
|
+
return (normalized.endsWith(`${path.sep}mcp${path.sep}servers${path.sep}lead.js`) || normalized.endsWith("xmux-lead-mcp-server.js"))
|
|
595
609
|
&& normalized.includes(`${path.sep}Cellar${path.sep}xmux${path.sep}`)
|
|
596
610
|
&& normalized.includes(`${path.sep}libexec${path.sep}`);
|
|
597
611
|
}
|
|
@@ -636,7 +650,7 @@ function doctor_codex(configPath, xmuxInstallDir, mcpConfigOrServerPath, skillsD
|
|
|
636
650
|
} else if (installedNames.size) {
|
|
637
651
|
notes.push(["OK", `XMux Codex skills installed under ${skills_root(configPath)}`]);
|
|
638
652
|
} else {
|
|
639
|
-
notes.push(["
|
|
653
|
+
notes.push(["OK", "optional XMux skills are not configured"]);
|
|
640
654
|
}
|
|
641
655
|
|
|
642
656
|
if (fs.existsSync(plugin_cache_path(configPath))) {
|
|
@@ -748,7 +762,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
748
762
|
const opts = parse_args(argv);
|
|
749
763
|
const configPath = resolve_config_path(opts);
|
|
750
764
|
opts.mcp_npx_prefix = abs(opts.mcp_npx_prefix || process.env.XMUX_MCP_NPX_PREFIX || default_mcp_npx_prefix(configPath));
|
|
751
|
-
const scriptInstallDir = path.dirname(path.dirname(abs(__filename)));
|
|
765
|
+
const scriptInstallDir = path.dirname(path.dirname(path.dirname(abs(__filename))));
|
|
752
766
|
const rawInstallDir = abs(opts.xmux_install_dir || scriptInstallDir);
|
|
753
767
|
const xmuxInstallDir = stable_homebrew_xmux_install_dir(rawInstallDir);
|
|
754
768
|
const xmuxProjectDir = abs(opts.xmux_project_dir || default_xmux_project_dir());
|
|
@@ -795,7 +809,9 @@ function main(argv = process.argv.slice(2)) {
|
|
|
795
809
|
console.log(" xmux_project_dir: inherited from xmux-launched Codex runtime");
|
|
796
810
|
console.log(" xmux_state_dir: inherited from xmux-launched Codex runtime");
|
|
797
811
|
if (installedSkills.length) console.log(` skills: ${installedSkills.join(", ")}`);
|
|
798
|
-
else if (opts.install_skills
|
|
812
|
+
else if (opts.install_skills && (opts.skills_dir || process.env.XMUX_CODEX_SKILLS_DIR)) {
|
|
813
|
+
console.log(" skills: no importable XMux skills found");
|
|
814
|
+
}
|
|
799
815
|
console.log(" plugin_cache: disabled; stale XMux plugin cache removed if present");
|
|
800
816
|
return 0;
|
|
801
817
|
}
|
|
@@ -18,16 +18,15 @@ const TOOLS = ["write_to_lead"];
|
|
|
18
18
|
|
|
19
19
|
function stableHomebrewXmuxFilePath(inputPath) {
|
|
20
20
|
const resolved = path.resolve(inputPath.replace(/^~(?=$|\/)/, os.homedir()));
|
|
21
|
-
const installDir = path.dirname(resolved);
|
|
22
21
|
const marker = `${path.sep}Cellar${path.sep}xmux${path.sep}`;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const prefix = installDir.split(marker, 1)[0];
|
|
22
|
+
const libexecSegment = `${path.sep}libexec${path.sep}`;
|
|
23
|
+
const libexecIndex = resolved.indexOf(libexecSegment);
|
|
24
|
+
if (!resolved.includes(marker) || libexecIndex < 0) return resolved;
|
|
25
|
+
const prefix = resolved.split(marker, 1)[0];
|
|
28
26
|
const optDir = path.join(prefix, "opt", "xmux", "libexec");
|
|
29
|
-
const
|
|
30
|
-
|
|
27
|
+
const relativePath = resolved.slice(libexecIndex + libexecSegment.length);
|
|
28
|
+
const candidate = path.join(optDir, relativePath);
|
|
29
|
+
if ((fs.existsSync(path.join(optDir, "runtime", "shell", "xmux.zsh")) || fs.existsSync(path.join(optDir, "xmux.zsh"))) && fs.existsSync(candidate)) {
|
|
31
30
|
return candidate;
|
|
32
31
|
}
|
|
33
32
|
return resolved;
|
|
@@ -18,16 +18,15 @@ const NPM_PIN = "xmux-bridge@^1.3.0";
|
|
|
18
18
|
|
|
19
19
|
function stableHomebrewXmuxFilePath(inputPath) {
|
|
20
20
|
const resolved = path.resolve(inputPath.replace(/^~(?=$|\/)/, os.homedir()));
|
|
21
|
-
const installDir = path.dirname(resolved);
|
|
22
21
|
const marker = `${path.sep}Cellar${path.sep}xmux${path.sep}`;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const prefix = installDir.split(marker, 1)[0];
|
|
22
|
+
const libexecSegment = `${path.sep}libexec${path.sep}`;
|
|
23
|
+
const libexecIndex = resolved.indexOf(libexecSegment);
|
|
24
|
+
if (!resolved.includes(marker) || libexecIndex < 0) return resolved;
|
|
25
|
+
const prefix = resolved.split(marker, 1)[0];
|
|
28
26
|
const optDir = path.join(prefix, "opt", "xmux", "libexec");
|
|
29
|
-
const
|
|
30
|
-
|
|
27
|
+
const relativePath = resolved.slice(libexecIndex + libexecSegment.length);
|
|
28
|
+
const candidate = path.join(optDir, relativePath);
|
|
29
|
+
if ((fs.existsSync(path.join(optDir, "runtime", "shell", "xmux.zsh")) || fs.existsSync(path.join(optDir, "xmux.zsh"))) && fs.existsSync(candidate)) {
|
|
31
30
|
return candidate;
|
|
32
31
|
}
|
|
33
32
|
return resolved;
|
package/package.json
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xmux-bridge",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "MCP
|
|
3
|
+
"version": "1.0.41",
|
|
4
|
+
"description": "MCP bridge and lead server package for XMux",
|
|
5
5
|
"bin": {
|
|
6
|
-
"xmux-
|
|
7
|
-
"xmux-
|
|
8
|
-
"xmux-mailbox": "./dist/bin/xmux-mailbox.js"
|
|
9
|
-
"xmux": "./bin/xmux",
|
|
10
|
-
"xmux-bridge-relay": "./xmux-bridge.zsh"
|
|
6
|
+
"xmux-lead-mcp": "./mcp/servers/lead.js",
|
|
7
|
+
"xmux-bridge": "./mcp/servers/bridge.js",
|
|
8
|
+
"xmux-mailbox": "./dist/bin/xmux-mailbox.js"
|
|
11
9
|
},
|
|
12
10
|
"files": [
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"xmux-bridge.zsh",
|
|
16
|
-
"bin/xmux",
|
|
17
|
-
"xmux.zsh",
|
|
18
|
-
"scripts/*.js",
|
|
11
|
+
"mcp/servers",
|
|
12
|
+
"mcp/setup",
|
|
19
13
|
"dist/bin/xmux-mailbox.js",
|
|
20
14
|
"dist/mailbox",
|
|
21
15
|
"src/runtime",
|
package/bin/xmux
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const os = require("node:os");
|
|
6
|
-
const path = require("node:path");
|
|
7
|
-
|
|
8
|
-
const TOML_PATH = path.join(os.homedir(), ".codex", "config.toml");
|
|
9
|
-
|
|
10
|
-
function usage() {
|
|
11
|
-
process.stderr.write("usage: trust_codex_project.js <path>\n");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function main(argv = process.argv.slice(2)) {
|
|
15
|
-
if (argv.length !== 1) {
|
|
16
|
-
usage();
|
|
17
|
-
return 1;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const projectPath = fs.realpathSync(path.resolve(argv[0]));
|
|
21
|
-
const section = `[projects."${projectPath}"]`;
|
|
22
|
-
let content = "";
|
|
23
|
-
if (fs.existsSync(TOML_PATH)) {
|
|
24
|
-
content = fs.readFileSync(TOML_PATH, "utf-8");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (content.includes(section)) {
|
|
28
|
-
return 0;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const entry = `${section}\ntrust_level = "trusted"\n`;
|
|
32
|
-
fs.mkdirSync(path.dirname(TOML_PATH), { recursive: true });
|
|
33
|
-
if (content && !content.endsWith("\n")) {
|
|
34
|
-
content += "\n";
|
|
35
|
-
}
|
|
36
|
-
fs.writeFileSync(TOML_PATH, `${content}\n${entry}`, "utf-8");
|
|
37
|
-
return 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (require.main === module) {
|
|
41
|
-
process.exitCode = main();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
module.exports = { main };
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const os = require("node:os");
|
|
6
|
-
const path = require("node:path");
|
|
7
|
-
|
|
8
|
-
const FILE_PATH = path.join(os.homedir(), ".copilot", "config.json");
|
|
9
|
-
|
|
10
|
-
function usage() {
|
|
11
|
-
process.stderr.write("usage: trust_copilot_project.js <path>\n");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function main(argv = process.argv.slice(2)) {
|
|
15
|
-
if (argv.length !== 1) {
|
|
16
|
-
usage();
|
|
17
|
-
return 1;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const projectPath = fs.realpathSync(path.resolve(argv[0]));
|
|
21
|
-
let data = {};
|
|
22
|
-
if (fs.existsSync(FILE_PATH)) {
|
|
23
|
-
try {
|
|
24
|
-
const parsed = JSON.parse(fs.readFileSync(FILE_PATH, "utf-8"));
|
|
25
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
26
|
-
data = parsed;
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
data = {};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const folders = Array.isArray(data.trusted_folders) ? data.trusted_folders : [];
|
|
34
|
-
if (folders.includes(projectPath)) {
|
|
35
|
-
return 0;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
folders.push(projectPath);
|
|
39
|
-
data.trusted_folders = folders;
|
|
40
|
-
fs.mkdirSync(path.dirname(FILE_PATH), { recursive: true });
|
|
41
|
-
fs.writeFileSync(FILE_PATH, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
|
|
42
|
-
return 0;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (require.main === module) {
|
|
46
|
-
process.exitCode = main();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = { main };
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const os = require("node:os");
|
|
6
|
-
const path = require("node:path");
|
|
7
|
-
|
|
8
|
-
const FILE_PATH = path.join(os.homedir(), ".gemini", "trustedFolders.json");
|
|
9
|
-
|
|
10
|
-
function usage() {
|
|
11
|
-
process.stderr.write("usage: trust_gemini_project.js <path>\n");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function main(argv = process.argv.slice(2)) {
|
|
15
|
-
if (argv.length !== 1) {
|
|
16
|
-
usage();
|
|
17
|
-
return 1;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const projectPath = fs.realpathSync(path.resolve(argv[0]));
|
|
21
|
-
let data = {};
|
|
22
|
-
if (fs.existsSync(FILE_PATH)) {
|
|
23
|
-
try {
|
|
24
|
-
const parsed = JSON.parse(fs.readFileSync(FILE_PATH, "utf-8"));
|
|
25
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
26
|
-
data = parsed;
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
data = {};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (data[projectPath] === "TRUST_FOLDER") {
|
|
34
|
-
return 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
data[projectPath] = "TRUST_FOLDER";
|
|
38
|
-
fs.mkdirSync(path.dirname(FILE_PATH), { recursive: true });
|
|
39
|
-
fs.writeFileSync(FILE_PATH, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
|
|
40
|
-
return 0;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (require.main === module) {
|
|
44
|
-
process.exitCode = main();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
module.exports = { main };
|