trinity-config 1.0.0 → 1.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/README.md +23 -40
- package/dist/index.js +129 -66
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,73 +1,56 @@
|
|
|
1
1
|
# trinity-config
|
|
2
2
|
|
|
3
|
-
> 一键把 [Trinity API](https://trinitydesk.ai) 接入 **Claude Code**
|
|
3
|
+
> 一键把 [Trinity API](https://trinitydesk.ai) 接入 **Claude Code** 或 **Codex CLI** —— 指定工具类型 + 粘贴 `xh-` Key 即用
|
|
4
4
|
|
|
5
5
|
## 快速开始
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### 交互式
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx -y trinity-config
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
按提示选择 **工具类型**、粘贴 Key、确认 **model_code**。
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
npx -y trinity-config -k xh-你的key
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### 只配其中一个
|
|
15
|
+
### 一行命令(推荐)
|
|
20
16
|
|
|
21
17
|
```bash
|
|
22
|
-
|
|
23
|
-
npx -y trinity-config -k xh-你的key
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
完成后:
|
|
18
|
+
# Claude Code
|
|
19
|
+
npx -y trinity-config --type claude -k xh-你的key
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
codex # Codex CLI
|
|
21
|
+
# Codex CLI
|
|
22
|
+
npx -y trinity-config --type codex -k xh-你的key --model gpt-5.4
|
|
31
23
|
```
|
|
32
24
|
|
|
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` 探测连通性
|
|
25
|
+
每次只配置 **一个** 工具;两个都要用时各执行一次。
|
|
40
26
|
|
|
41
27
|
## 命令行参数
|
|
42
28
|
|
|
43
29
|
| 参数 | 简写 | 说明 |
|
|
44
30
|
| --- | --- | --- |
|
|
31
|
+
| `--type` | `-t` | **必填(非交互)**:`claude` \| `codex` |
|
|
45
32
|
| `--api-key` | `-k` | Trinity API Key |
|
|
46
|
-
| `--
|
|
33
|
+
| `--model` | `-M` | 默认 model_code(省略则按 type 内置推荐) |
|
|
47
34
|
| `--base-url` | | 默认 `https://api.trinitydesk.ai` |
|
|
48
|
-
| `--claude-model` | | Claude 默认模型(默认 `claude-sonnet-4-6`) |
|
|
49
|
-
| `--codex-model` | | Codex 默认模型(默认 `gpt-5.4`) |
|
|
50
35
|
| `--skip-verify` | | 跳过连通性验证 |
|
|
51
36
|
| `--verbose` | `-v` | 显示 zcf 详细输出 |
|
|
52
|
-
| `--help` | `-h` | 帮助 |
|
|
53
|
-
| `--version` | `-V` | 版本 |
|
|
54
37
|
|
|
55
|
-
|
|
38
|
+
### 按 type 的默认模型
|
|
56
39
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
| `--type` | 默认 `--model` |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `claude` | `claude-sonnet-4-6` |
|
|
43
|
+
| `codex` | `gpt-5.4` |
|
|
60
44
|
|
|
61
|
-
##
|
|
45
|
+
## 扩展新工具
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
npm run build
|
|
67
|
-
node dist/index.js --help
|
|
68
|
-
```
|
|
47
|
+
后续增加 Cursor、OpenCode 等时,只需新增 `type` 枚举与对应写入逻辑,**不再**为每个工具增加 `--xxx-model` 参数。
|
|
48
|
+
|
|
49
|
+
## 注意
|
|
69
50
|
|
|
70
|
-
|
|
51
|
+
- 配置后**所有模型调用走 Trinity 计费**。
|
|
52
|
+
- 非交互模式必须同时提供 `--type` 与 `-k`。
|
|
53
|
+
- Codex 侧 zcf 写入仍可能有已知限制,见设计文档。
|
|
71
54
|
|
|
72
55
|
## License
|
|
73
56
|
|
package/dist/index.js
CHANGED
|
@@ -3,29 +3,55 @@
|
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
4
|
import pc from "picocolors";
|
|
5
5
|
|
|
6
|
+
// src/constants.ts
|
|
7
|
+
var PACKAGE_NAME = "trinity-config";
|
|
8
|
+
var PACKAGE_VERSION = "1.1.0";
|
|
9
|
+
var TRINITY_BASE_URL = "https://api.trinitydesk.ai";
|
|
10
|
+
var DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6";
|
|
11
|
+
var DEFAULT_CLAUDE_FAST_MODEL = "claude-sonnet-4-6";
|
|
12
|
+
var DEFAULT_CODEX_MODEL = "gpt-5.4";
|
|
13
|
+
var VERIFY_TIMEOUT_MS = 15e3;
|
|
14
|
+
|
|
15
|
+
// src/model.ts
|
|
16
|
+
function defaultModelForType(type) {
|
|
17
|
+
return type === "claude" ? DEFAULT_CLAUDE_MODEL : DEFAULT_CODEX_MODEL;
|
|
18
|
+
}
|
|
19
|
+
function resolveModel(type, model) {
|
|
20
|
+
return model?.trim() || defaultModelForType(type);
|
|
21
|
+
}
|
|
22
|
+
function parseToolType(raw) {
|
|
23
|
+
const value = raw.trim().toLowerCase();
|
|
24
|
+
if (value === "claude" || value === "codex") {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
throw new Error("--type \u987B\u4E3A claude \u6216 codex");
|
|
28
|
+
}
|
|
29
|
+
function toolLabel(type) {
|
|
30
|
+
return type === "claude" ? "Claude Code" : "Codex CLI";
|
|
31
|
+
}
|
|
32
|
+
|
|
6
33
|
// src/args.ts
|
|
7
34
|
function printHelp() {
|
|
8
35
|
console.log(`
|
|
9
|
-
trinity-config \u2014 \u4E00\u952E\u628A Trinity API \u63A5\u5165
|
|
36
|
+
trinity-config \u2014 \u4E00\u952E\u628A Trinity API \u63A5\u5165\u7EC8\u7AEF\u7F16\u7A0B\u52A9\u624B
|
|
10
37
|
|
|
11
38
|
\u7528\u6CD5:
|
|
12
|
-
npx -y trinity-config [\u9009\u9879]
|
|
39
|
+
npx -y trinity-config --type <claude|codex> [\u9009\u9879]
|
|
13
40
|
|
|
14
41
|
\u9009\u9879:
|
|
15
|
-
-
|
|
16
|
-
--
|
|
17
|
-
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-V, --version \u663E\u793A\u7248\u672C
|
|
42
|
+
-t, --type <claude|codex> \u8981\u914D\u7F6E\u7684\u5DE5\u5177\uFF08\u975E\u4EA4\u4E92\u987B\u6307\u5B9A\uFF1B\u4EA4\u4E92\u5F0F\u53EF\u7701\u7565\uFF09
|
|
43
|
+
-k, --api-key <key> Trinity API Key\uFF08xh- \u524D\u7F00\uFF09
|
|
44
|
+
-M, --model <model_code> \u9ED8\u8BA4\u6A21\u578B\uFF08\u7701\u7565\u5219\u6309 type \u4F7F\u7528\u5185\u7F6E\u63A8\u8350\uFF09
|
|
45
|
+
--base-url <url> API \u6839\u5730\u5740\uFF08\u9ED8\u8BA4 https://api.trinitydesk.ai\uFF09
|
|
46
|
+
--skip-verify \u8DF3\u8FC7 GET /v1/models \u8FDE\u901A\u6027\u68C0\u67E5
|
|
47
|
+
-v, --verbose \u663E\u793A zcf \u8BE6\u7EC6\u8F93\u51FA
|
|
48
|
+
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
49
|
+
-V, --version \u663E\u793A\u7248\u672C
|
|
24
50
|
|
|
25
51
|
\u793A\u4F8B:
|
|
52
|
+
npx -y trinity-config --type claude -k xh-your-key
|
|
53
|
+
npx -y trinity-config --type codex -k xh-your-key --model gpt-5.4
|
|
26
54
|
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
55
|
`);
|
|
30
56
|
}
|
|
31
57
|
function parseArgs(argv) {
|
|
@@ -59,22 +85,27 @@ function parseArgs(argv) {
|
|
|
59
85
|
case "--api-key":
|
|
60
86
|
opts.apiKey = args.shift();
|
|
61
87
|
break;
|
|
62
|
-
case "
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
case "-t":
|
|
89
|
+
case "--type":
|
|
90
|
+
opts.type = parseToolType(String(args.shift() ?? ""));
|
|
91
|
+
break;
|
|
92
|
+
case "-M":
|
|
93
|
+
case "--model":
|
|
94
|
+
opts.model = args.shift();
|
|
68
95
|
break;
|
|
69
|
-
}
|
|
70
96
|
case "--base-url":
|
|
71
97
|
opts.baseUrl = args.shift();
|
|
72
98
|
break;
|
|
73
|
-
case "--
|
|
74
|
-
|
|
99
|
+
case "--only": {
|
|
100
|
+
const legacy = args.shift();
|
|
101
|
+
console.warn("\u8B66\u544A: --only \u5DF2\u5E9F\u5F03\uFF0C\u8BF7\u6539\u7528 --type claude \u6216 --type codex");
|
|
102
|
+
opts.type = parseToolType(String(legacy ?? ""));
|
|
75
103
|
break;
|
|
104
|
+
}
|
|
105
|
+
case "--claude-model":
|
|
76
106
|
case "--codex-model":
|
|
77
|
-
|
|
107
|
+
console.warn("\u8B66\u544A: --claude-model / --codex-model \u5DF2\u5E9F\u5F03\uFF0C\u8BF7\u6539\u7528 --type + --model");
|
|
108
|
+
opts.model = args.shift();
|
|
78
109
|
break;
|
|
79
110
|
default:
|
|
80
111
|
if (arg.startsWith("-")) {
|
|
@@ -95,15 +126,11 @@ function normalizeApiKey(raw) {
|
|
|
95
126
|
}
|
|
96
127
|
return key.startsWith("xh-") ? key : `xh-${key}`;
|
|
97
128
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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;
|
|
129
|
+
function requireType(type, hasApiKeyFlag) {
|
|
130
|
+
if (type) return type;
|
|
131
|
+
if (!hasApiKeyFlag) return void 0;
|
|
132
|
+
throw new Error("\u975E\u4EA4\u4E92\u6A21\u5F0F\u987B\u6307\u5B9A --type claude \u6216 --type codex");
|
|
133
|
+
}
|
|
107
134
|
|
|
108
135
|
// src/merge-claude-env.ts
|
|
109
136
|
import fs from "fs";
|
|
@@ -206,11 +233,9 @@ function resolveBaseUrls(baseUrl) {
|
|
|
206
233
|
const claudeBaseUrl = root.endsWith("/v1") ? root.slice(0, -3) : root;
|
|
207
234
|
return { claudeBaseUrl, codexBaseUrl };
|
|
208
235
|
}
|
|
209
|
-
function
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
codexModel: codexModel ?? DEFAULT_CODEX_MODEL
|
|
213
|
-
};
|
|
236
|
+
function baseUrlForType(type, baseUrl) {
|
|
237
|
+
const { claudeBaseUrl, codexBaseUrl } = resolveBaseUrls(baseUrl);
|
|
238
|
+
return type === "claude" ? claudeBaseUrl : codexBaseUrl;
|
|
214
239
|
}
|
|
215
240
|
|
|
216
241
|
// src/verify.ts
|
|
@@ -328,10 +353,47 @@ async function promptApiKey(initial) {
|
|
|
328
353
|
}
|
|
329
354
|
return normalizeApiKey(String(value));
|
|
330
355
|
}
|
|
356
|
+
async function promptToolType(initial) {
|
|
357
|
+
if (initial) {
|
|
358
|
+
return initial;
|
|
359
|
+
}
|
|
360
|
+
const value = await p.select({
|
|
361
|
+
message: "\u8981\u914D\u7F6E\u54EA\u4E2A\u5DE5\u5177\uFF1F",
|
|
362
|
+
options: [
|
|
363
|
+
{ value: "claude", label: "Claude Code\uFF08/v1/messages\uFF09" },
|
|
364
|
+
{ value: "codex", label: "Codex CLI\uFF08/v1/responses\uFF09" }
|
|
365
|
+
]
|
|
366
|
+
});
|
|
367
|
+
if (p.isCancel(value)) {
|
|
368
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
369
|
+
process.exit(0);
|
|
370
|
+
}
|
|
371
|
+
return value;
|
|
372
|
+
}
|
|
373
|
+
async function promptModel(type, initial) {
|
|
374
|
+
if (initial?.trim()) {
|
|
375
|
+
return initial.trim();
|
|
376
|
+
}
|
|
377
|
+
const fallback = defaultModelForType(type);
|
|
378
|
+
const value = await p.text({
|
|
379
|
+
message: `${toolLabel(type)} \u9ED8\u8BA4 model_code`,
|
|
380
|
+
placeholder: fallback,
|
|
381
|
+
initialValue: fallback,
|
|
382
|
+
validate: (input) => {
|
|
383
|
+
if (!input?.trim()) return "\u8BF7\u8F93\u5165 model_code";
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
if (p.isCancel(value)) {
|
|
387
|
+
p.cancel("\u5DF2\u53D6\u6D88");
|
|
388
|
+
process.exit(0);
|
|
389
|
+
}
|
|
390
|
+
return String(value).trim();
|
|
391
|
+
}
|
|
331
392
|
async function run() {
|
|
332
393
|
let opts;
|
|
333
394
|
try {
|
|
334
395
|
opts = parseArgs(process.argv.slice(2));
|
|
396
|
+
requireType(opts.type, Boolean(opts.apiKey));
|
|
335
397
|
} catch (error) {
|
|
336
398
|
console.error(pc.red(error instanceof Error ? error.message : String(error)));
|
|
337
399
|
return 1;
|
|
@@ -344,34 +406,32 @@ async function run() {
|
|
|
344
406
|
return 0;
|
|
345
407
|
}
|
|
346
408
|
p.intro(pc.bgCyan(pc.black(` ${PACKAGE_NAME} v${PACKAGE_VERSION} `)));
|
|
409
|
+
const toolType = await promptToolType(opts.type);
|
|
347
410
|
const apiKey = await promptApiKey(opts.apiKey);
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
|
|
411
|
+
const model = opts.model?.trim() ? opts.model.trim() : opts.apiKey ? resolveModel(toolType) : await promptModel(toolType);
|
|
412
|
+
const apiBaseUrl = baseUrlForType(toolType, opts.baseUrl);
|
|
413
|
+
const { claudeBaseUrl } = resolveBaseUrls(opts.baseUrl);
|
|
414
|
+
p.log.info(`\u5DE5\u5177: ${toolLabel(toolType)} \xB7 \u6A21\u578B: ${model}`);
|
|
352
415
|
const spinner2 = p.spinner();
|
|
353
|
-
if (
|
|
416
|
+
if (toolType === "claude") {
|
|
354
417
|
spinner2.start("\u914D\u7F6E Claude Code\u2026");
|
|
355
418
|
try {
|
|
356
|
-
await configureClaude(apiKey,
|
|
357
|
-
mergeClaudeEnv(
|
|
419
|
+
await configureClaude(apiKey, apiBaseUrl, model, opts.verbose);
|
|
420
|
+
mergeClaudeEnv(model, model, apiBaseUrl);
|
|
358
421
|
spinner2.stop("Claude Code \u914D\u7F6E\u5DF2\u5199\u5165 ~/.claude/settings.json");
|
|
359
422
|
} catch (error) {
|
|
360
423
|
spinner2.stop(pc.red("Claude Code \u914D\u7F6E\u5931\u8D25"));
|
|
361
|
-
|
|
362
|
-
console.error(message);
|
|
424
|
+
console.error(formatZcfError(error));
|
|
363
425
|
return 1;
|
|
364
426
|
}
|
|
365
|
-
}
|
|
366
|
-
if (configureCodexTool) {
|
|
427
|
+
} else {
|
|
367
428
|
spinner2.start("\u914D\u7F6E Codex CLI\u2026");
|
|
368
429
|
try {
|
|
369
|
-
await configureCodex(apiKey,
|
|
430
|
+
await configureCodex(apiKey, apiBaseUrl, model, opts.verbose);
|
|
370
431
|
spinner2.stop("Codex \u914D\u7F6E\u5DF2\u5199\u5165 ~/.codex/");
|
|
371
432
|
} catch (error) {
|
|
372
433
|
spinner2.stop(pc.red("Codex \u914D\u7F6E\u5931\u8D25"));
|
|
373
|
-
|
|
374
|
-
console.error(message);
|
|
434
|
+
console.error(formatZcfError(error));
|
|
375
435
|
return 1;
|
|
376
436
|
}
|
|
377
437
|
}
|
|
@@ -381,14 +441,9 @@ async function run() {
|
|
|
381
441
|
if (result.ok) {
|
|
382
442
|
spinner2.stop(pc.green(result.message));
|
|
383
443
|
const recommended = pickRecommendedModels(result.modelIds ?? []);
|
|
384
|
-
|
|
385
|
-
|
|
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
|
-
);
|
|
444
|
+
const hint = toolType === "claude" ? recommended.claude : recommended.codex;
|
|
445
|
+
if (hint && hint !== model) {
|
|
446
|
+
p.note(`\u540C\u7C7B\u578B\u63A8\u8350\u6A21\u578B: ${hint}`, "\u6A21\u578B");
|
|
392
447
|
}
|
|
393
448
|
} else {
|
|
394
449
|
spinner2.stop(pc.yellow(result.message));
|
|
@@ -400,16 +455,24 @@ async function run() {
|
|
|
400
455
|
return 1;
|
|
401
456
|
}
|
|
402
457
|
}
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
458
|
+
const launchCmd = toolType === "claude" ? "claude" : "codex";
|
|
459
|
+
p.note(
|
|
460
|
+
[
|
|
461
|
+
`\u8FD0\u884C ${launchCmd} \u542F\u52A8 ${toolLabel(toolType)}`,
|
|
462
|
+
"\u5728\u5DE5\u5177\u5185\u7528 /model \u5207\u6362\u4E3A Trinity \u5DF2\u4E0A\u67B6\u7684 model_code",
|
|
463
|
+
"\u914D\u7F6E\u540E\u8BF7\u6C42\u8D70 Trinity \u8BA1\u8D39\uFF0C\u4E0D\u518D\u4F7F\u7528 Anthropic/OpenAI \u5B98\u65B9\u989D\u5EA6"
|
|
464
|
+
].join("\n"),
|
|
465
|
+
"\u4E0B\u4E00\u6B65"
|
|
466
|
+
);
|
|
410
467
|
p.outro(pc.green("\u5B8C\u6210"));
|
|
411
468
|
return 0;
|
|
412
469
|
}
|
|
470
|
+
function formatZcfError(error) {
|
|
471
|
+
if (error instanceof Error && "timed out" in error) {
|
|
472
|
+
return "zcf \u6267\u884C\u8D85\u65F6\uFF08>3 \u5206\u949F\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
473
|
+
}
|
|
474
|
+
return error instanceof Error ? error.message : String(error);
|
|
475
|
+
}
|
|
413
476
|
run().then((code) => process.exit(code)).catch((error) => {
|
|
414
477
|
console.error(pc.red(error instanceof Error ? error.stack ?? error.message : String(error)));
|
|
415
478
|
process.exit(1);
|