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.
Files changed (3) hide show
  1. package/README.md +23 -40
  2. package/dist/index.js +129 -66
  3. 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** **Codex CLI** —— 粘贴 `xh-` Key 即用
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
- ```bash
16
- npx -y trinity-config -k xh-你的key
17
- ```
18
-
19
- ### 只配其中一个
15
+ ### 一行命令(推荐)
20
16
 
21
17
  ```bash
22
- npx -y trinity-config -k xh-你的key --only claude
23
- npx -y trinity-config -k xh-你的key --only codex
24
- ```
25
-
26
- 完成后:
18
+ # Claude Code
19
+ npx -y trinity-config --type claude -k xh-你的key
27
20
 
28
- ```bash
29
- claude # Claude Code
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
- | `--only` | | `claude` \| `codex` |
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
- - 配置后**所有模型调用走 Trinity 计费**,不再使用 Anthropic/OpenAI 官方账号额度。
58
- - 工具内选择的模型名须为 Trinity 已上架的 `model_code`(可用 `GET /v1/models` 查看)。
59
- - Windows 下建议用 WSL2 运行 `claude` / `codex`;`npx trinity-config` 可在 PowerShell 执行。
40
+ | `--type` | 默认 `--model` |
41
+ | --- | --- |
42
+ | `claude` | `claude-sonnet-4-6` |
43
+ | `codex` | `gpt-5.4` |
60
44
 
61
- ## 开发
45
+ ## 扩展新工具
62
46
 
63
- ```bash
64
- cd tools/trinity-config
65
- npm install
66
- npm run build
67
- node dist/index.js --help
68
- ```
47
+ 后续增加 Cursor、OpenCode 等时,只需新增 `type` 枚举与对应写入逻辑,**不再**为每个工具增加 `--xxx-model` 参数。
48
+
49
+ ## 注意
69
50
 
70
- 发版见仓库 `部署相关/trinity-config-npm发版说明.md`。
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 Claude Code \u4E0E Codex CLI
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
- -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
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 "--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;
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 "--claude-model":
74
- opts.claudeModel = args.shift();
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
- opts.codexModel = args.shift();
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
- // 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;
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 defaultModels(claudeModel, codexModel) {
210
- return {
211
- claudeModel: claudeModel ?? DEFAULT_CLAUDE_MODEL,
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 { 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";
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 (configureClaudeTool) {
416
+ if (toolType === "claude") {
354
417
  spinner2.start("\u914D\u7F6E Claude Code\u2026");
355
418
  try {
356
- await configureClaude(apiKey, claudeBaseUrl, models.claudeModel, opts.verbose);
357
- mergeClaudeEnv(models.claudeModel, models.claudeModel, claudeBaseUrl);
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
- 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);
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, codexBaseUrl, models.codexModel, opts.verbose);
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
- 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);
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
- 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
- );
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 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");
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trinity-config",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "一键把 Trinity API 接入 Claude Code 与 Codex CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",