qantara 0.1.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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -0
- package/dist/debug.js +25 -0
- package/dist/debug.js.map +1 -0
- package/dist/exec.js +105 -0
- package/dist/exec.js.map +1 -0
- package/dist/jobs.js +22 -0
- package/dist/jobs.js.map +1 -0
- package/dist/registry.js +24 -0
- package/dist/registry.js.map +1 -0
- package/dist/runners/claude.js +73 -0
- package/dist/runners/claude.js.map +1 -0
- package/dist/runners/codex.js +81 -0
- package/dist/runners/codex.js.map +1 -0
- package/dist/runners/gemini.js +73 -0
- package/dist/runners/gemini.js.map +1 -0
- package/dist/server.js +232 -0
- package/dist/server.js.map +1 -0
- package/dist/sessions.js +14 -0
- package/dist/sessions.js.map +1 -0
- package/dist/setup.js +240 -0
- package/dist/setup.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ahmed Gamil (ahmed.gamil.codes@gmail.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ahmeedgamil/qantara/main/assets/qantara_logo.png" alt="Qantara" width="300">
|
|
3
|
+
<br>
|
|
4
|
+
<em>An arched bridge between your coding agents.</em>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<!-- After `npm publish`, replace this static badge with the dynamic one:
|
|
9
|
+
<a href="https://www.npmjs.com/package/qantara"><img alt="npm" src="https://img.shields.io/npm/v/qantara"></a> -->
|
|
10
|
+
<img alt="version 0.1.0" src="https://img.shields.io/badge/version-0.1.0-blue">
|
|
11
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green.svg"></a>
|
|
12
|
+
<img alt="Node.js >= 18" src="https://img.shields.io/badge/node-%E2%89%A518-339933?logo=node.js&logoColor=white">
|
|
13
|
+
<img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white">
|
|
14
|
+
<img alt="MCP server" src="https://img.shields.io/badge/MCP-server-8A2BE2">
|
|
15
|
+
<img alt="Bridges Claude, Codex, Gemini" src="https://img.shields.io/badge/bridges-Claude%20%C2%B7%20Codex%20%C2%B7%20Gemini-FF6B35">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
An MCP server that lets coding agents **delegate tasks to each other**. Claude Code can
|
|
21
|
+
hand a task to Codex or Gemini, Codex can hand a task to Claude, and the design extends
|
|
22
|
+
to any other agent (Aider, …) by adding a small adapter.
|
|
23
|
+
|
|
24
|
+
It works by shelling out to each tool's **headless CLI** (`claude -p`, `codex exec`,
|
|
25
|
+
`gemini -p`), so it **reuses your existing CLI logins** — no extra API keys, no separate
|
|
26
|
+
per-token billing.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Claude Code ──(MCP tool: ask_codex)──► qantara ──► codex exec --json ──► Codex
|
|
30
|
+
Codex ──(MCP tool: ask_claude)─► qantara ──► claude -p --json ──► Claude Code
|
|
31
|
+
Gemini ──(MCP tool: ask_codex)──► qantara ──► ...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Setup (one command)
|
|
35
|
+
|
|
36
|
+
```powershell
|
|
37
|
+
npm i -g qantara # or run from a clone: npm install && npm run build
|
|
38
|
+
qantara setup # detects claude/codex/gemini, registers the bridge in each
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`setup` detects which agent CLIs are installed, registers qantara into each host
|
|
42
|
+
(exposing the *other* agents as tools), pre-approves the tools so nothing prompts,
|
|
43
|
+
and forwards your proxy env to Codex if you have one. It backs up every file it
|
|
44
|
+
touches (`*.qantara.bak`), is idempotent, and `qantara setup --dry-run` shows the
|
|
45
|
+
plan without writing. Restart your hosts afterwards — MCP servers load at session
|
|
46
|
+
start. The sections below describe what it writes, for manual setup or auditing.
|
|
47
|
+
|
|
48
|
+
## The control model
|
|
49
|
+
|
|
50
|
+
There is no central controller — control follows whoever you talk to:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
You (the human)
|
|
54
|
+
└── Host agent — whichever one you opened (Claude Code OR Codex)
|
|
55
|
+
└── qantara (dumb pipe, controls nothing)
|
|
56
|
+
└── Delegated agent (contractor: gets a brief, works, reports, exits)
|
|
57
|
+
└── can delegate again, down to a depth limit
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **You** own all policy: which agents exist, what they may do, every limit. Policy is
|
|
61
|
+
written in config files *before* anything runs; there is no API for an agent to change
|
|
62
|
+
it at runtime.
|
|
63
|
+
- **The host agent** is the brain for the session: it decides when to delegate, writes the
|
|
64
|
+
brief, and judges the result. The roles are symmetric — open Claude Code and Claude
|
|
65
|
+
commands Codex; open Codex and Codex commands Claude.
|
|
66
|
+
- **The bridge makes no decisions** about the work. It translates one MCP tool call into
|
|
67
|
+
one CLI invocation and enforces mechanical guards (depth, timeout, output cap). All
|
|
68
|
+
intelligence and all safety policy live in the agents and in your config.
|
|
69
|
+
- **The delegated agent** is autonomous *within* its task, but its powers (sandbox,
|
|
70
|
+
permission mode) were fixed by your config before it started — not by the caller.
|
|
71
|
+
|
|
72
|
+
Delegation is a *letter, not a phone call*: the delegated agent gets only the task text,
|
|
73
|
+
none of the caller's conversation. Write briefs accordingly (file paths, constraints,
|
|
74
|
+
acceptance criteria). By default the bridge tells delegated agents to **reply with
|
|
75
|
+
questions instead of guessing** when a task is ambiguous; answer them with a
|
|
76
|
+
`continue_session: true` follow-up call.
|
|
77
|
+
|
|
78
|
+
## Prerequisites
|
|
79
|
+
|
|
80
|
+
- Node.js 18+
|
|
81
|
+
- The agent CLIs you want to bridge, installed and logged in:
|
|
82
|
+
- `npm i -g @anthropic-ai/claude-code` (`claude`)
|
|
83
|
+
- `npm i -g @openai/codex` (`codex`)
|
|
84
|
+
- `npm i -g @google/gemini-cli` (`gemini`) — log in by running `gemini` once
|
|
85
|
+
interactively (Google account), or set `GEMINI_API_KEY`
|
|
86
|
+
|
|
87
|
+
## Build
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
npm install
|
|
91
|
+
npm run build
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Manual registration (what `setup` writes)
|
|
95
|
+
|
|
96
|
+
### Into Claude Code (gives Claude `ask_codex` / `ask_gemini`)
|
|
97
|
+
|
|
98
|
+
```powershell
|
|
99
|
+
claude mcp add -s user qantara -e BRIDGE_EXPOSE=codex,gemini -- node "<path-to>/qantara/dist/server.js"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
To skip the per-session permission prompt, allowlist the server in
|
|
103
|
+
`~/.claude/settings.json` (one entry covers all its tools):
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"permissions": {
|
|
108
|
+
"allow": ["mcp__qantara"]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Into Codex (gives Codex `ask_claude` / `ask_gemini`)
|
|
114
|
+
|
|
115
|
+
Add to `~/.codex/config.toml`:
|
|
116
|
+
|
|
117
|
+
```toml
|
|
118
|
+
[mcp_servers.qantara]
|
|
119
|
+
command = "node"
|
|
120
|
+
args = ['<path-to>/qantara/dist/server.js']
|
|
121
|
+
enabled = true
|
|
122
|
+
startup_timeout_sec = 120
|
|
123
|
+
tool_timeout_sec = 600
|
|
124
|
+
# Required for headless `codex exec`: without this, Codex auto-cancels the
|
|
125
|
+
# MCP tool call ("user cancelled MCP tool call") because there is no human
|
|
126
|
+
# to approve it. Also removes the prompt in interactive Codex.
|
|
127
|
+
# Available since Codex 0.122.0.
|
|
128
|
+
default_tools_approval_mode = "approve"
|
|
129
|
+
|
|
130
|
+
[mcp_servers.qantara.env]
|
|
131
|
+
BRIDGE_EXPOSE = "claude"
|
|
132
|
+
# Codex spawns MCP servers with a stripped environment (a fixed whitelist of
|
|
133
|
+
# core OS vars). If your `claude` login needs a proxy (HTTP_PROXY/HTTPS_PROXY),
|
|
134
|
+
# you MUST re-declare it here or the spawned claude gets
|
|
135
|
+
# "API Error: 403 Request not allowed".
|
|
136
|
+
# HTTP_PROXY = "http://user:pass@host:port"
|
|
137
|
+
# HTTPS_PROXY = "http://user:pass@host:port"
|
|
138
|
+
# NO_PROXY = "localhost,127.0.0.1,::1"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> The general rule: **the caller's side pre-approves the tool call** (Claude's
|
|
142
|
+
> `permissions.allow`, Codex's `default_tools_approval_mode`); **the delegated agent never
|
|
143
|
+
> prompts** — its behavior is governed by the `BRIDGE_*` policy below. If you expose both
|
|
144
|
+
> agents in one host (`BRIDGE_EXPOSE = "all"`), allowlist both `ask_*` tool names.
|
|
145
|
+
|
|
146
|
+
## Tools
|
|
147
|
+
|
|
148
|
+
### `ask_<agent>` — delegate a task
|
|
149
|
+
|
|
150
|
+
| Param | Type | Notes |
|
|
151
|
+
| ------------------ | ----------------------------------------------- | ------------------------------------------------ |
|
|
152
|
+
| `task` | string (required) | The work to delegate. Include all context — the agent sees nothing else. |
|
|
153
|
+
| `cwd` | string | Working directory for the agent (set it to your project to work on the same files). |
|
|
154
|
+
| `continue_session` | boolean | Resume the last session with this agent (sequential shorthand). |
|
|
155
|
+
| `session_id` | string | Resume an exact session (id from a result's footer). Takes precedence over `continue_session`; use with parallel sessions. |
|
|
156
|
+
| `background` | boolean | Don't block: returns a job id immediately. Poll with `check_job`. For long tasks. |
|
|
157
|
+
| `model` | string | Override the model (e.g. `gpt-5.5`, `opus`). |
|
|
158
|
+
| `reasoning` | `low` \| `medium` \| `high` | Maps to Codex `model_reasoning_effort` / Claude `--effort`. |
|
|
159
|
+
| `thinking` | `off` \| `think` \| `think_hard` \| `ultrathink` | **Claude only** — extended thinking. |
|
|
160
|
+
|
|
161
|
+
Blocking calls time out after `BRIDGE_TIMEOUT_MS` (10 min default). For anything that
|
|
162
|
+
might run longer, use `background: true` — background jobs get `BRIDGE_JOB_TIMEOUT_MS`
|
|
163
|
+
(1 h default) and the caller keeps working while they run.
|
|
164
|
+
|
|
165
|
+
### `check_job` / `cancel_job` — manage background jobs
|
|
166
|
+
|
|
167
|
+
- `check_job` with a `job_id`: status, and the agent's full result once finished.
|
|
168
|
+
- `check_job` without arguments: list all jobs of the session.
|
|
169
|
+
- `cancel_job`: kill a running job.
|
|
170
|
+
|
|
171
|
+
Jobs live in the bridge process's memory: they last for your host session and are gone
|
|
172
|
+
after a restart. There is no push notification (MCP is request/response) — the caller
|
|
173
|
+
polls between its own steps.
|
|
174
|
+
|
|
175
|
+
## Working on the same project
|
|
176
|
+
|
|
177
|
+
Pass your project path as `cwd` and both agents edit the same real files. Within one
|
|
178
|
+
delegation there is no write conflict — the caller is paused (or, for background jobs,
|
|
179
|
+
should avoid editing the same areas). Two habits make this reliable:
|
|
180
|
+
|
|
181
|
+
1. **Re-read changed files after a delegation returns.** The caller's in-context copy is
|
|
182
|
+
stale the moment the child edits the file.
|
|
183
|
+
2. **One writer per area at a time.** There is no locking and no git isolation — the
|
|
184
|
+
working tree and the git index are shared. (Worktree isolation is on the roadmap.)
|
|
185
|
+
|
|
186
|
+
## Configuration (environment variables)
|
|
187
|
+
|
|
188
|
+
Set these in the host's MCP registration (see above). They are read once at startup —
|
|
189
|
+
**callers cannot change policy at runtime.**
|
|
190
|
+
|
|
191
|
+
| Variable | Default | Purpose |
|
|
192
|
+
| ------------------------- | ----------------- | ---------------------------------------------------- |
|
|
193
|
+
| `BRIDGE_EXPOSE` | `all` | Comma list of agents to expose (`codex`, `claude`, `gemini`). |
|
|
194
|
+
| `BRIDGE_MAX_DEPTH` | `3` | Max agent→agent recursion depth (loop guard). |
|
|
195
|
+
| `BRIDGE_TIMEOUT_MS` | `600000` | Timeout per blocking call (10 min). |
|
|
196
|
+
| `BRIDGE_JOB_TIMEOUT_MS` | `3600000` | Timeout per background job (1 h). |
|
|
197
|
+
| `BRIDGE_MAX_OUTPUT_BYTES` | `1000000` | Output cap (protects the caller's context). |
|
|
198
|
+
| `BRIDGE_CLARIFY` | `1` | Tell delegated agents to ask instead of guess. `0` to disable. |
|
|
199
|
+
| `BRIDGE_SANDBOX` | `workspace-write` | Codex sandbox (`read-only`/`workspace-write`/`danger-full-access`). |
|
|
200
|
+
| `BRIDGE_CLAUDE_PERMISSION`| `acceptEdits` | Claude permission mode (`bypassPermissions` for full autonomy). |
|
|
201
|
+
| `BRIDGE_GEMINI_APPROVAL` | `auto_edit` | Gemini approval mode (`default`/`auto_edit`/`yolo`/`plan`). |
|
|
202
|
+
| `BRIDGE_GEMINI_MODEL` | (CLI default) | Default Gemini model. |
|
|
203
|
+
| `BRIDGE_CODEX_MODEL` | (CLI default) | Default Codex model. |
|
|
204
|
+
| `BRIDGE_CODEX_REASONING` | (CLI default) | Default Codex reasoning effort. |
|
|
205
|
+
| `BRIDGE_CLAUDE_MODEL` | (CLI default) | Default Claude model. |
|
|
206
|
+
| `BRIDGE_DEBUG` | (off) | Path of a file to append diagnostic logs to. |
|
|
207
|
+
|
|
208
|
+
## Security notes
|
|
209
|
+
|
|
210
|
+
- **The caller's sandbox does not propagate.** Hosts run MCP servers *outside* their own
|
|
211
|
+
sandbox, so a tightly sandboxed Codex session can still delegate to a Claude that runs
|
|
212
|
+
with normal user privileges (and vice versa). A delegated agent's limits come entirely
|
|
213
|
+
from the `BRIDGE_*` policy you configured — never from the caller's restrictions. Choose
|
|
214
|
+
`BRIDGE_SANDBOX` / `BRIDGE_CLAUDE_PERMISSION` as if the delegated agent were launched
|
|
215
|
+
directly by you, because effectively it is.
|
|
216
|
+
- **Codex delegate:** headless Codex never prompts — actions outside its sandbox simply
|
|
217
|
+
fail. Note that `workspace-write` blocks network for shell commands by default (e.g.
|
|
218
|
+
`npm install` inside a task may fail).
|
|
219
|
+
- **Claude delegate:** `acceptEdits` auto-approves file edits but refuses Bash commands
|
|
220
|
+
outside your allowlist (headless mode cannot prompt). `bypassPermissions` removes all
|
|
221
|
+
gates — use deliberately.
|
|
222
|
+
- **Gemini delegate:** `auto_edit` auto-approves edits; shell commands are denied (no
|
|
223
|
+
prompt is possible headless). `yolo` approves everything. The bridge passes
|
|
224
|
+
`--skip-trust` so gemini's folder-trust feature doesn't silently downgrade the
|
|
225
|
+
approval mode — your `BRIDGE_GEMINI_APPROVAL` is the single source of policy.
|
|
226
|
+
- **Loop guard:** `BRIDGE_DEPTH` is threaded into each spawned child and incremented; at
|
|
227
|
+
`BRIDGE_MAX_DEPTH` further `ask_*` calls are refused, so A→B→A→B recursion cannot burn
|
|
228
|
+
your subscriptions. There is no per-session call-count or cost budget yet — the host
|
|
229
|
+
agent (and your subscription limits) govern how many delegations happen.
|
|
230
|
+
|
|
231
|
+
## Troubleshooting
|
|
232
|
+
|
|
233
|
+
| Symptom | Cause / fix |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| Codex replies "user cancelled MCP tool call" | Headless Codex auto-cancels MCP approval prompts. Set `default_tools_approval_mode = "approve"` on the server entry in `~/.codex/config.toml`. |
|
|
236
|
+
| Delegated `claude` fails: `API Error: 403 Request not allowed` | Your Anthropic traffic needs a proxy, and Codex stripped `HTTP_PROXY`/`HTTPS_PROXY` from the bridge's env. Re-declare them in `[mcp_servers.qantara.env]`. |
|
|
237
|
+
| `codex exec` hangs on "Reading additional input from stdin..." | When scripting Codex, close stdin (`$null | codex exec …` in PowerShell, `codex exec … < /dev/null` in sh). |
|
|
238
|
+
| `continue_session` doesn't resume across separate `codex exec` runs | Session ids live in the bridge process's memory; each headless run spawns a fresh bridge. Works as expected in interactive hosts. |
|
|
239
|
+
| A blocking call dies at 10 minutes | Use `background: true` (1 h budget), or raise `BRIDGE_TIMEOUT_MS`. |
|
|
240
|
+
| `ask_gemini` fails with exit code 41 / "set an Auth method" | The gemini CLI isn't logged in. Run `gemini` once interactively, or set `GEMINI_API_KEY` in the bridge's env block. |
|
|
241
|
+
| Gemini `continue_session` doesn't find the session | Gemini sessions are project-scoped (keyed by cwd) — resume only works with the same `cwd` as the original call. |
|
|
242
|
+
|
|
243
|
+
## Adding another agent
|
|
244
|
+
|
|
245
|
+
1. Create `src/runners/<name>.ts` implementing the `AgentRunner` interface from
|
|
246
|
+
[`src/registry.ts`](src/registry.ts).
|
|
247
|
+
2. `register(<name>Runner)` in [`src/server.ts`](src/server.ts).
|
|
248
|
+
|
|
249
|
+
No changes to the MCP layer or core are needed — an `ask_<name>` tool appears automatically.
|
|
250
|
+
|
|
251
|
+
## Smoke tests
|
|
252
|
+
|
|
253
|
+
```powershell
|
|
254
|
+
node smoke.mjs # calls each runner directly with a trivial task
|
|
255
|
+
node mcp-test.mjs # connects an MCP client, lists tools, calls one
|
|
256
|
+
node mcp-test-claude.mjs # drives the claude runner through MCP
|
|
257
|
+
node mcp-test-gemini.mjs # drives the gemini runner through MCP
|
|
258
|
+
node mcp-test-jobs.mjs # background jobs: start, poll, result, cancel, list
|
|
259
|
+
node mcp-test-session.mjs # explicit resume: teach a word, resume by session_id
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Roadmap
|
|
263
|
+
|
|
264
|
+
- **Git worktree isolation** — run each delegation in a temp worktree and return a
|
|
265
|
+
reviewable diff instead of editing the shared tree.
|
|
266
|
+
- Live progress for background jobs (parse the child's JSON stream incrementally).
|
|
267
|
+
- Per-session call-count / cost budget guard.
|
|
268
|
+
- More adapters (Aider, …). Gemini CLI is already included.
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* qantara CLI entry point.
|
|
4
|
+
* qantara → run the MCP server on stdio (what hosts invoke)
|
|
5
|
+
* qantara setup → detect installed agent CLIs and register the bridge
|
|
6
|
+
* into each host's config (--dry-run to preview)
|
|
7
|
+
* qantara --version → print the package version
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
const cmd = process.argv[2];
|
|
13
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
|
|
15
|
+
process.stdout.write(`qantara ${pkg.version}\n`);
|
|
16
|
+
}
|
|
17
|
+
else if (cmd === "setup") {
|
|
18
|
+
const { runSetup } = await import("./setup.js");
|
|
19
|
+
await runSetup(process.argv.slice(3));
|
|
20
|
+
}
|
|
21
|
+
else if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
22
|
+
process.stdout.write("qantara — an MCP bridge that lets coding agents delegate to each other\n\n" +
|
|
23
|
+
"Usage:\n" +
|
|
24
|
+
" qantara Run the MCP server on stdio (hosts invoke this)\n" +
|
|
25
|
+
" qantara setup Register the bridge into Claude Code / Codex / Gemini\n" +
|
|
26
|
+
" qantara setup --dry-run Show what setup would change, without writing\n" +
|
|
27
|
+
" qantara --version Print the package version\n");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
await import("./server.js");
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CACV,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,EACnE,MAAM,CACP,CACF,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;AACnD,CAAC;KAAM,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;IAC3B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC;KAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;IAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4EAA4E;QAC1E,UAAU;QACV,wEAAwE;QACxE,8EAA8E;QAC9E,6EAA6E;QAC7E,kDAAkD,CACrD,CAAC;AACJ,CAAC;KAAM,CAAC;IACN,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central, env-driven configuration. All knobs are environment variables so the
|
|
3
|
+
* same binary can be registered into different hosts with different scoping
|
|
4
|
+
* (e.g. expose only `codex` inside Claude Code, only `claude` inside Codex).
|
|
5
|
+
*/
|
|
6
|
+
function num(name, fallback) {
|
|
7
|
+
const raw = process.env[name];
|
|
8
|
+
if (!raw)
|
|
9
|
+
return fallback;
|
|
10
|
+
const n = Number(raw);
|
|
11
|
+
return Number.isFinite(n) ? n : fallback;
|
|
12
|
+
}
|
|
13
|
+
function list(name) {
|
|
14
|
+
const raw = process.env[name];
|
|
15
|
+
if (!raw)
|
|
16
|
+
return [];
|
|
17
|
+
return raw
|
|
18
|
+
.split(",")
|
|
19
|
+
.map((s) => s.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
export const config = {
|
|
23
|
+
/** Hard timeout per blocking agent invocation. */
|
|
24
|
+
timeoutMs: num("BRIDGE_TIMEOUT_MS", 600_000),
|
|
25
|
+
/** Hard timeout per background job (longer: jobs don't block the caller). */
|
|
26
|
+
jobTimeoutMs: num("BRIDGE_JOB_TIMEOUT_MS", 3_600_000),
|
|
27
|
+
/** Max bytes of captured output, to protect the calling model's context. */
|
|
28
|
+
maxOutputBytes: num("BRIDGE_MAX_OUTPUT_BYTES", 1_000_000),
|
|
29
|
+
/** Which agents to expose as tools (empty => all registered). */
|
|
30
|
+
expose: list("BRIDGE_EXPOSE"),
|
|
31
|
+
/**
|
|
32
|
+
* Tell delegated agents that a supervising agent can answer follow-up
|
|
33
|
+
* questions (via session resume), so they ask instead of guessing when a
|
|
34
|
+
* task is ambiguous. Disable with BRIDGE_CLARIFY=0.
|
|
35
|
+
*/
|
|
36
|
+
clarify: process.env.BRIDGE_CLARIFY !== "0",
|
|
37
|
+
/** Recursion / fan-out guard. */
|
|
38
|
+
maxDepth: num("BRIDGE_MAX_DEPTH", 3),
|
|
39
|
+
/** Current depth, threaded in from a parent bridge invocation (0 at the top). */
|
|
40
|
+
depth: num("BRIDGE_DEPTH", 0),
|
|
41
|
+
/** Codex execution settings. */
|
|
42
|
+
codex: {
|
|
43
|
+
sandbox: process.env.BRIDGE_SANDBOX ?? "workspace-write",
|
|
44
|
+
model: process.env.BRIDGE_CODEX_MODEL, // undefined => CLI default
|
|
45
|
+
reasoning: process.env.BRIDGE_CODEX_REASONING, // undefined => CLI default
|
|
46
|
+
},
|
|
47
|
+
/** Claude execution settings. */
|
|
48
|
+
claude: {
|
|
49
|
+
permissionMode: process.env.BRIDGE_CLAUDE_PERMISSION ?? "acceptEdits",
|
|
50
|
+
model: process.env.BRIDGE_CLAUDE_MODEL, // undefined => CLI default
|
|
51
|
+
},
|
|
52
|
+
/** Gemini execution settings. */
|
|
53
|
+
gemini: {
|
|
54
|
+
// default | auto_edit | yolo | plan — auto_edit mirrors Claude's acceptEdits.
|
|
55
|
+
approvalMode: process.env.BRIDGE_GEMINI_APPROVAL ?? "auto_edit",
|
|
56
|
+
model: process.env.BRIDGE_GEMINI_MODEL, // undefined => CLI default
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
/** Env to pass to a spawned child agent so depth increments and propagates. */
|
|
60
|
+
export function childEnv(currentDepth) {
|
|
61
|
+
return { BRIDGE_DEPTH: String(currentDepth + 1) };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,SAAS,GAAG,CAAC,IAAY,EAAE,QAAgB;IACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC3C,CAAC;AAED,SAAS,IAAI,CAAC,IAAY;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,kDAAkD;IAClD,SAAS,EAAE,GAAG,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAC5C,6EAA6E;IAC7E,YAAY,EAAE,GAAG,CAAC,uBAAuB,EAAE,SAAS,CAAC;IACrD,4EAA4E;IAC5E,cAAc,EAAE,GAAG,CAAC,yBAAyB,EAAE,SAAS,CAAC;IAEzD,iEAAiE;IACjE,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC;IAE7B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG;IAE3C,iCAAiC;IACjC,QAAQ,EAAE,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACpC,iFAAiF;IACjF,KAAK,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;IAE7B,gCAAgC;IAChC,KAAK,EAAE;QACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,iBAAiB;QACxD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,2BAA2B;QAClE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,2BAA2B;KAC3E;IAED,iCAAiC;IACjC,MAAM,EAAE;QACN,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,aAAa;QACrE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,2BAA2B;KACpE;IAED,iCAAiC;IACjC,MAAM,EAAE;QACN,8EAA8E;QAC9E,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,WAAW;QAC/D,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,2BAA2B;KACpE;CACO,CAAC;AAEX,+EAA+E;AAC/E,MAAM,UAAU,QAAQ,CAAC,YAAoB;IAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC,EAAE,CAAC;AACpD,CAAC"}
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { appendFileSync } from "node:fs";
|
|
2
|
+
const target = process.env.BRIDGE_DEBUG;
|
|
3
|
+
// Startup diagnostic. Hosts may launch the bridge with a stripped environment
|
|
4
|
+
// (Codex passes only a whitelist of core OS vars), so logging the received env
|
|
5
|
+
// keys up front makes those problems visible without a debugger.
|
|
6
|
+
debug(`bridge process started | ` +
|
|
7
|
+
`BRIDGE_EXPOSE=${process.env.BRIDGE_EXPOSE ?? "(unset)"} | ` +
|
|
8
|
+
`BRIDGE_DEPTH=${process.env.BRIDGE_DEPTH ?? "(unset)"} | ` +
|
|
9
|
+
`cwd=${process.cwd()} | ` +
|
|
10
|
+
`envKeys=${Object.keys(process.env).sort().join(",")}`);
|
|
11
|
+
/**
|
|
12
|
+
* Appends a timestamped line to the file named in BRIDGE_DEBUG (if set).
|
|
13
|
+
* Used to diagnose runs where stderr is not visible (e.g. spawned by a host).
|
|
14
|
+
*/
|
|
15
|
+
export function debug(msg) {
|
|
16
|
+
if (!target)
|
|
17
|
+
return;
|
|
18
|
+
try {
|
|
19
|
+
appendFileSync(target, `[${new Date().toISOString()}] ${msg}\n`);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// best-effort logging only
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAExC,8EAA8E;AAC9E,+EAA+E;AAC/E,iEAAiE;AACjE,KAAK,CACH,2BAA2B;IACzB,iBAAiB,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,SAAS,KAAK;IAC5D,gBAAgB,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,SAAS,KAAK;IAC1D,OAAO,OAAO,CAAC,GAAG,EAAE,KAAK;IACzB,WAAW,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACzD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,IAAI,CAAC;QACH,cAAc,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;AACH,CAAC"}
|
package/dist/exec.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { debug } from "./debug.js";
|
|
3
|
+
export class ExecError extends Error {
|
|
4
|
+
result;
|
|
5
|
+
constructor(message, result) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.result = result;
|
|
8
|
+
this.name = "ExecError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Spawn a command with an args array (never a shell string) so arbitrary task
|
|
13
|
+
* text cannot be interpreted by the shell. Enforces a timeout and an output cap.
|
|
14
|
+
*
|
|
15
|
+
* On Windows, `.cmd` shims (npm-installed CLIs) are not directly executable by
|
|
16
|
+
* spawn without a shell, so we resolve the actual command up front via PATHEXT.
|
|
17
|
+
*/
|
|
18
|
+
export function runCommand(command, args, opts) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
// On Windows, .cmd/.bat shims require shell:true to be invoked. We keep the
|
|
21
|
+
// args array form so values are still passed as discrete argv entries.
|
|
22
|
+
const isWindows = process.platform === "win32";
|
|
23
|
+
debug(`spawn command=${command} args=${JSON.stringify(args)} cwd=${opts.cwd ?? "(inherit)"} ` +
|
|
24
|
+
`shell=${isWindows} PATH_present=${Boolean(process.env.PATH || process.env.Path)}`);
|
|
25
|
+
const child = spawn(command, args, {
|
|
26
|
+
cwd: opts.cwd,
|
|
27
|
+
env: { ...process.env, ...opts.env },
|
|
28
|
+
shell: isWindows, // needed for npm .cmd shims on Windows
|
|
29
|
+
windowsHide: true,
|
|
30
|
+
});
|
|
31
|
+
let stdout = "";
|
|
32
|
+
let stderr = "";
|
|
33
|
+
let truncated = false;
|
|
34
|
+
let timedOut = false;
|
|
35
|
+
let cancelled = false;
|
|
36
|
+
let settled = false;
|
|
37
|
+
const cap = opts.maxOutputBytes;
|
|
38
|
+
const timer = setTimeout(() => {
|
|
39
|
+
timedOut = true;
|
|
40
|
+
child.kill("SIGKILL");
|
|
41
|
+
}, opts.timeoutMs);
|
|
42
|
+
const onAbort = () => {
|
|
43
|
+
cancelled = true;
|
|
44
|
+
child.kill("SIGKILL");
|
|
45
|
+
};
|
|
46
|
+
if (opts.signal) {
|
|
47
|
+
if (opts.signal.aborted)
|
|
48
|
+
onAbort();
|
|
49
|
+
else
|
|
50
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
51
|
+
}
|
|
52
|
+
if (opts.stdin !== undefined) {
|
|
53
|
+
child.stdin.write(opts.stdin);
|
|
54
|
+
child.stdin.end();
|
|
55
|
+
}
|
|
56
|
+
child.stdout.on("data", (chunk) => {
|
|
57
|
+
if (stdout.length < cap) {
|
|
58
|
+
stdout += chunk.toString("utf8");
|
|
59
|
+
if (stdout.length >= cap) {
|
|
60
|
+
stdout = stdout.slice(0, cap);
|
|
61
|
+
truncated = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
child.stderr.on("data", (chunk) => {
|
|
66
|
+
if (stderr.length < cap) {
|
|
67
|
+
stderr += chunk.toString("utf8");
|
|
68
|
+
if (stderr.length >= cap)
|
|
69
|
+
stderr = stderr.slice(0, cap);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
child.on("error", (err) => {
|
|
73
|
+
if (settled)
|
|
74
|
+
return;
|
|
75
|
+
settled = true;
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
debug(`child error for ${command}: ${err.message}`);
|
|
78
|
+
// ENOENT => binary missing; surface a clear, actionable message.
|
|
79
|
+
const hint = err.code === "ENOENT"
|
|
80
|
+
? ` (is "${command}" installed and on PATH?)`
|
|
81
|
+
: "";
|
|
82
|
+
reject(new ExecError(`Failed to launch "${command}"${hint}: ${err.message}`));
|
|
83
|
+
});
|
|
84
|
+
child.on("close", (code) => {
|
|
85
|
+
if (settled)
|
|
86
|
+
return;
|
|
87
|
+
settled = true;
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
debug(`child close ${command} code=${code} stdoutLen=${stdout.length} ` +
|
|
90
|
+
`stderrTail=${JSON.stringify(stderr.slice(-300))}`);
|
|
91
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
92
|
+
const result = { code, stdout, stderr, timedOut, truncated };
|
|
93
|
+
if (cancelled) {
|
|
94
|
+
reject(new ExecError(`"${command}" was cancelled`, result));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (timedOut) {
|
|
98
|
+
reject(new ExecError(`"${command}" timed out after ${opts.timeoutMs}ms`, result));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
resolve(result);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=exec.js.map
|
package/dist/exec.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AA0BnC,MAAM,OAAO,SAAU,SAAQ,KAAK;IACI;IAAtC,YAAY,OAAe,EAAW,MAAmB;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QADqB,WAAM,GAAN,MAAM,CAAa;QAEvD,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,IAAc,EACd,IAAiB;IAEjB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,4EAA4E;QAC5E,uEAAuE;QACvE,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;QAE/C,KAAK,CACH,iBAAiB,OAAO,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,IAAI,WAAW,GAAG;YACrF,SAAS,SAAS,iBAAiB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CACrF,CAAC;QAEF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YACjC,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;YACpC,KAAK,EAAE,SAAS,EAAE,uCAAuC;YACzD,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC;QAEhC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnB,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,SAAS,GAAG,IAAI,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,EAAE,CAAC;;gBAC9B,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACjC,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;oBACzB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;oBAC9B,SAAS,GAAG,IAAI,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACjC,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG;oBAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,CAAC,mBAAmB,OAAO,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/D,iEAAiE;YACjE,MAAM,IAAI,GACP,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAC9C,CAAC,CAAC,SAAS,OAAO,2BAA2B;gBAC7C,CAAC,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,SAAS,CAAC,qBAAqB,OAAO,IAAI,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,CACH,eAAe,OAAO,SAAS,IAAI,cAAc,MAAM,CAAC,MAAM,GAAG;gBAC/D,cAAc,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CACrD,CAAC;YACF,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,MAAM,GAAe,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YACzE,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,OAAO,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YACD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CACJ,IAAI,SAAS,CACX,IAAI,OAAO,qBAAqB,IAAI,CAAC,SAAS,IAAI,EAClD,MAAM,CACP,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/jobs.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const jobs = new Map();
|
|
2
|
+
let counter = 0;
|
|
3
|
+
export function createJob(agent, task) {
|
|
4
|
+
counter += 1;
|
|
5
|
+
const job = {
|
|
6
|
+
id: `${agent}-${counter}`,
|
|
7
|
+
agent,
|
|
8
|
+
taskSummary: task.split("\n")[0].slice(0, 120),
|
|
9
|
+
status: "running",
|
|
10
|
+
startedAt: Date.now(),
|
|
11
|
+
abort: new AbortController(),
|
|
12
|
+
};
|
|
13
|
+
jobs.set(job.id, job);
|
|
14
|
+
return job;
|
|
15
|
+
}
|
|
16
|
+
export function getJob(id) {
|
|
17
|
+
return jobs.get(id);
|
|
18
|
+
}
|
|
19
|
+
export function listJobs() {
|
|
20
|
+
return [...jobs.values()];
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=jobs.js.map
|
package/dist/jobs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAsBA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAe,CAAC;AACpC,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,IAAY;IACnD,OAAO,IAAI,CAAC,CAAC;IACb,MAAM,GAAG,GAAQ;QACf,EAAE,EAAE,GAAG,KAAK,IAAI,OAAO,EAAE;QACzB,KAAK;QACL,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9C,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,KAAK,EAAE,IAAI,eAAe,EAAE;KAC7B,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,EAAU;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAC5B,CAAC"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single abstraction the whole bridge is built on. Every backend agent
|
|
3
|
+
* (Codex, Claude, and later Gemini / Antigravity / Aider / ...) is just an
|
|
4
|
+
* adapter implementing this interface. Adding a new agent = one new adapter
|
|
5
|
+
* file + one registry entry, with no changes to the MCP server or core.
|
|
6
|
+
*/
|
|
7
|
+
const registry = new Map();
|
|
8
|
+
export function register(runner) {
|
|
9
|
+
registry.set(runner.name, runner);
|
|
10
|
+
}
|
|
11
|
+
export function getRunner(name) {
|
|
12
|
+
return registry.get(name);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns the runners that should be exposed as tools, filtered by the
|
|
16
|
+
* `expose` allow-list (empty/`all` => every registered runner).
|
|
17
|
+
*/
|
|
18
|
+
export function exposedRunners(expose) {
|
|
19
|
+
const all = [...registry.values()];
|
|
20
|
+
if (expose.length === 0 || expose.includes("all"))
|
|
21
|
+
return all;
|
|
22
|
+
return all.filter((r) => expose.includes(r.name));
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmDH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;AAEhD,MAAM,UAAU,QAAQ,CAAC,MAAmB;IAC1C,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAgB;IAC7C,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC9D,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC"}
|