trinity-config 1.2.3 → 1.2.5

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
@@ -4,7 +4,7 @@
4
4
 
5
5
  协议按 `GET /v1/models/{code}` 的 `capabilities.text.interfaces` **自动择优**(Anthropic → Responses → Completions),再与工具能力求交。
6
6
 
7
- 写入客户端的模型列表来自 `GET /v1/models?modality=text`(仅生文/多模态,不含生图/生视频)。
7
+ 写入客户端的模型列表来自该 API Key 的 `GET /v1/models?modality=text`(Call Allowlist 内生文/多模态;不含生图/生视频,也不含无权限模型)。
8
8
 
9
9
  ```bash
10
10
  npx -y trinity-config --type zcode -k xh-你的key --model gpt-5.4
@@ -0,0 +1,45 @@
1
+ // src/text-models.ts
2
+ function isLikelyNonTextModel(id) {
3
+ const lower = id.toLowerCase();
4
+ return lower.includes("image") || lower.includes("video") || lower.includes("seedance") || lower.includes("wan-") || lower.includes("kling") || lower.includes("og-image") || lower.includes("midjourney") || lower.includes("flux");
5
+ }
6
+ function filterTextModelIds(primaryModel, modelIds, limit = 80) {
7
+ const ids = [];
8
+ const seen = /* @__PURE__ */ new Set();
9
+ const push = (raw, enforceTextHeuristic) => {
10
+ const trimmed = raw?.trim();
11
+ if (!trimmed || seen.has(trimmed)) return;
12
+ if (enforceTextHeuristic && isLikelyNonTextModel(trimmed)) return;
13
+ seen.add(trimmed);
14
+ ids.push(trimmed);
15
+ };
16
+ if (modelIds && modelIds.length > 0) {
17
+ const allow = new Set(
18
+ modelIds.map((id) => id?.trim()).filter((id) => Boolean(id))
19
+ );
20
+ if (allow.has(primaryModel.trim())) {
21
+ push(primaryModel, false);
22
+ }
23
+ for (const id of modelIds) {
24
+ if (ids.length >= limit) break;
25
+ push(id, true);
26
+ }
27
+ return ids;
28
+ }
29
+ push(primaryModel, true);
30
+ return ids;
31
+ }
32
+ function pickAllowedDefaultModel(preferred, allowlist, candidates) {
33
+ const allow = new Set(allowlist.map((id) => id.trim()).filter(Boolean));
34
+ if (allow.has(preferred.trim())) return preferred.trim();
35
+ for (const c of candidates) {
36
+ const t = c?.trim();
37
+ if (t && allow.has(t)) return t;
38
+ }
39
+ return allowlist.map((id) => id.trim()).find(Boolean);
40
+ }
41
+
42
+ export {
43
+ filterTextModelIds,
44
+ pickAllowedDefaultModel
45
+ };
package/dist/index.js CHANGED
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ pickAllowedDefaultModel
4
+ } from "./chunk-5WZW5Y4W.js";
5
+
2
6
  // src/index.ts
3
7
  import * as p from "@clack/prompts";
4
8
  import pc from "picocolors";
5
9
 
6
10
  // src/constants.ts
7
11
  var PACKAGE_NAME = "trinity-config";
8
- var PACKAGE_VERSION = "1.2.3";
12
+ var PACKAGE_VERSION = "1.2.5";
9
13
  var TRINITY_BASE_URL = "https://api.trinitydesk.ai";
10
14
  var DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6";
11
15
  var DEFAULT_CLAUDE_FAST_MODEL = "claude-sonnet-4-6";
@@ -422,7 +426,7 @@ async function configureCodex(apiKey, codexBaseUrl, codexModel, _verbose) {
422
426
  return configureCodexDirect(apiKey, codexBaseUrl, codexModel);
423
427
  }
424
428
  async function configureOpenCode(apiKey, openCodeBaseUrl, model, modelIds, protocol) {
425
- const { configureOpenCodeDirect } = await import("./merge-opencode-config-XGIZYPOW.js");
429
+ const { configureOpenCodeDirect } = await import("./merge-opencode-config-JTWAGCET.js");
426
430
  return configureOpenCodeDirect({
427
431
  apiKey,
428
432
  baseUrl: openCodeBaseUrl,
@@ -432,7 +436,7 @@ async function configureOpenCode(apiKey, openCodeBaseUrl, model, modelIds, proto
432
436
  });
433
437
  }
434
438
  async function configureOpenClaw(apiKey, openClawBaseUrl, model, modelIds, protocol) {
435
- const { configureOpenClawDirect } = await import("./merge-openclaw-config-KMG3FYK6.js");
439
+ const { configureOpenClawDirect } = await import("./merge-openclaw-config-F77TZKPG.js");
436
440
  return configureOpenClawDirect({
437
441
  apiKey,
438
442
  baseUrl: openClawBaseUrl,
@@ -442,7 +446,7 @@ async function configureOpenClaw(apiKey, openClawBaseUrl, model, modelIds, proto
442
446
  });
443
447
  }
444
448
  async function configureZCode(apiKey, zcodeBaseUrl, model, modelIds, protocol) {
445
- const { configureZCodeDirect } = await import("./merge-zcode-config-IO3F3PZM.js");
449
+ const { configureZCodeDirect } = await import("./merge-zcode-config-LGTBU4UC.js");
446
450
  return configureZCodeDirect({
447
451
  apiKey,
448
452
  baseUrl: zcodeBaseUrl,
@@ -532,7 +536,7 @@ async function verifyTrinityApi(apiKey, baseUrl = TRINITY_BASE_URL) {
532
536
  return {
533
537
  ok: true,
534
538
  statusCode,
535
- message: `\u8FDE\u901A\u6210\u529F\uFF08${modelIds.length} \u4E2A\u751F\u6587\u6A21\u578B\uFF09`,
539
+ message: `\u8FDE\u901A\u6210\u529F\uFF08\u8BE5 Key \u53EF\u7528\u751F\u6587\u6A21\u578B ${modelIds.length} \u4E2A\uFF09`,
536
540
  modelIds
537
541
  };
538
542
  } catch (error) {
@@ -642,7 +646,7 @@ async function run() {
642
646
  p.intro(pc.bgCyan(pc.black(` ${PACKAGE_NAME} v${PACKAGE_VERSION} `)));
643
647
  const toolType = await promptToolType(opts.type);
644
648
  const apiKey = await promptApiKey(opts.apiKey);
645
- const model = opts.model?.trim() ? opts.model.trim() : opts.apiKey ? resolveModel(toolType) : await promptModel(toolType);
649
+ let model = opts.model?.trim() ? opts.model.trim() : opts.apiKey ? resolveModel(toolType) : await promptModel(toolType);
646
650
  const apiRoot = rootBaseUrl(opts.baseUrl);
647
651
  p.log.info(`\u5DE5\u5177: ${toolLabel(toolType)} \xB7 \u6A21\u578B: ${model}`);
648
652
  const spinner2 = p.spinner();
@@ -653,20 +657,48 @@ async function run() {
653
657
  const result = await verifyTrinityApi(apiKey, apiRoot);
654
658
  if (result.ok) {
655
659
  spinner2.stop(pc.green(result.message));
656
- verifiedModelIds = result.modelIds;
657
- const recommended = pickRecommendedModels(result.modelIds ?? []);
660
+ verifiedModelIds = result.modelIds ?? [];
661
+ if (verifiedModelIds.length === 0) {
662
+ p.outro(pc.red("\u8BE5 API Key \u6CA1\u6709\u53EF\u7528\u7684\u751F\u6587\u6A21\u578B\uFF08Call Allowlist \u4E3A\u7A7A\uFF09"));
663
+ return 1;
664
+ }
665
+ const recommended = pickRecommendedModels(verifiedModelIds);
658
666
  const hint = recommendedHint(toolType, recommended);
659
- if (hint && hint !== model) {
667
+ const allowed = pickAllowedDefaultModel(model, verifiedModelIds, [
668
+ hint,
669
+ recommended.claude,
670
+ recommended.codex,
671
+ recommended.opencode,
672
+ recommended.openclaw,
673
+ recommended.zcode,
674
+ defaultModelForType(toolType)
675
+ ]);
676
+ if (!allowed) {
677
+ p.outro(pc.red("\u65E0\u6CD5\u5728\u8BE5 API Key \u7684\u53EF\u7528\u751F\u6587\u6A21\u578B\u4E2D\u9009\u51FA\u9ED8\u8BA4\u6A21\u578B"));
678
+ return 1;
679
+ }
680
+ if (allowed !== model) {
681
+ p.note(
682
+ `\u6A21\u578B ${model} \u4E0D\u5728\u8BE5 Key \u53EF\u7528\u5217\u8868\u4E2D\uFF0C\u5DF2\u6539\u4E3A ${allowed}\uFF08\u5171 ${verifiedModelIds.length} \u4E2A\u53EF\u7528\u751F\u6587\u6A21\u578B\uFF09`,
683
+ "\u6743\u9650"
684
+ );
685
+ model = allowed;
686
+ } else if (hint && hint !== model) {
660
687
  p.note(`\u540C\u7C7B\u578B\u63A8\u8350\u6A21\u578B: ${hint}`, "\u6A21\u578B");
661
688
  }
662
689
  } else {
663
690
  verifyFailed = true;
664
691
  spinner2.stop(pc.yellow(result.message));
665
692
  p.note(
666
- "\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",
693
+ "\u8FDE\u901A\u6027\u68C0\u67E5\u672A\u901A\u8FC7\uFF1B\u4ECD\u5C06\u5C1D\u8BD5\u5199\u5165\u672C\u5730\u914D\u7F6E\uFF08\u4E0D\u4F1A\u540C\u6B65\u6A21\u578B\u5217\u8868\uFF09\u3002\u8BF7\u68C0\u67E5 API Key\u3001\u7F51\u7EDC\uFF0C\u6216\u5728\u63A7\u5236\u53F0\u786E\u8BA4 Key \u6709\u6548\u3002",
667
694
  "\u63D0\u793A"
668
695
  );
669
696
  }
697
+ } else {
698
+ p.note(
699
+ "--skip-verify \u5DF2\u8DF3\u8FC7\uFF1A\u65E0\u6CD5\u6309 Key \u6743\u76CA\u62C9\u53D6\u53EF\u7528\u6A21\u578B\u5217\u8868\uFF0C\u4EC5\u5199\u5165\u6307\u5B9A\u9ED8\u8BA4\u6A21\u578B\u3002",
700
+ "\u63D0\u793A"
701
+ );
670
702
  }
671
703
  spinner2.start("\u89E3\u6790\u6A21\u578B\u534F\u8BAE\u2026");
672
704
  let protocolRes;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  filterTextModelIds
3
- } from "./chunk-QS4QYSMM.js";
3
+ } from "./chunk-5WZW5Y4W.js";
4
4
 
5
5
  // src/merge-openclaw-config.ts
6
6
  import fs from "fs";
@@ -71,22 +71,13 @@ function mergeOpenClawConfig(options) {
71
71
  modelsBlock.mode = modelsBlock.mode ?? "merge";
72
72
  const providers = modelsBlock.providers && typeof modelsBlock.providers === "object" ? { ...modelsBlock.providers } : {};
73
73
  const existing = providers[TRINITY_OPENCLAW_PROVIDER] ?? {};
74
- const existingModels = Array.isArray(existing.models) ? existing.models : [];
75
- const byId = /* @__PURE__ */ new Map();
76
- for (const m of existingModels) {
77
- if (m && typeof m === "object" && typeof m.id === "string") {
78
- byId.set(m.id, m);
79
- }
80
- }
81
- for (const m of buildModelEntries(options.model, options.modelIds)) {
82
- byId.set(m.id, { ...byId.get(m.id), ...m });
83
- }
74
+ const nextModels = buildModelEntries(options.model, options.modelIds);
84
75
  providers[TRINITY_OPENCLAW_PROVIDER] = {
85
76
  ...existing,
86
77
  baseUrl: options.baseUrl.replace(/\/+$/, ""),
87
78
  apiKey: options.apiKey,
88
79
  api,
89
- models: [...byId.values()]
80
+ models: nextModels
90
81
  };
91
82
  modelsBlock.providers = providers;
92
83
  doc.models = modelsBlock;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  filterTextModelIds
3
- } from "./chunk-QS4QYSMM.js";
3
+ } from "./chunk-5WZW5Y4W.js";
4
4
 
5
5
  // src/merge-opencode-config.ts
6
6
  import fs from "fs";
@@ -65,7 +65,7 @@ function mergeOpenCodeConfig(options) {
65
65
  doc.model = `${TRINITY_OPENCODE_PROVIDER}/${options.model}`;
66
66
  const providers = doc.provider && typeof doc.provider === "object" ? { ...doc.provider } : {};
67
67
  const existing = providers[TRINITY_OPENCODE_PROVIDER] ?? {};
68
- const existingModels = existing.models && typeof existing.models === "object" ? existing.models : {};
68
+ const nextModels = buildModelMap(options.model, options.modelIds);
69
69
  providers[TRINITY_OPENCODE_PROVIDER] = {
70
70
  ...existing,
71
71
  npm,
@@ -75,10 +75,7 @@ function mergeOpenCodeConfig(options) {
75
75
  baseURL: options.baseUrl.replace(/\/+$/, ""),
76
76
  apiKey: options.apiKey
77
77
  },
78
- models: {
79
- ...existingModels,
80
- ...buildModelMap(options.model, options.modelIds)
81
- }
78
+ models: nextModels
82
79
  };
83
80
  doc.provider = providers;
84
81
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  filterTextModelIds
3
- } from "./chunk-QS4QYSMM.js";
3
+ } from "./chunk-5WZW5Y4W.js";
4
4
 
5
5
  // src/merge-zcode-config.ts
6
6
  import fs from "fs";
@@ -19,6 +19,38 @@ function zcodeConfigPath() {
19
19
  }
20
20
  return path.join(zcodeHome(), "v2", "config.json");
21
21
  }
22
+ function zcodeProtocolBinding(protocol = "chat_completions") {
23
+ switch (protocol) {
24
+ case "anthropic_messages":
25
+ return {
26
+ kind: "anthropic",
27
+ apiFormat: "anthropic-messages",
28
+ npm: "@ai-sdk/anthropic",
29
+ modelFormat: "anthropic",
30
+ pathKey: "anthropic",
31
+ pathSuffix: "/v1/messages"
32
+ };
33
+ case "responses":
34
+ return {
35
+ kind: "openai",
36
+ apiFormat: "openai-responses",
37
+ npm: "@ai-sdk/openai",
38
+ modelFormat: "responses",
39
+ pathKey: "openai",
40
+ pathSuffix: "/responses"
41
+ };
42
+ case "chat_completions":
43
+ default:
44
+ return {
45
+ kind: "openai-compatible",
46
+ apiFormat: "openai-chat-completions",
47
+ npm: "@ai-sdk/openai-compatible",
48
+ modelFormat: "openai",
49
+ pathKey: "openai-compatible",
50
+ pathSuffix: "/chat/completions"
51
+ };
52
+ }
53
+ }
22
54
  function timestampLabel() {
23
55
  const now = /* @__PURE__ */ new Date();
24
56
  const pad = (n) => String(n).padStart(2, "0");
@@ -47,13 +79,24 @@ function readZCodeConfig() {
47
79
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 ${configPath}\uFF0C\u8BF7\u68C0\u67E5 JSON \u8BED\u6CD5\u540E\u91CD\u8BD5\u3002`);
48
80
  }
49
81
  }
50
- function buildModels(primary, modelIds) {
82
+ function buildModels(primary, modelIds, binding) {
51
83
  const models = {};
52
84
  for (const id of filterTextModelIds(primary, modelIds)) {
53
- models[id] = { name: id };
85
+ models[id] = {
86
+ name: id,
87
+ kinds: [binding.kind],
88
+ defaultKind: binding.kind
89
+ };
54
90
  }
55
91
  return models;
56
92
  }
93
+ function buildModelSupportedFormats(primary, modelIds, modelFormat) {
94
+ const out = {};
95
+ for (const id of filterTextModelIds(primary, modelIds)) {
96
+ out[id] = [modelFormat];
97
+ }
98
+ return out;
99
+ }
57
100
  function mergeZCodeConfig(options) {
58
101
  const configPath = zcodeConfigPath();
59
102
  const doc = readZCodeConfig();
@@ -65,26 +108,46 @@ function mergeZCodeConfig(options) {
65
108
  }
66
109
  }
67
110
  const existing = providers[TRINITY_ZCODE_PROVIDER] ?? {};
68
- const existingModels = existing.models && typeof existing.models === "object" ? existing.models : {};
69
111
  const root = options.baseUrl.replace(/\/v1\/?$/, "").replace(/\/+$/, "");
70
112
  const withV1 = `${root}/v1`;
71
113
  const withoutV1 = root;
114
+ const binding = zcodeProtocolBinding(options.protocol);
72
115
  const primaryBase = options.protocol === "anthropic_messages" ? withoutV1 : withV1;
116
+ const now = Date.now();
117
+ const models = buildModels(options.model, options.modelIds, binding);
73
118
  providers[TRINITY_ZCODE_PROVIDER] = {
74
119
  ...existing,
75
120
  enabled: true,
76
121
  name: "Trinity",
122
+ source: "custom",
123
+ kind: binding.kind,
124
+ defaultKind: binding.kind,
125
+ apiFormat: binding.apiFormat,
126
+ npm: binding.npm,
127
+ endpoints: {
128
+ anthropic: withoutV1,
129
+ openai: withV1,
130
+ baseURL: primaryBase,
131
+ paths: {
132
+ anthropic: "/v1/messages",
133
+ openai: "/responses",
134
+ "openai-compatible": "/chat/completions",
135
+ [binding.pathKey]: binding.pathSuffix
136
+ }
137
+ },
77
138
  options: {
78
139
  ...existing.options ?? {},
79
140
  baseURL: primaryBase,
80
- apiKey: options.apiKey,
81
- openaiBaseURL: withV1,
82
- anthropicBaseURL: withoutV1
141
+ apiKey: options.apiKey
83
142
  },
84
- models: {
85
- ...existingModels,
86
- ...buildModels(options.model, options.modelIds)
87
- }
143
+ models,
144
+ modelSupportedFormats: buildModelSupportedFormats(
145
+ options.model,
146
+ options.modelIds,
147
+ binding.modelFormat
148
+ ),
149
+ createdAt: typeof existing.createdAt === "number" ? existing.createdAt : now,
150
+ updatedAt: now
88
151
  };
89
152
  doc.provider = providers;
90
153
  doc.model = `${TRINITY_ZCODE_PROVIDER}/${options.model}`;
@@ -103,5 +166,6 @@ export {
103
166
  configureZCodeDirect,
104
167
  mergeZCodeConfig,
105
168
  zcodeConfigPath,
106
- zcodeHome
169
+ zcodeHome,
170
+ zcodeProtocolBinding
107
171
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trinity-config",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "一键把 Trinity API 接入 Claude Code、Codex、OpenCode、OpenClaw、ZCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,26 +0,0 @@
1
- // src/text-models.ts
2
- function isLikelyNonTextModel(id) {
3
- const lower = id.toLowerCase();
4
- return lower.includes("image") || lower.includes("video") || lower.includes("seedance") || lower.includes("wan-") || lower.includes("kling") || lower.includes("og-image") || lower.includes("midjourney") || lower.includes("flux");
5
- }
6
- function filterTextModelIds(primaryModel, modelIds, limit = 80) {
7
- const ids = [];
8
- const seen = /* @__PURE__ */ new Set();
9
- const push = (raw) => {
10
- const trimmed = raw?.trim();
11
- if (!trimmed || seen.has(trimmed)) return;
12
- if (ids.length > 0 && isLikelyNonTextModel(trimmed)) return;
13
- seen.add(trimmed);
14
- ids.push(trimmed);
15
- };
16
- push(primaryModel);
17
- for (const id of modelIds ?? []) {
18
- if (ids.length >= limit) break;
19
- push(id);
20
- }
21
- return ids;
22
- }
23
-
24
- export {
25
- filterTextModelIds
26
- };