srpllm 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-PRESENT UfoMiao <https://github.com/UfoMiao> & Miao Da <https://github.com/WitMiao>
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,193 @@
1
+ [![npm version][npm-version-src]][npm-version-href]
2
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
3
+ [![License][license-src]][license-href]
4
+ [![Claude Code][claude-code-src]][claude-code-href]
5
+ [![codecov][codecov-src]][codecov-href]
6
+ [![JSDocs][jsdocs-src]][jsdocs-href]
7
+ [![Ask DeepWiki][deepwiki-src]][deepwiki-href]
8
+
9
+ <div align="center">
10
+ <img src="./src/assets/banner.webp" alt="Banner"/>
11
+
12
+ <h1>
13
+ ZCF - Zero-Config Code Flow
14
+ </h1>
15
+
16
+ <p align="center">
17
+ <b>English</b> | <a href="README_zh-CN.md">中文</a> | <a href="README_ja-JP.md">日本語</a> | <a href="CHANGELOG.md">Changelog</a>
18
+
19
+ **✨ Full Documentation**: [ZCF Docs](https://zcf.ufomiao.com/)
20
+
21
+ > Zero-config, one-click setup for Claude Code & Codex with bilingual support, intelligent agent system and personalized AI assistant
22
+ </p>
23
+ </div>
24
+
25
+ ## ♥️ Sponsors
26
+
27
+ [![GLM](./src/assets/GLM-en.png)](https://z.ai/subscribe?ic=8JVLJQFSKB)
28
+
29
+ This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN.
30
+ GLM CODING PLAN is a subscription service designed for AI coding, starting at just $10/month. It provides access to their flagship GLM-4.7 & (GLM-5 Only Available for Pro Users)model across 10+ popular AI coding tools (Claude Code, Cline, Roo Code, etc.), offering developers top-tier, fast, and stable coding experiences.
31
+ Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB
32
+
33
+ ---
34
+
35
+ [![Sponsor PatewayAI](./src/assets/pateway.ai-en.png)](https://pateway.ai/?ch=vnr0h5&aff=9AWWH87C)
36
+ PatewayAI is a high-quality model API relay service provider focused on official direct connections for serious AI developers, offering the full range of Claude and Codex series models. 100% official source supply with no adulteration — open to inspection. Transparent billing with token-level invoices auditable line by line. Enterprise-grade concurrency support with a dedicated management platform for enterprise customers, including formal contracts and invoicing. PatewayAI offers an exclusive benefit for ZCF users: register via <a href="https://pateway.ai/?ch=vnr0h5&aff=9AWWH87C">this link</a> to get $3 free trial credit, recharges as low as 60% off, with bilateral invite rewards up to $150!
37
+
38
+ ---
39
+
40
+ [![Sponsor AI API](./src/assets/302.ai-en.jpg)](https://share.302.ai/gAT9VG)
41
+ [302.AI](https://share.302.ai/gAT9VG) is a pay-as-you-go enterprise AI resource hub that offers the latest and most comprehensive AI models and APIs on the market, along with a variety of ready-to-use online AI applications.
42
+
43
+ ---
44
+
45
+ <table>
46
+ <tbody>
47
+ <tr>
48
+ <td width="180"><a href="https://www.packyapi.com/register?aff=zcf"><img src="./src/assets/packycode.png" alt="PackyCode" width="150"></a></td>
49
+ <td>Thanks to PackyCode for sponsoring this project! PackyCode is a reliable and efficient API relay service provider, offering relay services for Claude Code, Codex, Gemini, and more. PackyCode provides special discounts for our software users: register using <a href="https://www.packyapi.com/register?aff=zcf">this link</a> and enter the "zcf" promo code during recharge to get 10% off.</td>
50
+ </tr>
51
+ <tr>
52
+ <td width="180"><a href="https://apikey.fun/register?aff=ZCFZCF"><img src="./src/assets/apikey-fun.png" alt="APIKEY.FUN" width="150"></a></td>
53
+ <td>Thanks to APIKEY.FUN for sponsoring this project! APIKEY.FUN is a professional enterprise-grade AI relay platform dedicated to providing stable, efficient, and low-cost API access for both companies and individual developers. The platform supports popular models including Claude, OpenAI, and Gemini, with pricing as low as 7% of official rates. Register via <a href="https://apikey.fun/register?aff=ZCFZCF">this link</a> to enjoy an exclusive offer of up to permanent 95% recharge pricing (5% off).</td>
54
+ </tr>
55
+ <tr>
56
+ <td width="180"><a href="https://www.aicodemirror.com/register?invitecode=ZCFZCF"><img src="./src/assets/AICodeMirror.jpg" alt="AICodeMirror" width="150"></a></td>
57
+ <td>Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code/Codex/Gemini CLI, supporting enterprise-level high concurrency, fast invoicing, and 7x24 dedicated technical support. Official channels for Claude Code/Codex/Gemini at discounts as low as 38%/2%/10.9% off, with additional discounts on top-ups! AICodeMirror offers special benefits for ZCF users: users who register through <a href="https://www.aicodemirror.com/register?invitecode=ZCFZCF">this link</a> can enjoy 20% off on first top-up, and enterprise customers can get up to 25% off!</td>
58
+ </tr>
59
+ <tr>
60
+ <td width="180"><a href="https://crazyrouter.com/?utm_source=github&utm_medium=sponsor&utm_campaign=zcf&aff=yJFo"><img src="./src/assets/crazyrouter.svg" alt="Crazyrouter" width="150"></a></td>
61
+ <td>Thanks to Crazyrouter for sponsoring this project! Crazyrouter is a high-performance AI API aggregation gateway — one API key for 300+ models (GPT, Claude, Gemini, DeepSeek, and more). All models at 55% of official pricing with auto-failover, smart routing, and unlimited concurrency. Fully OpenAI-compatible, works seamlessly with Claude Code, Codex, and Gemini CLI. Crazyrouter offers an exclusive deal for ZCF users: register via <a href="https://crazyrouter.com/?utm_source=github&utm_medium=sponsor&utm_campaign=zcf&aff=yJFo">this link</a> to get $2 free credit instantly!</td>
62
+ </tr>
63
+ </tbody>
64
+ </table>
65
+
66
+ ## 🚀 Quick Start
67
+
68
+ - Recommended: `npx zcf` opens the interactive menu — pick what you need.
69
+ - Common commands:
70
+
71
+ ```bash
72
+ npx zcf i # Full initialization: install + workflows + API/CCR + MCP
73
+ npx zcf u # Update workflows only
74
+ npx zcf --lang zh-CN # Switch interface language (example)
75
+ ```
76
+
77
+ - Non-interactive example (provider preset):
78
+
79
+ ```bash
80
+ npx zcf i -s -p 302ai -k "sk-xxx"
81
+ ```
82
+
83
+ More usage, options, and workflows: see documentation.
84
+
85
+ ## 📖 Full Documentation
86
+
87
+ - https://zcf.ufomiao.com/
88
+
89
+ ## 💬 Community
90
+
91
+ Join our Telegram group for support, discussions, and updates:
92
+
93
+ [![Telegram](https://img.shields.io/badge/Telegram-Join%20Chat-blue?style=flat&logo=telegram)](https://t.me/ufomiao_zcf)
94
+
95
+ ## 🙏 Acknowledgments
96
+
97
+ This project is inspired by and incorporates work from:
98
+
99
+ - [LINUX DO - New Ideal Community](https://linux.do)
100
+ - [CCR](https://github.com/musistudio/claude-code-router)
101
+ - [CCometixLine](https://github.com/Haleclipse/CCometixLine)
102
+ - [ccusage](https://github.com/ryoppippi/ccusage)
103
+ - [BMad Method](https://github.com/bmad-code-org/BMAD-METHOD)
104
+
105
+ Thanks to these community contributors for sharing!
106
+
107
+ ## ❤️ Support & Sponsors
108
+
109
+ If you find this project helpful, please consider sponsoring its development. Your support is greatly appreciated!
110
+
111
+ [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/UfoMiao)
112
+
113
+ <table>
114
+ <tr>
115
+ <td><img src="/src/assets/alipay.webp" width="200" alt="Alipay" /></td>
116
+ <td><img src="/src/assets/wechat.webp" width="200" alt="WeChat Pay" /></td>
117
+ </tr>
118
+ </table>
119
+
120
+ ### Our Sponsors
121
+
122
+ A huge thank you to all our sponsors for their generous support!
123
+
124
+ 【Corporate Sponsors】
125
+
126
+ - [302.AI](https://share.302.ai/gAT9VG) (first corporate sponsorship 🤠)
127
+ - [GLM](https://z.ai/subscribe?ic=8JVLJQFSKB) (first AI model sponsorship 🤖)
128
+ - [PackyCode](https://www.packyapi.com/register?aff=zcf) (first API proxy service sponsor 🧝🏻‍♀️)
129
+ - [APIKEY.FUN](https://apikey.fun/register?aff=ZCFZCF) (enterprise AI relay sponsor 🎁)
130
+ - [AICodeMirror](https://www.aicodemirror.com/register?invitecode=ZCFZCF) (official high-stability relay service sponsor 🪞)
131
+ - [UUCode](https://www.uucode.org/auth?ref=JQ2DJ1T8) (sponsored $100 proxy credits 💰)
132
+ - [Crazyrouter](https://crazyrouter.com/?utm_source=github&utm_medium=sponsor&utm_campaign=zcf&aff=yJFo) (AI API aggregation gateway sponsor 🚀)
133
+ - [PatewayAI](https://pateway.ai/?ch=vnr0h5&aff=9AWWH87C) (official direct-connect relay service sponsor 🛡️)
134
+
135
+ 【Individual Sponsors】
136
+
137
+ - Tc (first sponsor)
138
+ - Argolinhas (first ko-fi sponsor ٩(•̤̀ᵕ•̤́๑))
139
+ - r\*r (first anonymous sponsor 🤣)
140
+ - \*\*康 (first KFC sponsor 🍗)
141
+ - \*东 (first coffee sponsor ☕️)
142
+ - 炼\*3 (first Termux user sponsor 📱)
143
+ - [chamo101](https://github.com/chamo101) (first GitHub issue sponsor 🎉)
144
+ - 初屿贤 (first Codex user sponsor 🙅🏻‍♂️)
145
+ - Protein (first 1688 sponsor 😏)
146
+ - [musistudio](https://github.com/musistudio) (first open source project author sponsor, the author of [CCR](https://github.com/musistudio/claude-code-router) 🤩)
147
+ - \*年 (first 100 CNY sponsor 💴)
148
+ - [BeatSeat](https://github.com/BeatSeat) (community expert 😎, provided $1000 Claude credits)
149
+ - [wenwen](https://github.com/wenwen12345) (community expert 🤓, provided daily $100 Claude&GPT credits)
150
+ - 16°C coffee (My best friend 🤪, offered ChatGPT Pro $200 package)
151
+
152
+ ### Promotion Thanks
153
+
154
+ Thanks to the following authors for promoting this project:
155
+
156
+ - 逛逛 GitHub, article: https://mp.weixin.qq.com/s/phqwSRb16MKCHHVozTFeiQ
157
+ - Geek, tweet: https://x.com/geekbb/status/1955174718618866076
158
+
159
+ ## 📄 License
160
+
161
+ [MIT License](LICENSE)
162
+
163
+ ---
164
+
165
+ ## 🚀 Contributors
166
+
167
+ <a href="https://github.com/UfoMiao/zcf/graphs/contributors">
168
+ <img src="https://contrib.rocks/image?repo=UfoMiao/zcf" />
169
+ </a>
170
+ <br /><br />
171
+
172
+ ## ⭐️ Star History
173
+
174
+ If this project helps you, please give me a ⭐️ Star!
175
+
176
+ [![Star History Chart](https://api.star-history.com/svg?repos=UfoMiao/zcf&type=Date)](https://star-history.com/#UfoMiao/zcf&Date)
177
+
178
+ <!-- Badges -->
179
+
180
+ [npm-version-src]: https://img.shields.io/npm/v/zcf?style=flat&colorA=080f12&colorB=1fa669
181
+ [npm-version-href]: https://npmjs.com/package/zcf
182
+ [npm-downloads-src]: https://img.shields.io/npm/dm/zcf?style=flat&colorA=080f12&colorB=1fa669
183
+ [npm-downloads-href]: https://npmjs.com/package/zcf
184
+ [license-src]: https://img.shields.io/github/license/ufomiao/zcf.svg?style=flat&colorA=080f12&colorB=1fa669
185
+ [license-href]: https://github.com/ufomiao/zcf/blob/main/LICENSE
186
+ [claude-code-src]: https://img.shields.io/badge/Claude-Code-1fa669?style=flat&colorA=080f12&colorB=1fa669
187
+ [claude-code-href]: https://claude.ai/code
188
+ [codecov-src]: https://codecov.io/gh/UfoMiao/zcf/graph/badge.svg?token=HZI6K4Y7D7&style=flat&colorA=080f12&colorB=1fa669
189
+ [codecov-href]: https://codecov.io/gh/UfoMiao/zcf
190
+ [jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-1fa669?style=flat&colorA=080f12&colorB=1fa669
191
+ [jsdocs-href]: https://www.jsdocs.io/package/zcf
192
+ [deepwiki-src]: https://img.shields.io/badge/Ask-DeepWiki-1fa669?style=flat&colorA=080f12&colorB=1fa669
193
+ [deepwiki-href]: https://deepwiki.com/UfoMiao/zcf
package/bin/srpllm.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.mjs'
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+
package/dist/cli.mjs ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import cac from 'cac';
3
+ import ansis from 'ansis';
4
+ import { i as init, u as uninstall, G as version } from './shared/srpllm.BB4ML_UM.mjs';
5
+ import 'node:process';
6
+ import 'inquirer';
7
+ import 'ora';
8
+ import 'node:os';
9
+ import 'pathe';
10
+ import 'node:fs';
11
+ import 'tinyexec';
12
+
13
+ function customizeHelp(sections) {
14
+ sections.unshift({
15
+ title: "",
16
+ body: ansis.cyan.bold(`SrP-LLM \u914D\u7F6E\u5DE5\u5177 v${version}`)
17
+ });
18
+ sections.push({
19
+ title: ansis.yellow("\u547D\u4EE4"),
20
+ body: [
21
+ ` ${ansis.cyan("srpllm")} \u4EA4\u4E92\u5F0F\u5F15\u5BFC\u914D\u7F6E\uFF08\u9ED8\u8BA4\uFF09`,
22
+ ` ${ansis.cyan("srpllm init")} \u5B89\u88C5 CLI \u5E76\u914D\u7F6E\u4E2D\u8F6C\u7AD9`,
23
+ ` ${ansis.cyan("srpllm uninstall")} \u6E05\u9664\u4E2D\u8F6C\u7AD9\u914D\u7F6E`,
24
+ "",
25
+ ansis.gray(" \u9009\u9879"),
26
+ ` ${ansis.green("--code-type, -T")} <type> \u6307\u5B9A\u5DE5\u5177 (claude-code/codex, cc/cx)`,
27
+ ` ${ansis.green("--base-url, -u")} <url> \u4E2D\u8F6C\u7AD9 base_url`,
28
+ ` ${ansis.green("--token, -k")} <token> api_token`,
29
+ ` ${ansis.green("--model, -m")} <model> \u4E3B\u6A21\u578B (ANTHROPIC_MODEL)`,
30
+ ` ${ansis.green("--opus-model, -O")} <model> Opus \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_OPUS_MODEL)`,
31
+ ` ${ansis.green("--sonnet-model, -S")} <model> Sonnet \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_SONNET_MODEL)`,
32
+ ` ${ansis.green("--haiku-model, -H")} <model> Haiku \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_HAIKU_MODEL)`,
33
+ ` ${ansis.green("--skip-prompt, -s")} \u975E\u4EA4\u4E92\u6A21\u5F0F`,
34
+ ` ${ansis.green("--help, -h")} \u663E\u793A\u5E2E\u52A9`,
35
+ ` ${ansis.green("--version, -v")} \u663E\u793A\u7248\u672C`,
36
+ "",
37
+ ansis.gray(" \u793A\u4F8B"),
38
+ ` ${ansis.cyan("npx srpllm")}`,
39
+ ` ${ansis.cyan("npx srpllm init -T codex -u https://api.srpllm.com -k sk-xxx -m gpt-5.2")}`
40
+ ].join("\n")
41
+ });
42
+ return sections;
43
+ }
44
+ function setupCommands(cli) {
45
+ cli.command("", "\u4EA4\u4E92\u5F0F\u5F15\u5BFC\u914D\u7F6E\u4E2D\u8F6C\u7AD9\uFF08\u9ED8\u8BA4\uFF09").option("--code-type, -T <type>", "\u5DE5\u5177\u7C7B\u578B (claude-code/codex, cc/cx)").option("--base-url, -u <url>", "\u4E2D\u8F6C\u7AD9 base_url").option("--token, -k <token>", "api_token").option("--model, -m <model>", "\u4E3B\u6A21\u578B (ANTHROPIC_MODEL)").option("--opus-model, -O <model>", "Opus \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_OPUS_MODEL)").option("--sonnet-model, -S <model>", "Sonnet \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_SONNET_MODEL)").option("--haiku-model, -H <model>", "Haiku \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_HAIKU_MODEL)").option("--skip-prompt, -s", "\u975E\u4EA4\u4E92\u6A21\u5F0F").action(async (options) => {
46
+ await init(options);
47
+ });
48
+ cli.command("init", "\u5B89\u88C5 CLI \u5E76\u914D\u7F6E\u4E2D\u8F6C\u7AD9").option("--code-type, -T <type>", "\u5DE5\u5177\u7C7B\u578B (claude-code/codex, cc/cx)").option("--base-url, -u <url>", "\u4E2D\u8F6C\u7AD9 base_url").option("--token, -k <token>", "api_token").option("--model, -m <model>", "\u4E3B\u6A21\u578B (ANTHROPIC_MODEL)").option("--opus-model, -O <model>", "Opus \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_OPUS_MODEL)").option("--sonnet-model, -S <model>", "Sonnet \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_SONNET_MODEL)").option("--haiku-model, -H <model>", "Haiku \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_HAIKU_MODEL)").option("--skip-prompt, -s", "\u975E\u4EA4\u4E92\u6A21\u5F0F").action(async (options) => {
49
+ await init(options);
50
+ });
51
+ cli.command("uninstall", "\u6E05\u9664\u4E2D\u8F6C\u7AD9\u914D\u7F6E").option("--code-type, -T <type>", "\u5DE5\u5177\u7C7B\u578B (claude-code/codex, cc/cx)").option("--skip-prompt, -s", "\u975E\u4EA4\u4E92\u6A21\u5F0F").action(async (options) => {
52
+ await uninstall(options);
53
+ });
54
+ cli.help((sections) => customizeHelp(sections));
55
+ cli.version(version);
56
+ }
57
+
58
+ async function main() {
59
+ const cli = cac("srpllm");
60
+ setupCommands(cli);
61
+ cli.parse();
62
+ }
63
+ main().catch(console.error);
@@ -0,0 +1,94 @@
1
+ interface ClaudeSettings {
2
+ env?: Record<string, string | undefined>;
3
+ [key: string]: any;
4
+ }
5
+ interface ClaudeApiConfig {
6
+ baseUrl: string;
7
+ token: string;
8
+ /** 主模型,对应 ANTHROPIC_MODEL */
9
+ model?: string;
10
+ /** Opus 档模型,对应 ANTHROPIC_DEFAULT_OPUS_MODEL */
11
+ opusModel?: string;
12
+ /** Sonnet 档模型,对应 ANTHROPIC_DEFAULT_SONNET_MODEL */
13
+ sonnetModel?: string;
14
+ /** Haiku 档模型,对应 ANTHROPIC_DEFAULT_HAIKU_MODEL */
15
+ haikuModel?: string;
16
+ }
17
+ declare function ensureClaudeDir(): void;
18
+ declare function readClaudeSettings(): ClaudeSettings;
19
+ declare function getExistingClaudeApiConfig(): ClaudeApiConfig | null;
20
+ declare function writeClaudeApiConfig(config: ClaudeApiConfig): void;
21
+ declare function clearClaudeApiConfig(): void;
22
+ declare function displayClaudeConfig(config: ClaudeApiConfig): void;
23
+
24
+ interface CodexApiConfig {
25
+ baseUrl: string;
26
+ token: string;
27
+ model?: string;
28
+ wireApi?: 'responses' | 'chat';
29
+ }
30
+ declare function getExistingCodexConfig(): CodexApiConfig | null;
31
+ declare function writeCodexApiConfig(config: CodexApiConfig): void;
32
+ declare function clearCodexApiConfig(): void;
33
+ declare function displayCodexConfig(config: CodexApiConfig): void;
34
+
35
+ interface InitOptions {
36
+ codeType?: string;
37
+ skipPrompt?: boolean;
38
+ baseUrl?: string;
39
+ token?: string;
40
+ model?: string;
41
+ opusModel?: string;
42
+ sonnetModel?: string;
43
+ haikuModel?: string;
44
+ }
45
+ declare function init(options?: InitOptions): Promise<void>;
46
+
47
+ interface UninstallOptions {
48
+ codeType?: string;
49
+ skipPrompt?: boolean;
50
+ }
51
+ declare function uninstall(options?: UninstallOptions): Promise<void>;
52
+
53
+ declare const CLAUDE_DIR: string;
54
+ declare const CLAUDE_SETTINGS_FILE: string;
55
+ declare const CODEX_DIR: string;
56
+ declare const CODEX_CONFIG_FILE: string;
57
+ declare const CODEX_AUTH_FILE: string;
58
+ declare const CODE_TOOL_TYPES: readonly ["claude-code", "codex"];
59
+ type CodeToolType = (typeof CODE_TOOL_TYPES)[number];
60
+ declare const DEFAULT_CODE_TOOL_TYPE: CodeToolType;
61
+ declare const CODE_TOOL_ALIASES: Record<string, CodeToolType>;
62
+ declare const CODE_TOOL_LABELS: Record<CodeToolType, string>;
63
+ declare function resolveCodeToolType(value: unknown): CodeToolType;
64
+ declare const RELAY_PROVIDER_ID = "srpllm";
65
+
66
+ type InstallMethod = 'npm' | 'homebrew' | 'curl' | 'powershell';
67
+ declare function isToolInstalled(tool: CodeToolType): Promise<boolean>;
68
+ declare function detectInstalledVersion(tool: CodeToolType): Promise<string | null>;
69
+ declare function selectInstallMethod(tool: CodeToolType): Promise<InstallMethod>;
70
+ declare function installTool(tool: CodeToolType, skipMethodSelection?: boolean): Promise<void>;
71
+ declare function uninstallTool(tool: CodeToolType): Promise<boolean>;
72
+
73
+ interface RemoteModel {
74
+ id: string;
75
+ ownedBy?: string;
76
+ }
77
+ /**
78
+ * 从中转站后端 litellm 拉取可用模型列表
79
+ * 标准接口:GET {baseUrl}/v1/models Authorization: Bearer {token}
80
+ * 返回 { data: [{ id, owned_by, ... }] }
81
+ */
82
+ declare function fetchModels(baseUrl: string, token: string): Promise<RemoteModel[]>;
83
+ declare function buildModelsChoices(models: RemoteModel[]): Array<{
84
+ name: string;
85
+ value: string;
86
+ }>;
87
+
88
+ type Platform = 'windows' | 'macos' | 'linux';
89
+ declare function getPlatform(): Platform;
90
+ declare function isWindows(): boolean;
91
+ declare function commandExists(command: string): Promise<boolean>;
92
+
93
+ export { CLAUDE_DIR, CLAUDE_SETTINGS_FILE, CODEX_AUTH_FILE, CODEX_CONFIG_FILE, CODEX_DIR, CODE_TOOL_ALIASES, CODE_TOOL_LABELS, CODE_TOOL_TYPES, DEFAULT_CODE_TOOL_TYPE, RELAY_PROVIDER_ID, buildModelsChoices, clearClaudeApiConfig, clearCodexApiConfig, commandExists, detectInstalledVersion, displayClaudeConfig, displayCodexConfig, ensureClaudeDir, fetchModels, getExistingClaudeApiConfig, getExistingCodexConfig, getPlatform, init, installTool, isToolInstalled, isWindows, readClaudeSettings, resolveCodeToolType, selectInstallMethod, uninstall, uninstallTool, writeClaudeApiConfig, writeCodexApiConfig };
94
+ export type { ClaudeApiConfig, CodeToolType, CodexApiConfig, InstallMethod, RemoteModel };
@@ -0,0 +1,94 @@
1
+ interface ClaudeSettings {
2
+ env?: Record<string, string | undefined>;
3
+ [key: string]: any;
4
+ }
5
+ interface ClaudeApiConfig {
6
+ baseUrl: string;
7
+ token: string;
8
+ /** 主模型,对应 ANTHROPIC_MODEL */
9
+ model?: string;
10
+ /** Opus 档模型,对应 ANTHROPIC_DEFAULT_OPUS_MODEL */
11
+ opusModel?: string;
12
+ /** Sonnet 档模型,对应 ANTHROPIC_DEFAULT_SONNET_MODEL */
13
+ sonnetModel?: string;
14
+ /** Haiku 档模型,对应 ANTHROPIC_DEFAULT_HAIKU_MODEL */
15
+ haikuModel?: string;
16
+ }
17
+ declare function ensureClaudeDir(): void;
18
+ declare function readClaudeSettings(): ClaudeSettings;
19
+ declare function getExistingClaudeApiConfig(): ClaudeApiConfig | null;
20
+ declare function writeClaudeApiConfig(config: ClaudeApiConfig): void;
21
+ declare function clearClaudeApiConfig(): void;
22
+ declare function displayClaudeConfig(config: ClaudeApiConfig): void;
23
+
24
+ interface CodexApiConfig {
25
+ baseUrl: string;
26
+ token: string;
27
+ model?: string;
28
+ wireApi?: 'responses' | 'chat';
29
+ }
30
+ declare function getExistingCodexConfig(): CodexApiConfig | null;
31
+ declare function writeCodexApiConfig(config: CodexApiConfig): void;
32
+ declare function clearCodexApiConfig(): void;
33
+ declare function displayCodexConfig(config: CodexApiConfig): void;
34
+
35
+ interface InitOptions {
36
+ codeType?: string;
37
+ skipPrompt?: boolean;
38
+ baseUrl?: string;
39
+ token?: string;
40
+ model?: string;
41
+ opusModel?: string;
42
+ sonnetModel?: string;
43
+ haikuModel?: string;
44
+ }
45
+ declare function init(options?: InitOptions): Promise<void>;
46
+
47
+ interface UninstallOptions {
48
+ codeType?: string;
49
+ skipPrompt?: boolean;
50
+ }
51
+ declare function uninstall(options?: UninstallOptions): Promise<void>;
52
+
53
+ declare const CLAUDE_DIR: string;
54
+ declare const CLAUDE_SETTINGS_FILE: string;
55
+ declare const CODEX_DIR: string;
56
+ declare const CODEX_CONFIG_FILE: string;
57
+ declare const CODEX_AUTH_FILE: string;
58
+ declare const CODE_TOOL_TYPES: readonly ["claude-code", "codex"];
59
+ type CodeToolType = (typeof CODE_TOOL_TYPES)[number];
60
+ declare const DEFAULT_CODE_TOOL_TYPE: CodeToolType;
61
+ declare const CODE_TOOL_ALIASES: Record<string, CodeToolType>;
62
+ declare const CODE_TOOL_LABELS: Record<CodeToolType, string>;
63
+ declare function resolveCodeToolType(value: unknown): CodeToolType;
64
+ declare const RELAY_PROVIDER_ID = "srpllm";
65
+
66
+ type InstallMethod = 'npm' | 'homebrew' | 'curl' | 'powershell';
67
+ declare function isToolInstalled(tool: CodeToolType): Promise<boolean>;
68
+ declare function detectInstalledVersion(tool: CodeToolType): Promise<string | null>;
69
+ declare function selectInstallMethod(tool: CodeToolType): Promise<InstallMethod>;
70
+ declare function installTool(tool: CodeToolType, skipMethodSelection?: boolean): Promise<void>;
71
+ declare function uninstallTool(tool: CodeToolType): Promise<boolean>;
72
+
73
+ interface RemoteModel {
74
+ id: string;
75
+ ownedBy?: string;
76
+ }
77
+ /**
78
+ * 从中转站后端 litellm 拉取可用模型列表
79
+ * 标准接口:GET {baseUrl}/v1/models Authorization: Bearer {token}
80
+ * 返回 { data: [{ id, owned_by, ... }] }
81
+ */
82
+ declare function fetchModels(baseUrl: string, token: string): Promise<RemoteModel[]>;
83
+ declare function buildModelsChoices(models: RemoteModel[]): Array<{
84
+ name: string;
85
+ value: string;
86
+ }>;
87
+
88
+ type Platform = 'windows' | 'macos' | 'linux';
89
+ declare function getPlatform(): Platform;
90
+ declare function isWindows(): boolean;
91
+ declare function commandExists(command: string): Promise<boolean>;
92
+
93
+ export { CLAUDE_DIR, CLAUDE_SETTINGS_FILE, CODEX_AUTH_FILE, CODEX_CONFIG_FILE, CODEX_DIR, CODE_TOOL_ALIASES, CODE_TOOL_LABELS, CODE_TOOL_TYPES, DEFAULT_CODE_TOOL_TYPE, RELAY_PROVIDER_ID, buildModelsChoices, clearClaudeApiConfig, clearCodexApiConfig, commandExists, detectInstalledVersion, displayClaudeConfig, displayCodexConfig, ensureClaudeDir, fetchModels, getExistingClaudeApiConfig, getExistingCodexConfig, getPlatform, init, installTool, isToolInstalled, isWindows, readClaudeSettings, resolveCodeToolType, selectInstallMethod, uninstall, uninstallTool, writeClaudeApiConfig, writeCodexApiConfig };
94
+ export type { ClaudeApiConfig, CodeToolType, CodexApiConfig, InstallMethod, RemoteModel };
package/dist/index.mjs ADDED
@@ -0,0 +1,9 @@
1
+ export { C as CLAUDE_DIR, b as CLAUDE_SETTINGS_FILE, f as CODEX_AUTH_FILE, e as CODEX_CONFIG_FILE, d as CODEX_DIR, j as CODE_TOOL_ALIASES, k as CODE_TOOL_LABELS, h as CODE_TOOL_TYPES, D as DEFAULT_CODE_TOOL_TYPE, R as RELAY_PROVIDER_ID, F as buildModelsChoices, o as clearClaudeApiConfig, t as clearCodexApiConfig, c as commandExists, y as detectInstalledVersion, p as displayClaudeConfig, v as displayCodexConfig, l as ensureClaudeDir, E as fetchModels, n as getExistingClaudeApiConfig, q as getExistingCodexConfig, g as getPlatform, i as init, A as installTool, x as isToolInstalled, a as isWindows, m as readClaudeSettings, r as resolveCodeToolType, z as selectInstallMethod, u as uninstall, B as uninstallTool, w as writeClaudeApiConfig, s as writeCodexApiConfig } from './shared/srpllm.BB4ML_UM.mjs';
2
+ import 'node:process';
3
+ import 'ansis';
4
+ import 'inquirer';
5
+ import 'ora';
6
+ import 'node:os';
7
+ import 'pathe';
8
+ import 'node:fs';
9
+ import 'tinyexec';
@@ -0,0 +1,747 @@
1
+ import process from 'node:process';
2
+ import ansis from 'ansis';
3
+ import inquirer from 'inquirer';
4
+ import ora from 'ora';
5
+ import { homedir, platform } from 'node:os';
6
+ import { join, dirname } from 'pathe';
7
+ import { writeFileSync, existsSync, readFileSync, mkdirSync } from 'node:fs';
8
+ import { exec } from 'tinyexec';
9
+
10
+ const CLAUDE_DIR = join(homedir(), ".claude");
11
+ const CLAUDE_SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
12
+ const CODEX_DIR = join(homedir(), ".codex");
13
+ const CODEX_CONFIG_FILE = join(CODEX_DIR, "config.toml");
14
+ const CODEX_AUTH_FILE = join(CODEX_DIR, "auth.json");
15
+ const CODE_TOOL_TYPES = ["claude-code", "codex"];
16
+ const DEFAULT_CODE_TOOL_TYPE = "claude-code";
17
+ const CODE_TOOL_ALIASES = {
18
+ cc: "claude-code",
19
+ cx: "codex"
20
+ };
21
+ const CODE_TOOL_LABELS = {
22
+ "claude-code": "Claude Code",
23
+ "codex": "Codex"
24
+ };
25
+ function resolveCodeToolType(value) {
26
+ if (value && CODE_TOOL_TYPES.includes(value)) {
27
+ return value;
28
+ }
29
+ if (typeof value === "string" && value in CODE_TOOL_ALIASES) {
30
+ return CODE_TOOL_ALIASES[value];
31
+ }
32
+ return DEFAULT_CODE_TOOL_TYPE;
33
+ }
34
+ const RELAY_PROVIDER_ID = "srpllm";
35
+
36
+ function exists(path) {
37
+ return existsSync(path);
38
+ }
39
+ function ensureDir(path) {
40
+ if (!existsSync(path)) {
41
+ mkdirSync(path, { recursive: true });
42
+ }
43
+ }
44
+ function ensureFileDir(filePath) {
45
+ ensureDir(dirname(filePath));
46
+ }
47
+ function writeFile(path, content) {
48
+ ensureFileDir(path);
49
+ writeFileSync(path, content, "utf-8");
50
+ }
51
+ function readJson(path) {
52
+ if (!existsSync(path))
53
+ return null;
54
+ try {
55
+ return JSON.parse(readFileSync(path, "utf-8"));
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+ function writeJson(path, data) {
61
+ ensureFileDir(path);
62
+ writeFileSync(path, `${JSON.stringify(data, null, 2)}
63
+ `, "utf-8");
64
+ }
65
+
66
+ const FIXED_ENV_DEFAULTS = {
67
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: "1000000",
68
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
69
+ };
70
+ function ensureClaudeDir() {
71
+ ensureDir(CLAUDE_DIR);
72
+ }
73
+ function readClaudeSettings() {
74
+ return readJson(CLAUDE_SETTINGS_FILE) || {};
75
+ }
76
+ function getExistingClaudeApiConfig() {
77
+ const settings = readClaudeSettings();
78
+ const env = settings.env;
79
+ if (!env)
80
+ return null;
81
+ const url = env.ANTHROPIC_BASE_URL;
82
+ const token = env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY;
83
+ if (!url && !token)
84
+ return null;
85
+ return {
86
+ baseUrl: url || "",
87
+ token: token || "",
88
+ model: env.ANTHROPIC_MODEL,
89
+ opusModel: env.ANTHROPIC_DEFAULT_OPUS_MODEL,
90
+ sonnetModel: env.ANTHROPIC_DEFAULT_SONNET_MODEL,
91
+ haikuModel: env.ANTHROPIC_DEFAULT_HAIKU_MODEL
92
+ };
93
+ }
94
+ function writeClaudeApiConfig(config) {
95
+ ensureClaudeDir();
96
+ const settings = readClaudeSettings();
97
+ settings.env = settings.env || {};
98
+ settings.env.ANTHROPIC_BASE_URL = config.baseUrl;
99
+ settings.env.ANTHROPIC_AUTH_TOKEN = config.token;
100
+ delete settings.env.ANTHROPIC_API_KEY;
101
+ setOrDelete(settings.env, "ANTHROPIC_MODEL", config.model);
102
+ setOrDelete(settings.env, "ANTHROPIC_DEFAULT_OPUS_MODEL", config.opusModel);
103
+ setOrDelete(settings.env, "ANTHROPIC_DEFAULT_SONNET_MODEL", config.sonnetModel);
104
+ setOrDelete(settings.env, "ANTHROPIC_DEFAULT_HAIKU_MODEL", config.haikuModel);
105
+ for (const [key, value] of Object.entries(FIXED_ENV_DEFAULTS)) {
106
+ settings.env[key] = value;
107
+ }
108
+ writeJson(CLAUDE_SETTINGS_FILE, settings);
109
+ }
110
+ function setOrDelete(env, key, value) {
111
+ if (value && value.trim()) {
112
+ env[key] = value.trim();
113
+ } else {
114
+ delete env[key];
115
+ }
116
+ }
117
+ function clearClaudeApiConfig() {
118
+ const settings = readClaudeSettings();
119
+ if (settings.env) {
120
+ delete settings.env.ANTHROPIC_BASE_URL;
121
+ delete settings.env.ANTHROPIC_AUTH_TOKEN;
122
+ delete settings.env.ANTHROPIC_API_KEY;
123
+ delete settings.env.ANTHROPIC_MODEL;
124
+ delete settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
125
+ delete settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL;
126
+ delete settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
127
+ delete settings.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
128
+ delete settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC;
129
+ }
130
+ writeJson(CLAUDE_SETTINGS_FILE, settings);
131
+ }
132
+ function displayClaudeConfig(config) {
133
+ console.log(ansis.gray(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CLAUDE_SETTINGS_FILE}`));
134
+ console.log(ansis.gray(` base_url\uFF1A${config.baseUrl}`));
135
+ console.log(ansis.gray(` token\uFF1A${maskToken$2(config.token)}`));
136
+ if (config.model)
137
+ console.log(ansis.gray(` \u4E3B\u6A21\u578B (ANTHROPIC_MODEL)\uFF1A${config.model}`));
138
+ if (config.opusModel)
139
+ console.log(ansis.gray(` Opus (ANTHROPIC_DEFAULT_OPUS_MODEL)\uFF1A${config.opusModel}`));
140
+ if (config.sonnetModel)
141
+ console.log(ansis.gray(` Sonnet (ANTHROPIC_DEFAULT_SONNET_MODEL)\uFF1A${config.sonnetModel}`));
142
+ if (config.haikuModel)
143
+ console.log(ansis.gray(` Haiku (ANTHROPIC_DEFAULT_HAIKU_MODEL)\uFF1A${config.haikuModel}`));
144
+ }
145
+ function maskToken$2(token) {
146
+ if (!token)
147
+ return "N/A";
148
+ if (token.length <= 8)
149
+ return "***";
150
+ return `${token.slice(0, 4)}***${token.slice(-4)}`;
151
+ }
152
+
153
+ function readFileRaw(path) {
154
+ return readFileSync(path, "utf-8");
155
+ }
156
+ const ENV_KEY = `${RELAY_PROVIDER_ID.toUpperCase()}_API_KEY`;
157
+ const SRPLLM_SECTION = `model_providers.${RELAY_PROVIDER_ID}`;
158
+ const SRPLLM_HEADER = "# --- SrP-LLM \u4E2D\u8F6C\u7AD9\u914D\u7F6E ---";
159
+ function timestamp() {
160
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[:T]/g, "-").slice(0, 19);
161
+ }
162
+ function backupCodexConfig() {
163
+ if (!exists(CODEX_CONFIG_FILE))
164
+ return null;
165
+ const backupPath = `${CODEX_CONFIG_FILE}.backup_${timestamp()}`;
166
+ try {
167
+ writeFile(backupPath, readFileRaw(CODEX_CONFIG_FILE));
168
+ return backupPath;
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
173
+ function renderSrpllmBlock(config) {
174
+ const wireApi = config.wireApi || "responses";
175
+ const lines = [SRPLLM_HEADER];
176
+ if (config.model)
177
+ lines.push(`model = "${config.model}"`);
178
+ lines.push(`model_provider = "${RELAY_PROVIDER_ID}"`);
179
+ lines.push("");
180
+ lines.push(`[${SRPLLM_SECTION}]`);
181
+ lines.push(`name = "SrP-LLM"`);
182
+ lines.push(`base_url = "${config.baseUrl}"`);
183
+ lines.push(`wire_api = "${wireApi}"`);
184
+ lines.push(`temp_env_key = "${ENV_KEY}"`);
185
+ lines.push(`requires_openai_auth = false`);
186
+ lines.push("");
187
+ return lines.join("\n");
188
+ }
189
+ function stripSrpllmManaged(content) {
190
+ const lines = content.split("\n");
191
+ let srpllmStart = -1;
192
+ let srpllmEnd = -1;
193
+ let currentSection = "";
194
+ for (let i = 0; i < lines.length; i++) {
195
+ const trimmed = lines[i].trim();
196
+ const sec = trimmed.match(/^\[([^\]]+)\]/);
197
+ if (sec) {
198
+ currentSection = sec[1];
199
+ if (currentSection === SRPLLM_SECTION && srpllmStart === -1)
200
+ srpllmStart = i;
201
+ else if (srpllmStart !== -1 && srpllmEnd === -1)
202
+ srpllmEnd = i;
203
+ }
204
+ }
205
+ if (srpllmStart !== -1 && srpllmEnd === -1)
206
+ srpllmEnd = lines.length;
207
+ while (srpllmStart > 0) {
208
+ const prev = lines[srpllmStart - 1].trim();
209
+ if (prev === "" || prev === SRPLLM_HEADER || /---\s*SrP-LLM\s*---/i.test(prev) || /---\s*model provider added by ZCF\s*---/i.test(prev))
210
+ srpllmStart--;
211
+ else
212
+ break;
213
+ }
214
+ const preserved = [];
215
+ let inSection = false;
216
+ for (let i = 0; i < lines.length; i++) {
217
+ if (i >= srpllmStart && i < srpllmEnd)
218
+ continue;
219
+ const line = lines[i];
220
+ const trimmed = line.trim();
221
+ const sec = trimmed.match(/^\[([^\]]+)\]/);
222
+ if (sec) {
223
+ inSection = true;
224
+ preserved.push(line);
225
+ continue;
226
+ }
227
+ if (!inSection) {
228
+ if (/^model\s*=/.test(trimmed) || /^model_provider\s*=/.test(trimmed))
229
+ continue;
230
+ if (trimmed === SRPLLM_HEADER || /---\s*SrP-LLM\s*---/i.test(trimmed) || /---\s*model provider added by ZCF\s*---/i.test(trimmed))
231
+ continue;
232
+ }
233
+ preserved.push(line);
234
+ }
235
+ return preserved.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
236
+ }
237
+ function mergeConfigContent(existing, config) {
238
+ const stripped = stripSrpllmManaged(existing);
239
+ const block = renderSrpllmBlock(config);
240
+ const merged = stripped.trim() ? `${block}
241
+ ${stripped}` : `${block}
242
+ `;
243
+ return `${merged.replace(/\n{3,}/g, "\n\n").trimEnd()}
244
+ `;
245
+ }
246
+ function getExistingCodexConfig() {
247
+ if (!exists(CODEX_CONFIG_FILE))
248
+ return null;
249
+ const content = readFileRaw(CODEX_CONFIG_FILE);
250
+ const baseUrl = content.match(new RegExp(`\\[${escapeRegex(SRPLLM_SECTION)}\\][\\s\\S]*?base_url\\s*=\\s*"([^"]+)"`))?.[1];
251
+ const model = content.match(/^model\s*=\s*"([^"]+)"/m)?.[1];
252
+ const auth = readJson(CODEX_AUTH_FILE) || {};
253
+ const token = auth[ENV_KEY] || auth.OPENAI_API_KEY;
254
+ if (!baseUrl && !token)
255
+ return null;
256
+ return { baseUrl: baseUrl || "", token: token || "", model };
257
+ }
258
+ function escapeRegex(s) {
259
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
260
+ }
261
+ function writeCodexApiConfig(config) {
262
+ ensureDir(CODEX_DIR);
263
+ const backup = backupCodexConfig();
264
+ if (backup)
265
+ console.log(ansis.gray(`\u2714 \u5DF2\u5907\u4EFD\u539F\u914D\u7F6E\uFF1A${backup}`));
266
+ const existing = exists(CODEX_CONFIG_FILE) ? readFileRaw(CODEX_CONFIG_FILE) : "";
267
+ writeFile(CODEX_CONFIG_FILE, mergeConfigContent(existing, config));
268
+ const auth = readJson(CODEX_AUTH_FILE) || {};
269
+ auth[ENV_KEY] = config.token;
270
+ auth.OPENAI_API_KEY = config.token;
271
+ writeJson(CODEX_AUTH_FILE, auth);
272
+ }
273
+ function clearCodexApiConfig() {
274
+ if (exists(CODEX_CONFIG_FILE)) {
275
+ const backup = `${CODEX_CONFIG_FILE}.backup_${timestamp()}`;
276
+ try {
277
+ writeFile(backup, readFileRaw(CODEX_CONFIG_FILE));
278
+ console.log(ansis.gray(`\u2714 \u5DF2\u5907\u4EFD\u539F\u914D\u7F6E\uFF1A${backup}`));
279
+ } catch {
280
+ }
281
+ const existing = readFileRaw(CODEX_CONFIG_FILE);
282
+ const cleaned = stripSrpllmManaged(existing);
283
+ writeFile(CODEX_CONFIG_FILE, cleaned ? `${cleaned}
284
+ ` : "# Codex \u914D\u7F6E\n");
285
+ }
286
+ const auth = readJson(CODEX_AUTH_FILE) || {};
287
+ delete auth[ENV_KEY];
288
+ delete auth.OPENAI_API_KEY;
289
+ writeJson(CODEX_AUTH_FILE, auth);
290
+ }
291
+ function displayCodexConfig(config) {
292
+ console.log(ansis.gray(` \u914D\u7F6E\u6587\u4EF6\uFF1A${CODEX_CONFIG_FILE}`));
293
+ console.log(ansis.gray(` \u51ED\u8BC1\u6587\u4EF6\uFF1A${CODEX_AUTH_FILE}`));
294
+ console.log(ansis.gray(` base_url\uFF1A${config.baseUrl}`));
295
+ console.log(ansis.gray(` token\uFF1A${maskToken$1(config.token)}`));
296
+ if (config.model)
297
+ console.log(ansis.gray(` \u6A21\u578B\uFF1A${config.model}`));
298
+ }
299
+ function maskToken$1(token) {
300
+ if (!token)
301
+ return "N/A";
302
+ if (token.length <= 8)
303
+ return "***";
304
+ return `${token.slice(0, 4)}***${token.slice(-4)}`;
305
+ }
306
+
307
+ function getPlatform() {
308
+ const p = platform();
309
+ if (p === "win32")
310
+ return "windows";
311
+ if (p === "darwin")
312
+ return "macos";
313
+ return "linux";
314
+ }
315
+ function isWindows() {
316
+ return getPlatform() === "windows";
317
+ }
318
+ function isTermux() {
319
+ return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
320
+ }
321
+ async function commandExists(command) {
322
+ const cmd = isWindows() ? "where" : "which";
323
+ try {
324
+ const result = await exec(cmd, [command]);
325
+ return result.exitCode === 0;
326
+ } catch {
327
+ return false;
328
+ }
329
+ }
330
+ function wrapCommandWithSudo(command, args) {
331
+ if (isWindows() || isTermux())
332
+ return { command, args, usedSudo: false };
333
+ const isGlobalNpm = args.includes("-g");
334
+ if (isGlobalNpm && needsSudo()) {
335
+ return { command: "sudo", args: [command, ...args], usedSudo: true };
336
+ }
337
+ return { command, args, usedSudo: false };
338
+ }
339
+ function needsSudo() {
340
+ try {
341
+ return !existsSync("/.dockerenv") && process.getuid?.() !== 0 && !process.env.SUDO_USER;
342
+ } catch {
343
+ return false;
344
+ }
345
+ }
346
+
347
+ const TOOL_PACKAGES = {
348
+ "claude-code": { command: "claude", npmPackage: "@anthropic-ai/claude-code", displayName: "Claude Code" },
349
+ "codex": { command: "codex", npmPackage: "@openai/codex", displayName: "Codex" }
350
+ };
351
+ async function isToolInstalled(tool) {
352
+ return await commandExists(TOOL_PACKAGES[tool].command);
353
+ }
354
+ async function detectInstalledVersion(tool) {
355
+ try {
356
+ const result = await exec(TOOL_PACKAGES[tool].command, ["--version"]);
357
+ if (result.exitCode === 0 && result.stdout) {
358
+ const match = result.stdout.match(/(\d+\.\d+\.\d+)/);
359
+ return match ? match[1] : result.stdout.trim();
360
+ }
361
+ } catch {
362
+ }
363
+ return null;
364
+ }
365
+ function getAvailableMethods(tool) {
366
+ const plat = getPlatform();
367
+ const methods = ["npm"];
368
+ if (plat === "macos" || plat === "linux")
369
+ methods.push("homebrew");
370
+ if (tool === "claude-code") {
371
+ if (plat !== "windows" || isTermux())
372
+ methods.push("curl");
373
+ if (isWindows())
374
+ methods.push("powershell");
375
+ }
376
+ return methods;
377
+ }
378
+ const METHOD_LABELS = {
379
+ npm: "npm \u5168\u5C40\u5B89\u88C5",
380
+ homebrew: "Homebrew \u5B89\u88C5",
381
+ curl: "curl \u811A\u672C\u5B89\u88C5",
382
+ powershell: "PowerShell \u811A\u672C\u5B89\u88C5"
383
+ };
384
+ async function selectInstallMethod(tool) {
385
+ const methods = getAvailableMethods(tool);
386
+ const { method } = await inquirer.prompt({
387
+ type: "list",
388
+ name: "method",
389
+ message: `\u8BF7\u9009\u62E9 ${TOOL_PACKAGES[tool].displayName} \u7684\u5B89\u88C5\u65B9\u5F0F\uFF1A`,
390
+ choices: methods.map((m, i) => ({
391
+ name: i === 0 ? `${METHOD_LABELS[m]} ${ansis.green("[\u63A8\u8350]")}` : METHOD_LABELS[m],
392
+ value: m
393
+ }))
394
+ });
395
+ return method;
396
+ }
397
+ async function runInstall(tool, method) {
398
+ const pkg = TOOL_PACKAGES[tool];
399
+ switch (method) {
400
+ case "npm": {
401
+ const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", pkg.npmPackage, "--force"]);
402
+ if (usedSudo)
403
+ console.log(ansis.yellow("\u2139 \u4F7F\u7528 sudo \u6267\u884C\u5168\u5C40\u5B89\u88C5"));
404
+ const result = await exec(command, args);
405
+ if (result.exitCode !== 0)
406
+ throw new Error(`npm \u5B89\u88C5\u5931\u8D25\uFF1A${result.stderr || result.stdout}`);
407
+ break;
408
+ }
409
+ case "homebrew": {
410
+ const brewArgs = tool === "claude-code" ? ["install", "--cask", "claude-code"] : ["install", "--cask", "codex"];
411
+ const brewResult = await exec("brew", brewArgs);
412
+ if (brewResult.exitCode !== 0)
413
+ throw new Error(`Homebrew \u5B89\u88C5\u5931\u8D25\uFF1A${brewResult.stderr || brewResult.stdout}`);
414
+ break;
415
+ }
416
+ case "curl": {
417
+ const curlResult = await exec("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"]);
418
+ if (curlResult.exitCode !== 0)
419
+ throw new Error(`curl \u811A\u672C\u5B89\u88C5\u5931\u8D25\uFF1A${curlResult.stderr || curlResult.stdout}`);
420
+ break;
421
+ }
422
+ case "powershell": {
423
+ const psResult = await exec("powershell", ["-Command", "irm https://claude.ai/install.ps1 | iex"]);
424
+ if (psResult.exitCode !== 0)
425
+ throw new Error(`PowerShell \u5B89\u88C5\u5931\u8D25\uFF1A${psResult.stderr || psResult.stdout}`);
426
+ break;
427
+ }
428
+ }
429
+ }
430
+ async function installTool(tool, skipMethodSelection = false) {
431
+ const pkg = TOOL_PACKAGES[tool];
432
+ if (await isToolInstalled(tool)) {
433
+ const ver = await detectInstalledVersion(tool);
434
+ console.log(ansis.green(`\u2714 ${pkg.displayName} \u5DF2\u5B89\u88C5`));
435
+ if (ver)
436
+ console.log(ansis.gray(` \u5F53\u524D\u7248\u672C\uFF1A${ver}`));
437
+ return;
438
+ }
439
+ const method = skipMethodSelection ? "npm" : await selectInstallMethod(tool);
440
+ const spinner = ora(`\u6B63\u5728\u901A\u8FC7 ${METHOD_LABELS[method]} \u5B89\u88C5 ${pkg.displayName}...`).start();
441
+ try {
442
+ await runInstall(tool, method);
443
+ spinner.succeed(`\u2714 ${pkg.displayName} \u5B89\u88C5\u5B8C\u6210`);
444
+ } catch (error) {
445
+ spinner.fail(`\u2716 ${pkg.displayName} \u5B89\u88C5\u5931\u8D25`);
446
+ if (error instanceof Error)
447
+ console.error(ansis.gray(error.message));
448
+ throw error;
449
+ }
450
+ }
451
+ async function uninstallTool(tool) {
452
+ const pkg = TOOL_PACKAGES[tool];
453
+ if (!await isToolInstalled(tool))
454
+ return true;
455
+ const spinner = ora(`\u6B63\u5728\u5378\u8F7D ${pkg.displayName} CLI...`).start();
456
+ try {
457
+ if (tool === "claude-code") {
458
+ try {
459
+ await exec("brew", ["uninstall", "--cask", "claude-code"]);
460
+ } catch {
461
+ const { command, args } = wrapCommandWithSudo("npm", ["uninstall", "-g", pkg.npmPackage]);
462
+ await exec(command, args);
463
+ }
464
+ } else {
465
+ try {
466
+ await exec("brew", ["uninstall", "--cask", "codex"]);
467
+ } catch {
468
+ const { command, args } = wrapCommandWithSudo("npm", ["uninstall", "-g", pkg.npmPackage]);
469
+ await exec(command, args);
470
+ }
471
+ }
472
+ spinner.succeed(`\u2714 ${pkg.displayName} CLI \u5DF2\u5378\u8F7D`);
473
+ return true;
474
+ } catch (error) {
475
+ spinner.fail(`\u2716 ${pkg.displayName} CLI \u5378\u8F7D\u5931\u8D25`);
476
+ if (error instanceof Error)
477
+ console.error(ansis.gray(error.message));
478
+ return false;
479
+ }
480
+ }
481
+
482
+ async function fetchModels(baseUrl, token) {
483
+ const url = `${baseUrl.replace(/\/$/, "")}/v1/models`;
484
+ const spinner = ora(`\u6B63\u5728\u4ECE ${url} \u62C9\u53D6\u6A21\u578B\u5217\u8868...`).start();
485
+ try {
486
+ const controller = new AbortController();
487
+ const timeout = setTimeout(() => controller.abort(), 15e3);
488
+ const response = await fetch(url, {
489
+ headers: { Authorization: `Bearer ${token}` },
490
+ signal: controller.signal
491
+ });
492
+ clearTimeout(timeout);
493
+ if (!response.ok) {
494
+ spinner.fail(`\u2716 \u62C9\u53D6\u6A21\u578B\u5217\u8868\u5931\u8D25\uFF1AHTTP ${response.status} ${response.statusText}`);
495
+ throw new Error(`\u62C9\u53D6\u6A21\u578B\u5217\u8868\u5931\u8D25\uFF1AHTTP ${response.status}`);
496
+ }
497
+ const json = await response.json();
498
+ const data = Array.isArray(json) ? json : json.data || json.models || [];
499
+ const models = data.map((m) => ({
500
+ id: String(m.id || m.name || m.model).trim(),
501
+ ownedBy: m.owned_by || m.ownedBy
502
+ })).filter((m) => m.id);
503
+ if (models.length === 0) {
504
+ spinner.fail("\u2716 \u4E2D\u8F6C\u7AD9\u8FD4\u56DE\u7684\u6A21\u578B\u5217\u8868\u4E3A\u7A7A");
505
+ throw new Error("\u6A21\u578B\u5217\u8868\u4E3A\u7A7A");
506
+ }
507
+ spinner.succeed(`\u2714 \u5DF2\u83B7\u53D6 ${models.length} \u4E2A\u53EF\u7528\u6A21\u578B`);
508
+ return models;
509
+ } catch (error) {
510
+ spinner.fail(`\u2716 \u62C9\u53D6\u6A21\u578B\u5217\u8868\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
511
+ throw error;
512
+ }
513
+ }
514
+ function buildModelsChoices(models) {
515
+ return models.map((m) => ({
516
+ name: m.ownedBy ? `${m.id} ${ansis.gray(`(${m.ownedBy})`)}` : m.id,
517
+ value: m.id
518
+ }));
519
+ }
520
+
521
+ const version = "1.0.0";
522
+
523
+ function displayBanner(codeTool) {
524
+ const tool = codeTool ? ` ${ansis.gray(`\xB7 ${CODE_TOOL_LABELS[codeTool]}`)}` : "";
525
+ console.log(ansis.cyan.bold(`
526
+ SrP-LLM \u914D\u7F6E\u5DE5\u5177 v${version}${tool}`));
527
+ console.log(ansis.gray(" \u4E2D\u8F6C\u7AD9\u5BA2\u6237\u7AEF\u4E00\u952E\u914D\u7F6E\uFF1A\u5B89\u88C5 CLI \xB7 \u586B\u5199 base_url / api_token \xB7 \u9009\u62E9\u6A21\u578B\n"));
528
+ }
529
+ async function selectCodeTool() {
530
+ const { tool } = await inquirer.prompt({
531
+ type: "list",
532
+ name: "tool",
533
+ message: "\u8BF7\u9009\u62E9\u8981\u914D\u7F6E\u7684\u5BA2\u6237\u7AEF\u5DE5\u5177\uFF1A",
534
+ choices: [
535
+ { name: "Claude Code", value: "claude-code" },
536
+ { name: "Codex", value: "codex" }
537
+ ]
538
+ });
539
+ return tool;
540
+ }
541
+ async function inputBaseUrl() {
542
+ const { url } = await inquirer.prompt({
543
+ type: "input",
544
+ name: "url",
545
+ message: "\u8BF7\u8F93\u5165\u4E2D\u8F6C\u7AD9 base_url\uFF08\u4F8B\u5982 https://api.srpllm.com\uFF09\uFF1A",
546
+ validate: (value) => {
547
+ const v = value.trim();
548
+ if (!v)
549
+ return "base_url \u4E0D\u80FD\u4E3A\u7A7A";
550
+ if (!/^https?:\/\//i.test(v))
551
+ return "\u8BF7\u8F93\u5165\u4EE5 http:// \u6216 https:// \u5F00\u5934\u7684\u5B8C\u6574\u5730\u5740";
552
+ if (/\/$/.test(v))
553
+ return "\u5730\u5740\u672B\u5C3E\u8BF7\u4E0D\u8981\u5E26 /";
554
+ return true;
555
+ }
556
+ });
557
+ return url.trim();
558
+ }
559
+ async function inputApiToken() {
560
+ const { token } = await inquirer.prompt({
561
+ type: "password",
562
+ name: "token",
563
+ message: "\u8BF7\u8F93\u5165 api_token\uFF1A",
564
+ mask: "*",
565
+ validate: (value) => !!value.trim() || "api_token \u4E0D\u80FD\u4E3A\u7A7A"
566
+ });
567
+ return token.trim();
568
+ }
569
+ async function confirm(message, defaultValue = true) {
570
+ const { ok } = await inquirer.prompt({
571
+ type: "confirm",
572
+ name: "ok",
573
+ message,
574
+ default: defaultValue
575
+ });
576
+ return ok;
577
+ }
578
+ function maskToken(token) {
579
+ if (!token)
580
+ return "N/A";
581
+ if (token.length <= 8)
582
+ return "***";
583
+ return `${token.slice(0, 4)}***${token.slice(-4)}`;
584
+ }
585
+
586
+ async function fetchModelList(baseUrl, token) {
587
+ try {
588
+ const models = await fetchModels(baseUrl, token);
589
+ return models.length > 0 ? models : null;
590
+ } catch {
591
+ return null;
592
+ }
593
+ }
594
+ async function promptModelFromList(models, message, preset, skipPrompt) {
595
+ if (preset && preset.trim())
596
+ return preset.trim();
597
+ if (!models) {
598
+ if (skipPrompt)
599
+ return void 0;
600
+ console.log(ansis.yellow("\u2139 \u65E0\u6CD5\u62C9\u53D6\u6A21\u578B\u5217\u8868\uFF0C\u53EF\u624B\u52A8\u8F93\u5165\u6A21\u578B\u540D\uFF08\u7559\u7A7A\u5219\u8DF3\u8FC7\uFF09"));
601
+ const { manual } = await inquirer.prompt({
602
+ type: "input",
603
+ name: "manual",
604
+ message
605
+ });
606
+ return manual.trim() || void 0;
607
+ }
608
+ const { choice } = await inquirer.prompt({
609
+ type: "list",
610
+ name: "choice",
611
+ message,
612
+ choices: [
613
+ ...buildModelsChoices(models),
614
+ { name: "\u6682\u4E0D\u8BBE\u7F6E\uFF08\u8DF3\u8FC7\uFF09", value: "__none__" }
615
+ ]
616
+ });
617
+ return choice === "__none__" ? void 0 : choice;
618
+ }
619
+ async function configureClaudeCode(options, baseUrl, token) {
620
+ const existing = getExistingClaudeApiConfig();
621
+ if (existing && !options.skipPrompt) {
622
+ console.log(ansis.blue("\n\u2139 \u68C0\u6D4B\u5230\u5DF2\u6709 Claude Code \u914D\u7F6E\uFF1A"));
623
+ console.log(ansis.gray(` base_url\uFF1A${existing.baseUrl || "N/A"}`));
624
+ console.log(ansis.gray(` token\uFF1A${maskToken(existing.token)}`));
625
+ if (existing.model)
626
+ console.log(ansis.gray(` \u4E3B\u6A21\u578B\uFF1A${existing.model}`));
627
+ if (existing.opusModel)
628
+ console.log(ansis.gray(` Opus\uFF1A${existing.opusModel}`));
629
+ if (existing.sonnetModel)
630
+ console.log(ansis.gray(` Sonnet\uFF1A${existing.sonnetModel}`));
631
+ if (existing.haikuModel)
632
+ console.log(ansis.gray(` Haiku\uFF1A${existing.haikuModel}`));
633
+ const overwrite = await confirm("\n\u5DF2\u5B58\u5728\u914D\u7F6E\uFF0C\u662F\u5426\u8986\u76D6\u4E3A\u4E2D\u8F6C\u7AD9\u914D\u7F6E\uFF1F", true);
634
+ if (!overwrite) {
635
+ console.log(ansis.yellow("\u2139 \u5DF2\u8DF3\u8FC7 Claude Code \u914D\u7F6E"));
636
+ return;
637
+ }
638
+ }
639
+ const models = await fetchModelList(baseUrl, token);
640
+ const model = await promptModelFromList(models, "\u8BF7\u9009\u62E9\u4E3B\u6A21\u578B (ANTHROPIC_MODEL)\uFF1A", options.model, options.skipPrompt);
641
+ const opusModel = await promptModelFromList(models, "\u8BF7\u9009\u62E9 Opus \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_OPUS_MODEL)\uFF1A", options.opusModel, options.skipPrompt);
642
+ const sonnetModel = await promptModelFromList(models, "\u8BF7\u9009\u62E9 Sonnet \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_SONNET_MODEL)\uFF1A", options.sonnetModel, options.skipPrompt);
643
+ const haikuModel = await promptModelFromList(models, "\u8BF7\u9009\u62E9 Haiku \u6863\u6A21\u578B (ANTHROPIC_DEFAULT_HAIKU_MODEL)\uFF1A", options.haikuModel, options.skipPrompt);
644
+ const config = { baseUrl, token, model, opusModel, sonnetModel, haikuModel };
645
+ writeClaudeApiConfig(config);
646
+ console.log(ansis.green("\n\u2714 Claude Code \u914D\u7F6E\u5B8C\u6210"));
647
+ displayClaudeConfig(config);
648
+ }
649
+ async function configureCodex(options, baseUrl, token) {
650
+ const existing = getExistingCodexConfig();
651
+ if (existing && !options.skipPrompt) {
652
+ console.log(ansis.blue("\n\u2139 \u68C0\u6D4B\u5230\u5DF2\u6709 Codex \u914D\u7F6E\uFF1A"));
653
+ console.log(ansis.gray(` base_url\uFF1A${existing.baseUrl || "N/A"}`));
654
+ console.log(ansis.gray(` token\uFF1A${maskToken(existing.token)}`));
655
+ if (existing.model)
656
+ console.log(ansis.gray(` \u6A21\u578B\uFF1A${existing.model}`));
657
+ const overwrite = await confirm("\n\u5DF2\u5B58\u5728\u914D\u7F6E\uFF0C\u662F\u5426\u8986\u76D6\u4E3A\u4E2D\u8F6C\u7AD9\u914D\u7F6E\uFF1F\uFF08\u539F\u914D\u7F6E\u5C06\u81EA\u52A8\u5907\u4EFD\uFF09", true);
658
+ if (!overwrite) {
659
+ console.log(ansis.yellow("\u2139 \u5DF2\u8DF3\u8FC7 Codex \u914D\u7F6E"));
660
+ return;
661
+ }
662
+ }
663
+ const models = await fetchModelList(baseUrl, token);
664
+ const model = await promptModelFromList(models, "\u8BF7\u9009\u62E9\u9ED8\u8BA4\u4F7F\u7528\u7684\u6A21\u578B\uFF1A", options.model, options.skipPrompt);
665
+ const config = { baseUrl, token, model };
666
+ writeCodexApiConfig(config);
667
+ console.log(ansis.green("\n\u2714 Codex \u914D\u7F6E\u5B8C\u6210"));
668
+ displayCodexConfig(config);
669
+ }
670
+ async function init(options = {}) {
671
+ try {
672
+ const tool = options.codeType ? resolveCodeToolType(options.codeType) : options.skipPrompt ? "claude-code" : await selectCodeTool();
673
+ displayBanner(tool);
674
+ if (options.skipPrompt) {
675
+ await installTool(tool, true);
676
+ } else {
677
+ const installed = await isToolInstalled(tool);
678
+ if (!installed) {
679
+ const shouldInstall = await confirm(`\u672A\u68C0\u6D4B\u5230 ${tool === "claude-code" ? "Claude Code" : "Codex"}\uFF0C\u662F\u5426\u7ACB\u5373\u5B89\u88C5\uFF1F`, true);
680
+ if (shouldInstall) {
681
+ await installTool(tool, false);
682
+ } else {
683
+ console.log(ansis.yellow("\u2139 \u5DF2\u8DF3\u8FC7 CLI \u5B89\u88C5\uFF0C\u4EC5\u5199\u5165\u914D\u7F6E\u6587\u4EF6"));
684
+ }
685
+ } else {
686
+ console.log(ansis.green(`\u2714 ${tool === "claude-code" ? "Claude Code" : "Codex"} \u5DF2\u5B89\u88C5`));
687
+ }
688
+ }
689
+ const baseUrl = options.baseUrl || (options.skipPrompt ? "" : await inputBaseUrl());
690
+ if (!baseUrl) {
691
+ console.error(ansis.red("\u2716 \u7F3A\u5C11 base_url\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u914D\u7F6E"));
692
+ process.exit(1);
693
+ }
694
+ const token = options.token || (options.skipPrompt ? "" : await inputApiToken());
695
+ if (!token) {
696
+ console.error(ansis.red("\u2716 \u7F3A\u5C11 api_token\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u914D\u7F6E"));
697
+ process.exit(1);
698
+ }
699
+ if (tool === "claude-code") {
700
+ await configureClaudeCode(options, baseUrl, token);
701
+ } else {
702
+ await configureCodex(options, baseUrl, token);
703
+ }
704
+ console.log(`
705
+ ${ansis.cyan("\u{1F389} \u914D\u7F6E\u5B8C\u6210\uFF01\u73B0\u5728\u53EF\u4EE5\u76F4\u63A5\u4F7F\u7528\u5BF9\u5E94 CLI \u5DE5\u5177\u8FDE\u63A5 SrP-LLM \u4E2D\u8F6C\u7AD9\u3002")}`);
706
+ } catch (error) {
707
+ if (error instanceof Error) {
708
+ console.error(ansis.red(`
709
+ \u2716 ${error.message}`));
710
+ } else {
711
+ console.error(ansis.red("\n\u2716 \u914D\u7F6E\u8FC7\u7A0B\u4E2D\u53D1\u751F\u672A\u77E5\u9519\u8BEF"));
712
+ }
713
+ process.exit(1);
714
+ }
715
+ }
716
+
717
+ async function uninstall(options = {}) {
718
+ try {
719
+ const tool = options.codeType ? resolveCodeToolType(options.codeType) : options.skipPrompt ? "claude-code" : await selectCodeTool();
720
+ displayBanner(tool);
721
+ const removeCli = options.skipPrompt ? false : await confirm(`\u662F\u5426\u540C\u65F6\u5378\u8F7D ${tool === "claude-code" ? "Claude Code" : "Codex"} CLI\uFF1F`, false);
722
+ console.log(ansis.blue(`
723
+ \u2139 \u6B63\u5728\u6E05\u7406 ${tool === "claude-code" ? "Claude Code" : "Codex"} \u7684\u4E2D\u8F6C\u7AD9\u914D\u7F6E...`));
724
+ if (tool === "claude-code") {
725
+ clearClaudeApiConfig();
726
+ } else {
727
+ clearCodexApiConfig();
728
+ }
729
+ console.log(ansis.green("\u2714 \u4E2D\u8F6C\u7AD9\u914D\u7F6E\u5DF2\u6E05\u9664"));
730
+ if (removeCli) {
731
+ const ok = await uninstallTool(tool);
732
+ if (!ok)
733
+ console.log(ansis.yellow("\u2139 CLI \u5378\u8F7D\u672A\u5B8C\u5168\u6210\u529F\uFF0C\u53EF\u624B\u52A8\u5378\u8F7D"));
734
+ }
735
+ console.log(`
736
+ ${ansis.cyan("\u{1F389} \u6E05\u7406\u5B8C\u6210")}`);
737
+ } catch (error) {
738
+ if (error instanceof Error)
739
+ console.error(ansis.red(`
740
+ \u2716 ${error.message}`));
741
+ else
742
+ console.error(ansis.red("\n\u2716 \u6E05\u7406\u8FC7\u7A0B\u4E2D\u53D1\u751F\u672A\u77E5\u9519\u8BEF"));
743
+ process.exit(1);
744
+ }
745
+ }
746
+
747
+ export { installTool as A, uninstallTool as B, CLAUDE_DIR as C, DEFAULT_CODE_TOOL_TYPE as D, fetchModels as E, buildModelsChoices as F, version as G, RELAY_PROVIDER_ID as R, isWindows as a, CLAUDE_SETTINGS_FILE as b, commandExists as c, CODEX_DIR as d, CODEX_CONFIG_FILE as e, CODEX_AUTH_FILE as f, getPlatform as g, CODE_TOOL_TYPES as h, init as i, CODE_TOOL_ALIASES as j, CODE_TOOL_LABELS as k, ensureClaudeDir as l, readClaudeSettings as m, getExistingClaudeApiConfig as n, clearClaudeApiConfig as o, displayClaudeConfig as p, getExistingCodexConfig as q, resolveCodeToolType as r, writeCodexApiConfig as s, clearCodexApiConfig as t, uninstall as u, displayCodexConfig as v, writeClaudeApiConfig as w, isToolInstalled as x, detectInstalledVersion as y, selectInstallMethod as z };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "srpllm",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "packageManager": "pnpm@10.17.1",
6
+ "description": "SrP-LLM 中转站客户端一键配置工具:安装 Claude Code / Codex 并引导填写 base_url、api_token 与模型",
7
+ "author": {
8
+ "name": "RolinShmily",
9
+ "url": "https://github.com/RolinShmily"
10
+ },
11
+ "license": "MIT",
12
+ "homepage": "https://github.com/RolinShmily/srpllm",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/RolinShmily/srpllm.git"
16
+ },
17
+ "bugs": "https://github.com/RolinShmily/srpllm/issues",
18
+ "keywords": [
19
+ "srpllm",
20
+ "litellm",
21
+ "relay",
22
+ "claude-code",
23
+ "codex",
24
+ "cli",
25
+ "config",
26
+ "proxy"
27
+ ],
28
+ "main": "dist/index.mjs",
29
+ "module": "dist/index.mjs",
30
+ "types": "dist/index.d.mts",
31
+ "bin": {
32
+ "srpllm": "bin/srpllm.mjs"
33
+ },
34
+ "files": [
35
+ "bin",
36
+ "dist"
37
+ ],
38
+ "scripts": {
39
+ "dev": "tsx ./src/cli.ts",
40
+ "build": "unbuild",
41
+ "start": "node bin/srpllm.mjs",
42
+ "typecheck": "tsc --noEmit",
43
+ "prepublishOnly": "pnpm build",
44
+ "lint": "eslint",
45
+ "lint:fix": "eslint --fix"
46
+ },
47
+ "dependencies": {
48
+ "ansis": "catalog:cli",
49
+ "cac": "catalog:cli",
50
+ "inquirer": "catalog:cli",
51
+ "ora": "catalog:cli",
52
+ "pathe": "catalog:runtime",
53
+ "tinyexec": "catalog:runtime"
54
+ },
55
+ "devDependencies": {
56
+ "@antfu/eslint-config": "catalog:build",
57
+ "@types/inquirer": "catalog:types",
58
+ "@types/node": "catalog:types",
59
+ "eslint": "catalog:build",
60
+ "eslint-plugin-format": "catalog:build",
61
+ "tsx": "catalog:build",
62
+ "typescript": "catalog:build",
63
+ "unbuild": "catalog:build"
64
+ }
65
+ }