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 +24 -40
- package/dist/index.js +135 -75
- package/dist/merge-codex-config-GC7ZDP7R.js +107 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,73 +1,57 @@
|
|
|
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
|
+
- **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
|
|
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";
|
|
@@ -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,
|
|
195
|
-
await
|
|
196
|
-
|
|
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
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
|
|
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 (
|
|
411
|
+
if (toolType === "claude") {
|
|
354
412
|
spinner2.start("\u914D\u7F6E Claude Code\u2026");
|
|
355
413
|
try {
|
|
356
|
-
await configureClaude(apiKey,
|
|
357
|
-
mergeClaudeEnv(
|
|
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
|
-
|
|
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,
|
|
370
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
);
|
|
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
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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.
|
|
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",
|