trinity-config 1.0.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 (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/dist/index.js +416 -0
  4. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Trinity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # trinity-config
2
+
3
+ > 一键把 [Trinity API](https://trinitydesk.ai) 接入 **Claude Code** 与 **Codex CLI** —— 粘贴 `xh-` Key 即用
4
+
5
+ ## 快速开始
6
+
7
+ ### 交互式(推荐)
8
+
9
+ ```bash
10
+ npx -y trinity-config
11
+ ```
12
+
13
+ ### 一行命令
14
+
15
+ ```bash
16
+ npx -y trinity-config -k xh-你的key
17
+ ```
18
+
19
+ ### 只配其中一个
20
+
21
+ ```bash
22
+ npx -y trinity-config -k xh-你的key --only claude
23
+ npx -y trinity-config -k xh-你的key --only codex
24
+ ```
25
+
26
+ 完成后:
27
+
28
+ ```bash
29
+ claude # Claude Code
30
+ codex # Codex CLI
31
+ ```
32
+
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` 探测连通性
40
+
41
+ ## 命令行参数
42
+
43
+ | 参数 | 简写 | 说明 |
44
+ | --- | --- | --- |
45
+ | `--api-key` | `-k` | Trinity API Key |
46
+ | `--only` | | `claude` \| `codex` |
47
+ | `--base-url` | | 默认 `https://api.trinitydesk.ai` |
48
+ | `--claude-model` | | Claude 默认模型(默认 `claude-sonnet-4-6`) |
49
+ | `--codex-model` | | Codex 默认模型(默认 `gpt-5.4`) |
50
+ | `--skip-verify` | | 跳过连通性验证 |
51
+ | `--verbose` | `-v` | 显示 zcf 详细输出 |
52
+ | `--help` | `-h` | 帮助 |
53
+ | `--version` | `-V` | 版本 |
54
+
55
+ ## 注意
56
+
57
+ - 配置后**所有模型调用走 Trinity 计费**,不再使用 Anthropic/OpenAI 官方账号额度。
58
+ - 工具内选择的模型名须为 Trinity 已上架的 `model_code`(可用 `GET /v1/models` 查看)。
59
+ - Windows 下建议用 WSL2 运行 `claude` / `codex`;`npx trinity-config` 可在 PowerShell 执行。
60
+
61
+ ## 开发
62
+
63
+ ```bash
64
+ cd tools/trinity-config
65
+ npm install
66
+ npm run build
67
+ node dist/index.js --help
68
+ ```
69
+
70
+ 发版见仓库 `部署相关/trinity-config-npm发版说明.md`。
71
+
72
+ ## License
73
+
74
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+ // src/index.ts
3
+ import * as p from "@clack/prompts";
4
+ import pc from "picocolors";
5
+
6
+ // src/args.ts
7
+ function printHelp() {
8
+ console.log(`
9
+ trinity-config \u2014 \u4E00\u952E\u628A Trinity API \u63A5\u5165 Claude Code \u4E0E Codex CLI
10
+
11
+ \u7528\u6CD5:
12
+ npx -y trinity-config [\u9009\u9879]
13
+
14
+ \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
24
+
25
+ \u793A\u4F8B:
26
+ 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
+ `);
30
+ }
31
+ function parseArgs(argv) {
32
+ const opts = {
33
+ skipVerify: false,
34
+ verbose: false,
35
+ help: false,
36
+ version: false
37
+ };
38
+ const args = [...argv];
39
+ while (args.length > 0) {
40
+ const arg = args.shift();
41
+ if (!arg) break;
42
+ switch (arg) {
43
+ case "-h":
44
+ case "--help":
45
+ opts.help = true;
46
+ break;
47
+ case "-V":
48
+ case "--version":
49
+ opts.version = true;
50
+ break;
51
+ case "-v":
52
+ case "--verbose":
53
+ opts.verbose = true;
54
+ break;
55
+ case "--skip-verify":
56
+ opts.skipVerify = true;
57
+ break;
58
+ case "-k":
59
+ case "--api-key":
60
+ opts.apiKey = args.shift();
61
+ 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;
68
+ break;
69
+ }
70
+ case "--base-url":
71
+ opts.baseUrl = args.shift();
72
+ break;
73
+ case "--claude-model":
74
+ opts.claudeModel = args.shift();
75
+ break;
76
+ case "--codex-model":
77
+ opts.codexModel = args.shift();
78
+ break;
79
+ default:
80
+ if (arg.startsWith("-")) {
81
+ throw new Error(`\u672A\u77E5\u53C2\u6570: ${arg}`);
82
+ }
83
+ throw new Error(`\u591A\u4F59\u53C2\u6570: ${arg}`);
84
+ }
85
+ }
86
+ if (opts.help) {
87
+ printHelp();
88
+ }
89
+ return opts;
90
+ }
91
+ function normalizeApiKey(raw) {
92
+ const key = raw.trim();
93
+ if (!key) {
94
+ throw new Error("API Key \u4E0D\u80FD\u4E3A\u7A7A");
95
+ }
96
+ return key.startsWith("xh-") ? key : `xh-${key}`;
97
+ }
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;
107
+
108
+ // src/merge-claude-env.ts
109
+ import fs from "fs";
110
+ import os from "os";
111
+ import path from "path";
112
+ function claudeSettingsPath() {
113
+ return path.join(os.homedir(), ".claude", "settings.json");
114
+ }
115
+ function mergeClaudeEnv(claudeModel, fastModel, baseUrl) {
116
+ const filePath = claudeSettingsPath();
117
+ if (!fs.existsSync(filePath)) {
118
+ return;
119
+ }
120
+ const raw = fs.readFileSync(filePath, "utf8");
121
+ let settings;
122
+ try {
123
+ settings = JSON.parse(raw);
124
+ } catch {
125
+ return;
126
+ }
127
+ settings.env = {
128
+ ...settings.env ?? {},
129
+ ANTHROPIC_BASE_URL: baseUrl,
130
+ ANTHROPIC_MODEL: claudeModel,
131
+ ANTHROPIC_SMALL_FAST_MODEL: fastModel
132
+ };
133
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
134
+ fs.writeFileSync(filePath, `${JSON.stringify(settings, null, 2)}
135
+ `, "utf8");
136
+ }
137
+
138
+ // src/zcf.ts
139
+ import { execa } from "execa";
140
+ function sharedZcfArgs(verbose) {
141
+ return [
142
+ "zcf",
143
+ "init",
144
+ "--skip-prompt",
145
+ "--provider",
146
+ "custom",
147
+ "--api-type",
148
+ "auth_token",
149
+ "--config-action",
150
+ "merge",
151
+ "--mcp-services",
152
+ "skip",
153
+ "--workflows",
154
+ "skip",
155
+ "--output-styles",
156
+ "skip",
157
+ "--install-cometix-line",
158
+ "false",
159
+ ...verbose ? [] : ["--lang", "en"]
160
+ ];
161
+ }
162
+ async function runZcfInit(options) {
163
+ const args = [
164
+ ...sharedZcfArgs(options.verbose),
165
+ "--code-type",
166
+ options.codeType,
167
+ "--api-url",
168
+ options.baseUrl,
169
+ "--api-key",
170
+ options.apiKey,
171
+ "--api-model",
172
+ options.primaryModel
173
+ ];
174
+ if (options.codeType === "cc" && options.fastModel) {
175
+ args.push("--api-haiku-model", options.fastModel);
176
+ args.push("--api-sonnet-model", options.primaryModel);
177
+ }
178
+ await execa("npx", ["-y", ...args], {
179
+ stdio: options.verbose ? "inherit" : "pipe",
180
+ reject: true,
181
+ timeout: 18e4
182
+ });
183
+ }
184
+ async function configureClaude(apiKey, baseUrl, claudeModel, verbose) {
185
+ await runZcfInit({
186
+ codeType: "cc",
187
+ apiKey,
188
+ baseUrl,
189
+ primaryModel: claudeModel,
190
+ fastModel: DEFAULT_CLAUDE_FAST_MODEL,
191
+ verbose
192
+ });
193
+ }
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
+ });
202
+ }
203
+ function resolveBaseUrls(baseUrl) {
204
+ const root = (baseUrl ?? TRINITY_BASE_URL).replace(/\/+$/, "");
205
+ const codexBaseUrl = root.endsWith("/v1") ? root : `${root}/v1`;
206
+ const claudeBaseUrl = root.endsWith("/v1") ? root.slice(0, -3) : root;
207
+ return { claudeBaseUrl, codexBaseUrl };
208
+ }
209
+ function defaultModels(claudeModel, codexModel) {
210
+ return {
211
+ claudeModel: claudeModel ?? DEFAULT_CLAUDE_MODEL,
212
+ codexModel: codexModel ?? DEFAULT_CODEX_MODEL
213
+ };
214
+ }
215
+
216
+ // src/verify.ts
217
+ import https from "https";
218
+ function getJson(url, apiKey) {
219
+ return new Promise((resolve, reject) => {
220
+ const target = new URL(url);
221
+ const req = https.request(
222
+ {
223
+ protocol: target.protocol,
224
+ hostname: target.hostname,
225
+ port: target.port || 443,
226
+ path: `${target.pathname}${target.search}`,
227
+ method: "GET",
228
+ headers: {
229
+ Accept: "application/json",
230
+ Authorization: `Bearer ${apiKey}`
231
+ },
232
+ timeout: VERIFY_TIMEOUT_MS
233
+ },
234
+ (res) => {
235
+ const chunks = [];
236
+ res.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
237
+ res.on("end", () => {
238
+ const text2 = Buffer.concat(chunks).toString("utf8");
239
+ let body;
240
+ try {
241
+ body = JSON.parse(text2);
242
+ } catch {
243
+ reject(new Error(`\u54CD\u5E94\u4E0D\u662F JSON\uFF08HTTP ${res.statusCode}\uFF09`));
244
+ return;
245
+ }
246
+ resolve({ statusCode: res.statusCode ?? 0, body });
247
+ });
248
+ }
249
+ );
250
+ req.on("timeout", () => {
251
+ req.destroy();
252
+ reject(new Error(`\u8FDE\u63A5\u8D85\u65F6\uFF08>${VERIFY_TIMEOUT_MS}ms\uFF09`));
253
+ });
254
+ req.on("error", reject);
255
+ req.end();
256
+ });
257
+ }
258
+ async function verifyTrinityApi(apiKey, baseUrl = TRINITY_BASE_URL) {
259
+ const root = baseUrl.replace(/\/+$/, "");
260
+ const modelsUrl = `${root}/v1/models`;
261
+ try {
262
+ const { statusCode, body } = await getJson(modelsUrl, apiKey);
263
+ if (statusCode === 401 || statusCode === 403) {
264
+ return {
265
+ ok: false,
266
+ statusCode,
267
+ message: "API Key \u65E0\u6548\u6216\u5DF2\u7981\u7528\uFF08HTTP 401/403\uFF09"
268
+ };
269
+ }
270
+ if (statusCode === 404) {
271
+ return {
272
+ ok: false,
273
+ statusCode,
274
+ message: "Base URL \u9519\u8BEF\uFF08\u662F\u5426\u591A\u5199\u4E86 /v1\uFF1F\u5E94\u4F7F\u7528 https://api.trinitydesk.ai\uFF09"
275
+ };
276
+ }
277
+ if (statusCode < 200 || statusCode >= 300) {
278
+ return {
279
+ ok: false,
280
+ statusCode,
281
+ message: `GET /v1/models \u5931\u8D25\uFF08HTTP ${statusCode}\uFF09`
282
+ };
283
+ }
284
+ const modelIds = (body.data ?? []).map((item) => item.id).filter(Boolean);
285
+ if (body.object !== "list" && modelIds.length === 0) {
286
+ return {
287
+ ok: true,
288
+ statusCode,
289
+ message: "\u5DF2\u8FDE\u901A\uFF0C\u4F46\u6A21\u578B\u5217\u8868\u683C\u5F0F\u975E\u5E38\u89C4",
290
+ modelIds
291
+ };
292
+ }
293
+ return {
294
+ ok: true,
295
+ statusCode,
296
+ message: `\u8FDE\u901A\u6210\u529F\uFF08${modelIds.length} \u4E2A\u6A21\u578B\uFF09`,
297
+ modelIds
298
+ };
299
+ } catch (error) {
300
+ const message = error instanceof Error ? error.message : String(error);
301
+ if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED")) {
302
+ return { ok: false, message: `\u65E0\u6CD5\u8FDE\u63A5 ${root}\uFF1A${message}` };
303
+ }
304
+ return { ok: false, message: `\u8FDE\u901A\u6027\u68C0\u67E5\u5931\u8D25\uFF1A${message}` };
305
+ }
306
+ }
307
+ function pickRecommendedModels(modelIds) {
308
+ const claude = modelIds.find((id) => id === DEFAULT_CLAUDE_MODEL) ?? modelIds.find((id) => id.includes("claude-sonnet")) ?? modelIds.find((id) => id.toLowerCase().includes("claude"));
309
+ 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"));
310
+ return { claude, codex };
311
+ }
312
+
313
+ // src/index.ts
314
+ async function promptApiKey(initial) {
315
+ if (initial) {
316
+ return normalizeApiKey(initial);
317
+ }
318
+ const value = await p.text({
319
+ message: "\u7C98\u8D34 Trinity API Key\uFF08xh- \u5F00\u5934\uFF09",
320
+ placeholder: "xh-...",
321
+ validate: (input) => {
322
+ if (!input?.trim()) return "\u8BF7\u8F93\u5165 API Key";
323
+ }
324
+ });
325
+ if (p.isCancel(value)) {
326
+ p.cancel("\u5DF2\u53D6\u6D88");
327
+ process.exit(0);
328
+ }
329
+ return normalizeApiKey(String(value));
330
+ }
331
+ async function run() {
332
+ let opts;
333
+ try {
334
+ opts = parseArgs(process.argv.slice(2));
335
+ } catch (error) {
336
+ console.error(pc.red(error instanceof Error ? error.message : String(error)));
337
+ return 1;
338
+ }
339
+ if (opts.help) {
340
+ return 0;
341
+ }
342
+ if (opts.version) {
343
+ console.log(`${PACKAGE_NAME} v${PACKAGE_VERSION}`);
344
+ return 0;
345
+ }
346
+ p.intro(pc.bgCyan(pc.black(` ${PACKAGE_NAME} v${PACKAGE_VERSION} `)));
347
+ 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";
352
+ const spinner2 = p.spinner();
353
+ if (configureClaudeTool) {
354
+ spinner2.start("\u914D\u7F6E Claude Code\u2026");
355
+ try {
356
+ await configureClaude(apiKey, claudeBaseUrl, models.claudeModel, opts.verbose);
357
+ mergeClaudeEnv(models.claudeModel, models.claudeModel, claudeBaseUrl);
358
+ spinner2.stop("Claude Code \u914D\u7F6E\u5DF2\u5199\u5165 ~/.claude/settings.json");
359
+ } catch (error) {
360
+ 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);
363
+ return 1;
364
+ }
365
+ }
366
+ if (configureCodexTool) {
367
+ spinner2.start("\u914D\u7F6E Codex CLI\u2026");
368
+ try {
369
+ await configureCodex(apiKey, codexBaseUrl, models.codexModel, opts.verbose);
370
+ spinner2.stop("Codex \u914D\u7F6E\u5DF2\u5199\u5165 ~/.codex/");
371
+ } catch (error) {
372
+ 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);
375
+ return 1;
376
+ }
377
+ }
378
+ if (!opts.skipVerify) {
379
+ spinner2.start("\u9A8C\u8BC1 Trinity API \u8FDE\u901A\u6027\u2026");
380
+ const result = await verifyTrinityApi(apiKey, claudeBaseUrl);
381
+ if (result.ok) {
382
+ spinner2.stop(pc.green(result.message));
383
+ 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
+ );
392
+ }
393
+ } else {
394
+ spinner2.stop(pc.yellow(result.message));
395
+ p.note(
396
+ "\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",
397
+ "\u63D0\u793A"
398
+ );
399
+ p.outro(pc.yellow("\u914D\u7F6E\u5B8C\u6210\uFF08\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF09"));
400
+ return 1;
401
+ }
402
+ }
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");
410
+ p.outro(pc.green("\u5B8C\u6210"));
411
+ return 0;
412
+ }
413
+ run().then((code) => process.exit(code)).catch((error) => {
414
+ console.error(pc.red(error instanceof Error ? error.stack ?? error.message : String(error)));
415
+ process.exit(1);
416
+ });
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "trinity-config",
3
+ "version": "1.0.0",
4
+ "description": "一键把 Trinity API 接入 Claude Code 与 Codex CLI",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "trinity-config": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup src/index.ts --format esm --target node18 --clean && node scripts/add-shebang.mjs",
17
+ "prepublishOnly": "npm run build",
18
+ "test": "vitest run",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "keywords": [
28
+ "trinity",
29
+ "trinitydesk",
30
+ "claude-code",
31
+ "codex",
32
+ "cli",
33
+ "ai-gateway",
34
+ "zcf"
35
+ ],
36
+ "author": "Trinity",
37
+ "homepage": "https://trinitydesk.ai",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/trinitydesk/trinity-config"
41
+ },
42
+ "dependencies": {
43
+ "@clack/prompts": "^0.11.0",
44
+ "execa": "^9.5.2",
45
+ "picocolors": "^1.1.1"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.10.0",
49
+ "tsup": "^8.3.5",
50
+ "typescript": "^5.7.2",
51
+ "vitest": "^3.0.0"
52
+ }
53
+ }