myapikey 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vat-wiki
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,295 @@
1
+ # MyAPIKey
2
+
3
+ [![npm version](https://img.shields.io/npm/v/myapikey?logo=npm)](https://www.npmjs.com/package/myapikey)
4
+ [![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
5
+ [![node](https://img.shields.io/badge/node.js-%3E%3D18-339933?logo=node.js&logoColor=white)](#quick-start)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)](#development)
7
+
8
+ English | [简体中文](README.zh-CN.md)
9
+
10
+ **One address, one key, all your models — without translation.**
11
+
12
+ MyAPIKey is a personal LLM gateway. Point every AI tool you use (Claude Code, OpenAI-compatible CLIs, editors, scripts) at a single local address with a single API key. Each call is forwarded **as-is** to a backend that speaks the same wire format, with automatic failover when a backend is down. Nothing is converted between OpenAI and Anthropic formats — what you send is exactly what reaches the backend.
13
+
14
+ > Built for a home server / LAN. No database, no TLS by default, no translation layer — just a thin, transparent forwarder you fully control.
15
+
16
+ ---
17
+
18
+ ## Table of contents
19
+
20
+ - [What is this, and who is it for?](#what-is-this-and-who-is-it-for)
21
+ - [Quick start](#quick-start)
22
+ - [Add your first backend](#add-your-first-backend)
23
+ - [Connect a tool](#connect-a-tool)
24
+ - [How routing works](#how-routing-works)
25
+ - [Model name mapping](#model-name-mapping)
26
+ - [Web UI](#web-ui)
27
+ - [CLI reference](#cli-reference)
28
+ - [API surface](#api-surface)
29
+ - [Storage](#storage)
30
+ - [Development](#development)
31
+ - [Notes and non-goals](#notes-and-non-goals)
32
+
33
+ ---
34
+
35
+ ## What is this, and who is it for?
36
+
37
+ If any of these sound familiar, MyAPIKey is for you:
38
+
39
+ - You juggle **several API keys** across OpenAI, Anthropic, OpenRouter, a local model, etc., and you're tired of pasting a different one into every tool.
40
+ - You want **one stable address** for your agents, even when the provider behind it changes.
41
+ - You want **automatic fallback** — if your primary backend rate-limits or goes down, the next one takes over.
42
+ - You want to **rename or swap a model** without editing every tool's config.
43
+ - You'd like a **log of every call** (latency, status, which backend served it).
44
+
45
+ MyAPIKey gives you all of that. You keep your real provider keys inside the gateway; your tools only ever know `http://<host>:7800` and one `sk-myapikey-…` key.
46
+
47
+ **The one thing to know up front:** the gateway **forwards in your tool's native format and never translates**. A call to the OpenAI endpoint reaches an OpenAI-speaking backend; a call to the Anthropic endpoint reaches an Anthropic-speaking backend. This keeps your requests and responses lossless. (See [How routing works](#how-routing-works) once you're set up.)
48
+
49
+ ---
50
+
51
+ ## Quick start
52
+
53
+ Requires **Node.js 18+**. No install needed — run it straight from npm:
54
+
55
+ ```bash
56
+ npx myapikey serve # → http://localhost:7800
57
+ ```
58
+
59
+ Or install it globally:
60
+
61
+ ```bash
62
+ npm install -g myapikey
63
+ myapikey serve
64
+ ```
65
+
66
+ On first run it prints generated credentials **and saves them to a file** so you can't lose them:
67
+
68
+ ```
69
+ First run — here are your credentials (save them):
70
+ username : <random> (web login)
71
+ password : <random> (web login)
72
+ api key : sk-myapikey-… (put this in the tool's "api key" field)
73
+ ↳ also written to ~/.myapikey/credentials.txt
74
+ ```
75
+
76
+ Open `http://localhost:7800` in a browser and sign in with the **username / password** to reach the web UI. The **API key** is separate — that's what your AI tools use later.
77
+
78
+ > Forgot the credentials? `cat ~/.myapikey/credentials.txt` (regenerated on every start). Override the data location with `--data-dir <path>` or the `MYAPIKEY_DATA_DIR` env var.
79
+
80
+ That's the gateway running. Next, give it a backend to forward to.
81
+
82
+ ---
83
+
84
+ ## Add your first backend
85
+
86
+ A **backend** (provider) is one upstream API: OpenAI, Anthropic, OpenRouter, a local Ollama, a vendor like Volcengine Ark, etc. You add it once with its real key, then enable models through it.
87
+
88
+ ### Easiest: the web UI
89
+
90
+ 1. Open the **Models** tab → **Add backend**.
91
+ 2. Enter a name, the backend's base URL(s), and its API key.
92
+ 3. Pick the wire format(s) it speaks: `openai`, `anthropic` (or both).
93
+ 4. Save, then click **discover** to pull its model list.
94
+ 5. **Enable** the models you want, per format slot.
95
+
96
+ ### Or, the CLI
97
+
98
+ ```bash
99
+ # An OpenAI-compatible backend. The OpenAI base includes the version segment
100
+ # (e.g. /v1, or a vendor's own like Ark's /api/v3).
101
+ myapikey provider add openai-direct \
102
+ --base-url-openai https://api.openai.com/v1 \
103
+ --key sk-... \
104
+ --formats openai
105
+
106
+ # An Anthropic backend. The Anthropic base EXCLUDES /v1
107
+ # (https://api.anthropic.com, or Ark's /api/plan).
108
+ myapikey provider add anthropic-direct \
109
+ --base-url-anthropic https://api.anthropic.com \
110
+ --key sk-ant-... \
111
+ --formats anthropic
112
+
113
+ # See what each offers, then enable a model on a slot:
114
+ myapikey provider models openai-direct
115
+ myapikey model enable gpt-4o-mini --format openai --via openai-direct
116
+ ```
117
+
118
+ That model is now reachable through the gateway. To add a **fallback** for the same model (used when the first one fails), add another backend and chain it:
119
+
120
+ ```bash
121
+ myapikey provider add backup --base-url-openai https://api.openrouter.ai/api/v1 --key sk-or-... --formats openai
122
+ myapikey model add-provider gpt-4o-mini backup --format openai
123
+ myapikey model prioritize gpt-4o-mini openai-direct backup --format openai # left = primary
124
+ myapikey model list # see the routing table
125
+ ```
126
+
127
+ > `--format` selects the routing **slot**: `openai` (for `/v1/chat/completions`), `anthropic` (for `/v1/messages`), or `responses` (for `/v1/responses`). The `responses` slot only accepts backends you've marked **supportsResponses** — set that toggle in the web UI (the CLI doesn't expose it yet).
128
+
129
+ ---
130
+
131
+ ## Connect a tool
132
+
133
+ Once a model is enabled, point any agent at the gateway. Get your connection info:
134
+
135
+ ```bash
136
+ myapikey whoami # prints base url + api key + ready-to-paste env lines
137
+ ```
138
+
139
+ For an **OpenAI-compatible** tool (covers `/chat/completions` *and* `/responses`):
140
+
141
+ ```bash
142
+ export OPENAI_BASE_URL=http://localhost:7800/v1
143
+ export OPENAI_API_KEY=<gateway api key>
144
+ ```
145
+
146
+ For **Anthropic / Claude Code**:
147
+
148
+ ```bash
149
+ export ANTHROPIC_BASE_URL=http://localhost:7800
150
+ export ANTHROPIC_API_KEY=<gateway api key>
151
+ ```
152
+
153
+ > Whatever a tool calls its "API key" field, put the **gateway API key** there — *not* your web login password. The login password is only for the web UI and CLI admin commands.
154
+
155
+ Quick smoke test without any tool:
156
+
157
+ ```bash
158
+ myapikey call gpt-4o-mini "Say hello in one sentence."
159
+ ```
160
+
161
+ Whichever endpoint you point a tool at, the gateway forwards in that format and never translates — so make sure each model you call is backed by at least one source on the matching slot ([see below](#how-routing-works)).
162
+
163
+ ---
164
+
165
+ ## How routing works
166
+
167
+ The gateway is a **directional forwarder, not a translator**. Four rules explain everything:
168
+
169
+ 1. **The endpoint picks the slot.** `/v1/chat/completions` → the **openai** slot. `/v1/responses` → the **responses** slot. `/v1/messages` → the **anthropic** slot. Each is a distinct wire format, and the body is forwarded verbatim — nothing is converted.
170
+
171
+ 2. **Each model has three independent slots.** For one model you can enable `openai`, `responses`, and `anthropic` separately, and each has its own ordered source chain. Enable a model only on the slots its backends actually speak.
172
+
173
+ 3. **A source qualifies per slot.** The `openai` and `anthropic` slots accept sources carrying that wire format; the `responses` slot only accepts sources marked `supportsResponses` (most OpenAI-compatible backends don't implement the Responses API). A `supportsResponses` source can sit in the **openai** chain *and* the **responses** chain at once.
174
+
175
+ 4. **Priority + failover, within a slot.** Candidates are tried in your priority order. On `429` / `5xx` / timeout, the gateway tries the next one — and the failing source enters a brief **circuit-breaker cooldown** (exponential, 30 s → 5 min) so later calls skip it until it recovers; a success closes the circuit. Other `4xx` errors are returned as-is (they're the caller's fault, not the backend's). Once streaming has started, there's no failover.
176
+
177
+ **The trade-off this buys you:** call an endpoint no source serves for that model and you get a `404` — not a silent translation. That's the price of zero-loss passthrough. (Translation could be layered on later without changing the agent-facing endpoints.)
178
+
179
+ This is the core difference from aggregators like OpenRouter or one-api, which convert between OpenAI and Anthropic request/response shapes.
180
+
181
+ ---
182
+
183
+ ## Model name mapping
184
+
185
+ Sometimes the name a tool asks for isn't the name the backend expects. MyAPIKey can **rewrite the model name per source**, on the way through — still pure passthrough, no format translation.
186
+
187
+ Set it in the web UI (**Models** tab): when adding a source, there's an **upstream model** field; or edit it inline on any chain member. (Not exposed in the CLI yet.)
188
+
189
+ Use it to:
190
+
191
+ - **Alias a friendly name to a versioned id** — expose `claude-sonnet-4` to your tools, while the gateway sends `claude-sonnet-4-20250514` upstream.
192
+ - **Point one public name at different real models per source** — e.g. your `gpt-4` slot's primary source sends `gpt-4o`, its fallback sends `gpt-4-turbo`.
193
+ - **Bridge naming quirks** between two backends that implement the "same" model under different ids.
194
+
195
+ If no mapping is set for a source, the public model name is sent unchanged.
196
+
197
+ ---
198
+
199
+ ## Web UI
200
+
201
+ Visit `http://localhost:7800`, sign in with your username/password. Ships in **English and Chinese** (toggle in the top bar). Five tabs:
202
+
203
+ - **Connect** — copy-paste base URL / API key / login, plus ready-to-use SDK env lines.
204
+ - **Models** — full CRUD: add sources, discover & enable models per slot, drag-order priority, toggle each slot, set [model name mapping](#model-name-mapping), and run an end-to-end **test** call against any model + slot.
205
+ - **Logs** — live recent-calls timeline (latency, status, source, format), with circuit-breaker cooldown events shown alongside the failures that triggered them.
206
+ - **Stats** — aggregate call counts / success rate / latency (p50, p95) by model, source, format, and day, over a selectable range (24h / 7d / 30d / 90d / all).
207
+ - **Settings** — rotate the API key, change the account password, view/reset circuit-breaker state, and see where `data.json` + `logs.jsonl` live.
208
+
209
+ It's the same admin API the CLI uses — configure either way.
210
+
211
+ ---
212
+
213
+ ## CLI reference
214
+
215
+ | Command | |
216
+ |---|---|
217
+ | `serve [--port 7800] [--data-dir <dir>] [--web-dir <path>]` | run the gateway |
218
+ | `whoami` | print connection info for agents (base url, api key, login, example env) |
219
+ | `provider add <name> --base-url-openai URL [--base-url-anthropic URL] --key KEY --formats openai,anthropic` | add a backend (set `supportsResponses` in the web UI) |
220
+ | `provider list` | list backends |
221
+ | `provider models <ref>` | discover models offered by a backend |
222
+ | `provider remove <ref>` | remove a backend (id or name) |
223
+ | `model enable <name> --format <fmt> [--via <ref>]` | enable a model on a slot (openai / anthropic / responses) |
224
+ | `model disable <name> --format <fmt>` | disable one slot (keeps config) |
225
+ | `model list` | show the per-slot routing table |
226
+ | `model add-provider <name> <ref> --format <fmt>` | add a fallback source for a slot |
227
+ | `model remove-provider <name> <ref> --format <fmt>` | remove a source from a slot |
228
+ | `model prioritize <name> <ref>... --format <fmt>` | set source order for a slot (left = primary) |
229
+ | `model remove <name>` | remove a model entirely (all slots) |
230
+ | `call <model> [prompt...]` | quick test through the gateway (OpenAI path) |
231
+
232
+ `<ref>` is a provider id or name. Global flags: `-u/--url`, `--user`, `--pass`, `--api-key` (or `MYAPIKEY_URL` / `MYAPIKEY_USER` / `MYAPIKEY_PASS` / `MYAPIKEY_API_KEY` env vars).
233
+
234
+ ---
235
+
236
+ ## API surface
237
+
238
+ **Agent-facing (`/v1`, API key — `Authorization: Bearer` or `x-api-key`):**
239
+
240
+ - `POST /v1/chat/completions` — OpenAI-format proxy
241
+ - `POST /v1/responses` — OpenAI Responses API (only sources marked `supportsResponses`)
242
+ - `POST /v1/messages` — Anthropic-format proxy
243
+ - `GET /v1/models` — models enabled on the **openai** slot, OpenAI list shape (Anthropic-only models aren't listed here — call them via `/v1/messages`)
244
+ - `GET /health` — public liveness check
245
+
246
+ **Admin (`/admin`, account password — HTTP Basic):**
247
+
248
+ - `/admin/account` `GET` / `PUT` — read or change username/password (either field optional)
249
+ - `/admin/api-key` `GET`, `/admin/api-key/rotate` `POST` — the key agents use
250
+ - `/admin/connection` `GET` — detected LAN IP (for pointing other machines at the gateway)
251
+ - `/admin/providers`, `/admin/providers/:id` `POST`/`PUT`/`DELETE`, `/admin/providers/:id/discover` `POST` — backend CRUD + model discovery
252
+ - `/admin/models`, `/admin/models/:name/{providers,priority,map,disable,test}` — routing-table CRUD, [per-source name mapping](#model-name-mapping), and end-to-end test
253
+ - `/admin/logs` `GET` — recent calls (tail of the on-disk log)
254
+ - `/admin/stats?range=24h|7d|30d|90d|all` `GET` — aggregate stats over retained history
255
+ - `/admin/storage` `GET` — where `data.json` + `logs.jsonl` live
256
+ - `/admin/circuit` `GET`, `/admin/circuit/:id/reset` `POST` — circuit-breaker snapshot + manual reset
257
+
258
+ ---
259
+
260
+ ## Storage
261
+
262
+ Everything lives in one data directory (default `~/.myapikey`):
263
+
264
+ | File | Contents |
265
+ |---|---|
266
+ | `data.json` | all config — providers, the per-model routing table, account, API key |
267
+ | `logs.jsonl` | call history, one JSON object per line (retained ~90 days / 1 M lines) |
268
+ | `credentials.txt` | human-readable web login + API key, regenerated on every startup |
269
+ | `client.json` | CLI client profile (base url + login + API key) |
270
+
271
+ Override the directory with `--data-dir <path>` or `MYAPIKEY_DATA_DIR`. Call logs are never held in memory: the Logs timeline tail-reads the newest ~200 entries, and stats stream over the full retained file on demand.
272
+
273
+ ---
274
+
275
+ ## Development
276
+
277
+ ```bash
278
+ npm install
279
+ npm run build:web # build the Vue UI into packages/web/dist (one time, and after UI changes)
280
+ npm start # tsx ... serve (gateway on :7800)
281
+ npm run dev # gateway, with watch reload
282
+ npm run dev:web # vite dev server (proxies /v1 and /admin to :7800)
283
+ npm run typecheck # tsc (core) + vue-tsc (web)
284
+ ```
285
+
286
+ Testing the gateway by hand: spin up a mock upstream and run `serve` on a scratch port with `--data-dir /tmp/myapikey`.
287
+
288
+ ---
289
+
290
+ ## Notes and non-goals
291
+
292
+ - **No OpenAI↔Anthropic translation.** If a backend doesn't speak the format an agent used, that call fails (`404`). Addable later without changing the agent contract.
293
+ - **No database.** One `data.json` for config; `logs.jsonl` for retained call history.
294
+ - **No TLS.** Intended for LAN / home-server use on your own network. Put it behind a reverse proxy with TLS if you expose it beyond that.
295
+ - **The login password and the API key are separate.** The API key is what agents use; rotate it (`POST /admin/api-key/rotate` or the web UI) if a tool's config leaks. Don't hand either to untrusted code.
@@ -0,0 +1,295 @@
1
+ # MyAPIKey
2
+
3
+ [![npm version](https://img.shields.io/npm/v/myapikey?logo=npm)](https://www.npmjs.com/package/myapikey)
4
+ [![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
5
+ [![node](https://img.shields.io/badge/node.js-%3E%3D18-339933?logo=node.js&logoColor=white)](#快速开始)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)](#开发指南)
7
+
8
+ [English](README.md) | 简体中文
9
+
10
+ **一个地址、一把密钥,接入你所有的模型——且绝不翻译。**
11
+
12
+ MyAPIKey 是一个个人 LLM 网关。把你用到的每个 AI 工具(Claude Code、兼容 OpenAI 的命令行、编辑器、脚本)都指向同一个本地地址、用同一把 API Key。每一次调用都会**原样转发**给一个说相同线材格式(wire format)的后端,并在某个后端掉线时自动故障转移。OpenAI 与 Anthropic 格式之间不做任何转换——你发出去什么,后端就收到什么。
13
+
14
+ > 面向家用服务器 / 局域网设计。默认无数据库、无 TLS、不做格式转换——只是一个你能完全掌控的、轻量透明的转发层。
15
+
16
+ ---
17
+
18
+ ## 目录
19
+
20
+ - [这是什么,适合谁用?](#这是什么适合谁用)
21
+ - [快速开始](#快速开始)
22
+ - [添加第一个后端](#添加第一个后端)
23
+ - [连接你的工具](#连接你的工具)
24
+ - [路由是怎么工作的](#路由是怎么工作的)
25
+ - [模型名映射](#模型名映射)
26
+ - [网页界面](#网页界面)
27
+ - [命令行参考](#命令行参考)
28
+ - [接口一览](#接口一览)
29
+ - [存储说明](#存储说明)
30
+ - [开发指南](#开发指南)
31
+ - [说明与非目标](#说明与非目标)
32
+
33
+ ---
34
+
35
+ ## 这是什么,适合谁用?
36
+
37
+ 如果下面几条说到你心坎里,那 MyAPIKey 就适合你:
38
+
39
+ - 你在 OpenAI、Anthropic、OpenRouter、本地模型之间来回切换**好几把 API Key**,受够了每个工具都要粘一份不同的密钥。
40
+ - 你想让 agent 始终指向**同一个稳定地址**,哪怕背后的供应商变了。
41
+ - 你想要**自动故障转移**——主后端被限流或掉线时,下一个顶上。
42
+ - 你想**在不改动任何工具配置**的前提下,重命名或替换某个模型。
43
+ - 你想要**每一次调用的记录**(延迟、状态、由哪个后端服务)。
44
+
45
+ MyAPIKey 一次性给你这些。你把真实的供应商密钥留在网关内部;你的工具只认识 `http://<主机>:7800` 和一把 `sk-myapikey-…` 密钥。
46
+
47
+ **先知道一件事:网关按你工具的原生格式转发,绝不翻译。** 调用 OpenAI 端点的请求只会到达支持 OpenAI 格式的后端;调用 Anthropic 端点的请求只会到达支持 Anthropic 格式的后端。这保证了请求与响应零损耗。(配好之后,可看 [路由是怎么工作的](#路由是怎么工作的)。)
48
+
49
+ ---
50
+
51
+ ## 快速开始
52
+
53
+ 需要 **Node.js 18+**。无需安装,直接用 npm 跑:
54
+
55
+ ```bash
56
+ npx myapikey serve # → http://localhost:7800
57
+ ```
58
+
59
+ 或全局安装:
60
+
61
+ ```bash
62
+ npm install -g myapikey
63
+ myapikey serve
64
+ ```
65
+
66
+ 首次运行会打印生成的凭据,**并同时写入文件**,这样你就不会弄丢:
67
+
68
+ ```
69
+ First run — here are your credentials (save them):
70
+ username : <随机> (网页登录用)
71
+ password : <随机> (网页登录用)
72
+ api key : sk-myapikey-… (填进工具的 "api key" 字段)
73
+ ↳ also written to ~/.myapikey/credentials.txt
74
+ ```
75
+
76
+ 在浏览器打开 `http://localhost:7800`,用**用户名 / 密码**登录进入网页界面。**API Key** 是分开的另一把——那是你的 AI 工具稍后用的。
77
+
78
+ > 忘了凭据?`cat ~/.myapikey/credentials.txt`(每次启动都会重新生成)。用 `--data-dir <路径>` 或环境变量 `MYAPIKEY_DATA_DIR` 可改数据存放位置。
79
+
80
+ 网关已经跑起来了。下一步,给它一个可以转发的后端。
81
+
82
+ ---
83
+
84
+ ## 添加第一个后端
85
+
86
+ 一个**后端**(provider / 来源)就是一个上游 API:OpenAI、Anthropic、OpenRouter、本地 Ollama、火山引擎 Ark 之类的厂商等。你只需用它的真实密钥加一次,然后通过它启用模型。
87
+
88
+ ### 最简单:网页界面
89
+
90
+ 1. 打开**模型**标签 → **添加模型来源**。
91
+ 2. 填名称、后端的 Base URL、它的 API Key。
92
+ 3. 勾选它支持的线材格式:`openai`、`anthropic`(或两者都勾)。
93
+ 4. 保存,然后点**刷新模型**拉取它的模型列表。
94
+ 5. 在对应的格式槽位上**启用**你想要的模型。
95
+
96
+ ### 或者:命令行
97
+
98
+ ```bash
99
+ # 一个 OpenAI 兼容后端。OpenAI 的 Base URL 含版本段
100
+ # (例如 /v1,或厂商自己的,如火山 Ark 的 /api/v3)。
101
+ myapikey provider add openai-direct \
102
+ --base-url-openai https://api.openai.com/v1 \
103
+ --key sk-... \
104
+ --formats openai
105
+
106
+ # 一个 Anthropic 后端。Anthropic 的 Base URL 不含 /v1
107
+ # (https://api.anthropic.com,或火山 Ark 的 /api/plan)。
108
+ myapikey provider add anthropic-direct \
109
+ --base-url-anthropic https://api.anthropic.com \
110
+ --key sk-ant-... \
111
+ --formats anthropic
112
+
113
+ # 看看各自提供哪些模型,然后在某个槽位上启用一个:
114
+ myapikey provider models openai-direct
115
+ myapikey model enable gpt-4o-mini --format openai --via openai-direct
116
+ ```
117
+
118
+ 这个模型现在就能通过网关访问了。要为同一个模型加**备用来源**(在第一个失败时顶上),再加一个后端并把它串进链里:
119
+
120
+ ```bash
121
+ myapikey provider add backup --base-url-openai https://api.openrouter.ai/api/v1 --key sk-or-... --formats openai
122
+ myapikey model add-provider gpt-4o-mini backup --format openai
123
+ myapikey model prioritize gpt-4o-mini openai-direct backup --format openai # 左边 = 主
124
+ myapikey model list # 查看路由表
125
+ ```
126
+
127
+ > `--format` 选择路由**槽位**:`openai`(对应 `/v1/chat/completions`)、`anthropic`(对应 `/v1/messages`)、或 `responses`(对应 `/v1/responses`)。`responses` 槽位只接受你标记了 **supportsResponses** 的后端——在网页界面里勾选那个开关(CLI 暂未暴露它)。
128
+
129
+ ---
130
+
131
+ ## 连接你的工具
132
+
133
+ 一旦启用某个模型,就可以把任意 agent 指向网关。先取出连接信息:
134
+
135
+ ```bash
136
+ myapikey whoami # 打印 base url + api key + 可直接粘贴的环境变量
137
+ ```
138
+
139
+ **兼容 OpenAI** 的工具(覆盖 `/chat/completions` *和* `/responses`):
140
+
141
+ ```bash
142
+ export OPENAI_BASE_URL=http://localhost:7800/v1
143
+ export OPENAI_API_KEY=<网关 API Key>
144
+ ```
145
+
146
+ **Anthropic / Claude Code**:
147
+
148
+ ```bash
149
+ export ANTHROPIC_BASE_URL=http://localhost:7800
150
+ export ANTHROPIC_API_KEY=<网关 API Key>
151
+ ```
152
+
153
+ > 不管某个工具把它叫什么 "API key" 字段,都填**网关 API Key**——*不是*你的网页登录密码。登录密码只用于网页界面和 CLI 管理命令。
154
+
155
+ 不用任何工具,也能快速冒烟测试:
156
+
157
+ ```bash
158
+ myapikey call gpt-4o-mini "用一句话打个招呼。"
159
+ ```
160
+
161
+ 不管你把工具指向哪个端点,网关都按那个格式转发、绝不翻译——所以确保你调用的每个模型,在对应的槽位上至少有一个来源在服务它([见下文](#路由是怎么工作的))。
162
+
163
+ ---
164
+
165
+ ## 路由是怎么工作的
166
+
167
+ 网关是一个**定向转发器,而非翻译器**。四条规则解释一切:
168
+
169
+ 1. **端点决定槽位。** `/v1/chat/completions` → **openai** 槽位;`/v1/responses` → **responses** 槽位;`/v1/messages` → **anthropic** 槽位。每种都是独立的线材格式,请求体原样转发,不做任何转换。
170
+
171
+ 2. **每个模型有三个互相独立的槽位。** 对同一个模型,你可以分别启用 `openai`、`responses`、`anthropic`,每个槽位都有自己的、按优先级排序的来源链。只在它的后端真正支持的槽位上启用即可。
172
+
173
+ 3. **来源按槽位资质匹配。** `openai` 和 `anthropic` 槽位接受携带对应线材格式的来源;`responses` 槽位只接受你标记了 `supportsResponses` 的来源(大多数 OpenAI 兼容后端并没有实现 Responses API)。一个 `supportsResponses` 来源可以**同时**位于 **openai** 链 *和* **responses** 链中。
174
+
175
+ 4. **槽位内按优先级 + 故障转移。** 候选来源按你的优先级顺序依次尝试。遇到 `429` / `5xx` / 超时,网关就试下一个——并且触发失败的来源会进入短暂的**熔断冷却**(指数退避,30 秒 → 5 分钟),让后续调用先跳过它、等它恢复;成功一次就闭合熔断。其他 `4xx` 错误原样返回(那是调用方的错,不是后端的)。一旦开始流式输出,就不再故障转移。
176
+
177
+ **由此带来的取舍:** 如果某个端点没有任何来源能为该模型服务,你会收到 `404`——而不是被悄悄翻译。这是零损耗透传的代价。(将来可以在不改变 agent 端接口的前提下叠加翻译层。)
178
+
179
+ 这也是它与 OpenRouter、one-api 等聚合器的根本区别——后者会在 OpenAI 与 Anthropic 的请求/响应结构之间互相转换。
180
+
181
+ ---
182
+
183
+ ## 模型名映射
184
+
185
+ 有时候,工具请求的模型名并不是后端期望的名字。MyAPIKey 可以**按来源改写模型名**,在转发过程中替换——依然是纯透传,不做格式翻译。
186
+
187
+ 在网页界面(**模型**标签)设置:添加来源时有一个**上游模型**字段;也可以在链中任意成员上就地编辑。(CLI 暂未暴露。)
188
+
189
+ 它的用途:
190
+
191
+ - **把友好名别名到带版本的 id**——对工具暴露 `claude-sonnet-4`,而网关向上游发送 `claude-sonnet-4-20250514`。
192
+ - **让同一个公开名在不同来源指向不同真实模型**——例如你的 `gpt-4` 槽位,主来源发 `gpt-4o`,备用来源发 `gpt-4-turbo`。
193
+ - **桥接两个后端的命名差异**——同一个模型在不同后端用不同 id。
194
+
195
+ 若某来源未设映射,则原样发送公开模型名。
196
+
197
+ ---
198
+
199
+ ## 网页界面
200
+
201
+ 浏览器打开 `http://localhost:7800`,用用户名 / 密码登录。内置**中英文**,顶栏可切换。五个标签:
202
+
203
+ - **使用方式** — 可直接复制粘贴的 Base URL / API Key / 登录信息,以及现成的 SDK 环境变量。
204
+ - **模型** — 完整增删改查:添加来源、按槽位发现并启用模型、拖拽排序优先级、切换每个槽位、设置[模型名映射](#模型名映射),还能对任意「模型 + 槽位」跑一次端到端**测试**。
205
+ - **最近调用** — 实时的近期调用时间线(延迟、状态、来源、格式),熔断冷却事件会和触发它的失败记录并排显示。
206
+ - **统计** — 按模型 / 来源 / 格式 / 日期汇总调用次数、成功率、延迟(p50、p95),可选时间范围(24 小时 / 7 天 / 30 天 / 90 天 / 全部)。
207
+ - **设置** — 轮换 API Key、修改账号密码、查看 / 重置熔断状态、查看 `data.json` 和 `logs.jsonl` 的位置。
208
+
209
+ 它和 CLI 用的是同一套 admin API——想用哪种配置都行。
210
+
211
+ ---
212
+
213
+ ## 命令行参考
214
+
215
+ | 命令 | |
216
+ |---|---|
217
+ | `serve [--port 7800] [--data-dir <目录>] [--web-dir <路径>]` | 运行网关 |
218
+ | `whoami` | 打印给 agent 用的连接信息(base url、api key、登录信息、示例环境变量) |
219
+ | `provider add <名称> --base-url-openai URL [--base-url-anthropic URL] --key KEY --formats openai,anthropic` | 添加后端(`supportsResponses` 在网页界面设置) |
220
+ | `provider list` | 列出后端 |
221
+ | `provider models <引用>` | 发现某个后端提供的模型 |
222
+ | `provider remove <引用>` | 移除后端(用 id 或名称) |
223
+ | `model enable <名称> --format <格式> [--via <引用>]` | 在某个槽位启用模型(openai / anthropic / responses) |
224
+ | `model disable <名称> --format <格式>` | 禁用某个槽位(保留配置) |
225
+ | `model list` | 查看按槽位的路由表 |
226
+ | `model add-provider <名称> <引用> --format <格式>` | 为某个槽位添加备用来源 |
227
+ | `model remove-provider <名称> <引用> --format <格式>` | 从某个槽位移除来源 |
228
+ | `model prioritize <名称> <引用>... --format <格式>` | 设置某个槽位的来源顺序(左 = 主) |
229
+ | `model remove <名称>` | 完全移除一个模型(所有槽位) |
230
+ | `call <模型> [提示词...]` | 通过网关快速测试(走 OpenAI 路径) |
231
+
232
+ `<引用>` 是来源的 id 或名称。全局参数:`-u/--url`、`--user`、`--pass`、`--api-key`(或 `MYAPIKEY_URL` / `MYAPIKEY_USER` / `MYAPIKEY_PASS` / `MYAPIKEY_API_KEY` 环境变量)。
233
+
234
+ ---
235
+
236
+ ## 接口一览
237
+
238
+ **面向 agent(`/v1`,API Key —— `Authorization: Bearer` 或 `x-api-key`):**
239
+
240
+ - `POST /v1/chat/completions` — OpenAI 格式代理
241
+ - `POST /v1/responses` — OpenAI Responses API(仅限标记了 `supportsResponses` 的来源)
242
+ - `POST /v1/messages` — Anthropic 格式代理
243
+ - `GET /v1/models` — 启用在 **openai** 槽位的模型,OpenAI 列表格式(纯 Anthropic 模型不会出现在这里——请通过 `/v1/messages` 调用)
244
+ - `GET /health` — 公开存活检查
245
+
246
+ **管理(`/admin`,账号密码 —— HTTP Basic):**
247
+
248
+ - `/admin/account` `GET` / `PUT` — 读取或修改用户名 / 密码(任一字段可选)
249
+ - `/admin/api-key` `GET`、`/admin/api-key/rotate` `POST` — agent 用的那把密钥
250
+ - `/admin/connection` `GET` — 探测到的局域网 IP(用于把其他机器指向网关)
251
+ - `/admin/providers`、`/admin/providers/:id` `POST`/`PUT`/`DELETE`、`/admin/providers/:id/discover` `POST` — 后端增删改查 + 模型发现
252
+ - `/admin/models`、`/admin/models/:name/{providers,priority,map,disable,test}` — 路由表增删改查、[按来源的模型名映射](#模型名映射)、端到端测试
253
+ - `/admin/logs` `GET` — 近期调用(落盘日志的尾部)
254
+ - `/admin/stats?range=24h|7d|30d|90d|all` `GET` — 基于保留历史的聚合统计
255
+ - `/admin/storage` `GET` — 查看 `data.json` 和 `logs.jsonl` 的位置
256
+ - `/admin/circuit` `GET`、`/admin/circuit/:id/reset` `POST` — 熔断快照 + 手动重置
257
+
258
+ ---
259
+
260
+ ## 存储说明
261
+
262
+ 所有东西都放在一个数据目录里(默认 `~/.myapikey`):
263
+
264
+ | 文件 | 内容 |
265
+ |---|---|
266
+ | `data.json` | 全部配置 —— 后端、按模型的路由表、账号、API Key |
267
+ | `logs.jsonl` | 调用历史,每行一个 JSON 对象(保留约 90 天 / 100 万行) |
268
+ | `credentials.txt` | 人可读的网页登录信息 + API Key,每次启动重新生成 |
269
+ | `client.json` | CLI 客户端配置(base url + 登录信息 + API Key) |
270
+
271
+ 用 `--data-dir <路径>` 或 `MYAPIKEY_DATA_DIR` 覆盖该目录。调用日志从不放进内存:「最近调用」时间线是尾部读取最新的约 200 条,统计则是按需流式扫描整个保留文件。
272
+
273
+ ---
274
+
275
+ ## 开发指南
276
+
277
+ ```bash
278
+ npm install
279
+ npm run build:web # 把 Vue 界面构建到 packages/web/dist(首次,以及每次改完界面后)
280
+ npm start # tsx ... serve(网关在 :7800)
281
+ npm run dev # 网关,带 watch 热重载
282
+ npm run dev:web # vite 开发服务器(把 /v1 和 /admin 代理到 :7800)
283
+ npm run typecheck # tsc(core)+ vue-tsc(web)
284
+ ```
285
+
286
+ 手动测试网关:起一个 mock 上游,然后在临时端口上用 `serve --data-dir /tmp/myapikey` 跑。
287
+
288
+ ---
289
+
290
+ ## 说明与非目标
291
+
292
+ - **不做 OpenAI ↔ Anthropic 翻译。** 如果某个后端不会说 agent 使用的格式,那次调用就会失败(`404`)。将来可以在不改变 agent 端接口的前提下叠加。
293
+ - **没有数据库。** 配置放一个 `data.json`,保留的调用历史放 `logs.jsonl`。
294
+ - **没有 TLS。** 面向局域网 / 家用服务器,在你自己的网络里用。若要暴露到外网,请放到带 TLS 的反向代理后面。
295
+ - **登录密码和 API Key 是分开的两把。** API Key 是 agent 用的;若某个工具的配置泄露了,就轮换它(`POST /admin/api-key/rotate` 或网页界面)。两把都不要交给不可信的代码路径。