trinity-config 1.1.1 → 1.2.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 +10 -43
- package/dist/index.js +162 -49
- package/dist/merge-openclaw-config-XV25MSSC.js +126 -0
- package/dist/merge-opencode-config-P4QJ63DF.js +107 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,57 +1,24 @@
|
|
|
1
1
|
# trinity-config
|
|
2
2
|
|
|
3
|
-
> 一键把 [Trinity API](https://trinitydesk.ai) 接入 **Claude Code**
|
|
3
|
+
> 一键把 [Trinity API](https://trinitydesk.ai) 接入 **Claude Code** / **Codex CLI** / **OpenCode** / **OpenClaw**
|
|
4
4
|
|
|
5
5
|
## 快速开始
|
|
6
6
|
|
|
7
|
-
### 交互式
|
|
8
|
-
|
|
9
7
|
```bash
|
|
10
|
-
npx -y trinity-config
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
按提示选择 **工具类型**、粘贴 Key、确认 **model_code**。
|
|
14
|
-
|
|
15
|
-
### 一行命令(推荐)
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# Claude Code
|
|
19
8
|
npx -y trinity-config --type claude -k xh-你的key
|
|
20
|
-
|
|
21
|
-
# Codex CLI
|
|
22
9
|
npx -y trinity-config --type codex -k xh-你的key --model gpt-5.4
|
|
10
|
+
npx -y trinity-config --type opencode -k xh-你的key --model gpt-5.4
|
|
11
|
+
npx -y trinity-config --type openclaw -k xh-你的key --model gpt-5.4
|
|
23
12
|
```
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| `--type` | `-t` | **必填(非交互)**:`claude` \| `codex` |
|
|
32
|
-
| `--api-key` | `-k` | Trinity API Key |
|
|
33
|
-
| `--model` | `-M` | 默认 model_code(省略则按 type 内置推荐) |
|
|
34
|
-
| `--base-url` | | 默认 `https://api.trinitydesk.ai` |
|
|
35
|
-
| `--skip-verify` | | 跳过连通性验证 |
|
|
36
|
-
| `--verbose` | `-v` | 显示 zcf 详细输出 |
|
|
37
|
-
|
|
38
|
-
### 按 type 的默认模型
|
|
39
|
-
|
|
40
|
-
| `--type` | 默认 `--model` |
|
|
41
|
-
| --- | --- |
|
|
42
|
-
| `claude` | `claude-sonnet-4-6` |
|
|
43
|
-
| `codex` | `gpt-5.4` |
|
|
44
|
-
|
|
45
|
-
## 扩展新工具
|
|
46
|
-
|
|
47
|
-
后续增加 Cursor、OpenCode 等时,只需新增 `type` 枚举与对应写入逻辑,**不再**为每个工具增加 `--xxx-model` 参数。
|
|
48
|
-
|
|
49
|
-
## 注意
|
|
14
|
+
| `--type` | 默认模型 | 接口 | 写入 |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| `claude` | `claude-sonnet-4-6` | `/v1/messages` | `~/.claude/settings.json` |
|
|
17
|
+
| `codex` | `gpt-5.4` | `/v1/responses` | `~/.codex/config.toml` + `auth.json` |
|
|
18
|
+
| `opencode` | `gpt-5.4` | `/v1/chat/completions` | `~/.config/opencode/opencode.json` |
|
|
19
|
+
| `openclaw` | `gpt-5.4` | `/v1/chat/completions` | `~/.openclaw/openclaw.json` |
|
|
50
20
|
|
|
51
|
-
|
|
52
|
-
- 非交互模式必须同时提供 `--type` 与 `-k`。
|
|
53
|
-
- **Claude Code**:经 zcf 写入 `~/.claude/settings.json`
|
|
54
|
-
- **Codex CLI**:**直接** merge `~/.codex/config.toml` + `auth.json`(不调用 zcf;zcf cx 会重置为 ChatGPT 官方登录)
|
|
21
|
+
OpenClaw 改完后请重启 Gateway(或重新 `openclaw dashboard`)。
|
|
55
22
|
|
|
56
23
|
## License
|
|
57
24
|
|
package/dist/index.js
CHANGED
|
@@ -5,52 +5,86 @@ import pc from "picocolors";
|
|
|
5
5
|
|
|
6
6
|
// src/constants.ts
|
|
7
7
|
var PACKAGE_NAME = "trinity-config";
|
|
8
|
-
var PACKAGE_VERSION = "1.
|
|
8
|
+
var PACKAGE_VERSION = "1.2.1";
|
|
9
9
|
var TRINITY_BASE_URL = "https://api.trinitydesk.ai";
|
|
10
10
|
var DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6";
|
|
11
11
|
var DEFAULT_CLAUDE_FAST_MODEL = "claude-sonnet-4-6";
|
|
12
12
|
var DEFAULT_CODEX_MODEL = "gpt-5.4";
|
|
13
|
+
var DEFAULT_OPENCODE_MODEL = "gpt-5.4";
|
|
14
|
+
var DEFAULT_OPENCLAW_MODEL = "gpt-5.4";
|
|
13
15
|
var VERIFY_TIMEOUT_MS = 15e3;
|
|
14
16
|
|
|
15
17
|
// src/model.ts
|
|
16
18
|
function defaultModelForType(type) {
|
|
17
|
-
|
|
19
|
+
switch (type) {
|
|
20
|
+
case "claude":
|
|
21
|
+
return DEFAULT_CLAUDE_MODEL;
|
|
22
|
+
case "codex":
|
|
23
|
+
return DEFAULT_CODEX_MODEL;
|
|
24
|
+
case "opencode":
|
|
25
|
+
return DEFAULT_OPENCODE_MODEL;
|
|
26
|
+
case "openclaw":
|
|
27
|
+
return DEFAULT_OPENCLAW_MODEL;
|
|
28
|
+
}
|
|
18
29
|
}
|
|
19
30
|
function resolveModel(type, model) {
|
|
20
31
|
return model?.trim() || defaultModelForType(type);
|
|
21
32
|
}
|
|
22
33
|
function parseToolType(raw) {
|
|
23
34
|
const value = raw.trim().toLowerCase();
|
|
24
|
-
if (value === "claude" || value === "codex") {
|
|
35
|
+
if (value === "claude" || value === "codex" || value === "opencode" || value === "openclaw") {
|
|
25
36
|
return value;
|
|
26
37
|
}
|
|
27
|
-
throw new Error("--type \u987B\u4E3A claude \u6216
|
|
38
|
+
throw new Error("--type \u987B\u4E3A claude\u3001codex\u3001opencode \u6216 openclaw");
|
|
28
39
|
}
|
|
29
40
|
function toolLabel(type) {
|
|
30
|
-
|
|
41
|
+
switch (type) {
|
|
42
|
+
case "claude":
|
|
43
|
+
return "Claude Code";
|
|
44
|
+
case "codex":
|
|
45
|
+
return "Codex CLI";
|
|
46
|
+
case "opencode":
|
|
47
|
+
return "OpenCode";
|
|
48
|
+
case "openclaw":
|
|
49
|
+
return "OpenClaw";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function launchCommand(type) {
|
|
53
|
+
switch (type) {
|
|
54
|
+
case "claude":
|
|
55
|
+
return "claude";
|
|
56
|
+
case "codex":
|
|
57
|
+
return "codex";
|
|
58
|
+
case "opencode":
|
|
59
|
+
return "opencode";
|
|
60
|
+
case "openclaw":
|
|
61
|
+
return "openclaw dashboard";
|
|
62
|
+
}
|
|
31
63
|
}
|
|
32
64
|
|
|
33
65
|
// src/args.ts
|
|
34
66
|
function printHelp() {
|
|
35
67
|
console.log(`
|
|
36
|
-
trinity-config \u2014 \u4E00\u952E\u628A Trinity API \u63A5\u5165\u7EC8\u7AEF\u7F16\u7A0B\u52A9\u624B
|
|
68
|
+
trinity-config \u2014 \u4E00\u952E\u628A Trinity API \u63A5\u5165\u7EC8\u7AEF\u7F16\u7A0B\u52A9\u624B / Agent \u7F51\u5173
|
|
37
69
|
|
|
38
70
|
\u7528\u6CD5:
|
|
39
|
-
npx -y trinity-config --type <claude|codex> [\u9009\u9879]
|
|
71
|
+
npx -y trinity-config --type <claude|codex|opencode|openclaw> [\u9009\u9879]
|
|
40
72
|
|
|
41
73
|
\u9009\u9879:
|
|
42
|
-
-t, --type <claude|codex> \u8981\u914D\u7F6E\u7684\u5DE5\u5177\uFF08\u975E\u4EA4\u4E92\u987B\u6307\u5B9A\
|
|
43
|
-
-k, --api-key <key>
|
|
44
|
-
-M, --model <model_code>
|
|
45
|
-
--base-url <url>
|
|
46
|
-
--skip-verify
|
|
47
|
-
-v, --verbose
|
|
48
|
-
-h, --help
|
|
49
|
-
-V, --version
|
|
74
|
+
-t, --type <claude|codex|opencode|openclaw> \u8981\u914D\u7F6E\u7684\u5DE5\u5177\uFF08\u975E\u4EA4\u4E92\u987B\u6307\u5B9A\uFF09
|
|
75
|
+
-k, --api-key <key> Trinity API Key\uFF08xh- \u524D\u7F00\uFF09
|
|
76
|
+
-M, --model <model_code> \u9ED8\u8BA4\u6A21\u578B\uFF08\u7701\u7565\u5219\u6309 type \u63A8\u8350\uFF09
|
|
77
|
+
--base-url <url> API \u6839\u5730\u5740\uFF08\u9ED8\u8BA4 https://api.trinitydesk.ai\uFF09
|
|
78
|
+
--skip-verify \u8DF3\u8FC7 GET /v1/models \u8FDE\u901A\u6027\u68C0\u67E5
|
|
79
|
+
-v, --verbose \u663E\u793A zcf \u8BE6\u7EC6\u8F93\u51FA\uFF08\u4EC5 Claude\uFF09
|
|
80
|
+
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
81
|
+
-V, --version \u663E\u793A\u7248\u672C
|
|
50
82
|
|
|
51
83
|
\u793A\u4F8B:
|
|
52
84
|
npx -y trinity-config --type claude -k xh-your-key
|
|
53
85
|
npx -y trinity-config --type codex -k xh-your-key --model gpt-5.4
|
|
86
|
+
npx -y trinity-config --type opencode -k xh-your-key --model gpt-5.4
|
|
87
|
+
npx -y trinity-config --type openclaw -k xh-your-key --model gpt-5.4
|
|
54
88
|
npx -y trinity-config
|
|
55
89
|
`);
|
|
56
90
|
}
|
|
@@ -98,7 +132,7 @@ function parseArgs(argv) {
|
|
|
98
132
|
break;
|
|
99
133
|
case "--only": {
|
|
100
134
|
const legacy = args.shift();
|
|
101
|
-
console.warn("\u8B66\u544A: --only \u5DF2\u5E9F\u5F03\uFF0C\u8BF7\u6539\u7528 --type
|
|
135
|
+
console.warn("\u8B66\u544A: --only \u5DF2\u5E9F\u5F03\uFF0C\u8BF7\u6539\u7528 --type");
|
|
102
136
|
opts.type = parseToolType(String(legacy ?? ""));
|
|
103
137
|
break;
|
|
104
138
|
}
|
|
@@ -129,7 +163,7 @@ function normalizeApiKey(raw) {
|
|
|
129
163
|
function requireType(type, hasApiKeyFlag) {
|
|
130
164
|
if (type) return type;
|
|
131
165
|
if (!hasApiKeyFlag) return void 0;
|
|
132
|
-
throw new Error("\u975E\u4EA4\u4E92\u6A21\u5F0F\u987B\u6307\u5B9A --type claude \u6216
|
|
166
|
+
throw new Error("\u975E\u4EA4\u4E92\u6A21\u5F0F\u987B\u6307\u5B9A --type claude\u3001codex\u3001opencode \u6216 openclaw");
|
|
133
167
|
}
|
|
134
168
|
|
|
135
169
|
// src/merge-claude-env.ts
|
|
@@ -232,6 +266,24 @@ function baseUrlForType(type, baseUrl) {
|
|
|
232
266
|
const { claudeBaseUrl, codexBaseUrl } = resolveBaseUrls(baseUrl);
|
|
233
267
|
return type === "claude" ? claudeBaseUrl : codexBaseUrl;
|
|
234
268
|
}
|
|
269
|
+
async function configureOpenCode(apiKey, openCodeBaseUrl, model, modelIds) {
|
|
270
|
+
const { configureOpenCodeDirect } = await import("./merge-opencode-config-P4QJ63DF.js");
|
|
271
|
+
return configureOpenCodeDirect({
|
|
272
|
+
apiKey,
|
|
273
|
+
baseUrl: openCodeBaseUrl,
|
|
274
|
+
model,
|
|
275
|
+
modelIds
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
async function configureOpenClaw(apiKey, openClawBaseUrl, model, modelIds) {
|
|
279
|
+
const { configureOpenClawDirect } = await import("./merge-openclaw-config-XV25MSSC.js");
|
|
280
|
+
return configureOpenClawDirect({
|
|
281
|
+
apiKey,
|
|
282
|
+
baseUrl: openClawBaseUrl,
|
|
283
|
+
model,
|
|
284
|
+
modelIds
|
|
285
|
+
});
|
|
286
|
+
}
|
|
235
287
|
|
|
236
288
|
// src/verify.ts
|
|
237
289
|
import https from "https";
|
|
@@ -327,7 +379,9 @@ async function verifyTrinityApi(apiKey, baseUrl = TRINITY_BASE_URL) {
|
|
|
327
379
|
function pickRecommendedModels(modelIds) {
|
|
328
380
|
const claude = modelIds.find((id) => id === DEFAULT_CLAUDE_MODEL) ?? modelIds.find((id) => id.includes("claude-sonnet")) ?? modelIds.find((id) => id.toLowerCase().includes("claude"));
|
|
329
381
|
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"));
|
|
330
|
-
|
|
382
|
+
const opencode = modelIds.find((id) => id === DEFAULT_OPENCODE_MODEL) ?? codex ?? modelIds.find((id) => id.toLowerCase().includes("qwen"));
|
|
383
|
+
const openclaw = modelIds.find((id) => id === DEFAULT_OPENCLAW_MODEL) ?? opencode ?? codex;
|
|
384
|
+
return { claude, codex, opencode, openclaw };
|
|
331
385
|
}
|
|
332
386
|
|
|
333
387
|
// src/index.ts
|
|
@@ -356,7 +410,9 @@ async function promptToolType(initial) {
|
|
|
356
410
|
message: "\u8981\u914D\u7F6E\u54EA\u4E2A\u5DE5\u5177\uFF1F",
|
|
357
411
|
options: [
|
|
358
412
|
{ value: "claude", label: "Claude Code\uFF08/v1/messages\uFF09" },
|
|
359
|
-
{ value: "codex", label: "Codex CLI\uFF08/v1/responses\uFF09" }
|
|
413
|
+
{ value: "codex", label: "Codex CLI\uFF08/v1/responses\uFF09" },
|
|
414
|
+
{ value: "opencode", label: "OpenCode\uFF08/v1/chat/completions\uFF09" },
|
|
415
|
+
{ value: "openclaw", label: "OpenClaw\uFF08/v1/chat/completions\uFF09" }
|
|
360
416
|
]
|
|
361
417
|
});
|
|
362
418
|
if (p.isCancel(value)) {
|
|
@@ -384,6 +440,18 @@ async function promptModel(type, initial) {
|
|
|
384
440
|
}
|
|
385
441
|
return String(value).trim();
|
|
386
442
|
}
|
|
443
|
+
function recommendedHint(toolType, recommended) {
|
|
444
|
+
switch (toolType) {
|
|
445
|
+
case "claude":
|
|
446
|
+
return recommended.claude;
|
|
447
|
+
case "codex":
|
|
448
|
+
return recommended.codex;
|
|
449
|
+
case "opencode":
|
|
450
|
+
return recommended.opencode;
|
|
451
|
+
case "openclaw":
|
|
452
|
+
return recommended.openclaw;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
387
455
|
async function run() {
|
|
388
456
|
let opts;
|
|
389
457
|
try {
|
|
@@ -408,6 +476,26 @@ async function run() {
|
|
|
408
476
|
const { claudeBaseUrl } = resolveBaseUrls(opts.baseUrl);
|
|
409
477
|
p.log.info(`\u5DE5\u5177: ${toolLabel(toolType)} \xB7 \u6A21\u578B: ${model}`);
|
|
410
478
|
const spinner2 = p.spinner();
|
|
479
|
+
let verifiedModelIds;
|
|
480
|
+
if (!opts.skipVerify) {
|
|
481
|
+
spinner2.start("\u9A8C\u8BC1 Trinity API \u8FDE\u901A\u6027\u2026");
|
|
482
|
+
const result = await verifyTrinityApi(apiKey, claudeBaseUrl);
|
|
483
|
+
if (result.ok) {
|
|
484
|
+
spinner2.stop(pc.green(result.message));
|
|
485
|
+
verifiedModelIds = result.modelIds;
|
|
486
|
+
const recommended = pickRecommendedModels(result.modelIds ?? []);
|
|
487
|
+
const hint = recommendedHint(toolType, recommended);
|
|
488
|
+
if (hint && hint !== model) {
|
|
489
|
+
p.note(`\u540C\u7C7B\u578B\u63A8\u8350\u6A21\u578B: ${hint}`, "\u6A21\u578B");
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
spinner2.stop(pc.yellow(result.message));
|
|
493
|
+
p.note(
|
|
494
|
+
"\u8FDE\u901A\u6027\u68C0\u67E5\u672A\u901A\u8FC7\uFF1B\u4ECD\u5C06\u5C1D\u8BD5\u5199\u5165\u672C\u5730\u914D\u7F6E\u3002\u8BF7\u68C0\u67E5 API Key\u3001\u7F51\u7EDC\uFF0C\u6216\u5728\u63A7\u5236\u53F0\u786E\u8BA4 Key \u6709\u6548\u3002",
|
|
495
|
+
"\u63D0\u793A"
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
411
499
|
if (toolType === "claude") {
|
|
412
500
|
spinner2.start("\u914D\u7F6E Claude Code\u2026");
|
|
413
501
|
try {
|
|
@@ -416,10 +504,10 @@ async function run() {
|
|
|
416
504
|
spinner2.stop("Claude Code \u914D\u7F6E\u5DF2\u5199\u5165 ~/.claude/settings.json");
|
|
417
505
|
} catch (error) {
|
|
418
506
|
spinner2.stop(pc.red("Claude Code \u914D\u7F6E\u5931\u8D25"));
|
|
419
|
-
console.error(
|
|
507
|
+
console.error(formatConfigError(error));
|
|
420
508
|
return 1;
|
|
421
509
|
}
|
|
422
|
-
} else {
|
|
510
|
+
} else if (toolType === "codex") {
|
|
423
511
|
spinner2.start("\u914D\u7F6E Codex CLI\u2026");
|
|
424
512
|
try {
|
|
425
513
|
const backupDir = await configureCodex(apiKey, apiBaseUrl, model, opts.verbose);
|
|
@@ -428,43 +516,68 @@ async function run() {
|
|
|
428
516
|
spinner2.stop(`Codex \u914D\u7F6E\u5DF2\u5199\u5165 ~/.codex/config.toml \u4E0E auth.json${backupHint}`);
|
|
429
517
|
} catch (error) {
|
|
430
518
|
spinner2.stop(pc.red("Codex \u914D\u7F6E\u5931\u8D25"));
|
|
431
|
-
console.error(
|
|
519
|
+
console.error(formatConfigError(error));
|
|
432
520
|
return 1;
|
|
433
521
|
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
p.note(
|
|
448
|
-
"\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",
|
|
449
|
-
"\u63D0\u793A"
|
|
522
|
+
} else if (toolType === "opencode") {
|
|
523
|
+
spinner2.start("\u914D\u7F6E OpenCode\u2026");
|
|
524
|
+
try {
|
|
525
|
+
const backupDir = await configureOpenCode(
|
|
526
|
+
apiKey,
|
|
527
|
+
apiBaseUrl,
|
|
528
|
+
model,
|
|
529
|
+
verifiedModelIds
|
|
530
|
+
);
|
|
531
|
+
const backupHint = backupDir ? `
|
|
532
|
+
\u5907\u4EFD: ${backupDir}` : "";
|
|
533
|
+
spinner2.stop(
|
|
534
|
+
`OpenCode \u914D\u7F6E\u5DF2\u5199\u5165 ~/.config/opencode/opencode.json${backupHint}`
|
|
450
535
|
);
|
|
451
|
-
|
|
536
|
+
} catch (error) {
|
|
537
|
+
spinner2.stop(pc.red("OpenCode \u914D\u7F6E\u5931\u8D25"));
|
|
538
|
+
console.error(formatConfigError(error));
|
|
452
539
|
return 1;
|
|
453
540
|
}
|
|
541
|
+
} else {
|
|
542
|
+
spinner2.start("\u914D\u7F6E OpenClaw\u2026");
|
|
543
|
+
try {
|
|
544
|
+
const backupDir = await configureOpenClaw(
|
|
545
|
+
apiKey,
|
|
546
|
+
apiBaseUrl,
|
|
547
|
+
model,
|
|
548
|
+
verifiedModelIds
|
|
549
|
+
);
|
|
550
|
+
const backupHint = backupDir ? `
|
|
551
|
+
\u5907\u4EFD: ${backupDir}` : "";
|
|
552
|
+
spinner2.stop(
|
|
553
|
+
`OpenClaw \u914D\u7F6E\u5DF2\u5199\u5165 ~/.openclaw/openclaw.json${backupHint}`
|
|
554
|
+
);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
spinner2.stop(pc.red("OpenClaw \u914D\u7F6E\u5931\u8D25"));
|
|
557
|
+
console.error(formatConfigError(error));
|
|
558
|
+
return 1;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (!opts.skipVerify && verifiedModelIds === void 0) {
|
|
562
|
+
p.outro(pc.yellow("\u914D\u7F6E\u5B8C\u6210\uFF08\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF09"));
|
|
563
|
+
return 1;
|
|
564
|
+
}
|
|
565
|
+
const nextSteps = [
|
|
566
|
+
`\u8FD0\u884C ${launchCommand(toolType)} \u542F\u52A8 ${toolLabel(toolType)}`
|
|
567
|
+
];
|
|
568
|
+
if (toolType === "opencode") {
|
|
569
|
+
nextSteps.push("\u5728 OpenCode \u5185\u7528 /models \u9009\u62E9 trinity/<model_code>");
|
|
570
|
+
} else if (toolType === "openclaw") {
|
|
571
|
+
nextSteps.push("\u9ED8\u8BA4\u6A21\u578B\u4E3A trinity/<model_code>\uFF1B\u6539\u5B8C\u914D\u7F6E\u540E\u8BF7\u91CD\u542F Gateway");
|
|
572
|
+
} else {
|
|
573
|
+
nextSteps.push("\u5728\u5DE5\u5177\u5185\u7528 /model \u5207\u6362\u4E3A Trinity \u5DF2\u4E0A\u67B6\u7684 model_code");
|
|
454
574
|
}
|
|
455
|
-
|
|
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
|
-
);
|
|
575
|
+
nextSteps.push("\u914D\u7F6E\u540E\u8BF7\u6C42\u8D70 Trinity \u8BA1\u8D39\uFF0C\u4E0D\u518D\u4F7F\u7528\u5B98\u65B9\u989D\u5EA6");
|
|
576
|
+
p.note(nextSteps.join("\n"), "\u4E0B\u4E00\u6B65");
|
|
464
577
|
p.outro(pc.green("\u5B8C\u6210"));
|
|
465
578
|
return 0;
|
|
466
579
|
}
|
|
467
|
-
function
|
|
580
|
+
function formatConfigError(error) {
|
|
468
581
|
if (error instanceof Error && "timed out" in error) {
|
|
469
582
|
return "zcf \u6267\u884C\u8D85\u65F6\uFF08>3 \u5206\u949F\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
470
583
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/merge-openclaw-config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var TRINITY_OPENCLAW_PROVIDER = "trinity";
|
|
6
|
+
function openclawHome() {
|
|
7
|
+
if (process.env.OPENCLAW_HOME?.trim()) {
|
|
8
|
+
return process.env.OPENCLAW_HOME.trim();
|
|
9
|
+
}
|
|
10
|
+
return path.join(os.homedir(), ".openclaw");
|
|
11
|
+
}
|
|
12
|
+
function openclawConfigPath() {
|
|
13
|
+
if (process.env.OPENCLAW_CONFIG?.trim()) {
|
|
14
|
+
return process.env.OPENCLAW_CONFIG.trim();
|
|
15
|
+
}
|
|
16
|
+
return path.join(openclawHome(), "openclaw.json");
|
|
17
|
+
}
|
|
18
|
+
function timestampLabel() {
|
|
19
|
+
const now = /* @__PURE__ */ new Date();
|
|
20
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
21
|
+
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
|
|
22
|
+
}
|
|
23
|
+
function backupOpenClawConfig() {
|
|
24
|
+
const configPath = openclawConfigPath();
|
|
25
|
+
if (!fs.existsSync(configPath)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const backupDir = path.join(openclawHome(), "backup", `trinity-config-${timestampLabel()}`);
|
|
29
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
30
|
+
fs.copyFileSync(configPath, path.join(backupDir, path.basename(configPath)));
|
|
31
|
+
return backupDir;
|
|
32
|
+
}
|
|
33
|
+
function readOpenClawConfig() {
|
|
34
|
+
const configPath = openclawConfigPath();
|
|
35
|
+
if (!fs.existsSync(configPath)) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
39
|
+
if (!raw.trim()) {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(raw);
|
|
44
|
+
} catch {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`\u65E0\u6CD5\u89E3\u6790 ${configPath}\u3002\u82E5\u6587\u4EF6\u4E3A JSON5\uFF0C\u8BF7\u5148\u6539\u4E3A\u6807\u51C6 JSON\uFF0C\u6216\u5907\u4EFD\u540E\u5220\u9664\u518D\u91CD\u8DD1\u3002`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function isLikelyNonTextModel(id) {
|
|
51
|
+
const lower = id.toLowerCase();
|
|
52
|
+
return lower.includes("image") || lower.includes("video") || lower.includes("seedance") || lower.includes("wan-") || lower.includes("kling");
|
|
53
|
+
}
|
|
54
|
+
function buildModelEntries(primaryModel, modelIds) {
|
|
55
|
+
const ids = [primaryModel];
|
|
56
|
+
const seen = new Set(ids);
|
|
57
|
+
for (const id of modelIds ?? []) {
|
|
58
|
+
const trimmed = id?.trim();
|
|
59
|
+
if (!trimmed || seen.has(trimmed) || isLikelyNonTextModel(trimmed)) continue;
|
|
60
|
+
seen.add(trimmed);
|
|
61
|
+
ids.push(trimmed);
|
|
62
|
+
if (ids.length >= 80) break;
|
|
63
|
+
}
|
|
64
|
+
return ids.map((id) => ({
|
|
65
|
+
id,
|
|
66
|
+
name: id,
|
|
67
|
+
reasoning: false,
|
|
68
|
+
input: ["text"],
|
|
69
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
70
|
+
contextWindow: 128e3,
|
|
71
|
+
maxTokens: 8192
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
function mergeOpenClawConfig(options) {
|
|
75
|
+
const configPath = openclawConfigPath();
|
|
76
|
+
const doc = readOpenClawConfig();
|
|
77
|
+
const modelsBlock = doc.models && typeof doc.models === "object" ? { ...doc.models } : {};
|
|
78
|
+
modelsBlock.mode = modelsBlock.mode ?? "merge";
|
|
79
|
+
const providers = modelsBlock.providers && typeof modelsBlock.providers === "object" ? { ...modelsBlock.providers } : {};
|
|
80
|
+
const existing = providers[TRINITY_OPENCLAW_PROVIDER] ?? {};
|
|
81
|
+
const existingModels = Array.isArray(existing.models) ? existing.models : [];
|
|
82
|
+
const byId = /* @__PURE__ */ new Map();
|
|
83
|
+
for (const m of existingModels) {
|
|
84
|
+
if (m && typeof m === "object" && typeof m.id === "string") {
|
|
85
|
+
byId.set(m.id, m);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const m of buildModelEntries(options.model, options.modelIds)) {
|
|
89
|
+
byId.set(m.id, { ...byId.get(m.id), ...m });
|
|
90
|
+
}
|
|
91
|
+
providers[TRINITY_OPENCLAW_PROVIDER] = {
|
|
92
|
+
...existing,
|
|
93
|
+
baseUrl: options.baseUrl.replace(/\/+$/, ""),
|
|
94
|
+
apiKey: options.apiKey,
|
|
95
|
+
api: "openai-completions",
|
|
96
|
+
models: [...byId.values()]
|
|
97
|
+
};
|
|
98
|
+
modelsBlock.providers = providers;
|
|
99
|
+
doc.models = modelsBlock;
|
|
100
|
+
const agents = doc.agents && typeof doc.agents === "object" ? { ...doc.agents } : {};
|
|
101
|
+
const defaults = agents.defaults && typeof agents.defaults === "object" ? { ...agents.defaults } : {};
|
|
102
|
+
const modelCfg = defaults.model && typeof defaults.model === "object" ? { ...defaults.model } : {};
|
|
103
|
+
modelCfg.primary = `${TRINITY_OPENCLAW_PROVIDER}/${options.model}`;
|
|
104
|
+
defaults.model = modelCfg;
|
|
105
|
+
const allow = defaults.models && typeof defaults.models === "object" ? { ...defaults.models } : {};
|
|
106
|
+
allow[`${TRINITY_OPENCLAW_PROVIDER}/*`] = allow[`${TRINITY_OPENCLAW_PROVIDER}/*`] ?? {};
|
|
107
|
+
defaults.models = allow;
|
|
108
|
+
agents.defaults = defaults;
|
|
109
|
+
doc.agents = agents;
|
|
110
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
111
|
+
fs.writeFileSync(configPath, `${JSON.stringify(doc, null, 2)}
|
|
112
|
+
`, "utf8");
|
|
113
|
+
}
|
|
114
|
+
function configureOpenClawDirect(options) {
|
|
115
|
+
const backupDir = backupOpenClawConfig();
|
|
116
|
+
mergeOpenClawConfig(options);
|
|
117
|
+
return backupDir;
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
TRINITY_OPENCLAW_PROVIDER,
|
|
121
|
+
backupOpenClawConfig,
|
|
122
|
+
configureOpenClawDirect,
|
|
123
|
+
mergeOpenClawConfig,
|
|
124
|
+
openclawConfigPath,
|
|
125
|
+
openclawHome
|
|
126
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/merge-opencode-config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var TRINITY_OPENCODE_PROVIDER = "trinity";
|
|
6
|
+
function opencodeConfigDir() {
|
|
7
|
+
if (process.env.OPENCODE_CONFIG_DIR?.trim()) {
|
|
8
|
+
return process.env.OPENCODE_CONFIG_DIR.trim();
|
|
9
|
+
}
|
|
10
|
+
return path.join(os.homedir(), ".config", "opencode");
|
|
11
|
+
}
|
|
12
|
+
function opencodeConfigPath() {
|
|
13
|
+
if (process.env.OPENCODE_CONFIG?.trim()) {
|
|
14
|
+
return process.env.OPENCODE_CONFIG.trim();
|
|
15
|
+
}
|
|
16
|
+
return path.join(opencodeConfigDir(), "opencode.json");
|
|
17
|
+
}
|
|
18
|
+
function timestampLabel() {
|
|
19
|
+
const now = /* @__PURE__ */ new Date();
|
|
20
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
21
|
+
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
|
|
22
|
+
}
|
|
23
|
+
function backupOpenCodeConfig() {
|
|
24
|
+
const configPath = opencodeConfigPath();
|
|
25
|
+
if (!fs.existsSync(configPath)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const backupDir = path.join(opencodeConfigDir(), "backup", `trinity-config-${timestampLabel()}`);
|
|
29
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
30
|
+
fs.copyFileSync(configPath, path.join(backupDir, path.basename(configPath)));
|
|
31
|
+
return backupDir;
|
|
32
|
+
}
|
|
33
|
+
function readOpenCodeConfig() {
|
|
34
|
+
const configPath = opencodeConfigPath();
|
|
35
|
+
if (!fs.existsSync(configPath)) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
39
|
+
if (!raw.trim()) {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(raw);
|
|
44
|
+
} catch {
|
|
45
|
+
throw new Error(`\u65E0\u6CD5\u89E3\u6790 ${configPath}\uFF0C\u8BF7\u68C0\u67E5 JSON \u8BED\u6CD5\u540E\u91CD\u8BD5\u3002`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function buildModelMap(primaryModel, modelIds) {
|
|
49
|
+
const ids = [primaryModel];
|
|
50
|
+
const seen = new Set(ids);
|
|
51
|
+
for (const id of modelIds ?? []) {
|
|
52
|
+
const trimmed = id?.trim();
|
|
53
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
54
|
+
const lower = trimmed.toLowerCase();
|
|
55
|
+
if (lower.includes("image") || lower.includes("video") || lower.includes("seedance") || lower.includes("wan-") || lower.includes("kling")) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
seen.add(trimmed);
|
|
59
|
+
ids.push(trimmed);
|
|
60
|
+
if (ids.length >= 80) break;
|
|
61
|
+
}
|
|
62
|
+
const models = {};
|
|
63
|
+
for (const id of ids) {
|
|
64
|
+
models[id] = { name: id };
|
|
65
|
+
}
|
|
66
|
+
return models;
|
|
67
|
+
}
|
|
68
|
+
function mergeOpenCodeConfig(options) {
|
|
69
|
+
const configPath = opencodeConfigPath();
|
|
70
|
+
const doc = readOpenCodeConfig();
|
|
71
|
+
doc.$schema = doc.$schema ?? "https://opencode.ai/config.json";
|
|
72
|
+
doc.model = `${TRINITY_OPENCODE_PROVIDER}/${options.model}`;
|
|
73
|
+
const providers = doc.provider && typeof doc.provider === "object" ? { ...doc.provider } : {};
|
|
74
|
+
const existing = providers[TRINITY_OPENCODE_PROVIDER] ?? {};
|
|
75
|
+
const existingModels = existing.models && typeof existing.models === "object" ? existing.models : {};
|
|
76
|
+
providers[TRINITY_OPENCODE_PROVIDER] = {
|
|
77
|
+
...existing,
|
|
78
|
+
npm: "@ai-sdk/openai-compatible",
|
|
79
|
+
name: "Trinity",
|
|
80
|
+
options: {
|
|
81
|
+
...existing.options ?? {},
|
|
82
|
+
baseURL: options.baseUrl.replace(/\/+$/, ""),
|
|
83
|
+
apiKey: options.apiKey
|
|
84
|
+
},
|
|
85
|
+
models: {
|
|
86
|
+
...existingModels,
|
|
87
|
+
...buildModelMap(options.model, options.modelIds)
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
doc.provider = providers;
|
|
91
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
92
|
+
fs.writeFileSync(configPath, `${JSON.stringify(doc, null, 2)}
|
|
93
|
+
`, "utf8");
|
|
94
|
+
}
|
|
95
|
+
function configureOpenCodeDirect(options) {
|
|
96
|
+
const backupDir = backupOpenCodeConfig();
|
|
97
|
+
mergeOpenCodeConfig(options);
|
|
98
|
+
return backupDir;
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
TRINITY_OPENCODE_PROVIDER,
|
|
102
|
+
backupOpenCodeConfig,
|
|
103
|
+
configureOpenCodeDirect,
|
|
104
|
+
mergeOpenCodeConfig,
|
|
105
|
+
opencodeConfigDir,
|
|
106
|
+
opencodeConfigPath
|
|
107
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trinity-config",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "一键把 Trinity API 接入 Claude Code
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "一键把 Trinity API 接入 Claude Code、Codex、OpenCode、OpenClaw",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"trinitydesk",
|
|
30
30
|
"claude-code",
|
|
31
31
|
"codex",
|
|
32
|
+
"opencode",
|
|
33
|
+
"openclaw",
|
|
32
34
|
"cli",
|
|
33
35
|
"ai-gateway",
|
|
34
36
|
"zcf"
|