trinity-config 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/index.js +416 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trinity
|
|
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,74 @@
|
|
|
1
|
+
# trinity-config
|
|
2
|
+
|
|
3
|
+
> 一键把 [Trinity API](https://trinitydesk.ai) 接入 **Claude Code** 与 **Codex CLI** —— 粘贴 `xh-` Key 即用
|
|
4
|
+
|
|
5
|
+
## 快速开始
|
|
6
|
+
|
|
7
|
+
### 交互式(推荐)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx -y trinity-config
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 一行命令
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx -y trinity-config -k xh-你的key
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 只配其中一个
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx -y trinity-config -k xh-你的key --only claude
|
|
23
|
+
npx -y trinity-config -k xh-你的key --only codex
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
完成后:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
claude # Claude Code
|
|
30
|
+
codex # Codex CLI
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 工作原理
|
|
34
|
+
|
|
35
|
+
1. 收集 Trinity API Key(`xh-` 前缀)
|
|
36
|
+
2. 调用 [zcf](https://github.com/UfoMiao/zcf) 写入本地配置:
|
|
37
|
+
- Claude Code → `~/.claude/settings.json`(`ANTHROPIC_BASE_URL` = `https://api.trinitydesk.ai`)
|
|
38
|
+
- Codex → `~/.codex/config.toml`(Base = `https://api.trinitydesk.ai/v1`)
|
|
39
|
+
3. `GET https://api.trinitydesk.ai/v1/models` 探测连通性
|
|
40
|
+
|
|
41
|
+
## 命令行参数
|
|
42
|
+
|
|
43
|
+
| 参数 | 简写 | 说明 |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `--api-key` | `-k` | Trinity API Key |
|
|
46
|
+
| `--only` | | `claude` \| `codex` |
|
|
47
|
+
| `--base-url` | | 默认 `https://api.trinitydesk.ai` |
|
|
48
|
+
| `--claude-model` | | Claude 默认模型(默认 `claude-sonnet-4-6`) |
|
|
49
|
+
| `--codex-model` | | Codex 默认模型(默认 `gpt-5.4`) |
|
|
50
|
+
| `--skip-verify` | | 跳过连通性验证 |
|
|
51
|
+
| `--verbose` | `-v` | 显示 zcf 详细输出 |
|
|
52
|
+
| `--help` | `-h` | 帮助 |
|
|
53
|
+
| `--version` | `-V` | 版本 |
|
|
54
|
+
|
|
55
|
+
## 注意
|
|
56
|
+
|
|
57
|
+
- 配置后**所有模型调用走 Trinity 计费**,不再使用 Anthropic/OpenAI 官方账号额度。
|
|
58
|
+
- 工具内选择的模型名须为 Trinity 已上架的 `model_code`(可用 `GET /v1/models` 查看)。
|
|
59
|
+
- Windows 下建议用 WSL2 运行 `claude` / `codex`;`npx trinity-config` 可在 PowerShell 执行。
|
|
60
|
+
|
|
61
|
+
## 开发
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd tools/trinity-config
|
|
65
|
+
npm install
|
|
66
|
+
npm run build
|
|
67
|
+
node dist/index.js --help
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
发版见仓库 `部署相关/trinity-config-npm发版说明.md`。
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/index.ts
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
|
|
6
|
+
// src/args.ts
|
|
7
|
+
function printHelp() {
|
|
8
|
+
console.log(`
|
|
9
|
+
trinity-config \u2014 \u4E00\u952E\u628A Trinity API \u63A5\u5165 Claude Code \u4E0E Codex CLI
|
|
10
|
+
|
|
11
|
+
\u7528\u6CD5:
|
|
12
|
+
npx -y trinity-config [\u9009\u9879]
|
|
13
|
+
|
|
14
|
+
\u9009\u9879:
|
|
15
|
+
-k, --api-key <key> Trinity API Key\uFF08xh- \u524D\u7F00\uFF09
|
|
16
|
+
--only <tool> \u53EA\u914D\u7F6E claude \u6216 codex
|
|
17
|
+
--base-url <url> API \u6839\u5730\u5740\uFF08\u9ED8\u8BA4 ${"https://api.trinitydesk.ai"})
|
|
18
|
+
--claude-model <model> Claude Code \u9ED8\u8BA4\u6A21\u578B
|
|
19
|
+
--codex-model <model> Codex \u9ED8\u8BA4\u6A21\u578B
|
|
20
|
+
--skip-verify \u8DF3\u8FC7 GET /v1/models \u8FDE\u901A\u6027\u68C0\u67E5
|
|
21
|
+
-v, --verbose \u663E\u793A zcf \u8BE6\u7EC6\u8F93\u51FA
|
|
22
|
+
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
23
|
+
-V, --version \u663E\u793A\u7248\u672C
|
|
24
|
+
|
|
25
|
+
\u793A\u4F8B:
|
|
26
|
+
npx -y trinity-config
|
|
27
|
+
npx -y trinity-config -k xh-your-key
|
|
28
|
+
npx -y trinity-config -k xh-your-key --only claude
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
function parseArgs(argv) {
|
|
32
|
+
const opts = {
|
|
33
|
+
skipVerify: false,
|
|
34
|
+
verbose: false,
|
|
35
|
+
help: false,
|
|
36
|
+
version: false
|
|
37
|
+
};
|
|
38
|
+
const args = [...argv];
|
|
39
|
+
while (args.length > 0) {
|
|
40
|
+
const arg = args.shift();
|
|
41
|
+
if (!arg) break;
|
|
42
|
+
switch (arg) {
|
|
43
|
+
case "-h":
|
|
44
|
+
case "--help":
|
|
45
|
+
opts.help = true;
|
|
46
|
+
break;
|
|
47
|
+
case "-V":
|
|
48
|
+
case "--version":
|
|
49
|
+
opts.version = true;
|
|
50
|
+
break;
|
|
51
|
+
case "-v":
|
|
52
|
+
case "--verbose":
|
|
53
|
+
opts.verbose = true;
|
|
54
|
+
break;
|
|
55
|
+
case "--skip-verify":
|
|
56
|
+
opts.skipVerify = true;
|
|
57
|
+
break;
|
|
58
|
+
case "-k":
|
|
59
|
+
case "--api-key":
|
|
60
|
+
opts.apiKey = args.shift();
|
|
61
|
+
break;
|
|
62
|
+
case "--only": {
|
|
63
|
+
const only = args.shift();
|
|
64
|
+
if (only !== "claude" && only !== "codex") {
|
|
65
|
+
throw new Error("--only \u987B\u4E3A claude \u6216 codex");
|
|
66
|
+
}
|
|
67
|
+
opts.only = only;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "--base-url":
|
|
71
|
+
opts.baseUrl = args.shift();
|
|
72
|
+
break;
|
|
73
|
+
case "--claude-model":
|
|
74
|
+
opts.claudeModel = args.shift();
|
|
75
|
+
break;
|
|
76
|
+
case "--codex-model":
|
|
77
|
+
opts.codexModel = args.shift();
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
if (arg.startsWith("-")) {
|
|
81
|
+
throw new Error(`\u672A\u77E5\u53C2\u6570: ${arg}`);
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`\u591A\u4F59\u53C2\u6570: ${arg}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (opts.help) {
|
|
87
|
+
printHelp();
|
|
88
|
+
}
|
|
89
|
+
return opts;
|
|
90
|
+
}
|
|
91
|
+
function normalizeApiKey(raw) {
|
|
92
|
+
const key = raw.trim();
|
|
93
|
+
if (!key) {
|
|
94
|
+
throw new Error("API Key \u4E0D\u80FD\u4E3A\u7A7A");
|
|
95
|
+
}
|
|
96
|
+
return key.startsWith("xh-") ? key : `xh-${key}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/constants.ts
|
|
100
|
+
var PACKAGE_NAME = "trinity-config";
|
|
101
|
+
var PACKAGE_VERSION = "1.0.0";
|
|
102
|
+
var TRINITY_BASE_URL = "https://api.trinitydesk.ai";
|
|
103
|
+
var DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6";
|
|
104
|
+
var DEFAULT_CLAUDE_FAST_MODEL = "claude-sonnet-4-6";
|
|
105
|
+
var DEFAULT_CODEX_MODEL = "gpt-5.4";
|
|
106
|
+
var VERIFY_TIMEOUT_MS = 15e3;
|
|
107
|
+
|
|
108
|
+
// src/merge-claude-env.ts
|
|
109
|
+
import fs from "fs";
|
|
110
|
+
import os from "os";
|
|
111
|
+
import path from "path";
|
|
112
|
+
function claudeSettingsPath() {
|
|
113
|
+
return path.join(os.homedir(), ".claude", "settings.json");
|
|
114
|
+
}
|
|
115
|
+
function mergeClaudeEnv(claudeModel, fastModel, baseUrl) {
|
|
116
|
+
const filePath = claudeSettingsPath();
|
|
117
|
+
if (!fs.existsSync(filePath)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
121
|
+
let settings;
|
|
122
|
+
try {
|
|
123
|
+
settings = JSON.parse(raw);
|
|
124
|
+
} catch {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
settings.env = {
|
|
128
|
+
...settings.env ?? {},
|
|
129
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
130
|
+
ANTHROPIC_MODEL: claudeModel,
|
|
131
|
+
ANTHROPIC_SMALL_FAST_MODEL: fastModel
|
|
132
|
+
};
|
|
133
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
134
|
+
fs.writeFileSync(filePath, `${JSON.stringify(settings, null, 2)}
|
|
135
|
+
`, "utf8");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/zcf.ts
|
|
139
|
+
import { execa } from "execa";
|
|
140
|
+
function sharedZcfArgs(verbose) {
|
|
141
|
+
return [
|
|
142
|
+
"zcf",
|
|
143
|
+
"init",
|
|
144
|
+
"--skip-prompt",
|
|
145
|
+
"--provider",
|
|
146
|
+
"custom",
|
|
147
|
+
"--api-type",
|
|
148
|
+
"auth_token",
|
|
149
|
+
"--config-action",
|
|
150
|
+
"merge",
|
|
151
|
+
"--mcp-services",
|
|
152
|
+
"skip",
|
|
153
|
+
"--workflows",
|
|
154
|
+
"skip",
|
|
155
|
+
"--output-styles",
|
|
156
|
+
"skip",
|
|
157
|
+
"--install-cometix-line",
|
|
158
|
+
"false",
|
|
159
|
+
...verbose ? [] : ["--lang", "en"]
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
async function runZcfInit(options) {
|
|
163
|
+
const args = [
|
|
164
|
+
...sharedZcfArgs(options.verbose),
|
|
165
|
+
"--code-type",
|
|
166
|
+
options.codeType,
|
|
167
|
+
"--api-url",
|
|
168
|
+
options.baseUrl,
|
|
169
|
+
"--api-key",
|
|
170
|
+
options.apiKey,
|
|
171
|
+
"--api-model",
|
|
172
|
+
options.primaryModel
|
|
173
|
+
];
|
|
174
|
+
if (options.codeType === "cc" && options.fastModel) {
|
|
175
|
+
args.push("--api-haiku-model", options.fastModel);
|
|
176
|
+
args.push("--api-sonnet-model", options.primaryModel);
|
|
177
|
+
}
|
|
178
|
+
await execa("npx", ["-y", ...args], {
|
|
179
|
+
stdio: options.verbose ? "inherit" : "pipe",
|
|
180
|
+
reject: true,
|
|
181
|
+
timeout: 18e4
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async function configureClaude(apiKey, baseUrl, claudeModel, verbose) {
|
|
185
|
+
await runZcfInit({
|
|
186
|
+
codeType: "cc",
|
|
187
|
+
apiKey,
|
|
188
|
+
baseUrl,
|
|
189
|
+
primaryModel: claudeModel,
|
|
190
|
+
fastModel: DEFAULT_CLAUDE_FAST_MODEL,
|
|
191
|
+
verbose
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async function configureCodex(apiKey, codexBaseUrl, codexModel, verbose) {
|
|
195
|
+
await runZcfInit({
|
|
196
|
+
codeType: "cx",
|
|
197
|
+
apiKey,
|
|
198
|
+
baseUrl: codexBaseUrl,
|
|
199
|
+
primaryModel: codexModel,
|
|
200
|
+
verbose
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function resolveBaseUrls(baseUrl) {
|
|
204
|
+
const root = (baseUrl ?? TRINITY_BASE_URL).replace(/\/+$/, "");
|
|
205
|
+
const codexBaseUrl = root.endsWith("/v1") ? root : `${root}/v1`;
|
|
206
|
+
const claudeBaseUrl = root.endsWith("/v1") ? root.slice(0, -3) : root;
|
|
207
|
+
return { claudeBaseUrl, codexBaseUrl };
|
|
208
|
+
}
|
|
209
|
+
function defaultModels(claudeModel, codexModel) {
|
|
210
|
+
return {
|
|
211
|
+
claudeModel: claudeModel ?? DEFAULT_CLAUDE_MODEL,
|
|
212
|
+
codexModel: codexModel ?? DEFAULT_CODEX_MODEL
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/verify.ts
|
|
217
|
+
import https from "https";
|
|
218
|
+
function getJson(url, apiKey) {
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
const target = new URL(url);
|
|
221
|
+
const req = https.request(
|
|
222
|
+
{
|
|
223
|
+
protocol: target.protocol,
|
|
224
|
+
hostname: target.hostname,
|
|
225
|
+
port: target.port || 443,
|
|
226
|
+
path: `${target.pathname}${target.search}`,
|
|
227
|
+
method: "GET",
|
|
228
|
+
headers: {
|
|
229
|
+
Accept: "application/json",
|
|
230
|
+
Authorization: `Bearer ${apiKey}`
|
|
231
|
+
},
|
|
232
|
+
timeout: VERIFY_TIMEOUT_MS
|
|
233
|
+
},
|
|
234
|
+
(res) => {
|
|
235
|
+
const chunks = [];
|
|
236
|
+
res.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
237
|
+
res.on("end", () => {
|
|
238
|
+
const text2 = Buffer.concat(chunks).toString("utf8");
|
|
239
|
+
let body;
|
|
240
|
+
try {
|
|
241
|
+
body = JSON.parse(text2);
|
|
242
|
+
} catch {
|
|
243
|
+
reject(new Error(`\u54CD\u5E94\u4E0D\u662F JSON\uFF08HTTP ${res.statusCode}\uFF09`));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
resolve({ statusCode: res.statusCode ?? 0, body });
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
req.on("timeout", () => {
|
|
251
|
+
req.destroy();
|
|
252
|
+
reject(new Error(`\u8FDE\u63A5\u8D85\u65F6\uFF08>${VERIFY_TIMEOUT_MS}ms\uFF09`));
|
|
253
|
+
});
|
|
254
|
+
req.on("error", reject);
|
|
255
|
+
req.end();
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async function verifyTrinityApi(apiKey, baseUrl = TRINITY_BASE_URL) {
|
|
259
|
+
const root = baseUrl.replace(/\/+$/, "");
|
|
260
|
+
const modelsUrl = `${root}/v1/models`;
|
|
261
|
+
try {
|
|
262
|
+
const { statusCode, body } = await getJson(modelsUrl, apiKey);
|
|
263
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
statusCode,
|
|
267
|
+
message: "API Key \u65E0\u6548\u6216\u5DF2\u7981\u7528\uFF08HTTP 401/403\uFF09"
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (statusCode === 404) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
statusCode,
|
|
274
|
+
message: "Base URL \u9519\u8BEF\uFF08\u662F\u5426\u591A\u5199\u4E86 /v1\uFF1F\u5E94\u4F7F\u7528 https://api.trinitydesk.ai\uFF09"
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
statusCode,
|
|
281
|
+
message: `GET /v1/models \u5931\u8D25\uFF08HTTP ${statusCode}\uFF09`
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const modelIds = (body.data ?? []).map((item) => item.id).filter(Boolean);
|
|
285
|
+
if (body.object !== "list" && modelIds.length === 0) {
|
|
286
|
+
return {
|
|
287
|
+
ok: true,
|
|
288
|
+
statusCode,
|
|
289
|
+
message: "\u5DF2\u8FDE\u901A\uFF0C\u4F46\u6A21\u578B\u5217\u8868\u683C\u5F0F\u975E\u5E38\u89C4",
|
|
290
|
+
modelIds
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
ok: true,
|
|
295
|
+
statusCode,
|
|
296
|
+
message: `\u8FDE\u901A\u6210\u529F\uFF08${modelIds.length} \u4E2A\u6A21\u578B\uFF09`,
|
|
297
|
+
modelIds
|
|
298
|
+
};
|
|
299
|
+
} catch (error) {
|
|
300
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
301
|
+
if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED")) {
|
|
302
|
+
return { ok: false, message: `\u65E0\u6CD5\u8FDE\u63A5 ${root}\uFF1A${message}` };
|
|
303
|
+
}
|
|
304
|
+
return { ok: false, message: `\u8FDE\u901A\u6027\u68C0\u67E5\u5931\u8D25\uFF1A${message}` };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function pickRecommendedModels(modelIds) {
|
|
308
|
+
const claude = modelIds.find((id) => id === DEFAULT_CLAUDE_MODEL) ?? modelIds.find((id) => id.includes("claude-sonnet")) ?? modelIds.find((id) => id.toLowerCase().includes("claude"));
|
|
309
|
+
const codex = modelIds.find((id) => id === DEFAULT_CODEX_MODEL) ?? modelIds.find((id) => id === "gpt-5.4") ?? modelIds.find((id) => id.startsWith("gpt-5")) ?? modelIds.find((id) => id.toLowerCase().includes("gpt"));
|
|
310
|
+
return { claude, codex };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/index.ts
|
|
314
|
+
async function promptApiKey(initial) {
|
|
315
|
+
if (initial) {
|
|
316
|
+
return normalizeApiKey(initial);
|
|
317
|
+
}
|
|
318
|
+
const value = await p.text({
|
|
319
|
+
message: "\u7C98\u8D34 Trinity API Key\uFF08xh- \u5F00\u5934\uFF09",
|
|
320
|
+
placeholder: "xh-...",
|
|
321
|
+
validate: (input) => {
|
|
322
|
+
if (!input?.trim()) return "\u8BF7\u8F93\u5165 API Key";
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
if (p.isCancel(value)) {
|
|
326
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
327
|
+
process.exit(0);
|
|
328
|
+
}
|
|
329
|
+
return normalizeApiKey(String(value));
|
|
330
|
+
}
|
|
331
|
+
async function run() {
|
|
332
|
+
let opts;
|
|
333
|
+
try {
|
|
334
|
+
opts = parseArgs(process.argv.slice(2));
|
|
335
|
+
} catch (error) {
|
|
336
|
+
console.error(pc.red(error instanceof Error ? error.message : String(error)));
|
|
337
|
+
return 1;
|
|
338
|
+
}
|
|
339
|
+
if (opts.help) {
|
|
340
|
+
return 0;
|
|
341
|
+
}
|
|
342
|
+
if (opts.version) {
|
|
343
|
+
console.log(`${PACKAGE_NAME} v${PACKAGE_VERSION}`);
|
|
344
|
+
return 0;
|
|
345
|
+
}
|
|
346
|
+
p.intro(pc.bgCyan(pc.black(` ${PACKAGE_NAME} v${PACKAGE_VERSION} `)));
|
|
347
|
+
const apiKey = await promptApiKey(opts.apiKey);
|
|
348
|
+
const { claudeBaseUrl, codexBaseUrl } = resolveBaseUrls(opts.baseUrl);
|
|
349
|
+
const models = defaultModels(opts.claudeModel, opts.codexModel);
|
|
350
|
+
const configureClaudeTool = !opts.only || opts.only === "claude";
|
|
351
|
+
const configureCodexTool = !opts.only || opts.only === "codex";
|
|
352
|
+
const spinner2 = p.spinner();
|
|
353
|
+
if (configureClaudeTool) {
|
|
354
|
+
spinner2.start("\u914D\u7F6E Claude Code\u2026");
|
|
355
|
+
try {
|
|
356
|
+
await configureClaude(apiKey, claudeBaseUrl, models.claudeModel, opts.verbose);
|
|
357
|
+
mergeClaudeEnv(models.claudeModel, models.claudeModel, claudeBaseUrl);
|
|
358
|
+
spinner2.stop("Claude Code \u914D\u7F6E\u5DF2\u5199\u5165 ~/.claude/settings.json");
|
|
359
|
+
} catch (error) {
|
|
360
|
+
spinner2.stop(pc.red("Claude Code \u914D\u7F6E\u5931\u8D25"));
|
|
361
|
+
const message = error instanceof Error && "timed out" in error ? "zcf \u6267\u884C\u8D85\u65F6\uFF08>3 \u5206\u949F\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u7A0D\u540E\u91CD\u8BD5\uFF1Anpx -y zcf init --help" : error instanceof Error ? error.message : String(error);
|
|
362
|
+
console.error(message);
|
|
363
|
+
return 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (configureCodexTool) {
|
|
367
|
+
spinner2.start("\u914D\u7F6E Codex CLI\u2026");
|
|
368
|
+
try {
|
|
369
|
+
await configureCodex(apiKey, codexBaseUrl, models.codexModel, opts.verbose);
|
|
370
|
+
spinner2.stop("Codex \u914D\u7F6E\u5DF2\u5199\u5165 ~/.codex/");
|
|
371
|
+
} catch (error) {
|
|
372
|
+
spinner2.stop(pc.red("Codex \u914D\u7F6E\u5931\u8D25"));
|
|
373
|
+
const message = error instanceof Error && "timed out" in error ? "zcf \u6267\u884C\u8D85\u65F6\uFF08>3 \u5206\u949F\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u7A0D\u540E\u91CD\u8BD5\u3002" : error instanceof Error ? error.message : String(error);
|
|
374
|
+
console.error(message);
|
|
375
|
+
return 1;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (!opts.skipVerify) {
|
|
379
|
+
spinner2.start("\u9A8C\u8BC1 Trinity API \u8FDE\u901A\u6027\u2026");
|
|
380
|
+
const result = await verifyTrinityApi(apiKey, claudeBaseUrl);
|
|
381
|
+
if (result.ok) {
|
|
382
|
+
spinner2.stop(pc.green(result.message));
|
|
383
|
+
const recommended = pickRecommendedModels(result.modelIds ?? []);
|
|
384
|
+
if (recommended.claude || recommended.codex) {
|
|
385
|
+
p.note(
|
|
386
|
+
[
|
|
387
|
+
recommended.claude ? `Claude \u63A8\u8350\u6A21\u578B: ${recommended.claude}` : null,
|
|
388
|
+
recommended.codex ? `Codex \u63A8\u8350\u6A21\u578B: ${recommended.codex}` : null
|
|
389
|
+
].filter(Boolean).join("\n"),
|
|
390
|
+
"\u6A21\u578B"
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
} else {
|
|
394
|
+
spinner2.stop(pc.yellow(result.message));
|
|
395
|
+
p.note(
|
|
396
|
+
"\u672C\u5730\u914D\u7F6E\u5DF2\u5199\u5165\uFF0C\u4F46\u8FDE\u901A\u6027\u68C0\u67E5\u672A\u901A\u8FC7\u3002\u8BF7\u68C0\u67E5 API Key\u3001\u7F51\u7EDC\uFF0C\u6216\u5728\u63A7\u5236\u53F0\u786E\u8BA4 Key \u6709\u6548\u3002",
|
|
397
|
+
"\u63D0\u793A"
|
|
398
|
+
);
|
|
399
|
+
p.outro(pc.yellow("\u914D\u7F6E\u5B8C\u6210\uFF08\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF09"));
|
|
400
|
+
return 1;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const nextSteps = [
|
|
404
|
+
configureClaudeTool ? "\u8FD0\u884C claude \u542F\u52A8 Claude Code" : null,
|
|
405
|
+
configureCodexTool ? "\u8FD0\u884C codex \u542F\u52A8 Codex CLI" : null,
|
|
406
|
+
"\u5728\u5DE5\u5177\u5185\u7528 /model \u5207\u6362\u4E3A Trinity \u5DF2\u4E0A\u67B6\u7684 model_code",
|
|
407
|
+
"\u914D\u7F6E\u540E\u8BF7\u6C42\u8D70 Trinity \u8BA1\u8D39\uFF0C\u4E0D\u518D\u4F7F\u7528 Anthropic/OpenAI \u5B98\u65B9\u989D\u5EA6"
|
|
408
|
+
].filter(Boolean).join("\n");
|
|
409
|
+
p.note(nextSteps, "\u4E0B\u4E00\u6B65");
|
|
410
|
+
p.outro(pc.green("\u5B8C\u6210"));
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
run().then((code) => process.exit(code)).catch((error) => {
|
|
414
|
+
console.error(pc.red(error instanceof Error ? error.stack ?? error.message : String(error)));
|
|
415
|
+
process.exit(1);
|
|
416
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "trinity-config",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "一键把 Trinity API 接入 Claude Code 与 Codex CLI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"trinity-config": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup src/index.ts --format esm --target node18 --clean && node scripts/add-shebang.mjs",
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"typecheck": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"trinity",
|
|
29
|
+
"trinitydesk",
|
|
30
|
+
"claude-code",
|
|
31
|
+
"codex",
|
|
32
|
+
"cli",
|
|
33
|
+
"ai-gateway",
|
|
34
|
+
"zcf"
|
|
35
|
+
],
|
|
36
|
+
"author": "Trinity",
|
|
37
|
+
"homepage": "https://trinitydesk.ai",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/trinitydesk/trinity-config"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@clack/prompts": "^0.11.0",
|
|
44
|
+
"execa": "^9.5.2",
|
|
45
|
+
"picocolors": "^1.1.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.10.0",
|
|
49
|
+
"tsup": "^8.3.5",
|
|
50
|
+
"typescript": "^5.7.2",
|
|
51
|
+
"vitest": "^3.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|