easyrouter-config 1.0.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/LICENSE +27 -0
- package/README.md +108 -0
- package/dist/index.js +311 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 QuantumNous
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This software is a thin wrapper around zcf (https://github.com/UfoMiao/zcf),
|
|
26
|
+
which is licensed under the MIT License. zcf is invoked as a separate process
|
|
27
|
+
via npx; no code from zcf is bundled or modified within this package.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# easyrouter-config
|
|
2
|
+
|
|
3
|
+
> 🚀 一键把 [EasyRouter](https://easyrouter.io) 接入 Claude Code & Codex —— 粘贴 Key 即用
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- ✅ 一条命令同时配置 Claude Code 和 Codex
|
|
8
|
+
- ✅ 交互式 / 非交互式两种模式
|
|
9
|
+
- ✅ 内置 EasyRouter 按量付费 BaseURL,无需手动输入
|
|
10
|
+
- ✅ 配置完成后自动验证链路连通性
|
|
11
|
+
- ✅ 极薄封装,复用业界成熟的 [zcf](https://github.com/UfoMiao/zcf) 写入逻辑
|
|
12
|
+
|
|
13
|
+
## 快速开始
|
|
14
|
+
|
|
15
|
+
### 交互式(推荐新手)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx -y easyrouter-config
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
按提示粘贴 API Key、回车即可。
|
|
22
|
+
|
|
23
|
+
### 一行命令(按量付费)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx -y easyrouter-config -k sk-你的token
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
仅此一种部署模式 —— 内置 `https://easyrouter.io`,按 token 实际用量计费。
|
|
30
|
+
|
|
31
|
+
#### 只配 Claude Code 或只配 Codex
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 只配 Claude Code
|
|
35
|
+
npx -y easyrouter-config -k sk-你的token --only claude
|
|
36
|
+
|
|
37
|
+
# 只配 Codex
|
|
38
|
+
npx -y easyrouter-config -k sk-你的token --only codex
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
完成后直接运行:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
claude # 启动 Claude Code
|
|
45
|
+
codex # 启动 Codex
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 命令行参数
|
|
49
|
+
|
|
50
|
+
| 参数 | 简写 | 说明 |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `--api-key <key>` | `-k` | EasyRouter API Key(可省略 `sk-` 前缀) |
|
|
53
|
+
| `--only <tool>` | | `claude` \| `codex` —— 只配置其一 |
|
|
54
|
+
| `--skip-verify` | | 跳过连通性验证 |
|
|
55
|
+
| `--verbose` | `-v` | 显示底层 zcf 子进程的详细输出 |
|
|
56
|
+
| `--help` | `-h` | 显示帮助 |
|
|
57
|
+
| `--version` | `-V` | 显示版本 |
|
|
58
|
+
|
|
59
|
+
## 工作原理
|
|
60
|
+
|
|
61
|
+
`easyrouter-config` 是 [zcf](https://github.com/UfoMiao/zcf) 的薄封装:
|
|
62
|
+
|
|
63
|
+
1. 通过交互或参数收集 API Key
|
|
64
|
+
2. 内置 EasyRouter 按量付费 BaseURL(`https://easyrouter.io`)
|
|
65
|
+
3. 调用 `npx zcf init` 两次(一次配 `cc`,一次配 `cx`)
|
|
66
|
+
4. zcf 实际写入:
|
|
67
|
+
- Claude Code → `~/.claude/settings.json`(注入 `ANTHROPIC_BASE_URL` 和 `ANTHROPIC_API_KEY` 到 `env`)
|
|
68
|
+
- Codex → `~/.codex/config.toml` + `~/.codex/auth.json`
|
|
69
|
+
5. 通过 `GET {BASE_URL}/v1/models` 探测连通性
|
|
70
|
+
|
|
71
|
+
完全等价于手动执行:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx -y zcf init -s --code-type cc -p custom --api-type auth_token \
|
|
75
|
+
-u https://easyrouter.io -k sk-xxx
|
|
76
|
+
npx -y zcf init -s --code-type cx -p custom --api-type auth_token \
|
|
77
|
+
-u https://easyrouter.io/v1 -k sk-xxx
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
但你只需要记住一行:`npx -y easyrouter-config`。
|
|
81
|
+
|
|
82
|
+
## EasyRouter 是什么
|
|
83
|
+
|
|
84
|
+
[EasyRouter](https://easyrouter.io) 是下一代 LLM 网关与 AI 资产管理平台:
|
|
85
|
+
|
|
86
|
+
- 统一接口聚合 40+ 上游模型供应商(OpenAI / Claude / Gemini / Azure / Bedrock …)
|
|
87
|
+
- 完整的用户、配额、计费、限流体系
|
|
88
|
+
- 支持按量付费
|
|
89
|
+
|
|
90
|
+
## 致谢
|
|
91
|
+
|
|
92
|
+
本工具的核心配置写入逻辑由 **[zcf](https://github.com/UfoMiao/zcf)** 提供。
|
|
93
|
+
我们仅在其上做了 EasyRouter 专属的预设封装与交互简化。
|
|
94
|
+
|
|
95
|
+
特别感谢 [@UfoMiao](https://github.com/UfoMiao) 与 zcf 团队的开源贡献。
|
|
96
|
+
推荐对工作流定制有更多需求的用户直接使用 zcf 原生命令。
|
|
97
|
+
|
|
98
|
+
## 开发
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm install
|
|
102
|
+
npm run build
|
|
103
|
+
node ./dist/index.js --help
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT © QuantumNous
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import {
|
|
5
|
+
intro,
|
|
6
|
+
outro,
|
|
7
|
+
text,
|
|
8
|
+
confirm,
|
|
9
|
+
spinner,
|
|
10
|
+
isCancel,
|
|
11
|
+
cancel,
|
|
12
|
+
log,
|
|
13
|
+
note
|
|
14
|
+
} from "@clack/prompts";
|
|
15
|
+
import { execa } from "execa";
|
|
16
|
+
import pc from "picocolors";
|
|
17
|
+
import { parseArgs } from "util";
|
|
18
|
+
var VERSION = "0.1.0";
|
|
19
|
+
var CLAUDE_BASE_URL = "https://easyrouter.io";
|
|
20
|
+
var CODEX_BASE_URL = "https://easyrouter.io/v1";
|
|
21
|
+
function parseCliArgs() {
|
|
22
|
+
try {
|
|
23
|
+
const { values } = parseArgs({
|
|
24
|
+
args: process.argv.slice(2),
|
|
25
|
+
options: {
|
|
26
|
+
"api-key": { type: "string", short: "k" },
|
|
27
|
+
only: { type: "string" },
|
|
28
|
+
"skip-verify": { type: "boolean" },
|
|
29
|
+
verbose: { type: "boolean", short: "v" },
|
|
30
|
+
help: { type: "boolean", short: "h" },
|
|
31
|
+
version: { type: "boolean", short: "V" }
|
|
32
|
+
},
|
|
33
|
+
allowPositionals: false,
|
|
34
|
+
strict: false
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
apiKey: values["api-key"],
|
|
38
|
+
only: values.only,
|
|
39
|
+
skipVerify: values["skip-verify"],
|
|
40
|
+
verbose: values.verbose,
|
|
41
|
+
help: values.help,
|
|
42
|
+
version: values.version
|
|
43
|
+
};
|
|
44
|
+
} catch {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function printHelp() {
|
|
49
|
+
console.log(`
|
|
50
|
+
${pc.bold(pc.cyan("easyrouter-config"))} ${pc.dim("v" + VERSION)}
|
|
51
|
+
\u4E00\u952E\u628A EasyRouter \u63A5\u5165 Claude Code & Codex\uFF08\u6309\u91CF\u4ED8\u8D39\uFF09
|
|
52
|
+
|
|
53
|
+
${pc.bold("\u7528\u6CD5:")}
|
|
54
|
+
${pc.green("npx -y easyrouter-config")} \u4EA4\u4E92\u5F0F\uFF08\u63A8\u8350\uFF09
|
|
55
|
+
${pc.green("npx -y easyrouter-config -k sk-xxx")} \u4E00\u884C\u547D\u4EE4\u914D\u7F6E
|
|
56
|
+
${pc.green("npx -y easyrouter-config -k sk-xxx --only claude")} \u53EA\u914D Claude Code
|
|
57
|
+
|
|
58
|
+
${pc.bold("\u53C2\u6570:")}
|
|
59
|
+
-k, --api-key <key> EasyRouter API Key\uFF08\u53EF\u7701\u7565 sk- \u524D\u7F00\uFF09
|
|
60
|
+
--only <tool> claude | codex \uFF08\u53EA\u914D\u5176\u4E00\uFF09
|
|
61
|
+
--skip-verify \u8DF3\u8FC7\u8FDE\u901A\u6027\u9A8C\u8BC1
|
|
62
|
+
-v, --verbose \u663E\u793A zcf \u5B50\u8FDB\u7A0B\u8BE6\u7EC6\u8F93\u51FA
|
|
63
|
+
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
64
|
+
-V, --version \u663E\u793A\u7248\u672C
|
|
65
|
+
|
|
66
|
+
${pc.bold("\u5185\u7F6E BaseURL:")}
|
|
67
|
+
Claude Code \u2192 ${CLAUDE_BASE_URL}
|
|
68
|
+
Codex \u2192 ${CODEX_BASE_URL}
|
|
69
|
+
|
|
70
|
+
${pc.bold("\u539F\u7406:")}
|
|
71
|
+
\u672C\u5DE5\u5177\u662F ${pc.underline("zcf")} (https://github.com/UfoMiao/zcf) \u7684\u8584\u5C01\u88C5\u3002
|
|
72
|
+
\u5B9E\u9645\u5199\u5165\u7684\u914D\u7F6E\u5B8C\u5168\u7531 zcf \u5B8C\u6210\uFF1A
|
|
73
|
+
\u2022 Claude Code \u2192 ~/.claude/settings.json
|
|
74
|
+
\u2022 Codex \u2192 ~/.codex/config.toml + ~/.codex/auth.json
|
|
75
|
+
`);
|
|
76
|
+
}
|
|
77
|
+
async function runZcf(opts) {
|
|
78
|
+
const args = [
|
|
79
|
+
"-y",
|
|
80
|
+
"zcf",
|
|
81
|
+
"init",
|
|
82
|
+
"-s",
|
|
83
|
+
// --skip-prompt
|
|
84
|
+
"--code-type",
|
|
85
|
+
opts.codeType,
|
|
86
|
+
"-p",
|
|
87
|
+
"custom",
|
|
88
|
+
"--api-type",
|
|
89
|
+
"auth_token",
|
|
90
|
+
// EasyRouter 走 Bearer
|
|
91
|
+
"-u",
|
|
92
|
+
opts.baseUrl,
|
|
93
|
+
"-k",
|
|
94
|
+
opts.apiKey,
|
|
95
|
+
"-r",
|
|
96
|
+
"backup",
|
|
97
|
+
// 已有配置自动备份
|
|
98
|
+
"--mcp-services",
|
|
99
|
+
"skip",
|
|
100
|
+
// 不装额外 MCP,加速
|
|
101
|
+
"--workflows",
|
|
102
|
+
"skip",
|
|
103
|
+
// 不装工作流模板
|
|
104
|
+
"--output-styles",
|
|
105
|
+
"skip",
|
|
106
|
+
// 不装输出样式
|
|
107
|
+
"--install-cometix-line",
|
|
108
|
+
"false"
|
|
109
|
+
// 不装状态栏
|
|
110
|
+
];
|
|
111
|
+
let capturedOut = "";
|
|
112
|
+
let capturedErr = "";
|
|
113
|
+
try {
|
|
114
|
+
const child = execa("npx", args, {
|
|
115
|
+
stdio: opts.verbose ? "inherit" : "pipe",
|
|
116
|
+
env: { ...process.env, FORCE_COLOR: opts.verbose ? "1" : "0" },
|
|
117
|
+
reject: true
|
|
118
|
+
});
|
|
119
|
+
if (!opts.verbose) {
|
|
120
|
+
child.stdout?.on("data", (chunk) => {
|
|
121
|
+
capturedOut += chunk.toString();
|
|
122
|
+
});
|
|
123
|
+
child.stderr?.on("data", (chunk) => {
|
|
124
|
+
capturedErr += chunk.toString();
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
await child;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
const detail = err?.stderr || err?.stdout || err?.message || String(err);
|
|
130
|
+
throw new Error(
|
|
131
|
+
`zcf \u6267\u884C\u5931\u8D25 (code-type=${opts.codeType}):
|
|
132
|
+
${detail}
|
|
133
|
+
|
|
134
|
+
\u{1F4A1} \u7528 ${pc.green("--verbose")} \u91CD\u65B0\u8FD0\u884C\u53EF\u770B\u5230\u5B8C\u6574\u65E5\u5FD7`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const combined = capturedOut + "\n" + capturedErr;
|
|
138
|
+
if (/CACError|Unknown option/i.test(combined)) {
|
|
139
|
+
const match = combined.match(/(CACError:.*?)(?:\n|$)/);
|
|
140
|
+
throw new Error(
|
|
141
|
+
`zcf \u62D2\u7EDD\u4E86\u6211\u4EEC\u4F20\u7684\u53C2\u6570\uFF08code-type=${opts.codeType}\uFF09\uFF1A
|
|
142
|
+
${match?.[1] || "Unknown option"}
|
|
143
|
+
|
|
144
|
+
\u8FD9\u901A\u5E38\u610F\u5473\u7740 zcf \u5347\u7EA7\u540E flag \u547D\u540D\u53D8\u4E86\u3002\u8BF7\u8054\u7CFB service@easyrouter.io\u3002`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function verifyConnection(baseUrl, apiKey) {
|
|
149
|
+
const probeUrl = baseUrl.replace(/\/+$/, "") + "/v1/models";
|
|
150
|
+
try {
|
|
151
|
+
const ctrl = new AbortController();
|
|
152
|
+
const timer = setTimeout(() => ctrl.abort(), 1e4);
|
|
153
|
+
const res = await fetch(probeUrl, {
|
|
154
|
+
method: "GET",
|
|
155
|
+
headers: {
|
|
156
|
+
Authorization: `Bearer ${apiKey}`,
|
|
157
|
+
"anthropic-version": "2023-06-01"
|
|
158
|
+
},
|
|
159
|
+
signal: ctrl.signal
|
|
160
|
+
});
|
|
161
|
+
clearTimeout(timer);
|
|
162
|
+
return { ok: res.ok, status: res.status };
|
|
163
|
+
} catch (err) {
|
|
164
|
+
return { ok: false, error: err?.message || String(err) };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function normalizeApiKey(raw) {
|
|
168
|
+
const trimmed = raw.trim();
|
|
169
|
+
if (!trimmed) return trimmed;
|
|
170
|
+
if (trimmed.startsWith("sk-")) return trimmed;
|
|
171
|
+
return "sk-" + trimmed;
|
|
172
|
+
}
|
|
173
|
+
function maskKey(key) {
|
|
174
|
+
if (key.length <= 8) return "*".repeat(key.length);
|
|
175
|
+
return key.slice(0, 6) + "*".repeat(Math.max(4, key.length - 10)) + key.slice(-4);
|
|
176
|
+
}
|
|
177
|
+
async function main() {
|
|
178
|
+
const args = parseCliArgs();
|
|
179
|
+
if (args.help) {
|
|
180
|
+
printHelp();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (args.version) {
|
|
184
|
+
console.log(VERSION);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
console.log();
|
|
188
|
+
intro(pc.bgCyan(pc.black(" EasyRouter Config ")) + pc.dim(" v" + VERSION));
|
|
189
|
+
let apiKey;
|
|
190
|
+
if (args.apiKey) {
|
|
191
|
+
apiKey = normalizeApiKey(args.apiKey);
|
|
192
|
+
log.info(`API Key: ${pc.dim(maskKey(apiKey))}\uFF08\u6765\u81EA\u547D\u4EE4\u884C\u53C2\u6570\uFF09`);
|
|
193
|
+
} else {
|
|
194
|
+
const input = await text({
|
|
195
|
+
message: "\u8BF7\u8F93\u5165\u4F60\u7684 EasyRouter API Key",
|
|
196
|
+
placeholder: "sk-xxxxxxxxxxxxxxxx",
|
|
197
|
+
validate(value) {
|
|
198
|
+
if (!value) return "\u4E0D\u80FD\u4E3A\u7A7A";
|
|
199
|
+
if (value.trim().length < 8) return "Key \u957F\u5EA6\u770B\u8D77\u6765\u4E0D\u5BF9";
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
if (isCancel(input)) {
|
|
204
|
+
cancel("\u5DF2\u53D6\u6D88");
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
apiKey = normalizeApiKey(input);
|
|
208
|
+
}
|
|
209
|
+
let configClaude = true;
|
|
210
|
+
let configCodex = true;
|
|
211
|
+
if (args.only === "claude") {
|
|
212
|
+
configCodex = false;
|
|
213
|
+
} else if (args.only === "codex") {
|
|
214
|
+
configClaude = false;
|
|
215
|
+
}
|
|
216
|
+
note(
|
|
217
|
+
[
|
|
218
|
+
`${pc.bold("\u8BA1\u8D39\u6A21\u5F0F")} \u6309\u91CF\u4ED8\u8D39`,
|
|
219
|
+
configClaude ? `${pc.bold("Claude URL")} ${CLAUDE_BASE_URL}` : null,
|
|
220
|
+
configCodex ? `${pc.bold("Codex URL")} ${CODEX_BASE_URL}` : null,
|
|
221
|
+
`${pc.bold("API Key")} ${maskKey(apiKey)}`,
|
|
222
|
+
`${pc.bold("\u5C06\u914D\u7F6E")} ${[
|
|
223
|
+
configClaude && "Claude Code",
|
|
224
|
+
configCodex && "Codex"
|
|
225
|
+
].filter(Boolean).join(" + ")}`
|
|
226
|
+
].filter(Boolean).join("\n"),
|
|
227
|
+
"\u914D\u7F6E\u9884\u89C8"
|
|
228
|
+
);
|
|
229
|
+
if (!args.apiKey) {
|
|
230
|
+
const ok = await confirm({ message: "\u7EE7\u7EED\uFF1F", initialValue: true });
|
|
231
|
+
if (isCancel(ok) || !ok) {
|
|
232
|
+
cancel("\u5DF2\u53D6\u6D88");
|
|
233
|
+
process.exit(0);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const verbose = !!args.verbose;
|
|
237
|
+
if (configClaude) {
|
|
238
|
+
if (verbose) {
|
|
239
|
+
log.step("\u914D\u7F6E Claude Code\uFF08\u8BE6\u7EC6\u65E5\u5FD7\uFF09");
|
|
240
|
+
}
|
|
241
|
+
const s = verbose || !process.stdout.isTTY ? null : spinner();
|
|
242
|
+
s?.start("\u914D\u7F6E Claude Code");
|
|
243
|
+
if (!s && !verbose) log.info("\u6B63\u5728\u914D\u7F6E Claude Code\uFF08\u9996\u6B21\u8FD0\u884C\u9700\u4E0B\u8F7D zcf\uFF0C\u7EA6 30~60 \u79D2\uFF09...");
|
|
244
|
+
try {
|
|
245
|
+
await runZcf({
|
|
246
|
+
codeType: "cc",
|
|
247
|
+
baseUrl: CLAUDE_BASE_URL,
|
|
248
|
+
apiKey,
|
|
249
|
+
verbose
|
|
250
|
+
});
|
|
251
|
+
s?.stop(pc.green("\u2713 Claude Code \u914D\u7F6E\u5B8C\u6210 ") + pc.dim("\u2192 ~/.claude/settings.json"));
|
|
252
|
+
if (!s) log.success(pc.green("\u2713 Claude Code \u914D\u7F6E\u5B8C\u6210 \u2192 ~/.claude/settings.json"));
|
|
253
|
+
} catch (err) {
|
|
254
|
+
s?.stop(pc.red("\u2717 Claude Code \u914D\u7F6E\u5931\u8D25"));
|
|
255
|
+
console.error(err.message);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (configCodex) {
|
|
260
|
+
if (verbose) {
|
|
261
|
+
log.step("\u914D\u7F6E Codex\uFF08\u8BE6\u7EC6\u65E5\u5FD7\uFF09");
|
|
262
|
+
}
|
|
263
|
+
const s = verbose || !process.stdout.isTTY ? null : spinner();
|
|
264
|
+
s?.start("\u914D\u7F6E Codex");
|
|
265
|
+
if (!s && !verbose) log.info("\u6B63\u5728\u914D\u7F6E Codex...");
|
|
266
|
+
try {
|
|
267
|
+
await runZcf({
|
|
268
|
+
codeType: "cx",
|
|
269
|
+
baseUrl: CODEX_BASE_URL,
|
|
270
|
+
apiKey,
|
|
271
|
+
verbose
|
|
272
|
+
});
|
|
273
|
+
s?.stop(pc.green("\u2713 Codex \u914D\u7F6E\u5B8C\u6210 ") + pc.dim("\u2192 ~/.codex/config.toml"));
|
|
274
|
+
if (!s) log.success(pc.green("\u2713 Codex \u914D\u7F6E\u5B8C\u6210 \u2192 ~/.codex/config.toml"));
|
|
275
|
+
} catch (err) {
|
|
276
|
+
s?.stop(pc.red("\u2717 Codex \u914D\u7F6E\u5931\u8D25"));
|
|
277
|
+
console.error(err.message);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (!args.skipVerify) {
|
|
282
|
+
const s = process.stdout.isTTY ? spinner() : null;
|
|
283
|
+
s?.start("\u9A8C\u8BC1\u8FDE\u901A\u6027");
|
|
284
|
+
if (!s) log.info("\u6B63\u5728\u9A8C\u8BC1\u8FDE\u901A\u6027...");
|
|
285
|
+
const result = await verifyConnection(CLAUDE_BASE_URL, apiKey);
|
|
286
|
+
if (result.ok) {
|
|
287
|
+
const msg = pc.green("\u2713 \u94FE\u8DEF\u9A8C\u8BC1\u901A\u8FC7");
|
|
288
|
+
s?.stop(msg) ?? log.success(msg);
|
|
289
|
+
} else {
|
|
290
|
+
const msg = pc.yellow(
|
|
291
|
+
`\u26A0 \u9A8C\u8BC1\u672A\u901A\u8FC7${result.status ? ` (HTTP ${result.status})` : ""}` + (result.error ? `: ${result.error}` : "") + ` \u2014\u2014 \u914D\u7F6E\u5DF2\u5199\u5165\uFF0C\u53EF\u624B\u52A8\u6D4B\u8BD5`
|
|
292
|
+
);
|
|
293
|
+
s?.stop(msg) ?? log.warn(msg);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const tips = [];
|
|
297
|
+
if (configClaude) tips.push(pc.cyan("claude") + pc.dim(" # \u542F\u52A8 Claude Code"));
|
|
298
|
+
if (configCodex) tips.push(pc.cyan("codex") + pc.dim(" # \u542F\u52A8 Codex"));
|
|
299
|
+
note(tips.join("\n"), "\u{1F389} \u5168\u90E8\u5B8C\u6210\uFF01\u73B0\u5728\u53EF\u4EE5\u8FD0\u884C\uFF1A");
|
|
300
|
+
outro(
|
|
301
|
+
pc.dim("\u611F\u8C22\u4F7F\u7528 EasyRouter ") + pc.underline(pc.cyan("https://easyrouter.io")) + pc.dim(" \xB7 powered by ") + pc.underline("zcf")
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
main().catch((err) => {
|
|
305
|
+
console.error();
|
|
306
|
+
console.error(pc.red("\u2717 \u81F4\u547D\u9519\u8BEF\uFF1A"), err?.message || err);
|
|
307
|
+
if (err?.stack && process.env.DEBUG) {
|
|
308
|
+
console.error(pc.dim(err.stack));
|
|
309
|
+
}
|
|
310
|
+
process.exit(1);
|
|
311
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "easyrouter-config",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "🚀 一键把 EasyRouter 接入 Claude Code & Codex —— 粘贴 Key 即用",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"easyrouter-config": "./dist/index.js",
|
|
8
|
+
"erc": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"start": "node ./dist/index.js",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"easyrouter",
|
|
24
|
+
"claude-code",
|
|
25
|
+
"codex",
|
|
26
|
+
"cli",
|
|
27
|
+
"ai-gateway",
|
|
28
|
+
"zcf"
|
|
29
|
+
],
|
|
30
|
+
"author": "QuantumNous <service@easyrouter.io>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"homepage": "https://easyrouter.io",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://gitlab.liebaopay.com/easyrouter/easyrouter-cli-config.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://gitlab.liebaopay.com/easyrouter/easyrouter-cli-config/-/issues"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@clack/prompts": "^0.11.0",
|
|
45
|
+
"execa": "^9.5.2",
|
|
46
|
+
"picocolors": "^1.1.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.10.0",
|
|
50
|
+
"tsup": "^8.3.5",
|
|
51
|
+
"typescript": "^5.7.2"
|
|
52
|
+
}
|
|
53
|
+
}
|