llm-switcher 1.2.12 → 1.3.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.
- package/CHANGELOG.md +28 -0
- package/README.md +109 -19
- package/README.vi.md +107 -19
- package/config.example.json +13 -1
- package/formats.mjs +128 -12
- package/hook-status.mjs +5 -4
- package/mcp.mjs +6 -5
- package/package.json +1 -1
- package/plugin.mjs +80 -9
- package/proxy.mjs +155 -18
- package/shim.mjs +62 -20
- package/skills/llm-switcher/SKILL.md +6 -6
- package/state.mjs +50 -13
- package/switch.mjs +15 -9
- package/tests/agy.e2e.test.mjs +534 -0
- package/tests/agy.unit.test.mjs +190 -0
- package/tests/dashboard.e2e.test.mjs +18 -17
- package/tests/formats.test.mjs +83 -2
- package/tests/gateway.e2e.test.mjs +20 -2
- package/tests/mcp.test.mjs +5 -5
- package/tests/plugin.test.mjs +4 -2
- package/tests/shim.test.mjs +165 -0
- package/tests/state.test.mjs +8 -8
- package/ui.html +51 -7
- package/verify-gateway.mjs +35 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog — LLM Switcher
|
|
2
2
|
|
|
3
|
+
## Release 1.3.1
|
|
4
|
+
|
|
5
|
+
- **agy note:** `switch plugin status` now names agy 1.2.14. This release of agy also loads `~/.gemini/antigravity-cli/hooks.json` but does not run its hooks (measured 2026-10-03), so the shim toast is still the notice for agy.
|
|
6
|
+
- **Tests:** A test covers an error in a converted agy stream. The error goes to agy bare, as Code Assist sends it. The agy end-to-end tests now pass in any order.
|
|
7
|
+
|
|
8
|
+
## Release 1.3.0
|
|
9
|
+
|
|
10
|
+
- **Antigravity CLI (`agy`):** A third tool, beside Claude Code and Codex. A profile with `"tool": "agy"` routes it, and `switch agy <profile>` turns it on. `switch off agy` turns it off and leaves the other tools alone. The dashboard has an agy row and an **agy on intact** template.
|
|
11
|
+
- **No proxy and no certificate for agy:** The `agy` shim sets `CLOUD_CODE_URL` to the gateway. agy is a Go program, and Go on Windows trusts only the system store, so an interceptor would need a certificate in that store. `CLOUD_CODE_URL` needs none. agy also ignores `HTTPS_PROXY`.
|
|
12
|
+
- **What agy sends where:** Only the agent turns of agy go to its profile. Checkpoint summaries, the model list, quota, sign-in state and analytics go to Google with the token of the person, unchanged. While agy is off, every call goes to Google.
|
|
13
|
+
- **Remote control:** agy reaches its remote control through `jetski-webchannel.googleapis.com`, not through `CLOUD_CODE_URL`. It stays connected while the switcher routes agy.
|
|
14
|
+
- **Model slot:** An agy profile has one slot, `main`. A `*` in the value stands for the model that agy picked, so `antigravity/*` keeps the choice of agy on an intact pool.
|
|
15
|
+
- **Conversion:** An agent turn on any other upstream is converted from Code Assist and back. Each answer chunk is wrapped as `{"response": …, "traceId": …}`, and each function call keeps its id, so agy matches the tool result.
|
|
16
|
+
- **Bifrost for agy and Codex:** When intact names the client of a model (`bifrost_ua`), the request crosses unchanged. agy goes to `/v1/v1internal:<method>` and Codex (HTTP) to `/v1/responses`. Only the key changes. The Google token of agy never reaches intact. Codex over the WebSocket transport keeps the normal route.
|
|
17
|
+
- **Usage of a passthrough:** The usage tap reads Gemini `usageMetadata`, so a Bifrost answer of agy reports its tokens in the inspector.
|
|
18
|
+
- **Launch notice:** `switch plugin install` also writes an entry into `~/.gemini/antigravity-cli/hooks.json`. agy 1.2.7 loads this file but does not run global hooks yet, so the shim toast is the notice for agy today. `switch plugin status` says so.
|
|
19
|
+
- **The agy shim proves the gateway first:** agy sends its Google token in clear text to `CLOUD_CODE_URL`, so the shim asks the gateway for its identity proof (`verify-gateway.mjs`). If no proof comes within 3 seconds, the shim removes the variable and agy keeps its official endpoint.
|
|
20
|
+
- **Passthrough limits:** A Code Assist call that the gateway does not route has an idle limit (`LLM_SWITCHER_CODE_ASSIST_TIMEOUT_MS`, default 5 minutes). A broken upstream answer cuts the connection to agy instead of ending it as if it were complete.
|
|
21
|
+
- **Passthrough headers:** A call to Google keeps every header of agy, `x-goog-*` included. Only the headers of the gateway (`x-llm-profile`, `x-profile`, `x-llm-switcher-token`, `x-intact-*`) and the proxy headers (`proxy-authorization`, `x-forwarded-*`, `x-real-ip`) are removed.
|
|
22
|
+
- **No silent fallback to Google:** While agy is routed, an agent turn that the gateway cannot route gets an error. A config that never loaded gives 503. A named profile that does not exist gives 400. A body that does not parse gives 400. A request with no model on a `*` slot gives 400.
|
|
23
|
+
- **Bifrost lookup:** A failed lookup (no answer, or any answer that is not 2xx, a 404 too) keeps the normal route for 30 seconds only (`LLM_SWITCHER_BIFROST_RETRY_MS`), and the gateway prints one line for each series of failures. Before, an error answer held the normal route for 10 minutes without a word.
|
|
24
|
+
- **Broken Bifrost stream:** If a Bifrost or direct stream breaks, the gateway cuts the connection. Before, the client got a clean end of a half answer.
|
|
25
|
+
- **`switch plugin`:** Each tool installs on its own, so a fault in one hooks file does not stop the others. If the path of `hook-status.mjs` holds a space, agy is skipped (`[Skip]`), not failed. An agy `hooks.json` whose root is not an object is never changed. `switch plugin status` names an agy `hooks.json` that does not parse.
|
|
26
|
+
- **Migration:** The migration of an old `config.json` keeps `activeProfiles.agy`, and a pointer to a profile renamed because its key became a command word (`agy` becomes `agy-profile`) follows the rename.
|
|
27
|
+
- **`switch shim status` on Windows:** It now finds the command the way Windows does, by directory and then by `PATHEXT`. Before, an `agy.exe` earlier on `PATH` than the shim was reported as "shim active".
|
|
28
|
+
- **The launch files of agy** (`env-agy.*`, `route-agy.txt`) are ignored by git and npm, like those of the other tools.
|
|
29
|
+
- **Needs intact 0.1.14 or newer** for Bifrost of agy and Codex.
|
|
30
|
+
|
|
3
31
|
## Release 1.2.12
|
|
4
32
|
|
|
5
33
|
- **Update now:** The update notice on the dashboard has an **Update now** button. The gateway installs the release, and a new gateway process starts on the new code. Then the dashboard reloads. `switch update` does the same from a terminal.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<b>Zero-dependency, multi-protocol edge gateway & provider switcher</b><br>
|
|
5
|
-
Bridge <b>Claude Code</b> and <b>
|
|
5
|
+
Bridge <b>Claude Code</b>, <b>Codex</b> and the <b>Antigravity CLI</b> to any upstream LLM API: OpenAI-compatible, Anthropic, or Vertex.<br>
|
|
6
6
|
Full bi-directional protocol conversion, official-model context windows, thinking protocol extraction, and edge message healing.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
> **LLM Switcher solves this at the local network edge:**
|
|
28
28
|
> - **100% Native Emulation:** Normalizes upstream APIs (intact, 9Router, Vertex, DeepSeek) into genuine Anthropic SSE (`thinking_delta` + `tool_use`) for Claude Code, and genuine Responses API events for Codex.
|
|
29
29
|
> - **Client-Side Edge Companion:** Intentionally offloads heavy account pooling and key rotation to **[intact](https://github.com/louisphamdev/intact)** (recommended) or 9Router (basic alternative), keeping LLM Switcher zero-dependency and bloat-free.
|
|
30
|
-
> - **Targeted Tool Scope:** Built specifically for **Claude Code** and **
|
|
30
|
+
> - **Targeted Tool Scope:** Built specifically for **Claude Code**, **OpenAI Codex** and the **Antigravity CLI** (`agy`). OpenCode supports custom models without shims; refer to intact for account pooling.
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
@@ -132,9 +132,9 @@ LLM Switcher acts as a transparent man-in-the-middle without ever touching clien
|
|
|
132
132
|
|
|
133
133
|
- **Zero-Dependency Architecture:** Built 100% on Node.js standard libraries (`http`, `fs`, `os`, `path`, `fetch`). No npm dependencies, no bundled runtime bloat, cold-start under 50ms.
|
|
134
134
|
- **Bi-Directional Protocol Conversion:**
|
|
135
|
-
- **
|
|
135
|
+
- **3 Client Inbound Formats:** Anthropic Messages (Claude Code), Codex Responses API (Codex), Code Assist (Antigravity CLI).
|
|
136
136
|
- **3 Upstream Outbound Formats:** OpenAI Chat, Anthropic Native, Vertex Native.
|
|
137
|
-
- **Bifrost:**
|
|
137
|
+
- **Bifrost:** A coding tool on an account of its own provider in intact goes through unchanged. Only the key changes. No conversion, no healer, no filter. This works for Claude Code, Codex and the Antigravity CLI. The gateway turns it on by itself. [Bifrost](#bifrost-a-coding-tool-to-its-own-account-on-intact)
|
|
138
138
|
- **Multi-Active CLI Routing:** Run Claude Code on Profile A, Codex on Profile B, and Cursor on Profile C simultaneously on a single gateway instance.
|
|
139
139
|
- **Deep Thinking & Reasoning Extraction:** Tested on 48 live response combinations. Accurately extracts `reasoning_content`, `<think>` tags, Vertex `thought` parts, and signatures into native `thinking_delta` blocks.
|
|
140
140
|
- **Edge Healer Engine (Anti-Collision for Token Optimizers):**
|
|
@@ -148,7 +148,12 @@ LLM Switcher acts as a transparent man-in-the-middle without ever touching clien
|
|
|
148
148
|
|
|
149
149
|
---
|
|
150
150
|
|
|
151
|
-
## Recent Highlights (v1.
|
|
151
|
+
## Recent Highlights (v1.3.0)
|
|
152
|
+
|
|
153
|
+
- **Antigravity CLI (`agy`):** A third tool with its own profile. `switch agy <profile>` routes it. agy reaches the gateway through `CLOUD_CODE_URL`, so it needs no proxy and no certificate. Remote control keeps working, because it uses its own host. [Antigravity CLI setup](#antigravity-cli-agy-setup)
|
|
154
|
+
- **Bifrost for agy and Codex:** On an intact pool, agy and Codex requests cross unchanged, as Claude Code requests already did.
|
|
155
|
+
|
|
156
|
+
## Earlier Highlights (v1.2.0)
|
|
152
157
|
|
|
153
158
|
- **Zero-Mutation Interceptor:** All traffic routes via `HTTPS_PROXY` without editing client configs (`~/.claude/settings.json`, `~/.codex/config.toml`).
|
|
154
159
|
- **Concurrent Multi-Tool Support:** Simultaneously configures `{ claude, codex }` profiles with dynamic, zero-downtime switching (`POST /_control/active-tools`).
|
|
@@ -224,12 +229,14 @@ Each tool gets its own file instead, and only the matching shim loads it:
|
|
|
224
229
|
| --- | --- |
|
|
225
230
|
| `env-claude.sh` / `env-claude.cmd` | the `claude` shim |
|
|
226
231
|
| `env-codex.sh` / `env-codex.cmd` | the `codex` shim |
|
|
232
|
+
| `env-agy.sh` / `env-agy.cmd` | the `agy` shim |
|
|
227
233
|
| `env.sh` / `env.cmd` | nobody. Neutral stub, kept only so an old rc line stays silent |
|
|
228
234
|
|
|
229
235
|
An empty per-tool file means that tool is off: the shim then leaves the environment alone and the
|
|
230
236
|
tool reaches its official endpoint. Before it loads anything, the shim also scrubs a stale
|
|
231
237
|
`ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` or `ANTHROPIC_DEFAULT_<TIER>_MODEL` inherited from an older
|
|
232
|
-
release or from your own shell, so `switch off` really is off.
|
|
238
|
+
release or from your own shell, so `switch off` really is off. The `agy` shim removes a
|
|
239
|
+
`CLOUD_CODE_URL` that points at the gateway while agy is off.
|
|
233
240
|
|
|
234
241
|
In practice you never call these files. `switch shim install` puts `~/.llm-switcher/bin` on `PATH`,
|
|
235
242
|
and every `claude` and `codex` invocation — including `claude --resume` in a brand-new terminal —
|
|
@@ -273,6 +280,63 @@ On Windows, add `%USERPROFILE%\.llm-switcher\bin` before the real Codex director
|
|
|
273
280
|
|
|
274
281
|
The shim never edits `~/.codex/config.toml`. Codex reaches the gateway cleanly through `HTTPS_PROXY` and the interceptor, retaining its own model names and context windows. Model catalog entries are served dynamically at `/v1/models`.
|
|
275
282
|
|
|
283
|
+
### Antigravity CLI (agy) setup
|
|
284
|
+
|
|
285
|
+
agy is the command-line client of Google Antigravity. It reads the variable `CLOUD_CODE_URL` and
|
|
286
|
+
sends every Code Assist call to that address. The `agy` shim sets this variable to the gateway,
|
|
287
|
+
so agy needs no proxy and no certificate.
|
|
288
|
+
|
|
289
|
+
1. Sign in to agy with your Google account. agy needs this sign-in for its own calls.
|
|
290
|
+
2. Create a profile with `"tool": "agy"`. The dashboard has a template, **agy on intact**.
|
|
291
|
+
3. Run these commands, then open a new terminal:
|
|
292
|
+
```bash
|
|
293
|
+
switch shim install
|
|
294
|
+
switch agy <profile>
|
|
295
|
+
agy
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The gateway routes the calls of agy as follows:
|
|
299
|
+
|
|
300
|
+
| Call | Where it goes |
|
|
301
|
+
| --- | --- |
|
|
302
|
+
| An agent turn (`requestType: "agent"`) | The profile of agy: Bifrost on intact, or a conversion to any upstream |
|
|
303
|
+
| A checkpoint summary, the model list, quota, sign-in state, analytics | Google, with your own token |
|
|
304
|
+
| Any call while agy is off | Google |
|
|
305
|
+
|
|
306
|
+
A call to Google keeps its headers and its bytes. The gateway removes only its own headers
|
|
307
|
+
(`x-llm-profile`, `x-profile`, `x-llm-switcher-token`, and the contract-lab headers `x-intact-probe`
|
|
308
|
+
and `x-intact-trace`) and the headers of a proxy in front of it (`proxy-authorization`,
|
|
309
|
+
`x-forwarded-*`, `x-real-ip`).
|
|
310
|
+
|
|
311
|
+
While agy is routed, the gateway refuses an agent turn that it cannot route. It never sends that
|
|
312
|
+
turn to Google instead:
|
|
313
|
+
|
|
314
|
+
| Condition | Answer to agy |
|
|
315
|
+
| --- | --- |
|
|
316
|
+
| `config.json` never loaded | 503 |
|
|
317
|
+
| The request names a profile that does not exist (`x-llm-profile`, `?profile=`) | 400 |
|
|
318
|
+
| The request body does not parse | 400 |
|
|
319
|
+
| The request names no model, and the `main` slot holds a `*` | 400 |
|
|
320
|
+
|
|
321
|
+
The profile has one model slot, `main`. A `*` in the value stands for the model that agy picked:
|
|
322
|
+
|
|
323
|
+
- `antigravity/*` sends `gemini-3.8-flash-high` to intact as `antigravity/gemini-3.8-flash-high`.
|
|
324
|
+
intact names agy as the own client of its Antigravity pool, so the request crosses through
|
|
325
|
+
Bifrost.
|
|
326
|
+
- `claude/claude-opus-5` sends every agent turn to that model. The gateway converts the Code
|
|
327
|
+
Assist request, and converts the answer back to the Code Assist shape.
|
|
328
|
+
|
|
329
|
+
Remote control of agy uses its own host (`jetski-webchannel.googleapis.com`). It does not use
|
|
330
|
+
`CLOUD_CODE_URL`, so it works while the switcher routes agy.
|
|
331
|
+
|
|
332
|
+
The Google token of agy goes only to Google. A Bifrost or converted request carries the key of the
|
|
333
|
+
profile, never this token.
|
|
334
|
+
|
|
335
|
+
agy sends this token in clear text to `CLOUD_CODE_URL`. For this reason, the `agy` shim asks the
|
|
336
|
+
gateway for its identity proof before it gives agy the variable. If the port does not answer with
|
|
337
|
+
the proof of this install within 3 seconds, the shim removes the variable, prints one line, and agy
|
|
338
|
+
uses its official endpoint.
|
|
339
|
+
|
|
276
340
|
### Blindfold mode (optional)
|
|
277
341
|
|
|
278
342
|
A base URL override makes Codex print one line on its own `/model` screen:
|
|
@@ -404,18 +468,27 @@ Add the server to your MCP configuration (for example `opencode.jsonc`, `claude_
|
|
|
404
468
|
|
|
405
469
|
---
|
|
406
470
|
|
|
407
|
-
## Bifrost:
|
|
471
|
+
## Bifrost: a coding tool to its own account on intact
|
|
408
472
|
|
|
409
|
-
Bifrost sends a
|
|
473
|
+
Bifrost sends a request of a coding tool to intact without a change. Only the key changes. The healer, `thinkingMode`, and the format conversion do not run.
|
|
410
474
|
|
|
411
475
|
Bifrost has no setting. The gateway turns it on for each request when two conditions are true:
|
|
412
476
|
|
|
413
|
-
1. intact gives `bifrost_ua` for the mapped model in `GET /v1/models/{model}`. intact gives it only for a model of
|
|
477
|
+
1. intact gives `bifrost_ua` for the mapped model in `GET /v1/models/{model}`. intact gives it only for a model of an account that belongs to the provider of the tool.
|
|
414
478
|
2. The `User-Agent` of the client starts with the `bifrost_ua` value.
|
|
415
479
|
|
|
416
|
-
|
|
480
|
+
| Tool | `bifrost_ua` | Path on intact |
|
|
481
|
+
| --- | --- | --- |
|
|
482
|
+
| Claude Code | `claude-cli/` | `/v1/messages` |
|
|
483
|
+
| Codex (HTTP) | `codex_cli_rs/` | `/v1/responses` |
|
|
484
|
+
| Antigravity CLI | `antigravity/cli/` | `/v1/v1internal:streamGenerateContent`, `/v1/v1internal:generateContent` |
|
|
485
|
+
|
|
486
|
+
For agy, intact also writes the project of the chosen account into the request. Codex over its
|
|
487
|
+
WebSocket transport still uses the normal route.
|
|
488
|
+
|
|
489
|
+
The gateway keeps the answer from intact for 10 minutes for each model. Only a 2xx answer is kept that long. If intact does not answer, or answers with an error (a 404 too), the gateway uses the normal route. It asks again after 30 seconds (`LLM_SWITCHER_BIFROST_RETRY_MS`), and it prints one line for each series of failures.
|
|
417
490
|
|
|
418
|
-
The gateway sends every client header, the body bytes, and the query string. It removes the credentials of the client and the hop-by-hop headers, and it sets `x-api-key` to the profile key. If the profile maps the model, the gateway also changes the `model` field. A different client, or a model that
|
|
491
|
+
The gateway sends every client header, the body bytes, and the query string. It removes the credentials of the client and the hop-by-hop headers, and it sets `x-api-key` to the profile key. If the profile maps the model, the gateway also changes the `model` field. A different client, or a model of an account that does not belong to the provider of the tool, uses the normal route of the profile.
|
|
419
492
|
|
|
420
493
|
## CLI Reference
|
|
421
494
|
|
|
@@ -429,25 +502,27 @@ switch on [profile] # Start the gateway and activate a profile
|
|
|
429
502
|
switch <profile> # Activate a profile for both tools
|
|
430
503
|
switch claude <profile> # Set the active profile for Claude Code only
|
|
431
504
|
switch codex <profile> # Set the active profile for Codex only
|
|
505
|
+
switch agy <profile> # Set the active profile for the Antigravity CLI only
|
|
432
506
|
switch port <number> # Change the gateway port (restarts it if running)
|
|
433
507
|
switch service install # Install OS background autostart service (Windows / macOS / Linux); updates at each logon
|
|
434
508
|
switch service uninstall # Remove background autostart service
|
|
435
|
-
switch shim install # Route new Claude and
|
|
509
|
+
switch shim install # Route new Claude, Codex and agy sessions through the gateway
|
|
436
510
|
switch shim status # Verify shims + detect running sessions that bypass the gateway
|
|
437
511
|
switch shim uninstall # Remove the launcher shims
|
|
438
|
-
switch plugin install # Optional: a launch notice inside Claude Code and
|
|
512
|
+
switch plugin install # Optional: a launch notice inside Claude Code, Codex and agy
|
|
439
513
|
switch plugin status # Report whether that notice is installed
|
|
440
514
|
switch plugin uninstall # Remove that notice
|
|
441
515
|
switch off # Stop everything and restore the official endpoints
|
|
442
516
|
switch off claude # Turn Claude Code off; Codex keeps running
|
|
443
|
-
switch off codex # Turn Codex off;
|
|
517
|
+
switch off codex # Turn Codex off; the other tools keep running
|
|
518
|
+
switch off agy # Turn the Antigravity CLI off; the other tools keep running
|
|
444
519
|
switch contract-probe [--model m] # Drive the contract-lab variants through the gateway
|
|
445
520
|
switch contract-check # Turn the open contract findings into failing tests
|
|
446
521
|
```
|
|
447
522
|
|
|
448
|
-
Targets are `claude
|
|
449
|
-
|
|
450
|
-
|
|
523
|
+
Targets are `claude`, `codex` and `agy`. A profile is one tool: `tool` is `"claude"`, `"codex"` or
|
|
524
|
+
`"agy"` (or `null` for a profile that is switched off), so two targets can never point at the same
|
|
525
|
+
profile by accident. There is no `openai` or `vertex`
|
|
451
526
|
target any more — the input routes those names stood for are gone.
|
|
452
527
|
|
|
453
528
|
The service runs without your shell. `switch service install` therefore copies `LLM_SWITCHER_HOME`, `CLAUDE_CONFIG_DIR`, `LLM_SWITCHER_CONFIG`, `LLM_SWITCHER_STATE_DIR` and `LLM_SWITCHER_BLINDFOLD_CERTS` into the systemd unit or the launchd plist when they are set. The Windows task cannot carry them; set them as User environment variables instead. If the installed definition differs from the new one, for example after a hand edit, the old file is kept as `<file>.bak`. On Windows the task is created from an XML definition, so paths with spaces need no extra quoting and the task has no run-time limit. This Windows path is not tested on Windows yet.
|
|
@@ -546,6 +621,7 @@ This writes one file for each tool, and it opens no configuration file of either
|
|
|
546
621
|
| --- | --- | --- |
|
|
547
622
|
| Claude Code | `~/.claude/skills/llm-switcher-status/` | A folder with `.claude-plugin/plugin.json` under a skills directory loads as a plugin on the next session. There is no marketplace and no install step. |
|
|
548
623
|
| Codex | `~/.codex/hooks.json` | Codex reads this file by itself. `config.toml` stays closed. |
|
|
624
|
+
| Antigravity CLI | `~/.gemini/antigravity-cli/hooks.json` | agy reads this file at the start. agy 1.2.14 loads it, but it does not run global hooks yet. Only `.agents/hooks.json` in a workspace runs. Until a release runs global hooks, the shim toast is the notice for agy. agy cannot run a quoted path, so if the path of `hook-status.mjs` holds a space, the install skips agy and prints `[Skip]`. |
|
|
549
625
|
|
|
550
626
|
An existing `~/.codex/hooks.json` is merged. Your own hooks stay, and `switch plugin uninstall` takes
|
|
551
627
|
only ours away. If the file does not parse, the install refuses it and changes nothing, because that
|
|
@@ -576,14 +652,15 @@ of the routing.
|
|
|
576
652
|
"port": 3456,
|
|
577
653
|
"activeProfiles": {
|
|
578
654
|
"claude": "claude-default", // Active profile for Claude Code (/v1/messages)
|
|
579
|
-
"codex": "codex-default"
|
|
655
|
+
"codex": "codex-default", // Active profile for Codex (/v1/responses)
|
|
656
|
+
"agy": "agy-default" // Active profile for the Antigravity CLI (/v1internal:*)
|
|
580
657
|
},
|
|
581
658
|
"blindfold": { "port": 3457 }, // Interceptor port. Top-level, optional, default 3457
|
|
582
659
|
"profiles": {
|
|
583
660
|
"claude-default": {
|
|
584
661
|
"name": "Intact Gateway",
|
|
585
662
|
"mode": "convert", // hybrid | convert | direct
|
|
586
|
-
"tool": "claude", // claude | codex | null (profile is off)
|
|
663
|
+
"tool": "claude", // claude | codex | agy | null (profile is off)
|
|
587
664
|
"outFormat": "openai-chat", // openai-chat | anthropic | vertex
|
|
588
665
|
"thinkingMode": "auto", // auto | native | off (see Advanced Options)
|
|
589
666
|
"baseURL": "https://intact.example.com/v1", // or https://api.9router.com/v1
|
|
@@ -613,6 +690,16 @@ of the routing.
|
|
|
613
690
|
"codexRoles": { "review": "gpt-5.6-sol" }, // optional: pair one role with another public name
|
|
614
691
|
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" },
|
|
615
692
|
"model1M": { "main": false, "review": false, "subagent": false }
|
|
693
|
+
},
|
|
694
|
+
"agy-default": {
|
|
695
|
+
"name": "agy via intact",
|
|
696
|
+
"mode": "convert",
|
|
697
|
+
"tool": "agy",
|
|
698
|
+
"outFormat": "openai-chat",
|
|
699
|
+
"baseURL": "https://intact.example.com/v1",
|
|
700
|
+
"apiKey": "sk-...",
|
|
701
|
+
// One slot. * stands for the model that agy picked; antigravity/* crosses Bifrost on intact.
|
|
702
|
+
"defaultModels": { "main": "antigravity/*" }
|
|
616
703
|
}
|
|
617
704
|
},
|
|
618
705
|
"debug": false,
|
|
@@ -663,6 +750,9 @@ Because of this split, a provider fingerprint is never a rule in this gateway. I
|
|
|
663
750
|
| `LLM_SWITCHER_CONFIG=/path/config.json` | Use a config file outside the data folder (the proxy, `switch` and `mcp.mjs` all honour it). |
|
|
664
751
|
| `--port <n>` / `LLM_SWITCHER_PORT` | Override the listening port of the gateway (priority: flag > env > `config.port`). |
|
|
665
752
|
| `LLM_SWITCHER_BLINDFOLD_PORT` | Override the listening port of the interceptor (priority: env > `config.blindfold.port` > 3457). Set it together with `LLM_SWITCHER_PORT` to run a second instance. |
|
|
753
|
+
| `LLM_SWITCHER_CODE_ASSIST_URL` | The Code Assist host that receives the agy calls the gateway does not route (default `https://daily-cloudcode-pa.googleapis.com`). |
|
|
754
|
+
| `LLM_SWITCHER_CODE_ASSIST_TIMEOUT_MS` | Idle time of such a call, from 1000 to 2147483647 ms (default 300000). Before the answer starts, agy gets a 504. After it starts, the connection is cut. |
|
|
755
|
+
| `LLM_SWITCHER_BIFROST_RETRY_MS` | Time before the gateway asks intact again after a failed Bifrost lookup (default 30000 ms). |
|
|
666
756
|
| `x-llm-profile: <key>` header (alias `x-profile`) or `?profile=<key>` | Route a single request through a specific profile. An unknown key returns HTTP 400 instead of silently falling back. |
|
|
667
757
|
| `profile.thinkingMode` | `auto` (default, for gateways like intact or 9Router): restore stripped thinking, inject a `<think>` guide for non-reasoning models, send `thinking` + `reasoning_effort`. `native` (strict OpenAI APIs): send only `reasoning_effort` when the client asks, never touch the prompt, use `max_completion_tokens`. `off`: never send reasoning parameters. |
|
|
668
758
|
| `profile.endpoints.countTokens` | Override the Anthropic `count_tokens` URL. |
|
package/README.vi.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<b>Cổng ngõ biên (Edge Gateway) chuyển đổi đa giao thức LLM siêu nhẹ, Zero-Dependency</b><br>
|
|
5
|
-
Cầu nối hai chiều giữa <b>Claude Code</b> và <b>
|
|
5
|
+
Cầu nối hai chiều giữa <b>Claude Code</b>, <b>Codex</b> và <b>Antigravity CLI</b> với mọi nhà cung cấp LLM: OpenAI-compatible, Anthropic hoặc Vertex.<br>
|
|
6
6
|
Chuyển đổi giao thức qua IR, cửa sổ context theo model chính thức, trích xuất thinking blocks và tự chữa lành đồ thị tin nhắn trước khi ra Internet.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
> **LLM Switcher giải quyết triệt để vấn đề này ngay tại network edge cục bộ:**
|
|
28
28
|
> - **100% Native Emulation:** Chuẩn hóa upstream API (intact, 9Router, Vertex, DeepSeek) thành luồng Anthropic SSE xịn (`thinking_delta` + `tool_use`) cho Claude Code, và Responses API event cho Codex.
|
|
29
29
|
> - **Client-Side Edge Companion:** Cố tình tách biệt các tác vụ nặng như account pooling, key rotation cho **[intact](https://github.com/louisphamdev/intact)** (khuyên dùng) hoặc 9Router (cơ bản), giúp Switcher giữ vững tiêu chí Zero-Dependency siêu nhẹ.
|
|
30
|
-
> - **Phạm vi Tập trung:** Tối ưu chuyên sâu cho **Claude Code** và **
|
|
30
|
+
> - **Phạm vi Tập trung:** Tối ưu chuyên sâu cho **Claude Code**, **OpenAI Codex** và **Antigravity CLI** (`agy`). OpenCode đã hỗ trợ đổi model native ngay trong config; muốn pooling thì dùng intact.
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
@@ -132,9 +132,9 @@ LLM Switcher hoạt động như một lớp trung gian mạng trong suốt (tra
|
|
|
132
132
|
|
|
133
133
|
- **Kiến trúc Zero-Dependency:** Xây dựng 100% bằng thư viện chuẩn của Node.js (`http`, `fs`, `os`, `path`, `fetch`). Không cần chạy `npm install`, không kéo theo runtime Bun hay binary nặng, khởi động dưới 50ms.
|
|
134
134
|
- **Chuyển đổi Giao thức 2 Chiều Đối xứng:**
|
|
135
|
-
- **
|
|
135
|
+
- **3 Định dạng đầu vào (Client):** Anthropic Messages (Claude Code), Codex Responses API (Codex), Code Assist (Antigravity CLI).
|
|
136
136
|
- **3 Định dạng đầu ra (Upstream):** OpenAI Chat, Anthropic Native, Vertex Native.
|
|
137
|
-
- **Bifrost:**
|
|
137
|
+
- **Bifrost:** Một coding tool tới tài khoản của chính nhà cung cấp của nó trên intact đi nguyên vẹn. Chỉ key thay đổi. Không chuyển định dạng, không healer, không lọc. Áp dụng cho Claude Code, Codex và Antigravity CLI. Gateway tự bật. [Bifrost](#bifrost-coding-tool-tới-tài-khoản-của-chính-nó-trên-intact)
|
|
138
138
|
- **Multi-Active CLI Routing:** Kích hoạt cùng lúc Claude Code dùng Profile A, Codex dùng Profile B, Cursor dùng Profile C trên cùng 1 gateway mà không tranh chấp cấu hình.
|
|
139
139
|
- **Trích xuất Thinking & Reasoning Chuyên sâu:** Kiểm chứng thực tế qua 48 tổ hợp mẫu response live. Tự động bóc tách `reasoning_content`, thẻ `<think>`, các block `thought` của Vertex và thought signature thành các `thinking_delta` chuẩn của Anthropic.
|
|
140
140
|
- **Edge Healer Engine (Tự chữa lành tin nhắn):**
|
|
@@ -148,7 +148,12 @@ LLM Switcher hoạt động như một lớp trung gian mạng trong suốt (tra
|
|
|
148
148
|
|
|
149
149
|
---
|
|
150
150
|
|
|
151
|
-
## Thay đổi gần đây (v1.
|
|
151
|
+
## Thay đổi gần đây (v1.3.0)
|
|
152
|
+
|
|
153
|
+
- **Antigravity CLI (`agy`):** Công cụ thứ ba, có profile riêng. `switch agy <profile>` định tuyến nó. agy tới gateway qua `CLOUD_CODE_URL`, nên không cần proxy và không cần chứng chỉ. Remote control vẫn chạy, vì nó dùng host riêng. [Cấu hình Antigravity CLI](#cấu-hình-antigravity-cli-agy)
|
|
154
|
+
- **Bifrost cho agy và Codex:** Trên pool intact, request của agy và Codex đi nguyên vẹn, giống Claude Code từ trước.
|
|
155
|
+
|
|
156
|
+
## Thay đổi trước đó (v1.2.0)
|
|
152
157
|
|
|
153
158
|
- **Zero-Mutation Interceptor:** Định tuyến qua `HTTPS_PROXY`, tuyệt đối không can thiệp hay sửa file cấu hình của tool (`~/.claude/settings.json`, `~/.codex/config.toml`).
|
|
154
159
|
- **Hỗ trợ đồng thời cả Claude Code & Codex:** Quản lý độc lập `{ claude, codex }`, chuyển đổi profile tức thì qua `POST /_control/active-tools` mà không cần restart cổng.
|
|
@@ -226,12 +231,14 @@ Mỗi công cụ có file riêng, và chỉ shim tương ứng mới nạp:
|
|
|
226
231
|
| --- | --- |
|
|
227
232
|
| `env-claude.sh` / `env-claude.cmd` | shim `claude` |
|
|
228
233
|
| `env-codex.sh` / `env-codex.cmd` | shim `codex` |
|
|
234
|
+
| `env-agy.sh` / `env-agy.cmd` | shim `agy` |
|
|
229
235
|
| `env.sh` / `env.cmd` | không ai. Stub trống, giữ lại chỉ để dòng rc cũ lặng lẽ |
|
|
230
236
|
|
|
231
237
|
File của công cụ rỗng nghĩa là công cụ đó đang tắt: shim để nguyên môi trường và công cụ gọi thẳng
|
|
232
238
|
endpoint chính thức. Trước khi nạp bất cứ thứ gì, shim cũng quét một `ANTHROPIC_BASE_URL`,
|
|
233
239
|
`OPENAI_BASE_URL` hay `ANTHROPIC_DEFAULT_<TIER>_MODEL` cũ còn sót từ bản trước hoặc từ shell của
|
|
234
|
-
bạn, nên `switch off` là off thật sự.
|
|
240
|
+
bạn, nên `switch off` là off thật sự. Shim `agy` gỡ một `CLOUD_CODE_URL` trỏ vào gateway khi agy
|
|
241
|
+
đang tắt.
|
|
235
242
|
|
|
236
243
|
Trong thực tế bạn không bao giờ tự gọi các file này. `switch shim install` đặt `~/.llm-switcher/bin`
|
|
237
244
|
vào `PATH`, và mọi lệnh `claude` hay `codex` — kể cả `claude --resume` trong một terminal hoàn toàn
|
|
@@ -275,6 +282,61 @@ Trên Windows, đặt `%USERPROFILE%\.llm-switcher\bin` trước thư mục Code
|
|
|
275
282
|
|
|
276
283
|
Shim tuyệt đối không sửa `~/.codex/config.toml`. Codex kết nối tới gateway qua `HTTPS_PROXY` và interceptor mạng, giữ nguyên tên model và context window chính thức. Danh sách model được phục vụ động tại `/v1/models`.
|
|
277
284
|
|
|
285
|
+
### Cấu hình Antigravity CLI (agy)
|
|
286
|
+
|
|
287
|
+
agy là client dòng lệnh của Google Antigravity. agy đọc biến `CLOUD_CODE_URL` và gửi mọi lệnh gọi
|
|
288
|
+
Code Assist tới địa chỉ đó. Shim `agy` đặt biến này trỏ vào gateway, nên agy không cần proxy và
|
|
289
|
+
không cần chứng chỉ.
|
|
290
|
+
|
|
291
|
+
1. Đăng nhập agy bằng tài khoản Google của bạn. agy cần đăng nhập cho các lệnh gọi của chính nó.
|
|
292
|
+
2. Tạo một profile có `"tool": "agy"`. Dashboard có sẵn template **agy on intact**.
|
|
293
|
+
3. Chạy các lệnh sau, rồi mở terminal mới:
|
|
294
|
+
```bash
|
|
295
|
+
switch shim install
|
|
296
|
+
switch agy <profile>
|
|
297
|
+
agy
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Gateway định tuyến các lệnh gọi của agy như sau:
|
|
301
|
+
|
|
302
|
+
| Lệnh gọi | Đi tới đâu |
|
|
303
|
+
| --- | --- |
|
|
304
|
+
| Lượt agent (`requestType: "agent"`) | Profile của agy: Bifrost trên intact, hoặc chuyển đổi sang upstream bất kỳ |
|
|
305
|
+
| Tóm tắt checkpoint, danh sách model, quota, trạng thái đăng nhập, analytics | Google, bằng token của chính bạn |
|
|
306
|
+
| Mọi lệnh gọi khi agy đang tắt | Google |
|
|
307
|
+
|
|
308
|
+
Lệnh gọi tới Google giữ nguyên header và byte. Gateway chỉ bỏ header của chính nó
|
|
309
|
+
(`x-llm-profile`, `x-profile`, `x-llm-switcher-token`, và header contract-lab `x-intact-probe`,
|
|
310
|
+
`x-intact-trace`) và header của proxy đứng trước nó (`proxy-authorization`, `x-forwarded-*`,
|
|
311
|
+
`x-real-ip`).
|
|
312
|
+
|
|
313
|
+
Khi agy đang được định tuyến, gateway từ chối lượt agent mà nó không định tuyến được. Gateway không
|
|
314
|
+
bao giờ gửi lượt đó sang Google thay thế:
|
|
315
|
+
|
|
316
|
+
| Điều kiện | agy nhận |
|
|
317
|
+
| --- | --- |
|
|
318
|
+
| `config.json` chưa từng nạp được | 503 |
|
|
319
|
+
| Request chỉ định một profile không tồn tại (`x-llm-profile`, `?profile=`) | 400 |
|
|
320
|
+
| Body request không parse được | 400 |
|
|
321
|
+
| Request không có model, và slot `main` chứa `*` | 400 |
|
|
322
|
+
|
|
323
|
+
Profile có một slot model là `main`. Dấu `*` trong giá trị thay cho model mà agy đã chọn:
|
|
324
|
+
|
|
325
|
+
- `antigravity/*` gửi `gemini-3.8-flash-high` tới intact dưới tên `antigravity/gemini-3.8-flash-high`.
|
|
326
|
+
intact nhận agy là client của chính pool Antigravity, nên request đi qua Bifrost.
|
|
327
|
+
- `claude/claude-opus-5` gửi mọi lượt agent tới model đó. Gateway chuyển request Code Assist sang
|
|
328
|
+
định dạng của upstream, rồi chuyển câu trả lời về lại định dạng Code Assist.
|
|
329
|
+
|
|
330
|
+
Remote control của agy dùng host riêng (`jetski-webchannel.googleapis.com`). Nó không dùng
|
|
331
|
+
`CLOUD_CODE_URL`, nên vẫn chạy khi switcher định tuyến agy.
|
|
332
|
+
|
|
333
|
+
Token Google của agy chỉ đi tới Google. Request Bifrost hay request đã chuyển đổi mang key của
|
|
334
|
+
profile, không bao giờ mang token này.
|
|
335
|
+
|
|
336
|
+
agy gửi token này dạng rõ tới `CLOUD_CODE_URL`. Vì vậy shim `agy` hỏi gateway bằng chứng danh tính
|
|
337
|
+
trước khi giao biến này cho agy. Nếu cổng không trả lời đúng bằng chứng của bản cài này trong 3 giây,
|
|
338
|
+
shim gỡ biến, in một dòng thông báo, và agy dùng endpoint chính thức.
|
|
339
|
+
|
|
278
340
|
### Chế độ blindfold (tùy chọn)
|
|
279
341
|
|
|
280
342
|
Khi có override base URL, Codex in một dòng ngay trên màn `/model` của nó:
|
|
@@ -401,18 +463,27 @@ Thêm server vào cấu hình MCP (ví dụ `opencode.jsonc`, `claude_desktop_co
|
|
|
401
463
|
|
|
402
464
|
---
|
|
403
465
|
|
|
404
|
-
## Bifrost:
|
|
466
|
+
## Bifrost: coding tool tới tài khoản của chính nó trên intact
|
|
405
467
|
|
|
406
|
-
Bifrost gửi request của
|
|
468
|
+
Bifrost gửi request của một coding tool tới intact nguyên vẹn. Chỉ key thay đổi. Healer, `thinkingMode` và bước chuyển định dạng không chạy.
|
|
407
469
|
|
|
408
470
|
Bifrost không có cấu hình. Gateway tự bật Bifrost cho từng request khi đủ hai điều kiện:
|
|
409
471
|
|
|
410
|
-
1. intact trả `bifrost_ua` cho model đã map trong `GET /v1/models/{model}`. intact chỉ trả trường này cho model của tài khoản
|
|
472
|
+
1. intact trả `bifrost_ua` cho model đã map trong `GET /v1/models/{model}`. intact chỉ trả trường này cho model của tài khoản thuộc nhà cung cấp của công cụ.
|
|
411
473
|
2. `User-Agent` của client bắt đầu bằng giá trị `bifrost_ua`.
|
|
412
474
|
|
|
413
|
-
|
|
475
|
+
| Công cụ | `bifrost_ua` | Path trên intact |
|
|
476
|
+
| --- | --- | --- |
|
|
477
|
+
| Claude Code | `claude-cli/` | `/v1/messages` |
|
|
478
|
+
| Codex (HTTP) | `codex_cli_rs/` | `/v1/responses` |
|
|
479
|
+
| Antigravity CLI | `antigravity/cli/` | `/v1/v1internal:streamGenerateContent`, `/v1/v1internal:generateContent` |
|
|
480
|
+
|
|
481
|
+
Với agy, intact ghi thêm project của tài khoản được chọn vào request. Codex qua WebSocket vẫn đi
|
|
482
|
+
đường thường.
|
|
483
|
+
|
|
484
|
+
Gateway giữ câu trả lời của intact 10 phút cho mỗi model. Chỉ câu trả lời 2xx được giữ lâu như vậy. Nếu intact không trả lời, hoặc trả lỗi (kể cả 404), gateway dùng đường thường. Gateway hỏi lại sau 30 giây (`LLM_SWITCHER_BIFROST_RETRY_MS`) và in một dòng cho mỗi chuỗi lỗi.
|
|
414
485
|
|
|
415
|
-
Gateway gửi mọi header của client, nguyên byte body và query string. Gateway bỏ credential của client và các header hop-by-hop, rồi đặt `x-api-key` bằng key của profile. Nếu profile map model, gateway đổi thêm trường `model`. Client khác, hoặc model không thuộc
|
|
486
|
+
Gateway gửi mọi header của client, nguyên byte body và query string. Gateway bỏ credential của client và các header hop-by-hop, rồi đặt `x-api-key` bằng key của profile. Nếu profile map model, gateway đổi thêm trường `model`. Client khác, hoặc model của tài khoản không thuộc nhà cung cấp của công cụ, đi đường thường của profile.
|
|
416
487
|
|
|
417
488
|
## Bảng Tra cứu Lệnh CLI (`switch`)
|
|
418
489
|
|
|
@@ -426,25 +497,27 @@ switch on [profile] # Khởi động gateway và kích hoạt một p
|
|
|
426
497
|
switch <profile> # Kích hoạt một profile cho cả hai công cụ
|
|
427
498
|
switch claude <profile> # Đặt profile kích hoạt riêng cho Claude Code
|
|
428
499
|
switch codex <profile> # Đặt profile kích hoạt riêng cho Codex
|
|
500
|
+
switch agy <profile> # Đặt profile kích hoạt riêng cho Antigravity CLI
|
|
429
501
|
switch port <number> # Đổi cổng gateway (tự restart nếu đang chạy)
|
|
430
502
|
switch service install # Cài đặt gateway thành service chạy ngầm tự bật cùng máy; tự update mỗi lần đăng nhập
|
|
431
503
|
switch service uninstall # Gỡ bỏ service chạy ngầm
|
|
432
|
-
switch shim install # Route phiên Claude và
|
|
504
|
+
switch shim install # Route phiên Claude, Codex và agy mới qua gateway
|
|
433
505
|
switch shim status # Kiểm tra shim + phát hiện phiên đang chạy ngoài gateway
|
|
434
506
|
switch shim uninstall # Gỡ shim khỏi launcher
|
|
435
|
-
switch plugin install # Tuỳ chọn: nhắc nhở ngay trong Claude Code và
|
|
507
|
+
switch plugin install # Tuỳ chọn: nhắc nhở ngay trong Claude Code, Codex và agy
|
|
436
508
|
switch plugin status # Xem nhắc nhở đó đã cài chưa
|
|
437
509
|
switch plugin uninstall # Gỡ nhắc nhở đó
|
|
438
510
|
switch off # Tắt tất cả và quay về endpoint chính thức
|
|
439
511
|
switch off claude # Tắt Claude Code; Codex vẫn chạy tiếp
|
|
440
|
-
switch off codex # Tắt Codex;
|
|
512
|
+
switch off codex # Tắt Codex; các công cụ khác vẫn chạy tiếp
|
|
513
|
+
switch off agy # Tắt Antigravity CLI; các công cụ khác vẫn chạy tiếp
|
|
441
514
|
switch contract-probe [--model m] # Chạy các biến thể contract-lab qua gateway
|
|
442
515
|
switch contract-check # Chuyển các findings hợp đồng còn mở thành test case
|
|
443
516
|
```
|
|
444
517
|
|
|
445
|
-
Target
|
|
446
|
-
|
|
447
|
-
|
|
518
|
+
Target là `claude`, `codex` và `agy`. Một profile phục vụ đúng một công cụ: `tool` là `"claude"`,
|
|
519
|
+
`"codex"` hoặc `"agy"` (hoặc `null` khi profile đang tắt), nên hai target không bao giờ chỉ nhầm vào
|
|
520
|
+
cùng một profile. Không còn target `openai` hay `vertex` —
|
|
448
521
|
các route đầu vào mà chúng đại diện đã bị gỡ.
|
|
449
522
|
|
|
450
523
|
|
|
@@ -541,6 +614,7 @@ Lệnh này ghi một file cho mỗi tool, và không mở file cấu hình nào
|
|
|
541
614
|
| --- | --- | --- |
|
|
542
615
|
| Claude Code | `~/.claude/skills/llm-switcher-status/` | Một thư mục có `.claude-plugin/plugin.json` nằm dưới skills directory sẽ được nạp như một plugin ở phiên kế tiếp. Không cần marketplace, không cần bước install. |
|
|
543
616
|
| Codex | `~/.codex/hooks.json` | Codex tự đọc file này. `config.toml` không bị mở. |
|
|
617
|
+
| Antigravity CLI | `~/.gemini/antigravity-cli/hooks.json` | agy đọc file này khi khởi động. agy 1.2.14 nạp file này nhưng chưa chạy hook toàn cục. Chỉ `.agents/hooks.json` trong workspace mới chạy. Cho tới khi có bản chạy hook toàn cục, toast của shim là nhắc nhở cho agy. agy không chạy được đường dẫn có dấu nháy, nên nếu đường dẫn của `hook-status.mjs` có dấu cách, lệnh cài bỏ qua agy và in `[Skip]`. |
|
|
544
618
|
|
|
545
619
|
Nếu `~/.codex/hooks.json` đã có sẵn thì lệnh sẽ **merge**. Hook của bạn được giữ nguyên, và
|
|
546
620
|
`switch plugin uninstall` chỉ lấy đi phần của switcher. Nếu file không parse được thì lệnh từ chối và
|
|
@@ -572,14 +646,15 @@ route.
|
|
|
572
646
|
"port": 3456,
|
|
573
647
|
"activeProfiles": {
|
|
574
648
|
"claude": "claude-default", // Profile active cho Claude Code (/v1/messages)
|
|
575
|
-
"codex": "codex-default"
|
|
649
|
+
"codex": "codex-default", // Profile active cho Codex (/v1/responses)
|
|
650
|
+
"agy": "agy-default" // Profile active cho Antigravity CLI (/v1internal:*)
|
|
576
651
|
},
|
|
577
652
|
"blindfold": { "port": 3457 }, // Cổng interceptor. Cấp cao nhất, không bắt buộc, mặc định 3457
|
|
578
653
|
"profiles": {
|
|
579
654
|
"claude-default": {
|
|
580
655
|
"name": "Intact Gateway",
|
|
581
656
|
"mode": "convert", // hybrid | convert | direct
|
|
582
|
-
"tool": "claude", // claude | codex | null (profile đang tắt)
|
|
657
|
+
"tool": "claude", // claude | codex | agy | null (profile đang tắt)
|
|
583
658
|
"outFormat": "openai-chat", // openai-chat | anthropic | vertex
|
|
584
659
|
"thinkingMode": "auto", // auto | native | off (xem Tuỳ chọn Nâng cao)
|
|
585
660
|
"baseURL": "https://intact.example.com/v1", // hoặc https://api.9router.com/v1
|
|
@@ -609,6 +684,16 @@ route.
|
|
|
609
684
|
"codexRoles": { "review": "gpt-5.6-sol" }, // không bắt buộc: ghép một vai trò với tên public khác
|
|
610
685
|
"defaultModels": { "main": "gemini-3.8-flash", "review": "gemini-3.7-flash-medium", "subagent": "gemini-3.6-flash-low" },
|
|
611
686
|
"model1M": { "main": false, "review": false, "subagent": false }
|
|
687
|
+
},
|
|
688
|
+
"agy-default": {
|
|
689
|
+
"name": "agy qua intact",
|
|
690
|
+
"mode": "convert",
|
|
691
|
+
"tool": "agy",
|
|
692
|
+
"outFormat": "openai-chat",
|
|
693
|
+
"baseURL": "https://intact.example.com/v1",
|
|
694
|
+
"apiKey": "sk-...",
|
|
695
|
+
// Một slot. * thay cho model agy đã chọn; antigravity/* đi Bifrost trên intact.
|
|
696
|
+
"defaultModels": { "main": "antigravity/*" }
|
|
612
697
|
}
|
|
613
698
|
},
|
|
614
699
|
"debug": false,
|
|
@@ -660,6 +745,9 @@ Nhờ cách chia này, fingerprint của provider không bao giờ là rule tron
|
|
|
660
745
|
| `LLM_SWITCHER_CONFIG=/path/config.json` | Dùng file cấu hình nằm ngoài thư mục dữ liệu (proxy, `switch` và `mcp.mjs` đều hỗ trợ). |
|
|
661
746
|
| `--port <n>` / `LLM_SWITCHER_PORT` | Ghi đè cổng lắng nghe của gateway (ưu tiên: flag > env > `config.port`). |
|
|
662
747
|
| `LLM_SWITCHER_BLINDFOLD_PORT` | Ghi đè cổng lắng nghe của interceptor (ưu tiên: env > `config.blindfold.port` > 3457). Đảm bảo đặt cùng `LLM_SWITCHER_PORT` khi chạy instance thứ hai. |
|
|
748
|
+
| `LLM_SWITCHER_CODE_ASSIST_URL` | Host Code Assist nhận các lệnh gọi của agy mà gateway không định tuyến (mặc định `https://daily-cloudcode-pa.googleapis.com`). |
|
|
749
|
+
| `LLM_SWITCHER_CODE_ASSIST_TIMEOUT_MS` | Thời gian chờ không có dữ liệu của lệnh gọi đó, từ 1000 tới 2147483647 ms (mặc định 300000). Trước khi câu trả lời bắt đầu, agy nhận 504. Sau khi đã bắt đầu, kết nối bị cắt. |
|
|
750
|
+
| `LLM_SWITCHER_BIFROST_RETRY_MS` | Thời gian trước khi gateway hỏi lại intact sau một lần tra cứu Bifrost thất bại (mặc định 30000 ms). |
|
|
663
751
|
| Header `x-llm-profile: <key>` (tên khác `x-profile`) hoặc `?profile=<key>` | Định tuyến riêng 1 request qua profile chỉ định. Key không tồn tại trả HTTP 400 thay vì âm thầm dùng profile khác. |
|
|
664
752
|
| `profile.thinkingMode` | `auto` (mặc định, cho gateway như intact hoặc 9Router): phục hồi thinking bị xoá, inject hướng dẫn `<think>` cho model không có reasoning, gửi `thinking` + `reasoning_effort`. `native` (API OpenAI nghiêm ngặt): chỉ gửi `reasoning_effort` khi client yêu cầu, không sửa prompt, dùng `max_completion_tokens`. `off`: không bao giờ gửi tham số reasoning. |
|
|
665
753
|
| `profile.endpoints.countTokens` | Ghi đè URL `count_tokens` của Anthropic. |
|
package/config.example.json
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
"port": 3456,
|
|
3
3
|
"activeProfiles": {
|
|
4
4
|
"claude": "claude-default",
|
|
5
|
-
"codex": "codex-default"
|
|
5
|
+
"codex": "codex-default",
|
|
6
|
+
"agy": "agy-default"
|
|
6
7
|
},
|
|
7
8
|
"blindfold": {
|
|
8
9
|
"port": 3457
|
|
@@ -51,6 +52,17 @@
|
|
|
51
52
|
"review": false,
|
|
52
53
|
"subagent": false
|
|
53
54
|
}
|
|
55
|
+
},
|
|
56
|
+
"agy-default": {
|
|
57
|
+
"name": "agy default (Antigravity CLI -> intact Antigravity pool, Bifrost)",
|
|
58
|
+
"mode": "convert",
|
|
59
|
+
"tool": "agy",
|
|
60
|
+
"outFormat": "openai-chat",
|
|
61
|
+
"baseURL": "https://YOUR-INTACT-HOST/v1",
|
|
62
|
+
"apiKey": "sk-REPLACE-ME",
|
|
63
|
+
"defaultModels": {
|
|
64
|
+
"main": "antigravity/*"
|
|
65
|
+
}
|
|
54
66
|
}
|
|
55
67
|
},
|
|
56
68
|
"debug": false,
|