Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ before the client runs them.
## Highlights

- **Connect once, switch freely.** Claude Code, Claude Desktop, Codex,
opencode, Pi, oh-my-pi, Zed, Aider and DeepSeek Harness are pointed at the
gateway in one step, with the change previewed, the original file backed up
and a restore always available; Cursor, Continue and Antigravity CLI come
with instructions. Switching upstreams then happens in the gateway alone.
opencode, Pi, oh-my-pi, Grok Build, Qwen Code, Hermes Agent, Zed, Aider and
DeepSeek Harness are pointed at the gateway in one step, with the change
previewed, the original file backed up and a restore always available;
Cursor, Continue and Antigravity CLI come with instructions. Switching
upstreams then happens in the gateway alone.
- **Protection against relays.** A relay sees every request in full and can
rewrite every answer. Outbound redaction swaps API keys, private keys, JWTs,
connection-string passwords, Chinese resident ID numbers and bank card numbers
Expand All @@ -50,8 +51,8 @@ before the client runs them.
- **Upstream check-up.** Each upstream is compared with the others serving the
same model: answers naming a different model, reported input well above or
below theirs and low prompt-cache reads are marked, with sample sizes.
- **MCP servers, skills and hooks, scanned.** The MCP servers of ten clients
side by side, with third-party servers marked, and a scan of client
- **MCP servers, skills and hooks, scanned.** The MCP servers of thirteen
clients side by side, with third-party servers marked, and a scan of client
configuration, skills, hooks and project instructions for hidden characters,
prompt injection, dangerous commands and overly broad permissions.
- **Every request traceable.** The rule a request matched, each upstream it
Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ Claude Code、Codex 等 AI 客户端的本地网关,支持 macOS、Windows 与

## 要点

- **一次接入,随时切换。** Claude Code、Claude Desktop、Codex、opencode、Pi、oh-my-pi、Zed、Aider 与 DeepSeek Harness 可一键指向网关,写入前预览改动、备份原文件,随时可以还原;Cursor、Continue 与 Antigravity CLI 提供配置说明。此后切换上游只在网关中完成。
- **一次接入,随时切换。** Claude Code、Claude Desktop、Codex、opencode、Pi、oh-my-pi、Grok Build、Qwen Code、Hermes Agent、Zed、Aider 与 DeepSeek Harness 可一键指向网关,写入前预览改动、备份原文件,随时可以还原;Cursor、Continue 与 Antigravity CLI 提供配置说明。此后切换上游只在网关中完成。
- **防范中转站。** 中转站能看到请求的全部内容,也能改写每一次回答。出站脱敏在请求发出前把 API 密钥、私钥、JWT、连接串口令、身份证号与银行卡号换成占位符,中转站拿不到原值。回答中若出现下载即执行、外发环境变量或凭据文件、读取私钥、写入开机启动项或定时任务之类的工具调用,工具调用审查会在客户端执行之前切断回答;隐藏字符与提示注入也可以直接拒绝。各项防护出厂只记录,逐项切换到拦截即可生效。
- **上游体检。** 每个上游都与服务同一模型的其他上游对照:回答中的模型名与发出的不同、报告的输入明显偏多或偏少、提示缓存读取偏低,都会标出,并附样本数。
- **扫描 MCP、技能与钩子。** 十款客户端的 MCP 服务器并列显示并标出第三方服务器;客户端配置、技能、钩子与项目指令中的隐藏字符、提示注入、危险命令与过宽权限会被找出。
- **扫描 MCP、技能与钩子。** 十三款客户端的 MCP 服务器并列显示并标出第三方服务器;客户端配置、技能、钩子与项目指令中的隐藏字符、提示注入、危险命令与过宽权限会被找出。
- **每个请求都可追溯。** 命中的规则、尝试过的每个上游、API 格式转换与费用的计算依据都在请求详情中;已结束的请求可以重放到另一个上游,并排对比。全部请求记录都可以搜索,包括请求与回答的内容。
- **按规则分流,失败自动换。** 按模型、工具、图片、扩展思考等条件分流。回答开始前上游出错时换用下一个,同一会话固定使用同一上游,提示缓存保持有效。标题生成等辅助请求可在本地应答。
- **多种上游,接口互转。** API 密钥、Amazon Bedrock、ChatGPT 与 Z.ai 账号、OpenRouter 等中转服务与本机模型均可作为上游,Anthropic、OpenAI、Gemini 接口之间自动转换。
Expand Down
130 changes: 129 additions & 1 deletion scripts/shots/mock/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const set = (path: string, value: string): FieldChange => ({ op: "set", path, va
const secret = (path: string): FieldChange => ({ op: "set", path, value: null, secret: true });
const file = (f: string) => msg("adopt.manual.file", `Open ${f} and set the fields below.`, { file: f });

/** 网关给这台机器上的密钥列出来的模型,和 opencode 那一条写进去的是同一份 */
const MOCK_MODELS = ["claude-sonnet-5", "gpt-5.5", "deepseek-chat"] as const;

/** 接管时写哪几项(tw-adopt 的 `edits`),也是手动配置那一页列的字段 */
function setup(id: string, path: string): ManualSetup {
switch (id) {
Expand Down Expand Up @@ -136,6 +139,51 @@ function setup(id: string, path: string): ManualSetup {
),
],
};
case "grok-build":
// 一个模型一张表,每张都带自己的密钥(tw-adopt grok.rs 的 `fields`)
return {
steps: [file(path)],
endpoint: v1(),
fields: [
...MOCK_MODELS.flatMap((m) => [
set(`model.thinkwatch/${m}.model`, m),
set(`model.thinkwatch/${m}.name`, `${m} (ThinkWatch)`),
set(`model.thinkwatch/${m}.base_url`, v1()),
set(`model.thinkwatch/${m}.api_backend`, m.startsWith("claude") ? "messages" : m.startsWith("gpt-") ? "responses" : "chat_completions"),
secret(`model.thinkwatch/${m}.api_key`),
]),
set("models.default", `thinkwatch/${MOCK_MODELS[0]}`),
set("features.campaigns", "false"),
],
};
case "qwen-code":
return {
steps: [file(path)],
endpoint: v1(),
fields: [
set(
"modelProviders.thinkwatch",
`[${MOCK_MODELS.map((m) => `{id: ${m}, name: ${m} (ThinkWatch), baseUrl: ${v1()}, envKey: THINKWATCH_QWEN_API_KEY}`).join(", ")}]`,
),
set("providerProtocol.thinkwatch", "openai"),
secret("env.THINKWATCH_QWEN_API_KEY"),
set("security.auth.selectedType", "openai"),
set("model.name", MOCK_MODELS[0]),
set("model.baseUrl", v1()),
],
};
case "hermes-agent":
return {
steps: [file(path)],
endpoint: v1(),
fields: [
set("model.provider", "custom"),
set("model.base_url", v1()),
secret("model.api_key"),
set("model.api_mode", "anthropic_messages"),
set("model.default", MOCK_MODELS[0]),
],
};
default:
return { steps: [file(path)], endpoint: v1(), fields: [set("openai-api-base", v1()), secret("openai-api-key")] };
}
Expand Down Expand Up @@ -315,6 +363,68 @@ function clientsNow(): DetectedClient[] {
),
],
}),
// 这台机器上同样没装的三个:照默认位置给出手动配置的方法(tw-adopt clients.rs 的原句)
detected({
id: "grok-build",
name: "Grok Build",
path: "~/.grok/config.toml",
installed: false,
has_config: false,
costs: [
msg(
"adopt.cost.grok_build.builtin_models",
"Grok's built-in models stay in the model picker and still connect to xAI directly; the gateway's models are listed as thinkwatch/<model>.",
),
msg(
"adopt.cost.grok_build.helper_models",
"Web search, image descriptions, session titles and prompt suggestions still use Grok's built-in models, which connect to xAI directly.",
),
msg(
"adopt.cost.grok_build.campaigns",
"Grok's remote campaigns, which can change the default model, are turned off while this is in place.",
),
],
}),
detected({
id: "qwen-code",
name: "Qwen Code",
path: "~/.qwen/settings.json",
installed: false,
has_config: false,
takes_effect: "on_restart",
warns_when_silent: false,
costs: [
msg("adopt.cost.qwen_code.restart", "Qwen Code has to be restarted afterwards."),
msg("adopt.cost.qwen_code.version", "This needs Qwen Code 0.19.3 or later; earlier versions ignore the gateway's models."),
msg(
"adopt.cost.qwen_code.other_models",
"Models set separately for fast replies, vision, compaction and similar tasks keep their own providers.",
),
msg(
"adopt.cost.qwen_code.proxy",
"When Qwen Code uses a proxy, the gateway address has to be listed in NO_PROXY, or requests to the gateway go through the proxy.",
),
],
}),
detected({
id: "hermes-agent",
name: "Hermes Agent",
path: "~/.hermes/config.yaml",
installed: false,
has_config: false,
takes_effect: "on_restart",
warns_when_silent: false,
costs: [
msg(
"adopt.cost.hermes_agent.restart",
"Hermes Agent sessions that are already open keep their provider until they are restarted; the messaging gateway picks up the change with the next message.",
),
msg(
"adopt.cost.hermes_agent.probes",
"Hermes Agent checks whether the gateway is a local model server such as LM Studio or Ollama; those checks appear in Traffic as failed requests.",
),
],
}),
];
}

Expand Down Expand Up @@ -414,7 +524,7 @@ export function plan(id: string, restore: boolean): PlanView {

// ───────────────────────────────────────── MCP 与扫描

/** MCP 能写进哪几个客户端(tw-adopt mcp.rs 的 `targets`,一个不少)。Zed、Antigravity CLI、Pi、oh-my-pi 与 DeepSeek Harness 这台机器上没有,画在矩阵下方 */
/** MCP 能写进哪几个客户端(tw-adopt mcp.rs 的 `targets`,一个不少)。Zed、Antigravity CLI、Pi、oh-my-pi、Grok Build、Qwen Code、Hermes Agent 与 DeepSeek Harness 这台机器上没有,画在矩阵下方 */
export function mcpTargets(): McpTargetView[] {
return [
{ client: "claude-code", name: "Claude Code", path: "~/.claude.json", copyable: true, why_not: null, movable: true, present: true },
Expand Down Expand Up @@ -473,6 +583,24 @@ export function mcpTargets(): McpTargetView[] {
),
movable: true, present: false,
},
...(
[
["grok-build", "Grok Build", "~/.grok/config.toml"],
["qwen-code", "Qwen Code", "~/.qwen/settings.json"],
["hermes-agent", "Hermes Agent", "~/.hermes/config.yaml"],
] as const
).map(([client, name, path]) => ({
client,
name,
path,
copyable: false,
why_not: msg(
"adopt.mcp.unverified_format",
"this client's MCP configuration format is not verified yet, and writing to it could leave the client unable to read its own configuration",
),
movable: true,
present: false,
})),
{
client: "dsh",
name: "DeepSeek Harness",
Expand Down
Loading
Loading