trinity-config 1.0.0 → 1.1.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/README.md CHANGED
@@ -1,73 +1,57 @@
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
+ - **Claude Code**:经 zcf 写入 `~/.claude/settings.json`
54
+ - **Codex CLI**:**直接** merge `~/.codex/config.toml` + `auth.json`(不调用 zcf;zcf cx 会重置为 ChatGPT 官方登录)
71
55
 
72
56
  ## License
73
57
 
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.1";
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";
@@ -191,14 +218,9 @@ async function configureClaude(apiKey, baseUrl, claudeModel, verbose) {
191
218
  verbose
192
219
  });
193
220
  }
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
- });
221
+ async function configureCodex(apiKey, codexBaseUrl, codexModel, _verbose) {
222
+ const { configureCodexDirect } = await import("./merge-codex-config-GC7ZDP7R.js");
223
+ return configureCodexDirect(apiKey, codexBaseUrl, codexModel);
202
224
  }
203
225
  function resolveBaseUrls(baseUrl) {
204
226
  const root = (baseUrl ?? TRINITY_BASE_URL).replace(/\/+$/, "");
@@ -206,11 +228,9 @@ function resolveBaseUrls(baseUrl) {
206
228
  const claudeBaseUrl = root.endsWith("/v1") ? root.slice(0, -3) : root;
207
229
  return { claudeBaseUrl, codexBaseUrl };
208
230
  }
209
- function defaultModels(claudeModel, codexModel) {
210
- return {
211
- claudeModel: claudeModel ?? DEFAULT_CLAUDE_MODEL,
212
- codexModel: codexModel ?? DEFAULT_CODEX_MODEL
213
- };
231
+ function baseUrlForType(type, baseUrl) {
232
+ const { claudeBaseUrl, codexBaseUrl } = resolveBaseUrls(baseUrl);
233
+ return type === "claude" ? claudeBaseUrl : codexBaseUrl;
214
234
  }
215
235
 
216
236
  // src/verify.ts
@@ -328,10 +348,47 @@ async function promptApiKey(initial) {
328
348
  }
329
349
  return normalizeApiKey(String(value));
330
350
  }
351
+ async function promptToolType(initial) {
352
+ if (initial) {
353
+ return initial;
354
+ }
355
+ const value = await p.select({
356
+ message: "\u8981\u914D\u7F6E\u54EA\u4E2A\u5DE5\u5177\uFF1F",
357
+ options: [
358
+ { value: "claude", label: "Claude Code\uFF08/v1/messages\uFF09" },
359
+ { value: "codex", label: "Codex CLI\uFF08/v1/responses\uFF09" }
360
+ ]
361
+ });
362
+ if (p.isCancel(value)) {
363
+ p.cancel("\u5DF2\u53D6\u6D88");
364
+ process.exit(0);
365
+ }
366
+ return value;
367
+ }
368
+ async function promptModel(type, initial) {
369
+ if (initial?.trim()) {
370
+ return initial.trim();
371
+ }
372
+ const fallback = defaultModelForType(type);
373
+ const value = await p.text({
374
+ message: `${toolLabel(type)} \u9ED8\u8BA4 model_code`,
375
+ placeholder: fallback,
376
+ initialValue: fallback,
377
+ validate: (input) => {
378
+ if (!input?.trim()) return "\u8BF7\u8F93\u5165 model_code";
379
+ }
380
+ });
381
+ if (p.isCancel(value)) {
382
+ p.cancel("\u5DF2\u53D6\u6D88");
383
+ process.exit(0);
384
+ }
385
+ return String(value).trim();
386
+ }
331
387
  async function run() {
332
388
  let opts;
333
389
  try {
334
390
  opts = parseArgs(process.argv.slice(2));
391
+ requireType(opts.type, Boolean(opts.apiKey));
335
392
  } catch (error) {
336
393
  console.error(pc.red(error instanceof Error ? error.message : String(error)));
337
394
  return 1;
@@ -344,34 +401,34 @@ async function run() {
344
401
  return 0;
345
402
  }
346
403
  p.intro(pc.bgCyan(pc.black(` ${PACKAGE_NAME} v${PACKAGE_VERSION} `)));
404
+ const toolType = await promptToolType(opts.type);
347
405
  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";
406
+ const model = opts.model?.trim() ? opts.model.trim() : opts.apiKey ? resolveModel(toolType) : await promptModel(toolType);
407
+ const apiBaseUrl = baseUrlForType(toolType, opts.baseUrl);
408
+ const { claudeBaseUrl } = resolveBaseUrls(opts.baseUrl);
409
+ p.log.info(`\u5DE5\u5177: ${toolLabel(toolType)} \xB7 \u6A21\u578B: ${model}`);
352
410
  const spinner2 = p.spinner();
353
- if (configureClaudeTool) {
411
+ if (toolType === "claude") {
354
412
  spinner2.start("\u914D\u7F6E Claude Code\u2026");
355
413
  try {
356
- await configureClaude(apiKey, claudeBaseUrl, models.claudeModel, opts.verbose);
357
- mergeClaudeEnv(models.claudeModel, models.claudeModel, claudeBaseUrl);
414
+ await configureClaude(apiKey, apiBaseUrl, model, opts.verbose);
415
+ mergeClaudeEnv(model, model, apiBaseUrl);
358
416
  spinner2.stop("Claude Code \u914D\u7F6E\u5DF2\u5199\u5165 ~/.claude/settings.json");
359
417
  } catch (error) {
360
418
  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);
419
+ console.error(formatZcfError(error));
363
420
  return 1;
364
421
  }
365
- }
366
- if (configureCodexTool) {
422
+ } else {
367
423
  spinner2.start("\u914D\u7F6E Codex CLI\u2026");
368
424
  try {
369
- await configureCodex(apiKey, codexBaseUrl, models.codexModel, opts.verbose);
370
- spinner2.stop("Codex \u914D\u7F6E\u5DF2\u5199\u5165 ~/.codex/");
425
+ const backupDir = await configureCodex(apiKey, apiBaseUrl, model, opts.verbose);
426
+ const backupHint = backupDir ? `
427
+ \u5907\u4EFD: ${backupDir}` : "";
428
+ spinner2.stop(`Codex \u914D\u7F6E\u5DF2\u5199\u5165 ~/.codex/config.toml \u4E0E auth.json${backupHint}`);
371
429
  } catch (error) {
372
430
  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);
431
+ console.error(formatZcfError(error));
375
432
  return 1;
376
433
  }
377
434
  }
@@ -381,14 +438,9 @@ async function run() {
381
438
  if (result.ok) {
382
439
  spinner2.stop(pc.green(result.message));
383
440
  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
- );
441
+ const hint = toolType === "claude" ? recommended.claude : recommended.codex;
442
+ if (hint && hint !== model) {
443
+ p.note(`\u540C\u7C7B\u578B\u63A8\u8350\u6A21\u578B: ${hint}`, "\u6A21\u578B");
392
444
  }
393
445
  } else {
394
446
  spinner2.stop(pc.yellow(result.message));
@@ -400,16 +452,24 @@ async function run() {
400
452
  return 1;
401
453
  }
402
454
  }
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");
455
+ const launchCmd = toolType === "claude" ? "claude" : "codex";
456
+ p.note(
457
+ [
458
+ `\u8FD0\u884C ${launchCmd} \u542F\u52A8 ${toolLabel(toolType)}`,
459
+ "\u5728\u5DE5\u5177\u5185\u7528 /model \u5207\u6362\u4E3A Trinity \u5DF2\u4E0A\u67B6\u7684 model_code",
460
+ "\u914D\u7F6E\u540E\u8BF7\u6C42\u8D70 Trinity \u8BA1\u8D39\uFF0C\u4E0D\u518D\u4F7F\u7528 Anthropic/OpenAI \u5B98\u65B9\u989D\u5EA6"
461
+ ].join("\n"),
462
+ "\u4E0B\u4E00\u6B65"
463
+ );
410
464
  p.outro(pc.green("\u5B8C\u6210"));
411
465
  return 0;
412
466
  }
467
+ function formatZcfError(error) {
468
+ if (error instanceof Error && "timed out" in error) {
469
+ return "zcf \u6267\u884C\u8D85\u65F6\uFF08>3 \u5206\u949F\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
470
+ }
471
+ return error instanceof Error ? error.message : String(error);
472
+ }
413
473
  run().then((code) => process.exit(code)).catch((error) => {
414
474
  console.error(pc.red(error instanceof Error ? error.stack ?? error.message : String(error)));
415
475
  process.exit(1);
@@ -0,0 +1,107 @@
1
+ // src/merge-codex-config.ts
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import { parse, stringify } from "smol-toml";
6
+ var TRINITY_CODEX_PROVIDER = "trinity";
7
+ var TRINITY_CODEX_TEMP_ENV_KEY = "TRINITY_API_KEY";
8
+ function codexHome() {
9
+ return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
10
+ }
11
+ function codexConfigPath() {
12
+ return path.join(codexHome(), "config.toml");
13
+ }
14
+ function codexAuthPath() {
15
+ return path.join(codexHome(), "auth.json");
16
+ }
17
+ function timestampLabel() {
18
+ const now = /* @__PURE__ */ new Date();
19
+ const pad = (n) => String(n).padStart(2, "0");
20
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
21
+ }
22
+ function backupCodexFiles() {
23
+ const home = codexHome();
24
+ const configPath = codexConfigPath();
25
+ const authPath = codexAuthPath();
26
+ if (!fs.existsSync(configPath) && !fs.existsSync(authPath)) {
27
+ return null;
28
+ }
29
+ const backupDir = path.join(home, "backup", `trinity-config-${timestampLabel()}`);
30
+ fs.mkdirSync(backupDir, { recursive: true });
31
+ for (const src of [configPath, authPath]) {
32
+ if (fs.existsSync(src)) {
33
+ fs.copyFileSync(src, path.join(backupDir, path.basename(src)));
34
+ }
35
+ }
36
+ return backupDir;
37
+ }
38
+ function readCodexConfigDoc() {
39
+ const configPath = codexConfigPath();
40
+ if (!fs.existsSync(configPath)) {
41
+ return {};
42
+ }
43
+ const raw = fs.readFileSync(configPath, "utf8");
44
+ if (!raw.trim()) {
45
+ return {};
46
+ }
47
+ try {
48
+ return parse(raw);
49
+ } catch {
50
+ throw new Error(`\u65E0\u6CD5\u89E3\u6790 ${configPath}\uFF0C\u8BF7\u68C0\u67E5 TOML \u8BED\u6CD5\u540E\u91CD\u8BD5\u3002`);
51
+ }
52
+ }
53
+ function buildTrinityProvider(baseUrl) {
54
+ return {
55
+ name: "Trinity API",
56
+ base_url: baseUrl.replace(/\/+$/, ""),
57
+ wire_api: "responses",
58
+ temp_env_key: TRINITY_CODEX_TEMP_ENV_KEY,
59
+ requires_openai_auth: false
60
+ };
61
+ }
62
+ function mergeCodexConfig(model, baseUrl) {
63
+ const configPath = codexConfigPath();
64
+ const doc = readCodexConfigDoc();
65
+ doc.model = model;
66
+ doc.model_provider = TRINITY_CODEX_PROVIDER;
67
+ doc.disable_response_storage = true;
68
+ const providers = doc.model_providers && typeof doc.model_providers === "object" ? { ...doc.model_providers } : {};
69
+ providers[TRINITY_CODEX_PROVIDER] = buildTrinityProvider(baseUrl);
70
+ doc.model_providers = providers;
71
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
72
+ fs.writeFileSync(configPath, `${stringify(doc)}
73
+ `, "utf8");
74
+ }
75
+ function mergeCodexAuth(apiKey) {
76
+ const authPath = codexAuthPath();
77
+ let auth = {};
78
+ if (fs.existsSync(authPath)) {
79
+ try {
80
+ auth = JSON.parse(fs.readFileSync(authPath, "utf8"));
81
+ } catch {
82
+ auth = {};
83
+ }
84
+ }
85
+ auth[TRINITY_CODEX_TEMP_ENV_KEY] = apiKey;
86
+ auth.OPENAI_API_KEY = apiKey;
87
+ fs.mkdirSync(path.dirname(authPath), { recursive: true });
88
+ fs.writeFileSync(authPath, `${JSON.stringify(auth, null, 2)}
89
+ `, "utf8");
90
+ }
91
+ function configureCodexDirect(apiKey, codexBaseUrl, model) {
92
+ const backupDir = backupCodexFiles();
93
+ mergeCodexConfig(model, codexBaseUrl);
94
+ mergeCodexAuth(apiKey);
95
+ return backupDir;
96
+ }
97
+ export {
98
+ TRINITY_CODEX_PROVIDER,
99
+ TRINITY_CODEX_TEMP_ENV_KEY,
100
+ backupCodexFiles,
101
+ codexAuthPath,
102
+ codexConfigPath,
103
+ codexHome,
104
+ configureCodexDirect,
105
+ mergeCodexAuth,
106
+ mergeCodexConfig
107
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trinity-config",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "一键把 Trinity API 接入 Claude Code 与 Codex CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,7 +42,8 @@
42
42
  "dependencies": {
43
43
  "@clack/prompts": "^0.11.0",
44
44
  "execa": "^9.5.2",
45
- "picocolors": "^1.1.1"
45
+ "picocolors": "^1.1.1",
46
+ "smol-toml": "^1.3.1"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/node": "^22.10.0",