@done-coding/cli-cc-switch 0.0.1-alpha.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/README.md +61 -0
- package/es/cli.mjs +3 -0
- package/es/index.mjs +14 -0
- package/es/main-DPg5hFgO.js +337 -0
- package/package.json +62 -0
- package/types/cli.d.ts +1 -0
- package/types/index.d.ts +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @done-coding/cli-cc-switch
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
模型路由
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
## 使用
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @done-coding/cli-cc-switch
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
安装后提供两个命令入口(指向同一 `es/cli.mjs`):
|
|
14
|
+
|
|
15
|
+
- `dc-cc-switch` — 正式命令名
|
|
16
|
+
- `cc-router` — 兼容入口(迁移自 `@ccsuite/cli-router` 保留)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
dc-cc-switch [command] [options] # 正式
|
|
20
|
+
cc-router [command] [options] # 兼容
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
除 `--meta-*` 外的所有参数**原样透传**给 claude。
|
|
24
|
+
|
|
25
|
+
## meta 选项(cc-switch 自身命令面,不透传)
|
|
26
|
+
|
|
27
|
+
| 选项 | 行为 |
|
|
28
|
+
| ---- | ---- |
|
|
29
|
+
| `--meta-profile=<name>` | 显式指定 profile 启动 |
|
|
30
|
+
| `--meta-pick` | 终端交互选择 profile 启动(需 TTY) |
|
|
31
|
+
| `--meta-help` | 显示自身帮助 |
|
|
32
|
+
| `--meta-version` | 显示自身版本 |
|
|
33
|
+
|
|
34
|
+
优先级:`--meta-help` > `--meta-version` > `--meta-pick` > `--meta-profile=`。
|
|
35
|
+
|
|
36
|
+
<!-- repo-map:start -->
|
|
37
|
+
|
|
38
|
+
## 目录导航(自动维护,勿手改段内 — 由项目初始化脚本重跑刷新)
|
|
39
|
+
|
|
40
|
+
- `.done-coding/` — 注入配置
|
|
41
|
+
- `src/` — 源码
|
|
42
|
+
- `__tests__/` — 单元测试
|
|
43
|
+
- `config.test.ts`
|
|
44
|
+
- `deepseek-template.test.ts`
|
|
45
|
+
- `env-guard.test.ts`
|
|
46
|
+
- `parse-argv.test.ts`
|
|
47
|
+
- `prompt-interactive.test.ts`
|
|
48
|
+
- `prompt.test.ts`
|
|
49
|
+
- `run-router.test.ts`
|
|
50
|
+
- `select-profile.test.ts`
|
|
51
|
+
- `handlers/` — 子命令处理器
|
|
52
|
+
- `types/` — 类型定义
|
|
53
|
+
- `utils/` — 工具函数
|
|
54
|
+
- `cli.ts` — CLI 入口
|
|
55
|
+
- `index.ts` — 库出口
|
|
56
|
+
- `main.ts` — 路由主流程
|
|
57
|
+
|
|
58
|
+
> 用途:供协作者/AI 按图定位,免遍历目录树。结构变更后重跑初始化刷新。
|
|
59
|
+
> (模块根的本段展开该模块**内部**结构;深层大目录 ≥8 项或继续分叉则嵌套列出,叶子小目录不展。)
|
|
60
|
+
|
|
61
|
+
<!-- repo-map:end -->
|
package/es/cli.mjs
ADDED
package/es/index.mjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { D as e, P as E, b as i, c as n, f as r, h as l, i as o, m as c, p as d, s as m } from "./main-DPg5hFgO.js";
|
|
3
|
+
export {
|
|
4
|
+
e as DEEPSEEK_TEMPLATE,
|
|
5
|
+
E as PROFILE_PATH,
|
|
6
|
+
i as buildChildEnv,
|
|
7
|
+
n as createAsSubcommand,
|
|
8
|
+
r as findEmptyKeys,
|
|
9
|
+
l as hasModelEnvConflict,
|
|
10
|
+
o as isSecretKey,
|
|
11
|
+
c as maskValue,
|
|
12
|
+
d as parseArgv,
|
|
13
|
+
m as selectProfile
|
|
14
|
+
};
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn as j } from "node:child_process";
|
|
3
|
+
import C from "node:os";
|
|
4
|
+
import _ from "node:path";
|
|
5
|
+
import { resolveHandlerContext as F, xPrompts as H } from "@done-coding/cli-utils";
|
|
6
|
+
import p from "node:fs";
|
|
7
|
+
import U from "node:readline";
|
|
8
|
+
const n = _.join(
|
|
9
|
+
C.homedir(),
|
|
10
|
+
".done-coding",
|
|
11
|
+
"cc-switch",
|
|
12
|
+
"profile.json"
|
|
13
|
+
), d = "--meta-profile=", h = "--meta-pick", w = "--meta-help", y = "--meta-version", P = {
|
|
14
|
+
run: 0,
|
|
15
|
+
profile: 1,
|
|
16
|
+
pick: 2,
|
|
17
|
+
version: 3,
|
|
18
|
+
help: 4
|
|
19
|
+
}, m = (e, t) => P[t] > P[e] ? t : e, G = (e) => e.startsWith("--meta-") && e !== h && e !== w && e !== y && !e.startsWith(d), K = () => {
|
|
20
|
+
process.stdout.write(
|
|
21
|
+
[
|
|
22
|
+
"dc-cc-switch(cc-router)— claude-code 模型路由透传",
|
|
23
|
+
"",
|
|
24
|
+
"用法:",
|
|
25
|
+
" dc-cc-switch [meta 选项] <claude 参数...>",
|
|
26
|
+
"",
|
|
27
|
+
"meta 选项(cc-switch 自身命令面,不透传给 claude):",
|
|
28
|
+
` ${d}<name> 显式指定 profile 启动`,
|
|
29
|
+
` ${h} 终端交互选择 profile 启动`,
|
|
30
|
+
` ${w} 显示本帮助`,
|
|
31
|
+
` ${y} 显示版本`,
|
|
32
|
+
"",
|
|
33
|
+
"其余所有参数原样透传给 claude。",
|
|
34
|
+
`配置: ${n}`,
|
|
35
|
+
""
|
|
36
|
+
].join(`
|
|
37
|
+
`)
|
|
38
|
+
);
|
|
39
|
+
}, V = (e) => {
|
|
40
|
+
process.stdout.write(`${e}
|
|
41
|
+
`);
|
|
42
|
+
}, B = async (e) => {
|
|
43
|
+
const t = Object.keys(e.profiles);
|
|
44
|
+
t.length === 0 && (process.stderr.write(
|
|
45
|
+
`配置中没有可用 profile,请先编辑 ${n}。
|
|
46
|
+
`
|
|
47
|
+
), process.exit(1)), F().interactive || (process.stderr.write(
|
|
48
|
+
`--meta-pick 需要交互式终端,请改用 --meta-profile=<name> 显式指定。
|
|
49
|
+
`
|
|
50
|
+
), process.exit(1));
|
|
51
|
+
const { profile: r } = await H({
|
|
52
|
+
type: "select",
|
|
53
|
+
name: "profile",
|
|
54
|
+
message: "选择以哪个配置启动 claude",
|
|
55
|
+
choices: t.map((s) => ({ title: s, value: s }))
|
|
56
|
+
});
|
|
57
|
+
return r;
|
|
58
|
+
}, J = (e) => {
|
|
59
|
+
let t, r = "run";
|
|
60
|
+
const s = [];
|
|
61
|
+
for (const o of e) {
|
|
62
|
+
if (o.startsWith(d)) {
|
|
63
|
+
t = o.slice(d.length), r = m(r, "profile");
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (o === h) {
|
|
67
|
+
r = m(r, "pick");
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (o === w) {
|
|
71
|
+
r = m(r, "help");
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (o === y) {
|
|
75
|
+
r = m(r, "version");
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (G(o))
|
|
79
|
+
throw new Error(
|
|
80
|
+
`未知 meta 选项:${o}。可用选项:${d}<name>、${h}、${w}、${y}`
|
|
81
|
+
);
|
|
82
|
+
s.push(o);
|
|
83
|
+
}
|
|
84
|
+
return { action: r, profileName: t, passthrough: s };
|
|
85
|
+
}, W = (e, t) => {
|
|
86
|
+
const r = t ?? e.defaultProfile, s = e.profiles[r];
|
|
87
|
+
if (!s) {
|
|
88
|
+
const o = Object.keys(e.profiles).join(", ") || "(无)";
|
|
89
|
+
throw new Error(
|
|
90
|
+
`profile "${r}" 不存在。可用 profile:${o}。配置文件:${n}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return { name: r, profile: s };
|
|
94
|
+
}, Y = /^ANTHROPIC_/, X = [
|
|
95
|
+
"CLAUDE_CODE_SUBAGENT_MODEL",
|
|
96
|
+
"CLAUDE_CODE_EFFORT_LEVEL"
|
|
97
|
+
], b = _.join(
|
|
98
|
+
C.homedir(),
|
|
99
|
+
".claude",
|
|
100
|
+
"settings.json"
|
|
101
|
+
), R = (e) => Y.test(e) || X.includes(e), q = (e, t) => {
|
|
102
|
+
const r = {};
|
|
103
|
+
for (const [s, o] of Object.entries(e))
|
|
104
|
+
o !== void 0 && (R(s) || (r[s] = o));
|
|
105
|
+
for (const [s, o] of Object.entries(t))
|
|
106
|
+
r[s] = o;
|
|
107
|
+
return r;
|
|
108
|
+
}, Q = (e) => !e || typeof e != "object" ? [] : Object.keys(e).filter((t) => R(t)), z = () => {
|
|
109
|
+
let e;
|
|
110
|
+
try {
|
|
111
|
+
e = JSON.parse(p.readFileSync(b, "utf8"));
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
if (typeof e != "object" || e === null)
|
|
116
|
+
return null;
|
|
117
|
+
const t = e.env;
|
|
118
|
+
return typeof t != "object" || t === null || Array.isArray(t) ? null : t;
|
|
119
|
+
}, L = {
|
|
120
|
+
defaultProfile: "deepseek",
|
|
121
|
+
profiles: {
|
|
122
|
+
deepseek: {
|
|
123
|
+
env: {
|
|
124
|
+
ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
|
|
125
|
+
ANTHROPIC_AUTH_TOKEN: "",
|
|
126
|
+
ANTHROPIC_MODEL: "deepseek-v4-pro[1m]",
|
|
127
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "deepseek-v4-pro[1m]",
|
|
128
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "deepseek-v4-pro[1m]",
|
|
129
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "deepseek-v4-flash[1m]",
|
|
130
|
+
CLAUDE_CODE_SUBAGENT_MODEL: "deepseek-v4-pro[1m]",
|
|
131
|
+
CLAUDE_CODE_EFFORT_LEVEL: "max"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}, g = (e) => {
|
|
136
|
+
p.mkdirSync(_.dirname(n), { recursive: !0 }), p.writeFileSync(n, JSON.stringify(e, null, 2)), p.chmodSync(n, 384);
|
|
137
|
+
}, Z = () => {
|
|
138
|
+
if (!p.existsSync(n))
|
|
139
|
+
return g(L), L;
|
|
140
|
+
const e = p.readFileSync(n, "utf8");
|
|
141
|
+
let t;
|
|
142
|
+
try {
|
|
143
|
+
t = JSON.parse(e);
|
|
144
|
+
} catch (s) {
|
|
145
|
+
const o = s instanceof Error ? s.message : String(s);
|
|
146
|
+
throw new Error(`配置文件非法 JSON:${n}(${o})`);
|
|
147
|
+
}
|
|
148
|
+
if (typeof t != "object" || t === null)
|
|
149
|
+
throw new Error(`配置文件结构非法:${n}(应为 JSON 对象)`);
|
|
150
|
+
const r = t;
|
|
151
|
+
if (typeof r.defaultProfile != "string")
|
|
152
|
+
throw new Error(`配置文件缺少字符串字段 defaultProfile:${n}`);
|
|
153
|
+
if (typeof r.profiles != "object" || r.profiles === null || Array.isArray(r.profiles))
|
|
154
|
+
throw new Error(`配置文件缺少对象字段 profiles:${n}`);
|
|
155
|
+
return r;
|
|
156
|
+
}, ee = /TOKEN|KEY|SECRET/i;
|
|
157
|
+
class T extends Error {
|
|
158
|
+
constructor() {
|
|
159
|
+
super("用户中断输入"), this.name = "PromptAbortError";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const te = (e) => ee.test(e), we = (e) => e === "" ? "" : "******", k = (e) => Object.keys(e).filter((t) => e[t] === "");
|
|
163
|
+
let A = !1, f = null;
|
|
164
|
+
const re = (e) => new Promise((t, r) => {
|
|
165
|
+
const s = U.createInterface({
|
|
166
|
+
input: process.stdin,
|
|
167
|
+
output: process.stderr
|
|
168
|
+
});
|
|
169
|
+
f = s, s.on("close", () => {
|
|
170
|
+
f === s && (f = null);
|
|
171
|
+
}), s.question(e, (o) => {
|
|
172
|
+
s.close(), t(o);
|
|
173
|
+
}), s.on("SIGINT", () => {
|
|
174
|
+
s.close(), r(new T());
|
|
175
|
+
});
|
|
176
|
+
}), se = 3, oe = 4, ne = 127, ce = (e) => new Promise((t, r) => {
|
|
177
|
+
process.stderr.write(e);
|
|
178
|
+
const s = process.stdin;
|
|
179
|
+
typeof s.setRawMode == "function" && (s.setRawMode(!0), A = !0), s.resume(), s.setEncoding("utf8");
|
|
180
|
+
let o = "";
|
|
181
|
+
const c = () => {
|
|
182
|
+
s.removeListener("data", a);
|
|
183
|
+
}, a = (S) => {
|
|
184
|
+
for (const i of S) {
|
|
185
|
+
const l = i.charCodeAt(0);
|
|
186
|
+
if (i === "\r" || i === `
|
|
187
|
+
`) {
|
|
188
|
+
c(), process.stderr.write(`
|
|
189
|
+
`), t(o);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (l === se || l === oe) {
|
|
193
|
+
c(), process.stderr.write(`
|
|
194
|
+
`), r(new T());
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (l === ne || i === "\b") {
|
|
198
|
+
o = o.slice(0, -1);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
o += i;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
s.on("data", a);
|
|
205
|
+
}), ie = async (e) => {
|
|
206
|
+
const t = k(e.env);
|
|
207
|
+
for (const r of t) {
|
|
208
|
+
const s = te(r);
|
|
209
|
+
let o = "";
|
|
210
|
+
for (; o === ""; ) {
|
|
211
|
+
const c = `请输入 ${r}: `;
|
|
212
|
+
o = s ? await ce(c) : await re(c);
|
|
213
|
+
}
|
|
214
|
+
e.env[r] = o;
|
|
215
|
+
}
|
|
216
|
+
}, ae = () => {
|
|
217
|
+
f && (f.close(), f = null);
|
|
218
|
+
const e = process.stdin;
|
|
219
|
+
A && typeof e.setRawMode == "function" && (e.setRawMode(!1), A = !1), e.removeAllListeners("data"), e.removeAllListeners("keypress"), e.removeAllListeners("readable"), e.pause();
|
|
220
|
+
}, M = {
|
|
221
|
+
version: "0.0.1-alpha.0",
|
|
222
|
+
cliConfig: {
|
|
223
|
+
moduleName: "cc-switch"
|
|
224
|
+
}
|
|
225
|
+
}, le = "claude", $ = async (e) => {
|
|
226
|
+
const { action: t, profileName: r, passthrough: s } = J(
|
|
227
|
+
e ?? process.argv.slice(2)
|
|
228
|
+
);
|
|
229
|
+
t === "help" && (K(), process.exit(0)), t === "version" && (V(M.version), process.exit(0));
|
|
230
|
+
const o = Z(), c = t === "pick" ? await B(o) : r, { profile: a } = W(o, c);
|
|
231
|
+
if (k(a.env).length > 0) {
|
|
232
|
+
try {
|
|
233
|
+
await ie(a);
|
|
234
|
+
} catch (v) {
|
|
235
|
+
if (v instanceof T)
|
|
236
|
+
return process.stderr.write(`已取消,未写入配置。
|
|
237
|
+
`), process.exit(1);
|
|
238
|
+
throw v;
|
|
239
|
+
}
|
|
240
|
+
g(o), ae();
|
|
241
|
+
}
|
|
242
|
+
const i = z(), l = Q(i);
|
|
243
|
+
if (l.length > 0)
|
|
244
|
+
return process.stderr.write(
|
|
245
|
+
`检测到 ${b} 的 env 块含模型路由类 key:${l.join(", ")}。请手动清理这些 key 后重试。
|
|
246
|
+
`
|
|
247
|
+
), process.exit(1);
|
|
248
|
+
const D = q(process.env, a.env), I = j(le, s, {
|
|
249
|
+
stdio: "inherit",
|
|
250
|
+
shell: !1,
|
|
251
|
+
env: D
|
|
252
|
+
});
|
|
253
|
+
return new Promise((v, O) => {
|
|
254
|
+
I.on("error", (u) => {
|
|
255
|
+
try {
|
|
256
|
+
if (u.code === "ENOENT") {
|
|
257
|
+
process.stderr.write(
|
|
258
|
+
`未找到 claude 可执行文件,请确认 claude-code 已安装并在 PATH 中。
|
|
259
|
+
`
|
|
260
|
+
), process.exit(127);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
process.stderr.write(`启动 claude 失败:${u.message}
|
|
264
|
+
`), process.exit(1);
|
|
265
|
+
} catch (E) {
|
|
266
|
+
O(E);
|
|
267
|
+
}
|
|
268
|
+
}), I.on("exit", (u, E) => {
|
|
269
|
+
try {
|
|
270
|
+
if (E) {
|
|
271
|
+
process.exit(128 + pe(E));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
process.exit(u ?? 1);
|
|
275
|
+
} catch (x) {
|
|
276
|
+
O(x);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}, pe = (e) => ({
|
|
281
|
+
SIGHUP: 1,
|
|
282
|
+
SIGINT: 2,
|
|
283
|
+
SIGQUIT: 3,
|
|
284
|
+
SIGILL: 4,
|
|
285
|
+
SIGABRT: 6,
|
|
286
|
+
SIGFPE: 8,
|
|
287
|
+
SIGKILL: 9,
|
|
288
|
+
SIGSEGV: 11,
|
|
289
|
+
SIGPIPE: 13,
|
|
290
|
+
SIGALRM: 14,
|
|
291
|
+
SIGTERM: 15
|
|
292
|
+
})[e] ?? 1, {
|
|
293
|
+
cliConfig: { moduleName: N }
|
|
294
|
+
} = M, ye = async () => {
|
|
295
|
+
try {
|
|
296
|
+
await $();
|
|
297
|
+
} catch (e) {
|
|
298
|
+
const t = e instanceof Error ? e.message : String(e);
|
|
299
|
+
process.stderr.write(`${t}
|
|
300
|
+
`), process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
}, ve = () => ({
|
|
303
|
+
command: N,
|
|
304
|
+
describe: "claude-code 模型路由透传(dc-cc-switch / cc-router)",
|
|
305
|
+
builder(e) {
|
|
306
|
+
return e.strict(!1).option("meta-profile", {
|
|
307
|
+
type: "string",
|
|
308
|
+
describe: "选择 profile(~/.done-coding/cc-switch/profile.json 中的 profile 名)"
|
|
309
|
+
}).option("meta-pick", {
|
|
310
|
+
type: "boolean",
|
|
311
|
+
describe: "终端交互选择 profile 启动"
|
|
312
|
+
}).option("meta-help", {
|
|
313
|
+
type: "boolean",
|
|
314
|
+
describe: "显示 cc-switch 自身帮助"
|
|
315
|
+
}).option("meta-version", {
|
|
316
|
+
type: "boolean",
|
|
317
|
+
describe: "显示 cc-switch 自身版本"
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
handler() {
|
|
321
|
+
const e = process.argv.findIndex((r) => r === N), t = e >= 0 ? process.argv.slice(e + 1) : process.argv.slice(2);
|
|
322
|
+
return $(t);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
export {
|
|
326
|
+
L as D,
|
|
327
|
+
n as P,
|
|
328
|
+
ye as a,
|
|
329
|
+
q as b,
|
|
330
|
+
ve as c,
|
|
331
|
+
k as f,
|
|
332
|
+
Q as h,
|
|
333
|
+
te as i,
|
|
334
|
+
we as m,
|
|
335
|
+
J as p,
|
|
336
|
+
W as s
|
|
337
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@done-coding/cli-cc-switch",
|
|
3
|
+
"version": "0.0.1-alpha.0",
|
|
4
|
+
"description": "claude-code路由",
|
|
5
|
+
"private": false,
|
|
6
|
+
"module": "es/index.mjs",
|
|
7
|
+
"types": "types/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"bin": {
|
|
10
|
+
"dc-cc-switch": "es/cli.mjs",
|
|
11
|
+
"cc-router": "es/cli.mjs"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./es/index.mjs",
|
|
16
|
+
"types": "./types/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"es",
|
|
21
|
+
"lib",
|
|
22
|
+
"types"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "vite build -w -m hotBuild",
|
|
26
|
+
"build": "vite build",
|
|
27
|
+
"prepack": "pnpm build",
|
|
28
|
+
"prebuild": "rimraf types",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"coverage": "vitest run --coverage"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/done-coding/done-coding-cli.git",
|
|
35
|
+
"directory": "packages/cc-switch"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"registry": "https://registry.npmjs.org/"
|
|
40
|
+
},
|
|
41
|
+
"author": "JustSoSu",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"sideEffects": false,
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@done-coding/cli-inject": "0.6.5",
|
|
46
|
+
"@types/node": "^18.0.0",
|
|
47
|
+
"@types/yargs": "^17.0.28",
|
|
48
|
+
"@vitest/coverage-v8": "^1.6.1",
|
|
49
|
+
"rimraf": "^5.0.1",
|
|
50
|
+
"typescript": "^5.8.3",
|
|
51
|
+
"vite": "^5.0.10",
|
|
52
|
+
"vite-plugin-dts": "^3.7.0",
|
|
53
|
+
"vitest": "^1.6.1"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@done-coding/cli-utils": "0.12.0"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=18.0.0"
|
|
60
|
+
},
|
|
61
|
+
"gitHead": "13ba28f171c3a209b451ce1cfa09506cbd4abbcb"
|
|
62
|
+
}
|
package/types/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { }
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { CommandModule } from '@done-coding/cli-utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* REQ-7 层 1:strip-then-inject(纯函数,可单测)。
|
|
5
|
+
* 复制 processEnv → 删除所有模型路由类 key → 注入 profileEnv(顺序不可反)。
|
|
6
|
+
* 仅作用于返回的子进程 env 对象,[MUST NOT] 修改 cc-router 自身 process.env,
|
|
7
|
+
* [MUST NOT] 读/写任何文件。空 profileEnv `{}` 仍 strip(不回退继承值)。
|
|
8
|
+
* 非模型 CLAUDE_CODE_* / PATH / HOME 等一律不动。
|
|
9
|
+
*/
|
|
10
|
+
export declare const buildChildEnv: (processEnv: NodeJS.ProcessEnv, profileEnv: Record<string, string>) => Record<string, string>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 作为子命令注册进 done-coding 主 CLI(`DC cc-switch`)。
|
|
14
|
+
* 门面路由:builder 宽松(strict(false) + 声明 meta 家族选项,不拦截
|
|
15
|
+
* claude 透传参数);handler 内从 process.argv 在子命令名边界后取**原始切片**
|
|
16
|
+
* 喂给 runRouter —— 不消费 yargs 解析产物,守 REQ-1 逐字保真语义
|
|
17
|
+
* (独立入口 dc-cc-switch / cc-router 行为与未注册前完全一致)。
|
|
18
|
+
* meta 自身命令面(REQ-1):--meta-profile=<name> / --meta-pick /
|
|
19
|
+
* --meta-help / --meta-version 由 runRouter 消费,[MUST NOT] 透传给 claude。
|
|
20
|
+
*/
|
|
21
|
+
export declare const createAsSubcommand: () => CommandModule;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* REQ-3 内置 deepseek 模板。键集合与 requirements REQ-3 表逐键一致;
|
|
25
|
+
* ANTHROPIC_AUTH_TOKEN 恒为空字符串(绝不含真实 token)。
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEEPSEEK_TEMPLATE: ProfileConfig;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 找出值严格等于 `""` 的键(纯函数,可单测)。
|
|
31
|
+
* 仅严格空字符串命中("REPLACE_ME" 等非空占位不算);按 Object.keys 插入顺序返回。
|
|
32
|
+
*/
|
|
33
|
+
export declare const findEmptyKeys: (env: Record<string, string>) => string[];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* REQ-7 层 2 纯判定(纯函数,可单测):返回 settings.json env 块中
|
|
37
|
+
* 命中模型路由类判定的 key 列表(判定规则与层 1 完全一致)。
|
|
38
|
+
*/
|
|
39
|
+
export declare const hasModelEnvConflict: (settingsEnvObj: Record<string, unknown> | null | undefined) => string[];
|
|
40
|
+
|
|
41
|
+
/** 键名是否为敏感(token 类)键(纯函数,可单测)。 */
|
|
42
|
+
export declare const isSecretKey: (key: string) => boolean;
|
|
43
|
+
|
|
44
|
+
/** 固定遮蔽:不含任何明文片段(纯函数,可单测,REQ-6 报错脱敏用)。 */
|
|
45
|
+
export declare const maskValue: (v: string) => string;
|
|
46
|
+
|
|
47
|
+
/** meta 动作:run=透传启动 / profile=显式指定 / pick=交互选择 / help / version */
|
|
48
|
+
export declare type MetaAction = "run" | "profile" | "pick" | "help" | "version";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 解析 argv(纯函数,可单测)。
|
|
52
|
+
* meta 自身命令面(REQ-1):--meta-profile=<name>(多个取最后一个生效)、
|
|
53
|
+
* --meta-pick / --meta-help / --meta-version,按 REQ-6 优先级归并 action,
|
|
54
|
+
* 被消费项均不入 passthrough;未知 --meta-* 前缀 fail-fast throw;
|
|
55
|
+
* 其余所有参数按原始顺序原样收集,[MUST NOT] trim/去引号/合并/重排/校验。
|
|
56
|
+
*/
|
|
57
|
+
export declare const parseArgv: (argv: string[]) => ParsedArgv;
|
|
58
|
+
|
|
59
|
+
/** argv 解析结果:meta 动作归并 + 消费的 --meta-profile + 其余原样透传 */
|
|
60
|
+
export declare interface ParsedArgv {
|
|
61
|
+
action: MetaAction;
|
|
62
|
+
profileName?: string;
|
|
63
|
+
passthrough: string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** 单个 profile:可选描述 + env 注入键值 */
|
|
67
|
+
export declare interface Profile {
|
|
68
|
+
describe?: string;
|
|
69
|
+
env: Record<string, string>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* profile 配置文件唯一路径:~/.done-coding/cc-switch/profile.json
|
|
74
|
+
* `~` 不写死具体用户,由 os.homedir() 解析当前用户 home。
|
|
75
|
+
*/
|
|
76
|
+
export declare const PROFILE_PATH: string;
|
|
77
|
+
|
|
78
|
+
/** profile 配置文件结构(~/.done-coding/cc-switch/profile.json) */
|
|
79
|
+
export declare interface ProfileConfig {
|
|
80
|
+
defaultProfile: string;
|
|
81
|
+
profiles: Record<string, Profile>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 选择 profile(纯函数,可单测)。
|
|
86
|
+
* 有 profileName 用之,否则用 cfg.defaultProfile。
|
|
87
|
+
* 目标不存在 / defaultProfile 悬空 → throw(列可用名 + 配置路径),
|
|
88
|
+
* [MUST NOT] 猜最近名 / 回退 default。profile.env 为空 `{}` 合法。
|
|
89
|
+
*/
|
|
90
|
+
export declare const selectProfile: (cfg: ProfileConfig, profileName?: string) => {
|
|
91
|
+
name: string;
|
|
92
|
+
profile: Profile;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export { }
|