ccem 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -27
- package/dist/index.js +22 -20
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -4,18 +4,32 @@
|
|
|
4
4
|
|
|
5
5
|
切换 API 服务商、配置权限模式、查看用量统计、安装 Skills。
|
|
6
6
|
|
|
7
|
+
[](https://www.npmjs.com/package/ccem)
|
|
8
|
+
[](https://github.com/genuifx/claude-code-env-manager/blob/main/LICENSE)
|
|
9
|
+
|
|
7
10
|

|
|
8
11
|
|
|
9
12
|
## 这工具干嘛的
|
|
10
13
|
|
|
11
14
|
用 Claude Code 的人可能会遇到几个烦心事:
|
|
12
15
|
|
|
13
|
-
- 想用国产模型(GLM、KIMI、DeepSeek)但每次都要手动设置环境变量
|
|
16
|
+
- 想用国产模型(GLM、KIMI、DeepSeek、MiniMax)但每次都要手动设置环境变量
|
|
14
17
|
- 每次执行命令都要点"允许",烦死了,但又不想用 `--dangerously-skip-permissions`
|
|
15
18
|
- 想知道这个月花了多少钱,但 Claude 没有用量统计界面
|
|
19
|
+
- 团队想共享 API 配置,但不想把密钥明文传来传去
|
|
20
|
+
- 想快速安装官方和社区的 Skills,但手动 clone 太麻烦
|
|
16
21
|
|
|
17
22
|
ccem 就是解决这些问题的。
|
|
18
23
|
|
|
24
|
+
## 功能特性
|
|
25
|
+
|
|
26
|
+
- 🔄 **环境切换** - 一键切换 API 服务商(官方/GLM/KIMI/DeepSeek/MiniMax)
|
|
27
|
+
- 🔐 **权限管理** - 6 种预设权限模式,在安全和便捷之间找平衡
|
|
28
|
+
- 📊 **用量统计** - 日历热力图 + 按模型统计 + 费用计算
|
|
29
|
+
- 🛠️ **Skill 管理** - 从官方/GitHub/Plugin Marketplace 安装 Skills
|
|
30
|
+
- 🌐 **远程配置** - 团队共享 API 配置,加密传输
|
|
31
|
+
- ⚡ **性能优化** - 增量缓存、流式解析、后台更新
|
|
32
|
+
|
|
19
33
|
## 安装
|
|
20
34
|
|
|
21
35
|
```bash
|
|
@@ -32,7 +46,8 @@ npx ccem
|
|
|
32
46
|
ccem # 进入交互菜单
|
|
33
47
|
ccem add kimi # 添加 KIMI 环境,自动填好 URL 和模型
|
|
34
48
|
ccem use kimi # 切换到 KIMI
|
|
35
|
-
ccem dev # 用开发模式启动 Claude Code
|
|
49
|
+
ccem dev # 用开发模式启动 Claude Code(临时)
|
|
50
|
+
ccem --mode # 查看当前权限模式
|
|
36
51
|
```
|
|
37
52
|
|
|
38
53
|

|
|
@@ -50,7 +65,7 @@ ccem dev # 用开发模式启动 Claude Code
|
|
|
50
65
|
| Start Claude Code | 启动,如果设了默认权限模式会自动带上 |
|
|
51
66
|
| Switch Environment | 切换 API 环境 |
|
|
52
67
|
| Permission Mode | 选个权限模式再启动 |
|
|
53
|
-
| View Usage |
|
|
68
|
+
| View Usage | 看用量和花费(带日历热力图) |
|
|
54
69
|
| Set Default Mode | 设置默认权限模式 |
|
|
55
70
|
|
|
56
71
|
### 命令
|
|
@@ -70,12 +85,14 @@ ccem run <command> # 带着环境变量跑命令
|
|
|
70
85
|
|
|
71
86
|
添加环境时可以选预设,省得自己填 URL:
|
|
72
87
|
|
|
73
|
-
| 预设 | Base URL |
|
|
74
|
-
|
|
75
|
-
| GLM(智谱) | `https://open.bigmodel.cn/api/anthropic` | glm-4.6 |
|
|
76
|
-
| KIMI(月之暗面) | `https://api.moonshot.cn/anthropic` | kimi-k2-thinking-turbo |
|
|
77
|
-
| MiniMax | `https://api.minimaxi.com/anthropic` | MiniMax-M2 |
|
|
78
|
-
| DeepSeek | `https://api.deepseek.com/anthropic` | deepseek-chat |
|
|
88
|
+
| 预设 | Base URL | 主模型 | 快速模型 |
|
|
89
|
+
|------|----------|--------|----------|
|
|
90
|
+
| GLM(智谱) | `https://open.bigmodel.cn/api/anthropic` | glm-4.6 | glm-4.5-air |
|
|
91
|
+
| KIMI(月之暗面) | `https://api.moonshot.cn/anthropic` | kimi-k2-thinking-turbo | kimi-k2-turbo-preview |
|
|
92
|
+
| MiniMax | `https://api.minimaxi.com/anthropic` | MiniMax-M2 | MiniMax-M2 |
|
|
93
|
+
| DeepSeek | `https://api.deepseek.com/anthropic` | deepseek-chat | deepseek-chat |
|
|
94
|
+
|
|
95
|
+
> 💡 **官方环境**:默认使用 `claude-sonnet-4-5-20250929` 和 `claude-haiku-4-5-20251001`
|
|
79
96
|
|
|
80
97
|
### Shell 集成
|
|
81
98
|
|
|
@@ -126,7 +143,7 @@ ccem ci # CI
|
|
|
126
143
|
ccem audit # 审计
|
|
127
144
|
```
|
|
128
145
|
|
|
129
|
-
实现方式是通过 `--allowedTools` 和 `--disallowedTools` 参数传给 Claude Code。
|
|
146
|
+
实现方式是通过 `--permission-mode`、`--allowedTools` 和 `--disallowedTools` 参数传给 Claude Code。
|
|
130
147
|
|
|
131
148
|
### 永久模式
|
|
132
149
|
|
|
@@ -200,9 +217,12 @@ ccem --list-modes # 列出所有模式
|
|
|
200
217
|
|
|
201
218
|
ccem 会读 Claude Code 的日志(在 `~/.claude/projects/` 下面的 JSONL 文件),统计 token 用量和费用。
|
|
202
219
|
|
|
203
|
-
价格数据从 LiteLLM 的 GitHub
|
|
220
|
+
价格数据从 LiteLLM 的 GitHub 仓库拉取,会缓存到本地。如果网络不可用,会依次尝试:
|
|
221
|
+
1. 本地缓存(`~/.ccem/model-prices.json`)
|
|
222
|
+
2. 内置价格文件(随 ccem 安装)
|
|
223
|
+
3. 默认价格(Claude Opus/Sonnet/Haiku)
|
|
204
224
|
|
|
205
|
-
交互菜单里选 "View Usage"
|
|
225
|
+
交互菜单里选 "View Usage" 可以看详细统计,包括**日历热力图**:
|
|
206
226
|
|
|
207
227
|
```
|
|
208
228
|
Token Usage Statistics
|
|
@@ -210,38 +230,76 @@ ccem 会读 Claude Code 的日志(在 `~/.claude/projects/` 下面的 JSONL
|
|
|
210
230
|
Oct Nov Dec Jan
|
|
211
231
|
Mon · ░ ▒ ▓ █ ░ · ▒ ...
|
|
212
232
|
Tue ░ ▒ · █ ▓ ░ ▒ · ...
|
|
233
|
+
Wed ▒ ▓ █ ░ · ▒ ▓ █ ...
|
|
213
234
|
...
|
|
214
235
|
|
|
215
236
|
Less · ░ ▒ ▓ █ More
|
|
216
237
|
|
|
238
|
+
────────────────────────────────────────────────────────
|
|
217
239
|
Period Input Output Cache Read Cost
|
|
218
240
|
Today 12.5K 8.2K 45.3K $0.15
|
|
219
241
|
This Week 89.2K 52.1K 312.4K $1.23
|
|
220
242
|
All Time 1.2M 823.5K 4.5M $15.67
|
|
221
243
|
|
|
244
|
+
────────────────────────────────────────────────────────
|
|
222
245
|
By Model
|
|
223
246
|
claude-sonnet-4-5 823.5K $12.34
|
|
224
247
|
claude-haiku-4-5 412.3K $3.33
|
|
225
248
|
```
|
|
226
249
|
|
|
227
|
-
|
|
250
|
+
### 性能优化
|
|
251
|
+
|
|
252
|
+
- **增量缓存**:日志解析结果缓存到 `~/.ccem/usage-cache.json`,只解析新增/修改的文件
|
|
253
|
+
- **后台更新**:打开菜单时先显示缓存数据,后台异步更新
|
|
254
|
+
- **流式解析**:大文件使用流式读取(readline),避免内存占用过高
|
|
255
|
+
- **并发控制**:限制并发解析数量(5 个),避免阻塞事件循环
|
|
256
|
+
- **缓存版本**:缓存结构变更时自动失效重建
|
|
228
257
|
|
|
229
258
|
---
|
|
230
259
|
|
|
231
260
|
## Skill 管理
|
|
232
261
|
|
|
233
|
-
可以从 GitHub
|
|
262
|
+
可以从 GitHub 或 Plugin Marketplace 安装 Claude Code 的 Skills。装完会放到当前目录的 `.claude/skills/` 下面。
|
|
234
263
|
|
|
235
264
|
```bash
|
|
236
|
-
ccem skill add #
|
|
265
|
+
ccem skill add # 交互选择(Tab 切换分组)
|
|
237
266
|
ccem skill add <name> # 装预设的
|
|
238
267
|
ccem skill add <github-url> # 从 GitHub 装
|
|
239
268
|
ccem skill ls # 列出已装的
|
|
240
269
|
ccem skill rm <name> # 删掉
|
|
241
270
|
```
|
|
242
271
|
|
|
272
|
+
### 交互式选择器
|
|
273
|
+
|
|
274
|
+
运行 `ccem skill add` 会打开分组选择界面(使用 Ink 渲染):
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
🏢 官方 ⭐ 精选 📦 其他
|
|
278
|
+
|
|
279
|
+
──────────────────────────────────────────────────
|
|
280
|
+
❯ frontend-design - 创建高质量前端界面设计
|
|
281
|
+
skill-creator - 创建新的 Claude Code skills
|
|
282
|
+
web-artifacts-builder - 构建可交互的 Web 组件
|
|
283
|
+
...
|
|
284
|
+
输入自定义 GitHub URL
|
|
285
|
+
|
|
286
|
+
Tab 切换分组 | ↑↓ 选择 | Enter 确认 | Esc 取消
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 安装方式
|
|
290
|
+
|
|
291
|
+
ccem 支持三种安装方式:
|
|
292
|
+
|
|
293
|
+
| 类型 | 说明 | 示例 |
|
|
294
|
+
|------|------|------|
|
|
295
|
+
| preset | 官方预设,从 anthropics/skills 仓库安装 | `ccem skill add frontend-design` |
|
|
296
|
+
| github | 从任意 GitHub 仓库/子目录安装 | `ccem skill add owner/repo` |
|
|
297
|
+
| plugin | 从 Plugin Marketplace 安装(实验性) | 通过交互菜单选择 |
|
|
298
|
+
|
|
243
299
|
### 预设列表
|
|
244
300
|
|
|
301
|
+
#### 🏢 官方 Skills
|
|
302
|
+
|
|
245
303
|
| Skill | 干嘛用 |
|
|
246
304
|
|-------|--------|
|
|
247
305
|
| frontend-design | 前端界面设计 |
|
|
@@ -261,14 +319,114 @@ ccem skill rm <name> # 删掉
|
|
|
261
319
|
| internal-comms | 内部通信文档 |
|
|
262
320
|
| slack-gif-creator | 做 Slack GIF |
|
|
263
321
|
|
|
322
|
+
#### ⭐ 精选 Skills
|
|
323
|
+
|
|
324
|
+
| Skill | 干嘛用 |
|
|
325
|
+
|-------|--------|
|
|
326
|
+
| superpowers | Claude Code Plan 模式升级版,连续追问讨论确定开发方案 |
|
|
327
|
+
| ui-ux-pro-max | 专业 UI/UX 设计 |
|
|
328
|
+
| Humanizer-zh | 去除文本中 AI 生成痕迹,改写得更自然 |
|
|
329
|
+
|
|
330
|
+
#### 📦 其他 Skills
|
|
331
|
+
|
|
332
|
+
| Skill | 干嘛用 |
|
|
333
|
+
|-------|--------|
|
|
334
|
+
| skill-writer | 指导用户为 Claude Code 创建代理技能 |
|
|
335
|
+
|
|
264
336
|
### 从 GitHub 装
|
|
265
337
|
|
|
338
|
+
支持多种 URL 格式:
|
|
339
|
+
|
|
266
340
|
```bash
|
|
341
|
+
# 完整 URL
|
|
267
342
|
ccem skill add https://github.com/owner/repo
|
|
268
343
|
ccem skill add https://github.com/owner/repo/tree/main/path/to/skill
|
|
344
|
+
|
|
345
|
+
# 简写格式
|
|
269
346
|
ccem skill add owner/repo
|
|
270
347
|
```
|
|
271
348
|
|
|
349
|
+
> 💡 使用 git sparse-checkout 只下载指定目录,不会 clone 整个仓库
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 远程配置加载
|
|
354
|
+
|
|
355
|
+
团队可以部署一个配置服务器,成员用 `ccem load` 命令拉取共享的环境配置。
|
|
356
|
+
|
|
357
|
+
### 客户端使用
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
ccem load https://your-server.com/api/env?key=YOUR_KEY --secret YOUR_SECRET
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
- `key`:服务器分配的访问密钥
|
|
364
|
+
- `secret`:服务器启动时生成的解密密钥
|
|
365
|
+
|
|
366
|
+
加载成功后,环境会自动添加到本地配置。如果名称冲突,会自动重命名(如 `kimi` → `kimi-remote`)。
|
|
367
|
+
|
|
368
|
+
### 服务端部署
|
|
369
|
+
|
|
370
|
+
服务端代码在 `server/` 目录下。
|
|
371
|
+
|
|
372
|
+
#### 1. 配置文件
|
|
373
|
+
|
|
374
|
+
**keys.json** - 访问密钥配置:
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"team-key-abc123": {
|
|
378
|
+
"environments": ["kimi", "glm"]
|
|
379
|
+
},
|
|
380
|
+
"personal-key-xyz": {
|
|
381
|
+
"environments": ["deepseek"]
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**environments.json** - 环境配置:
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"kimi": {
|
|
390
|
+
"ANTHROPIC_BASE_URL": "https://api.moonshot.cn/anthropic",
|
|
391
|
+
"ANTHROPIC_API_KEY": "sk-xxx",
|
|
392
|
+
"ANTHROPIC_MODEL": "kimi-k2-thinking-turbo"
|
|
393
|
+
},
|
|
394
|
+
"glm": {
|
|
395
|
+
"ANTHROPIC_BASE_URL": "https://open.bigmodel.cn/api/anthropic",
|
|
396
|
+
"ANTHROPIC_API_KEY": "xxx.xxx",
|
|
397
|
+
"ANTHROPIC_MODEL": "glm-4.6"
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### 2. 启动服务
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
cd server
|
|
406
|
+
npm install
|
|
407
|
+
node index.js
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
启动后会显示 `secret`,分发给团队成员用于 `--secret` 参数。
|
|
411
|
+
|
|
412
|
+
#### 3. 安全特性
|
|
413
|
+
|
|
414
|
+
- **AES-256-CBC 加密**:API Key 在传输中加密
|
|
415
|
+
- **Rate Limiting**:每分钟最多 10 次请求
|
|
416
|
+
- **指数退避**:连续失败后冷却时间递增(最长 30 分钟)
|
|
417
|
+
- **Helmet**:安全响应头
|
|
418
|
+
- **热加载**:修改配置文件无需重启服务
|
|
419
|
+
|
|
420
|
+
#### 4. 生产部署
|
|
421
|
+
|
|
422
|
+
推荐使用 PM2:
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
pm2 start ecosystem.config.cjs
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
配合 nginx 反代,记得设置 `trust proxy`。
|
|
429
|
+
|
|
272
430
|
---
|
|
273
431
|
|
|
274
432
|
## 初始化
|
|
@@ -282,8 +440,8 @@ ccem setup init
|
|
|
282
440
|
会做三件事:
|
|
283
441
|
|
|
284
442
|
1. 设置 `hasCompletedOnboarding: true`,跳过新手引导
|
|
285
|
-
2. 禁用遥测(设置 `DISABLE_TELEMETRY=1
|
|
286
|
-
3. 装 `chrome-devtools` MCP
|
|
443
|
+
2. 禁用遥测(设置 `DISABLE_TELEMETRY=1`、`DISABLE_ERROR_REPORTING=1`、`DISABLE_BUG_COMMAND=1`)
|
|
444
|
+
3. 装 `chrome-devtools` MCP 工具(用于浏览器调试)
|
|
287
445
|
|
|
288
446
|
配置写到:
|
|
289
447
|
- `~/.claude.json`
|
|
@@ -306,17 +464,18 @@ ccem setup init
|
|
|
306
464
|
| `ccem env` | 输出环境变量 |
|
|
307
465
|
| `ccem env --json` | JSON 格式 |
|
|
308
466
|
| `ccem run <cmd>` | 带环境变量跑命令 |
|
|
467
|
+
| `ccem load <url> --secret <s>` | 从远程加载配置 |
|
|
309
468
|
|
|
310
469
|
### 权限(临时)
|
|
311
470
|
|
|
312
471
|
| 命令 | 说明 |
|
|
313
472
|
|------|------|
|
|
314
|
-
| `ccem yolo` | YOLO
|
|
315
|
-
| `ccem dev` | 开发模式 |
|
|
316
|
-
| `ccem readonly` | 只读模式 |
|
|
317
|
-
| `ccem safe` | 安全模式 |
|
|
318
|
-
| `ccem ci` | CI 模式 |
|
|
319
|
-
| `ccem audit` | 审计模式 |
|
|
473
|
+
| `ccem yolo` | 🔓 YOLO 模式(全部放开) |
|
|
474
|
+
| `ccem dev` | 💻 开发模式 |
|
|
475
|
+
| `ccem readonly` | 👀 只读模式 |
|
|
476
|
+
| `ccem safe` | 🛡️ 安全模式 |
|
|
477
|
+
| `ccem ci` | 🔧 CI 模式 |
|
|
478
|
+
| `ccem audit` | 🔍 审计模式 |
|
|
320
479
|
| `ccem --mode` | 看当前模式 |
|
|
321
480
|
| `ccem --list-modes` | 列出所有模式 |
|
|
322
481
|
|
|
@@ -334,7 +493,7 @@ ccem setup init
|
|
|
334
493
|
|
|
335
494
|
| 命令 | 说明 |
|
|
336
495
|
|------|------|
|
|
337
|
-
| `ccem skill add` |
|
|
496
|
+
| `ccem skill add` | 交互添加(分组选择) |
|
|
338
497
|
| `ccem skill add <name>` | 添加预设 |
|
|
339
498
|
| `ccem skill add <url>` | 从 GitHub 添加 |
|
|
340
499
|
| `ccem skill ls` | 列出已装 |
|
|
@@ -352,14 +511,26 @@ ccem setup init
|
|
|
352
511
|
|
|
353
512
|
| 路径 | 内容 |
|
|
354
513
|
|------|------|
|
|
355
|
-
| `~/.config/claude-code-env-manager/` |
|
|
356
|
-
| `~/.ccem/usage-cache.json` |
|
|
357
|
-
| `~/.ccem/model-prices.json` |
|
|
514
|
+
| `~/.config/claude-code-env-manager/` | 环境配置(加密存储 API Key) |
|
|
515
|
+
| `~/.ccem/usage-cache.json` | 用量缓存(增量解析结果) |
|
|
516
|
+
| `~/.ccem/model-prices.json` | 价格缓存(从 LiteLLM 拉取) |
|
|
358
517
|
| `.claude/settings.json` | 项目权限配置 |
|
|
359
518
|
| `.claude/skills/` | 已装的 skills |
|
|
360
519
|
|
|
361
520
|
---
|
|
362
521
|
|
|
522
|
+
## 技术栈
|
|
523
|
+
|
|
524
|
+
- **CLI 框架**: Commander.js
|
|
525
|
+
- **配置存储**: Conf(加密存储敏感信息)
|
|
526
|
+
- **交互界面**: Inquirer.js + Ink (React for CLI)
|
|
527
|
+
- **表格渲染**: cli-table3
|
|
528
|
+
- **样式**: Chalk
|
|
529
|
+
|
|
530
|
+
## Contributing
|
|
531
|
+
|
|
532
|
+
欢迎提 Issue 和 PR!
|
|
533
|
+
|
|
363
534
|
## License
|
|
364
535
|
|
|
365
536
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as go}from"commander";import fo from"conf";import F from"inquirer";import d from"chalk";import Ot from"cli-table3";import{spawn as Mt}from"child_process";import*as ye from"readline";import*as It from"fs";import*as _e from"path";import{fileURLToPath as ho}from"url";import ce from"crypto";import Q from"fs";import B from"path";var $e="aes-256-cbc",Ue=ce.scryptSync("claude-code-env-manager-secret","salt",32),le=e=>{if(!e)return e;let t=ce.randomBytes(16),o=ce.createCipheriv($e,Ue,t),n=o.update(e,"utf8","hex");return n+=o.final("hex"),`enc:${t.toString("hex")}:${n}`},K=e=>{if(!e||!e.startsWith("enc:"))return e;try{let t=e.split(":");if(t.length!==3)return e;let o=Buffer.from(t[1],"hex"),n=t[2],s=ce.createDecipheriv($e,Ue,o),i=s.update(n,"hex","utf8");return i+=s.final("utf8"),i}catch{return e}},Fe=()=>{let e=process.cwd(),t=B.parse(e).root;for(;e!==t;){if(Q.existsSync(B.join(e,".git"))||Q.existsSync(B.join(e,"package.json")))return e;e=B.dirname(e)}return process.cwd()},de=(e=!0)=>{let t=Fe(),o=B.join(t,".claude"),n=e?"settings.local.json":"settings.json";return B.join(o,n)},je=()=>{let e=Fe(),t=B.join(e,".claude");return Q.existsSync(t)||Q.mkdirSync(t,{recursive:!0}),t},Pe=()=>process.env.HOME||process.env.USERPROFILE||"",Ae=()=>B.join(Pe(),".claude.json"),Ee=()=>B.join(Pe(),".claude","settings.json"),Ke=()=>{let e=B.join(Pe(),".claude");return Q.existsSync(e)||Q.mkdirSync(e,{recursive:!0}),e};var Ce={GLM:{ANTHROPIC_BASE_URL:"https://open.bigmodel.cn/api/anthropic",ANTHROPIC_MODEL:"glm-4.6",ANTHROPIC_SMALL_FAST_MODEL:"glm-4.5-air"},KIMI:{ANTHROPIC_BASE_URL:"https://api.moonshot.cn/anthropic",ANTHROPIC_MODEL:"kimi-k2-thinking-turbo",ANTHROPIC_SMALL_FAST_MODEL:"kimi-k2-turbo-preview"},MiniMax:{ANTHROPIC_BASE_URL:"https://api.minimaxi.com/anthropic",ANTHROPIC_MODEL:"MiniMax-M2",ANTHROPIC_SMALL_FAST_MODEL:"MiniMax-M2"},DeepSeek:{ANTHROPIC_BASE_URL:"https://api.deepseek.com/anthropic",ANTHROPIC_MODEL:"deepseek-chat",ANTHROPIC_SMALL_FAST_MODEL:"deepseek-chat"}},O={yolo:{name:"YOLO \u6A21\u5F0F",description:"\u5168\u90E8\u653E\u5F00\uFF0C\u65E0\u4EFB\u4F55\u9650\u5236",permissionMode:"bypassPermissions",permissions:{allow:["Bash(*)","Read(*)","Edit(*)","Write(*)","WebFetch(*)","WebSearch(*)","Glob(*)","Grep(*)","LSP(*)","NotebookEdit(*)"],deny:[]}},dev:{name:"\u5F00\u53D1\u6A21\u5F0F",description:"\u65E5\u5E38\u5F00\u53D1\u6743\u9650\uFF0C\u4FDD\u62A4\u654F\u611F\u6587\u4EF6",permissionMode:"acceptEdits",permissions:{allow:["Read(*)","Edit(*)","Write(*)","Glob(*)","Grep(*)","LSP(*)","NotebookEdit(*)","Bash(npm:*)","Bash(pnpm:*)","Bash(yarn:*)","Bash(bun:*)","Bash(node:*)","Bash(npx:*)","Bash(git:*)","Bash(tsc:*)","Bash(tsx:*)","Bash(eslint:*)","Bash(prettier:*)","Bash(jest:*)","Bash(vitest:*)","Bash(cargo:*)","Bash(python:*)","Bash(pip:*)","Bash(go:*)","Bash(make:*)","Bash(cmake:*)","Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(find:*)","Bash(wc:*)","Bash(mkdir:*)","Bash(cp:*)","Bash(mv:*)","Bash(touch:*)","WebSearch"],deny:["Read(.env)","Read(.env.*)","Read(**/secrets/**)","Read(**/*.pem)","Read(**/*.key)","Read(**/*credential*)","Bash(rm -rf:*)","Bash(sudo:*)","Bash(chmod:*)","Bash(chown:*)"]}},readonly:{name:"\u53EA\u8BFB\u6A21\u5F0F",description:"\u4EC5\u5141\u8BB8\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7981\u6B62\u4EFB\u4F55\u4FEE\u6539",permissionMode:"plan",permissions:{allow:["Read(*)","Glob(*)","Grep(*)","LSP(*)","Bash(git status:*)","Bash(git log:*)","Bash(git diff:*)","Bash(git branch:*)","Bash(git show:*)","Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(find:*)","Bash(wc:*)","Bash(file:*)","WebSearch"],deny:["Edit(*)","Write(*)","NotebookEdit(*)","Bash(rm:*)","Bash(mv:*)","Bash(cp:*)","Bash(mkdir:*)","Bash(touch:*)","Bash(git add:*)","Bash(git commit:*)","Bash(git push:*)","Bash(git checkout:*)","Bash(git reset:*)","Bash(npm install:*)","Bash(pnpm install:*)","Bash(yarn add:*)"]}},safe:{name:"\u5B89\u5168\u6A21\u5F0F",description:"\u4FDD\u5B88\u6743\u9650\uFF0C\u9002\u5408\u4E0D\u719F\u6089\u7684\u4EE3\u7801\u5E93",permissionMode:"default",permissions:{allow:["Read(*)","Glob(*)","Grep(*)","LSP(*)","Bash(git status:*)","Bash(git log:*)","Bash(git diff:*)","Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(find:*)","Bash(wc:*)"],deny:["Read(.env)","Read(.env.*)","Read(**/secrets/**)","Read(**/*.pem)","Read(**/*.key)","Read(**/*credential*)","Read(**/*password*)","Edit(*)","Write(*)","NotebookEdit(*)","Bash(curl:*)","Bash(wget:*)","Bash(ssh:*)","Bash(scp:*)","Bash(rm:*)","Bash(mv:*)","WebFetch(*)"]}},ci:{name:"CI/CD \u6A21\u5F0F",description:"\u9002\u5408\u81EA\u52A8\u5316\u6D41\u6C34\u7EBF\u7684\u6743\u9650",permissionMode:"default",permissions:{allow:["Read(*)","Edit(*)","Write(*)","Glob(*)","Grep(*)","LSP(*)","Bash(npm:*)","Bash(pnpm:*)","Bash(yarn:*)","Bash(node:*)","Bash(git:*)","Bash(docker:*)","Bash(make:*)","Bash(cargo:*)","Bash(go:*)","Bash(python:*)","Bash(pip:*)","Bash(pytest:*)","Bash(jest:*)","Bash(vitest:*)"],deny:["Read(.env.local)","Read(**/secrets/**)","Bash(sudo:*)","Bash(ssh:*)","Bash(scp:*)","WebFetch(*)","WebSearch"]}},audit:{name:"\u5BA1\u8BA1\u6A21\u5F0F",description:"\u4EC5\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7528\u4E8E\u5B89\u5168\u5BA1\u8BA1",permissionMode:"plan",permissions:{allow:["Read(*)","Glob(*)","Grep(*)","LSP(*)","Bash(git log:*)","Bash(git blame:*)","Bash(git show:*)","Bash(git diff:*)","Bash(ls:*)","Bash(find:*)","Bash(wc:*)","Bash(file:*)","Bash(stat:*)"],deny:["Edit(*)","Write(*)","NotebookEdit(*)","Bash(rm:*)","Bash(mv:*)","Bash(cp:*)","Bash(curl:*)","Bash(wget:*)","Bash(ssh:*)","WebFetch(*)"]}}},Re=()=>Object.keys(O);import v from"chalk";import ze from"cli-table3";import*as J from"fs";import*as I from"fs/promises";import*as G from"path";import*as ke from"os";import*as Ge from"readline";var Te=G.join(ke.homedir(),".claude","projects"),pe=G.join(ke.homedir(),".ccem"),we=1,Oe=()=>G.join(pe,"usage-cache.json"),vt=()=>G.join(pe,"model-prices.json");async function Ye(){try{await I.access(pe)}catch{await I.mkdir(pe,{recursive:!0})}}var Bt="https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",Dt=()=>G.join(__dirname,"..","model-prices.json"),ee={"claude-opus-4-5":{input_cost_per_token:5e-6,output_cost_per_token:25e-6,cache_read_input_token_cost:5e-7,cache_creation_input_token_cost:625e-8},"claude-sonnet-4-5":{input_cost_per_token:3e-6,output_cost_per_token:15e-6,cache_read_input_token_cost:3e-7,cache_creation_input_token_cost:375e-8},"claude-haiku-4-5":{input_cost_per_token:1e-6,output_cost_per_token:5e-6,cache_read_input_token_cost:1e-7,cache_creation_input_token_cost:125e-8}};function ue(e){return e.replace(/-20\d{6}.*$/,"").replace(/-v\d+:\d+$/,"").replace(/^anthropic\./,"").replace(/^vertex_ai\//,"").replace(/@.*$/,"")}var Z=null;async function Ht(){if(Z)return Z;await Ye();let e=vt();try{let t=await fetch(Bt,{signal:AbortSignal.timeout(1e3)});if(t.ok){let o=await t.json(),n={};for(let[s,i]of Object.entries(o))i.input_cost_per_token&&i.output_cost_per_token&&(n[s]={input_cost_per_token:i.input_cost_per_token,output_cost_per_token:i.output_cost_per_token,cache_read_input_token_cost:i.cache_read_input_token_cost,cache_creation_input_token_cost:i.cache_creation_input_token_cost});return await I.writeFile(e,JSON.stringify(n,null,2)),Z=n,n}}catch{}try{let t=await I.readFile(e,"utf-8"),o=JSON.parse(t);return Z=o,o}catch{}try{let t=Dt(),o=await I.readFile(t,"utf-8"),n=JSON.parse(o);return Z=n,n}catch{}return Z=ee,ee}function $t(e,t){if(t[e])return t[e];let o=ue(e);if(t[o])return t[o];for(let[n,s]of Object.entries(t))if(n.includes(o)||o.includes(ue(n)))return s;return e.includes("opus")?ee["claude-opus-4-5"]:e.includes("sonnet")?ee["claude-sonnet-4-5"]:e.includes("haiku")?ee["claude-haiku-4-5"]:ee["claude-sonnet-4-5"]}function Ut(e,t){return e.inputTokens*t.input_cost_per_token+e.outputTokens*t.output_cost_per_token+e.cacheReadTokens*(t.cache_read_input_token_cost||0)+e.cacheCreationTokens*(t.cache_creation_input_token_cost||0)}function D(){return{inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,cost:0}}function H(e,t){return{inputTokens:e.inputTokens+t.inputTokens,outputTokens:e.outputTokens+t.outputTokens,cacheReadTokens:e.cacheReadTokens+t.cacheReadTokens,cacheCreationTokens:e.cacheCreationTokens+t.cacheCreationTokens,cost:e.cost+t.cost}}async function Ft(e){try{let t=await I.stat(e);return{mtime:t.mtimeMs,size:t.size}}catch{return null}}function jt(){try{let e=Oe();if(!J.existsSync(e))return null;let t=JSON.parse(J.readFileSync(e,"utf-8"));return t.version!==we?null:t}catch{return null}}async function Kt(){try{let e=Oe(),t=await I.readFile(e,"utf-8"),o=JSON.parse(t);return o.version!==we?null:o}catch{return null}}function We(){let e=jt();if(!e)return null;let t=new Date,o=new Date(t.getFullYear(),t.getMonth(),t.getDate()),n=new Date(o);n.setDate(n.getDate()-n.getDay());let s={today:D(),week:D(),total:D(),dailyHistory:{},byModel:{},lastUpdated:t.toISOString()};for(let i of Object.values(e.files))for(let a of i.stats.entries){let c=new Date(a.timestamp),l=a.timestamp.split("T")[0];s.total=H(s.total,a.usage),s.dailyHistory[l]||(s.dailyHistory[l]=D()),s.dailyHistory[l]=H(s.dailyHistory[l],a.usage);let u=ue(a.model);s.byModel[u]||(s.byModel[u]=D()),s.byModel[u]=H(s.byModel[u],a.usage),c>=o&&(s.today=H(s.today,a.usage)),c>=n&&(s.week=H(s.week,a.usage))}return s}async function Gt(e){try{await Ye(),await I.writeFile(Oe(),JSON.stringify(e,null,2))}catch{}}async function Yt(e,t,o){let n=[];try{let s=J.createReadStream(e,{encoding:"utf-8"}),i=Ge.createInterface({input:s,crlfDelay:1/0}),a=0;for await(let c of i){if(a++,a%100===0){if(o?.aborted)throw i.close(),s.destroy(),new Error("Aborted");a%1e3===0&&await new Promise(l=>setTimeout(l,0))}if(c.trim())try{let l=JSON.parse(c);if(l.type!=="assistant"||!l.message?.usage)continue;let u=l.message.model||"unknown",m=l.message.usage,y={inputTokens:m.input_tokens||0,outputTokens:m.output_tokens||0,cacheReadTokens:m.cache_read_input_tokens||0,cacheCreationTokens:m.cache_creation_input_tokens||0},p=$t(u,t),_=Ut(y,p);n.push({timestamp:l.timestamp||new Date().toISOString(),model:u,usage:{...y,cost:_}})}catch{}}}catch(s){if(s.message==="Aborted")throw s}return{entries:n}}async function Wt(){let e=[];try{if(!await I.access(Te).then(()=>!0).catch(()=>!1))return e;let o=await I.readdir(Te);for(let n of o){let s=G.join(Te,n);try{if((await I.stat(s)).isDirectory()){let a=await I.readdir(s);for(let c of a)c.endsWith(".jsonl")&&e.push(G.join(s,c))}}catch{}}}catch{}return e}async function Je(e){if(e?.aborted)throw new Error("Aborted");let t=await Ht();if(e?.aborted)throw new Error("Aborted");let o=await Wt();if(e?.aborted)throw new Error("Aborted");let n=await Kt(),s={version:we,files:{},lastUpdated:new Date().toISOString()},i=[],a=5,c=[];for(let p=0;p<o.length;p+=a)c.push(o.slice(p,p+a));for(let p of c){if(e?.aborted)throw new Error("Aborted");let _=p.map(async f=>{let S=await Ft(f);if(!S)return null;let k=n?.files[f],R=k&&k.meta.mtime===S.mtime&&k.meta.size===S.size,T;return R?T=k.stats:(T=await Yt(f,t,e),await new Promise(E=>setTimeout(E,0))),{file:f,meta:S,stats:T}}),g=await Promise.all(_);for(let f of g)f&&(s.files[f.file]={meta:f.meta,stats:f.stats},i.push(...f.stats.entries))}e?.aborted||Gt(s).catch(()=>{});let l=new Date,u=new Date(l.getFullYear(),l.getMonth(),l.getDate()),m=new Date(u);m.setDate(m.getDate()-m.getDay());let y={today:D(),week:D(),total:D(),dailyHistory:{},byModel:{},lastUpdated:l.toISOString()};for(let p of i){let _=new Date(p.timestamp),g=p.timestamp.split("T")[0];y.total=H(y.total,p.usage),y.dailyHistory[g]||(y.dailyHistory[g]=D()),y.dailyHistory[g]=H(y.dailyHistory[g],p.usage);let f=ue(p.model);y.byModel[f]||(y.byModel[f]=D()),y.byModel[f]=H(y.byModel[f],p.usage),_>=u&&(y.today=H(y.today,p.usage)),_>=m&&(y.week=H(y.week,p.usage))}return y}function N(e){return e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"K":e.toString()}function te(e){return e>=1||e>=.01?"$"+e.toFixed(2):"$"+e.toFixed(4)}function z(e){return e.inputTokens+e.outputTokens+e.cacheReadTokens+e.cacheCreationTokens}var r={primary:v.hex("#89B4FA"),accent:v.hex("#CBA6F7"),success:v.hex("#A6E3A1"),warning:v.hex("#F9E2AF"),danger:v.hex("#F38BA8"),text:v.white,muted:v.hex("#6C7086"),dim:v.hex("#45475A")},qe=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Me=0,ne=null,Jt=()=>r.primary(qe[Me]),Ve=e=>{ne||(ne=setInterval(()=>{Me=(Me+1)%qe.length,e()},80))},me=()=>{ne&&(clearInterval(ne),ne=null)};var Xe=()=>process.stdout.columns||80,Qe=(e,t,o)=>{let n=v.hex("#89B4FA"),s=v.hex("#45475A"),i=v.hex("#6C7086"),a=Xe(),c=a<60,l=a<45,u=r.primary("CCEM")+" "+r.muted("Claude Code Env Manager"),m=r.primary("CCEM"),y=r.muted("Env: ")+r.primary(e),p=t.ANTHROPIC_BASE_URL||"-",_=t.ANTHROPIC_MODEL||"-",g=t.ANTHROPIC_SMALL_FAST_MODEL||"-",f=t.ANTHROPIC_API_KEY?t.ANTHROPIC_API_KEY.slice(0,2)+"\u2022\u2022\u2022\u2022"+t.ANTHROPIC_API_KEY.slice(-4):"-",S=(w,j)=>w.length>j?w.slice(0,j-3)+"...":w,k=(w,j)=>{if(w.length<=j)return w;try{let U=new URL(w),xt=U.protocol+"//",He=U.host,Se=U.pathname+U.search,bt=He.slice(0,8),Lt=He.slice(-4),Nt=Se.length>10?Se.slice(0,7)+"...":Se;return`${xt}${bt}...${Lt}${Nt}`}catch{return S(w,j)}},R=7,T;c?T=[y,r.muted("Model:".padEnd(R))+r.dim(S(_,25)),r.muted("Key:".padEnd(R))+r.dim(f)]:T=[y+(o&&O[o]?" "+r.accent(`[${O[o].name}]`):""),r.muted("URL:".padEnd(R))+r.dim(k(p,40)),r.muted("Model:".padEnd(R))+r.dim(S(_,15))+" "+r.muted("Fast:".padEnd(R))+r.dim(S(g,15)),r.muted("Key:".padEnd(R))+r.dim(f)];let E=[];if(l)E.push(` ${i("*")} ${s("\u2584\u2588\u2584")} ${i("*")}`),E.push(` ${i("*")} ${n("\u2590\u259B\u2588\u2588\u2588\u2588\u2588\u259C\u258C")} ${i("*")}`),E.push(` ${i("*")} ${n("\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u259B\u2598")} ${i("*")}`),E.push(` ${i("*")} ${n("\u2598\u2598 \u259D\u259D")} ${i("*")}`),E.push(""),E.push(m),E.push(""),T.forEach(w=>E.push(w));else{let w=c?" ":" ";E.push(""),E.push(` ${s("\u2584\u2588\u2588\u2584")} ${w}${c?m:u}`),E.push(` ${i("*")} ${n("\u2590\u259B\u2588\u2588\u2588\u2588\u2588\u259C\u258C")} ${i("*")} ${w}${T[0]||""}`),E.push(` ${i("*")} ${n("\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u259B\u2598")} ${i("*")} ${w}${T[1]||""}`),E.push(` ${i("*")} ${n("\u2598\u2598 \u259D\u259D")} ${i("*")} ${w}${T[2]||""}`),T[3]&&E.push(` ${w}${T[3]}`)}return E.join(`
|
|
3
|
-
`)},
|
|
4
|
-
`)},
|
|
5
|
-
`)}
|
|
6
|
-
`)
|
|
7
|
-
`):"(\
|
|
8
|
-
`):"(\u65E0)"]),
|
|
9
|
-
`)),console.log(
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import{Command as go}from"commander";import fo from"conf";import M from"inquirer";import p from"chalk";import Ot from"cli-table3";import{spawn as It}from"child_process";import*as wt from"fs";import*as ye from"path";import{fileURLToPath as ho}from"url";import le from"crypto";import ee from"fs";import D from"path";var ve="aes-256-cbc",Be=le.scryptSync("claude-code-env-manager-secret","salt",32),J=e=>{if(!e)return e;let t=le.randomBytes(16),o=le.createCipheriv(ve,Be,t),s=o.update(e,"utf8","hex");return s+=o.final("hex"),`enc:${t.toString("hex")}:${s}`},Y=e=>{if(!e||!e.startsWith("enc:"))return e;try{let t=e.split(":");if(t.length!==3)return e;let o=Buffer.from(t[1],"hex"),s=t[2],n=le.createDecipheriv(ve,Be,o),i=n.update(s,"hex","utf8");return i+=n.final("utf8"),i}catch{return e}},De=()=>{let e=process.cwd(),t=D.parse(e).root;for(;e!==t;){if(ee.existsSync(D.join(e,".git"))||ee.existsSync(D.join(e,"package.json")))return e;e=D.dirname(e)}return process.cwd()},de=(e=!0)=>{let t=De(),o=D.join(t,".claude"),s=e?"settings.local.json":"settings.json";return D.join(o,s)},$e=()=>{let e=De(),t=D.join(e,".claude");return ee.existsSync(t)||ee.mkdirSync(t,{recursive:!0}),t},Ae=()=>process.env.HOME||process.env.USERPROFILE||"",Pe=()=>D.join(Ae(),".claude.json"),Se=()=>D.join(Ae(),".claude","settings.json"),Ue=()=>{let e=D.join(Ae(),".claude");return ee.existsSync(e)||ee.mkdirSync(e,{recursive:!0}),e};var Re={GLM:{ANTHROPIC_BASE_URL:"https://open.bigmodel.cn/api/anthropic",ANTHROPIC_MODEL:"glm-4.6",ANTHROPIC_SMALL_FAST_MODEL:"glm-4.5-air"},KIMI:{ANTHROPIC_BASE_URL:"https://api.moonshot.cn/anthropic",ANTHROPIC_MODEL:"kimi-k2-thinking-turbo",ANTHROPIC_SMALL_FAST_MODEL:"kimi-k2-turbo-preview"},MiniMax:{ANTHROPIC_BASE_URL:"https://api.minimaxi.com/anthropic",ANTHROPIC_MODEL:"MiniMax-M2",ANTHROPIC_SMALL_FAST_MODEL:"MiniMax-M2"},DeepSeek:{ANTHROPIC_BASE_URL:"https://api.deepseek.com/anthropic",ANTHROPIC_MODEL:"deepseek-chat",ANTHROPIC_SMALL_FAST_MODEL:"deepseek-chat"}},w={yolo:{name:"YOLO \u6A21\u5F0F",description:"\u5168\u90E8\u653E\u5F00\uFF0C\u65E0\u4EFB\u4F55\u9650\u5236",permissionMode:"bypassPermissions",permissions:{allow:["Bash(*)","Read(*)","Edit(*)","Write(*)","WebFetch(*)","WebSearch(*)","Glob(*)","Grep(*)","LSP(*)","NotebookEdit(*)"],deny:[]}},dev:{name:"\u5F00\u53D1\u6A21\u5F0F",description:"\u65E5\u5E38\u5F00\u53D1\u6743\u9650\uFF0C\u4FDD\u62A4\u654F\u611F\u6587\u4EF6",permissionMode:"acceptEdits",permissions:{allow:["Read(*)","Edit(*)","Write(*)","Glob(*)","Grep(*)","LSP(*)","NotebookEdit(*)","Bash(npm:*)","Bash(pnpm:*)","Bash(yarn:*)","Bash(bun:*)","Bash(node:*)","Bash(npx:*)","Bash(git:*)","Bash(tsc:*)","Bash(tsx:*)","Bash(eslint:*)","Bash(prettier:*)","Bash(jest:*)","Bash(vitest:*)","Bash(cargo:*)","Bash(python:*)","Bash(pip:*)","Bash(go:*)","Bash(make:*)","Bash(cmake:*)","Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(find:*)","Bash(wc:*)","Bash(mkdir:*)","Bash(cp:*)","Bash(mv:*)","Bash(touch:*)","WebSearch"],deny:["Read(.env)","Read(.env.*)","Read(**/secrets/**)","Read(**/*.pem)","Read(**/*.key)","Read(**/*credential*)","Bash(rm -rf:*)","Bash(sudo:*)","Bash(chmod:*)","Bash(chown:*)"]}},readonly:{name:"\u53EA\u8BFB\u6A21\u5F0F",description:"\u4EC5\u5141\u8BB8\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7981\u6B62\u4EFB\u4F55\u4FEE\u6539",permissionMode:"plan",permissions:{allow:["Read(*)","Glob(*)","Grep(*)","LSP(*)","Bash(git status:*)","Bash(git log:*)","Bash(git diff:*)","Bash(git branch:*)","Bash(git show:*)","Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(find:*)","Bash(wc:*)","Bash(file:*)","WebSearch"],deny:["Edit(*)","Write(*)","NotebookEdit(*)","Bash(rm:*)","Bash(mv:*)","Bash(cp:*)","Bash(mkdir:*)","Bash(touch:*)","Bash(git add:*)","Bash(git commit:*)","Bash(git push:*)","Bash(git checkout:*)","Bash(git reset:*)","Bash(npm install:*)","Bash(pnpm install:*)","Bash(yarn add:*)"]}},safe:{name:"\u5B89\u5168\u6A21\u5F0F",description:"\u4FDD\u5B88\u6743\u9650\uFF0C\u9002\u5408\u4E0D\u719F\u6089\u7684\u4EE3\u7801\u5E93",permissionMode:"default",permissions:{allow:["Read(*)","Glob(*)","Grep(*)","LSP(*)","Bash(git status:*)","Bash(git log:*)","Bash(git diff:*)","Bash(ls:*)","Bash(cat:*)","Bash(head:*)","Bash(tail:*)","Bash(find:*)","Bash(wc:*)"],deny:["Read(.env)","Read(.env.*)","Read(**/secrets/**)","Read(**/*.pem)","Read(**/*.key)","Read(**/*credential*)","Read(**/*password*)","Edit(*)","Write(*)","NotebookEdit(*)","Bash(curl:*)","Bash(wget:*)","Bash(ssh:*)","Bash(scp:*)","Bash(rm:*)","Bash(mv:*)","WebFetch(*)"]}},ci:{name:"CI/CD \u6A21\u5F0F",description:"\u9002\u5408\u81EA\u52A8\u5316\u6D41\u6C34\u7EBF\u7684\u6743\u9650",permissionMode:"default",permissions:{allow:["Read(*)","Edit(*)","Write(*)","Glob(*)","Grep(*)","LSP(*)","Bash(npm:*)","Bash(pnpm:*)","Bash(yarn:*)","Bash(node:*)","Bash(git:*)","Bash(docker:*)","Bash(make:*)","Bash(cargo:*)","Bash(go:*)","Bash(python:*)","Bash(pip:*)","Bash(pytest:*)","Bash(jest:*)","Bash(vitest:*)"],deny:["Read(.env.local)","Read(**/secrets/**)","Bash(sudo:*)","Bash(ssh:*)","Bash(scp:*)","WebFetch(*)","WebSearch"]}},audit:{name:"\u5BA1\u8BA1\u6A21\u5F0F",description:"\u4EC5\u8BFB\u53D6\u548C\u641C\u7D22\uFF0C\u7528\u4E8E\u5B89\u5168\u5BA1\u8BA1",permissionMode:"plan",permissions:{allow:["Read(*)","Glob(*)","Grep(*)","LSP(*)","Bash(git log:*)","Bash(git blame:*)","Bash(git show:*)","Bash(git diff:*)","Bash(ls:*)","Bash(find:*)","Bash(wc:*)","Bash(file:*)","Bash(stat:*)"],deny:["Edit(*)","Write(*)","NotebookEdit(*)","Bash(rm:*)","Bash(mv:*)","Bash(cp:*)","Bash(curl:*)","Bash(wget:*)","Bash(ssh:*)","WebFetch(*)"]}}},Ee=()=>Object.keys(w);import B from"chalk";import Ge from"cli-table3";import*as z from"fs";import*as k from"fs/promises";import*as G from"path";import*as Ce from"os";import*as Fe from"readline";var Te=G.join(Ce.homedir(),".claude","projects"),me=G.join(Ce.homedir(),".ccem"),Oe=1,Ie=()=>G.join(me,"usage-cache.json"),bt=()=>G.join(me,"model-prices.json");async function je(){try{await k.access(me)}catch{await k.mkdir(me,{recursive:!0})}}var Ht="https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",vt=()=>G.join(__dirname,"..","model-prices.json"),oe={"claude-opus-4-5":{input_cost_per_token:5e-6,output_cost_per_token:25e-6,cache_read_input_token_cost:5e-7,cache_creation_input_token_cost:625e-8},"claude-sonnet-4-5":{input_cost_per_token:3e-6,output_cost_per_token:15e-6,cache_read_input_token_cost:3e-7,cache_creation_input_token_cost:375e-8},"claude-haiku-4-5":{input_cost_per_token:1e-6,output_cost_per_token:5e-6,cache_read_input_token_cost:1e-7,cache_creation_input_token_cost:125e-8}};function pe(e){return e.replace(/-20\d{6}.*$/,"").replace(/-v\d+:\d+$/,"").replace(/^anthropic\./,"").replace(/^vertex_ai\//,"").replace(/@.*$/,"")}var te=null;async function Bt(){if(te)return te;await je();let e=bt();try{let t=await fetch(Ht,{signal:AbortSignal.timeout(1e3)});if(t.ok){let o=await t.json(),s={};for(let[n,i]of Object.entries(o))i.input_cost_per_token&&i.output_cost_per_token&&(s[n]={input_cost_per_token:i.input_cost_per_token,output_cost_per_token:i.output_cost_per_token,cache_read_input_token_cost:i.cache_read_input_token_cost,cache_creation_input_token_cost:i.cache_creation_input_token_cost});return await k.writeFile(e,JSON.stringify(s,null,2)),te=s,s}}catch{}try{let t=await k.readFile(e,"utf-8"),o=JSON.parse(t);return te=o,o}catch{}try{let t=vt(),o=await k.readFile(t,"utf-8"),s=JSON.parse(o);return te=s,s}catch{}return te=oe,oe}function Dt(e,t){if(t[e])return t[e];let o=pe(e);if(t[o])return t[o];for(let[s,n]of Object.entries(t))if(s.includes(o)||o.includes(pe(s)))return n;return e.includes("opus")?oe["claude-opus-4-5"]:e.includes("sonnet")?oe["claude-sonnet-4-5"]:e.includes("haiku")?oe["claude-haiku-4-5"]:oe["claude-sonnet-4-5"]}function $t(e,t){return e.inputTokens*t.input_cost_per_token+e.outputTokens*t.output_cost_per_token+e.cacheReadTokens*(t.cache_read_input_token_cost||0)+e.cacheCreationTokens*(t.cache_creation_input_token_cost||0)}function $(){return{inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,cost:0}}function U(e,t){return{inputTokens:e.inputTokens+t.inputTokens,outputTokens:e.outputTokens+t.outputTokens,cacheReadTokens:e.cacheReadTokens+t.cacheReadTokens,cacheCreationTokens:e.cacheCreationTokens+t.cacheCreationTokens,cost:e.cost+t.cost}}async function Ut(e){try{let t=await k.stat(e);return{mtime:t.mtimeMs,size:t.size}}catch{return null}}function Ft(){try{let e=Ie();if(!z.existsSync(e))return null;let t=JSON.parse(z.readFileSync(e,"utf-8"));return t.version!==Oe?null:t}catch{return null}}async function jt(){try{let e=Ie(),t=await k.readFile(e,"utf-8"),o=JSON.parse(t);return o.version!==Oe?null:o}catch{return null}}function Ke(){let e=Ft();if(!e)return null;let t=new Date,o=new Date(t.getFullYear(),t.getMonth(),t.getDate()),s=new Date(o);s.setDate(s.getDate()-s.getDay());let n={today:$(),week:$(),total:$(),dailyHistory:{},byModel:{},lastUpdated:t.toISOString()};for(let i of Object.values(e.files))for(let a of i.stats.entries){let l=new Date(a.timestamp),c=a.timestamp.split("T")[0];n.total=U(n.total,a.usage),n.dailyHistory[c]||(n.dailyHistory[c]=$()),n.dailyHistory[c]=U(n.dailyHistory[c],a.usage);let m=pe(a.model);n.byModel[m]||(n.byModel[m]=$()),n.byModel[m]=U(n.byModel[m],a.usage),l>=o&&(n.today=U(n.today,a.usage)),l>=s&&(n.week=U(n.week,a.usage))}return n}async function Kt(e){try{await je(),await k.writeFile(Ie(),JSON.stringify(e,null,2))}catch{}}async function Yt(e,t,o){let s=[];try{let n=z.createReadStream(e,{encoding:"utf-8"}),i=Fe.createInterface({input:n,crlfDelay:1/0}),a=0;for await(let l of i){if(a++,a%100===0){if(o?.aborted)throw i.close(),n.destroy(),new Error("Aborted");a%1e3===0&&await new Promise(c=>setTimeout(c,0))}if(l.trim())try{let c=JSON.parse(l);if(c.type!=="assistant"||!c.message?.usage)continue;let m=c.message.model||"unknown",u=c.message.usage,A={inputTokens:u.input_tokens||0,outputTokens:u.output_tokens||0,cacheReadTokens:u.cache_read_input_tokens||0,cacheCreationTokens:u.cache_creation_input_tokens||0},g=Dt(m,t),_=$t(A,g);s.push({timestamp:c.timestamp||new Date().toISOString(),model:m,usage:{...A,cost:_}})}catch{}}}catch(n){if(n.message==="Aborted")throw n}return{entries:s}}async function Gt(){let e=[];try{if(!await k.access(Te).then(()=>!0).catch(()=>!1))return e;let o=await k.readdir(Te);for(let s of o){let n=G.join(Te,s);try{if((await k.stat(n)).isDirectory()){let a=await k.readdir(n);for(let l of a)l.endsWith(".jsonl")&&e.push(G.join(n,l))}}catch{}}}catch{}return e}async function Ye(e){if(e?.aborted)throw new Error("Aborted");let t=await Bt();if(e?.aborted)throw new Error("Aborted");let o=await Gt();if(e?.aborted)throw new Error("Aborted");let s=await jt(),n={version:Oe,files:{},lastUpdated:new Date().toISOString()},i=[],a=5,l=[];for(let g=0;g<o.length;g+=a)l.push(o.slice(g,g+a));for(let g of l){if(e?.aborted)throw new Error("Aborted");let _=g.map(async f=>{let d=await Ut(f);if(!d)return null;let P=s?.files[f],S=P&&P.meta.mtime===d.mtime&&P.meta.size===d.size,O;return S?O=P.stats:(O=await Yt(f,t,e),await new Promise(R=>setTimeout(R,0))),{file:f,meta:d,stats:O}}),y=await Promise.all(_);for(let f of y)f&&(n.files[f.file]={meta:f.meta,stats:f.stats},i.push(...f.stats.entries))}e?.aborted||Kt(n).catch(()=>{});let c=new Date,m=new Date(c.getFullYear(),c.getMonth(),c.getDate()),u=new Date(m);u.setDate(u.getDate()-u.getDay());let A={today:$(),week:$(),total:$(),dailyHistory:{},byModel:{},lastUpdated:c.toISOString()};for(let g of i){let _=new Date(g.timestamp),y=g.timestamp.split("T")[0];A.total=U(A.total,g.usage),A.dailyHistory[y]||(A.dailyHistory[y]=$()),A.dailyHistory[y]=U(A.dailyHistory[y],g.usage);let f=pe(g.model);A.byModel[f]||(A.byModel[f]=$()),A.byModel[f]=U(A.byModel[f],g.usage),_>=m&&(A.today=U(A.today,g.usage)),_>=u&&(A.week=U(A.week,g.usage))}return A}function v(e){return e>=1e6?(e/1e6).toFixed(1)+"M":e>=1e3?(e/1e3).toFixed(1)+"K":e.toString()}function ne(e){return e>=1||e>=.01?"$"+e.toFixed(2):"$"+e.toFixed(4)}function q(e){return e.inputTokens+e.outputTokens+e.cacheReadTokens+e.cacheCreationTokens}var r={primary:B.hex("#89B4FA"),accent:B.hex("#CBA6F7"),success:B.hex("#A6E3A1"),warning:B.hex("#F9E2AF"),danger:B.hex("#F38BA8"),text:B.white,muted:B.hex("#6C7086"),dim:B.hex("#45475A")},We=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],we=0,re=null,Wt=()=>r.primary(We[we]),Je=e=>{re||(re=setInterval(()=>{we=(we+1)%We.length,e()},80))},ue=()=>{re&&(clearInterval(re),re=null)};var ze=()=>process.stdout.columns||80,qe=(e,t,o)=>{let s=B.hex("#89B4FA"),n=B.hex("#45475A"),i=B.hex("#6C7086"),a=ze(),l=a<60,c=a<45,m=r.primary("CCEM")+" "+r.muted("Claude Code Env Manager"),u=r.primary("CCEM"),A=r.muted("Env: ")+r.primary(e),g=t.ANTHROPIC_BASE_URL||"-",_=t.ANTHROPIC_MODEL||"-",y=t.ANTHROPIC_SMALL_FAST_MODEL||"-",f=t.ANTHROPIC_API_KEY?t.ANTHROPIC_API_KEY.slice(0,2)+"\u2022\u2022\u2022\u2022"+t.ANTHROPIC_API_KEY.slice(-4):"-",d=(I,K)=>I.length>K?I.slice(0,K-3)+"...":I,P=(I,K)=>{if(I.length<=K)return I;try{let j=new URL(I),kt=j.protocol+"//",He=j.host,_e=j.pathname+j.search,Mt=He.slice(0,8),Nt=He.slice(-4),xt=_e.length>10?_e.slice(0,7)+"...":_e;return`${kt}${Mt}...${Nt}${xt}`}catch{return d(I,K)}},S=7,O;l?O=[A,r.muted("Model:".padEnd(S))+r.dim(d(_,25)),r.muted("Key:".padEnd(S))+r.dim(f)]:O=[A+(o&&w[o]?" "+r.accent(`[${w[o].name}]`):""),r.muted("URL:".padEnd(S))+r.dim(P(g,40)),r.muted("Model:".padEnd(S))+r.dim(d(_,15))+" "+r.muted("Fast:".padEnd(S))+r.dim(d(y,15)),r.muted("Key:".padEnd(S))+r.dim(f)];let R=[];if(c)R.push(` ${i("*")} ${n("\u2584\u2588\u2584")} ${i("*")}`),R.push(` ${i("*")} ${s("\u2590\u259B\u2588\u2588\u2588\u2588\u2588\u259C\u258C")} ${i("*")}`),R.push(` ${i("*")} ${s("\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u259B\u2598")} ${i("*")}`),R.push(` ${i("*")} ${s("\u2598\u2598 \u259D\u259D")} ${i("*")}`),R.push(""),R.push(u),R.push(""),O.forEach(I=>R.push(I));else{let I=l?" ":" ";R.push(""),R.push(` ${n("\u2584\u2588\u2588\u2584")} ${I}${l?u:m}`),R.push(` ${i("*")} ${s("\u2590\u259B\u2588\u2588\u2588\u2588\u2588\u259C\u258C")} ${i("*")} ${I}${O[0]||""}`),R.push(` ${i("*")} ${s("\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u259B\u2598")} ${i("*")} ${I}${O[1]||""}`),R.push(` ${i("*")} ${s("\u2598\u2598 \u259D\u259D")} ${i("*")} ${I}${O[2]||""}`),O[3]&&R.push(` ${I}${O[3]}`)}return R.join(`
|
|
3
|
+
`)},Ve=(e,t)=>{if(t)return r.muted(" Usage: ")+Wt()+r.dim(" Loading...");if(!e)return r.muted(" Usage: ")+r.dim("No data");let s=ze()<70,n=q(e.today),i=q(e.week),a=q(e.total);return s?r.muted(" Usage: ")+r.text("Today ")+r.primary(v(n))+r.dim(" | ")+r.text("Week ")+r.primary(v(i))+r.dim(" | ")+r.text("Total ")+r.primary(v(a)):r.muted(" Usage: ")+r.text("Today ")+r.primary(v(n).padStart(6))+r.dim(` (${ne(e.today.cost)})`)+r.dim(" | ")+r.text("Week ")+r.primary(v(i).padStart(6))+r.dim(` (${ne(e.week.cost)})`)+r.dim(" | ")+r.text("Total ")+r.primary(v(a).padStart(6))+r.dim(` (${ne(e.total.cost)})`)},Xe=()=>{let e=process.stdout.columns||80;return r.dim("\u2500".repeat(e))};var Qe=e=>{let t="Start Claude Code";return e&&w[e]&&(t=`Start Claude Code ${r.muted(`(${w[e].name})`)}`),[{name:r.success(t),value:"start",short:"Start"},{name:r.primary("Switch Environment"),value:"switch",short:"Switch"},{name:r.primary("Permission Mode"),value:"perm",short:"Permission"},{name:r.accent("View Usage"),value:"usage",short:"Usage"},{name:r.text("Set Default Mode"),value:"setDefault",short:"Default"},{name:r.muted("Exit"),value:"exit",short:"Exit"}]},Jt={yolo:r.danger,dev:r.success,readonly:r.primary,safe:r.warning,ci:r.accent,audit:r.primary},Le=(e,t=!1)=>{let s=["yolo","dev","readonly","safe","ci","audit"].map(n=>{let i=w[n],a=Jt[n],c=t&&n===e?r.success(" *"):"";return{name:a(i.name.padEnd(12))+r.muted(i.description)+c,value:n,short:i.name}});return t&&s.push({name:r.muted("Clear default"),value:"clear",short:"Clear"}),s.push({name:r.muted("Back"),value:"back",short:"Back"}),s};var b={success:e=>console.log(r.success("\u2713 ")+r.text(e)),error:e=>console.log(r.danger("\u2717 ")+r.text(e)),warning:e=>console.log(r.warning("! ")+r.text(e)),info:e=>console.log(r.primary("\u203A ")+r.text(e))},Ze=()=>r.muted("Starting Claude Code...");var zt=(e,t=6)=>{let o=[],s=new Date,n=new Date(s);n.setMonth(n.getMonth()-t);let i=n.getDay(),a=n.getDate()-i+(i===0?-6:1);n.setDate(a),n.setHours(0,0,0,0);let l=[],c=new Date(n);for(;(c<=s||c.getDay()!==1)&&(l.push(c.toISOString().split("T")[0]),c.setDate(c.getDate()+1),!(c>s&&c.getDay()===1)););let m=0;for(let d of l){let P=e.dailyHistory[d];if(P){let S=q(P);S>m&&(m=S)}}let u=" ",A=-1,g=Math.ceil(l.length/7);for(let d=0;d<g;d++){let P=d*7;if(P<l.length){let S=new Date(l[P]),O=S.getMonth();if(O!==A&&d<g-1){let R=S.toLocaleString("default",{month:"short"});u+=r.muted(R),A=O}}}u=" ";let _=-1;for(let d=0;d<g;d++){let P=d*7;if(P>=l.length)break;let S=new Date(l[P]),O=S.getMonth();if(O!==_){let R=S.toLocaleString("default",{month:"short"});u+=r.muted(R.padEnd(4)),d++,_=O}else u+=" "}o.push(u);let y=["Mon","","Wed","","Fri","","Sun"],f=[" ","\u2591","\u2592","\u2593","\u2588"];for(let d=0;d<7;d++){let P=r.muted((y[d]||"").padEnd(4)+" ");for(let S=0;S<g;S++){let O=S*7+d;if(O<l.length){let R=l[O];if(new Date(R)>s)P+=" ";else{let I=e.dailyHistory[R],K=I?q(I):0,j=0;K>0&&(m===0?j=0:j=Math.ceil(K/m*4)),P+=(j===0?r.dim("\xB7"):r.primary(f[j]))+" "}}}o.push(P)}return o.push(""),o.push(" "+r.dim("Less ")+r.dim("\xB7")+" "+r.primary(f[1])+" "+r.primary(f[2])+" "+r.primary(f[3])+" "+r.primary(f[4])+" "+r.dim(" More")),o.join(`
|
|
4
|
+
`)},et=e=>{let t=[];t.push(""),t.push(r.primary(" Token Usage Statistics")),t.push(r.dim("\u2500".repeat(60))),t.push(""),t.push(zt(e)),t.push(""),t.push(r.dim("\u2500".repeat(60)));let o=new Ge({head:[r.muted("Period"),r.muted("Input"),r.muted("Output"),r.muted("Cache Read"),r.muted("Cost")],style:{head:[],border:[]},chars:{top:"","top-mid":"","top-left":"","top-right":"",bottom:"","bottom-mid":"","bottom-left":"","bottom-right":"",left:" ","left-mid":"",mid:"","mid-mid":"",right:"","right-mid":"",middle:" "}}),s=(i,a)=>[r.text(i),r.primary(v(a.inputTokens)),r.primary(v(a.outputTokens)),r.primary(v(a.cacheReadTokens)),r.success(ne(a.cost))];o.push(s("Today",e.today)),o.push(s("This Week",e.week)),o.push(s("All Time",e.total)),t.push(o.toString());let n=Object.entries(e.byModel).sort((i,a)=>a[1].cost-i[1].cost);if(n.length>0){t.push(""),t.push(r.dim("\u2500".repeat(60))),t.push(r.muted(" By Model")),t.push("");let i=new Ge({head:[r.muted("Model"),r.muted("Tokens"),r.muted("Cost")],style:{head:[],border:[]},chars:{top:"","top-mid":"","top-left":"","top-right":"",bottom:"","bottom-mid":"","bottom-left":"","bottom-right":"",left:" ","left-mid":"",mid:"","mid-mid":"",right:"","right-mid":"",middle:" "}});for(let[a,l]of n){let c=q(l);i.push([r.text(a),r.primary(v(c)),r.success(ne(l.cost))])}t.push(i.toString())}return t.push(""),t.push(r.dim("\u2500".repeat(60))),t.push(r.muted(` Last updated: ${new Date(e.lastUpdated).toLocaleString()}`)),t.join(`
|
|
5
|
+
`)},qt=e=>{let t=process.stdout.columns||80,o=e.map(a=>r.primary(a.key)+r.dim(a.label)).join(" "),s=e.map(a=>r.primary(a.key)+r.dim(a.short||a.label)).join(" "),n=e.map(a=>r.primary(a.key)).join(" "),i=a=>a.replace(/\x1B\[[0-9;]*m/g,"");return i(o).length<=t?o:i(s).length<=t?s:n},tt=(e,t)=>new Promise(o=>{let s=Object.keys(e),n=s.indexOf(t);n===-1&&(n=0);let i=process.stdin,a=i.isRaw;i.setRawMode(!0),i.resume();let l=[{key:"[\u2191\u2193]",label:" navigate",short:" nav"},{key:"[Enter]",label:" select",short:" sel"},{key:"[e]",label:"dit",short:""},{key:"[r]",label:"ename",short:"en"},{key:"[c]",label:"opy",short:"py"},{key:"[d]",label:"elete",short:"el"}],c=s.length+1,m=f=>{let d=s[f],P=d===t,S=f===n,O=S?r.primary("\u276F "):" ",R=P?r.success(" *"):"",I=S||P?r.primary(d):r.text(d);return O+I+R},u=(f,d)=>{let P=c-f;process.stdout.write(`\x1B[${P}A`),process.stdout.write("\x1B[2K"),process.stdout.write("\x1B[G"),process.stdout.write(d),process.stdout.write(`\x1B[${P}B`),process.stdout.write("\x1B[G")},A=()=>{process.stdout.write("\x1B[?25l"),s.forEach((f,d)=>{console.log(m(d))}),console.log(qt(l))},g=(f,d)=>{f!==d&&(n=d,u(f,m(f)),u(d,m(d)))};A();let _=()=>{i.setRawMode(a??!1),i.removeListener("data",y),process.stdout.write("\x1B[?25h")},y=f=>{let d=f.toString();if(d===""&&(_(),process.exit(0)),d==="\x1B"&&f.length===1){_(),o({action:"cancel"});return}if(d==="\x1B[A"||d==="k"){let P=n,S=Math.max(0,n-1);g(P,S);return}if(d==="\x1B[B"||d==="j"){let P=n,S=Math.min(s.length-1,n+1);g(P,S);return}if(d==="\r"||d===`
|
|
6
|
+
`){_(),o({action:"select",name:s[n]});return}if(d==="e"||d==="E"){_(),o({action:"edit",name:s[n]});return}if(d==="r"||d==="R"){_(),o({action:"rename",name:s[n]});return}if(d==="c"||d==="C"){_(),o({action:"copy",name:s[n]});return}if(d==="d"||d==="D"){_(),o({action:"delete",name:s[n]});return}};i.on("data",y)});import V from"fs";import T from"chalk";import ot from"cli-table3";import{spawn as Vt}from"child_process";var ke=e=>{if(V.existsSync(e))try{let t=V.readFileSync(e,"utf-8");return JSON.parse(t)}catch{console.warn(T.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${e}\uFF0C\u5C06\u521B\u5EFA\u5907\u4EFD`));let t=e+".error."+Date.now();return V.copyFileSync(e,t),console.log(T.gray(`\u5907\u4EFD\u5DF2\u4FDD\u5B58\u5230: ${t}`)),{}}return{}},nt=(e,t)=>{$e(),V.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
7
|
+
`)},Xt=(e,t)=>{let o=e.permissions?.allow||[],s=e.permissions?.deny||[],n=[...new Set([...t.allow,...o])],i=[...new Set([...t.deny,...s])];return{...e,permissions:{allow:n,deny:i}}},st=e=>{let t=w[e];t||(console.error(T.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${e}`)),console.log(T.yellow("\u53EF\u7528\u6A21\u5F0F: "+Ee().join(", "))),process.exit(1));let o=de(!0),s=ke(o),n=Xt(s,t.permissions);nt(o,n),console.log(T.green(`\u5DF2\u5E94\u7528 ${t.name}`)),console.log(T.gray(`\u914D\u7F6E\u5DF2\u5199\u5165: ${o}`)),console.log(T.gray(`\u8BF4\u660E: ${t.description}`))},rt=()=>{let e=de(!0);if(!V.existsSync(e)){console.log(T.yellow("\u6CA1\u6709\u81EA\u5B9A\u4E49\u6743\u9650\u914D\u7F6E\u9700\u8981\u91CD\u7F6E"));return}let t=ke(e);delete t.permissions,Object.keys(t).length===0?(V.unlinkSync(e),console.log(T.green("\u5DF2\u5220\u9664\u914D\u7F6E\u6587\u4EF6\uFF08\u6587\u4EF6\u4E3A\u7A7A\uFF09"))):(nt(e,t),console.log(T.green("\u6743\u9650\u914D\u7F6E\u5DF2\u91CD\u7F6E"))),console.log(T.gray(`\u6587\u4EF6: ${e}`))},it=()=>{let e=de(!0);if(!V.existsSync(e)){console.log(T.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650")),console.log(T.gray(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${e}`));return}let t=ke(e);if(!t.permissions){console.log(T.yellow("\u672A\u914D\u7F6E\u81EA\u5B9A\u4E49\u6743\u9650"));return}let o=Object.entries(w).find(([a,l])=>{let c=new Set(t.permissions?.allow||[]),m=new Set(t.permissions?.deny||[]),u=new Set(l.permissions.allow),A=new Set(l.permissions.deny),g=l.permissions.allow.every(y=>c.has(y)),_=l.permissions.deny.every(y=>m.has(y));return g&&_});console.log(o?T.green(`\u5F53\u524D\u6A21\u5F0F: ${o[0]} (${o[1].name})`):T.yellow("\u5F53\u524D\u6A21\u5F0F: \u81EA\u5B9A\u4E49")),console.log(T.gray(`\u914D\u7F6E\u6587\u4EF6: ${e}`));let s=new ot({head:["\u7C7B\u578B","\u89C4\u5219"],style:{head:["cyan"]},colWidths:[10,70]}),n=t.permissions.allow||[],i=t.permissions.deny||[];s.push(["Allow",n.length>0?n.join(`
|
|
8
|
+
`):"(\u65E0)"]),s.push(["Deny",i.length>0?i.join(`
|
|
9
|
+
`):"(\u65E0)"]),console.log(s.toString())},at=()=>{let e=new ot({head:["\u6A21\u5F0F","\u6807\u5FD7","\u8BF4\u660E"],style:{head:["cyan"]},colWidths:[15,15,50]});Object.entries(w).forEach(([t,o])=>{e.push([o.name,`--${t}`,o.description])}),console.log(T.bold(`\u53EF\u7528\u6743\u9650\u6A21\u5F0F:
|
|
10
|
+
`)),console.log(e.toString()),console.log(T.gray(`
|
|
11
|
+
\u4E34\u65F6\u6A21\u5F0F: ccem <mode>`)),console.log(T.gray("\u6C38\u4E45\u6A21\u5F0F: ccem setup perms --<mode>"))},ge=async(e,t)=>{let o=w[e];o||(console.error(T.red(`\u672A\u77E5\u7684\u6743\u9650\u6A21\u5F0F: ${e}`)),console.log(T.yellow("\u53EF\u7528\u6A21\u5F0F: "+Ee().join(", "))),process.exit(1));let s=[];if(s.push("--permission-mode",o.permissionMode),o.permissions.allow.length>0){let i=o.permissions.allow.map(a=>`"${a}"`).join(" ");s.push("--allowedTools",i)}if(o.permissions.deny.length>0){let i=o.permissions.deny.map(a=>`"${a}"`).join(" ");s.push("--disallowedTools",i)}console.log(T.green(`\u5DF2\u5E94\u7528 ${o.name}\uFF08\u4E34\u65F6\uFF09`)),console.log(T.gray(`\u8BF4\u660E: ${o.description}`)),console.log("");let n={...process.env};return t&&(t.ANTHROPIC_BASE_URL&&(n.ANTHROPIC_BASE_URL=t.ANTHROPIC_BASE_URL),t.ANTHROPIC_API_KEY&&(n.ANTHROPIC_API_KEY=Y(t.ANTHROPIC_API_KEY)),t.ANTHROPIC_MODEL&&(n.ANTHROPIC_MODEL=t.ANTHROPIC_MODEL),t.ANTHROPIC_SMALL_FAST_MODEL&&(n.ANTHROPIC_SMALL_FAST_MODEL=t.ANTHROPIC_SMALL_FAST_MODEL)),new Promise(i=>{let a=Vt("claude",s,{stdio:"inherit",shell:!0,env:n});a.on("exit",l=>{process.exit(l??0)}),a.on("error",l=>{console.error(T.red(`\u542F\u52A8 Claude Code \u5931\u8D25: ${l.message}`)),process.exit(1)})})};import Me from"fs";import E from"chalk";import{spawn as Qt}from"child_process";var ct=e=>{if(Me.existsSync(e))try{let t=Me.readFileSync(e,"utf-8");return JSON.parse(t)}catch{return console.warn(E.yellow(`\u8B66\u544A: \u65E0\u6CD5\u89E3\u6790 ${e}`)),{}}return{}},lt=(e,t)=>{Me.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
12
|
+
`)},Zt=()=>{let e=Pe();try{let t=ct(e);return t.hasCompletedOnboarding===!0?(console.log(E.gray(" \u2713 hasCompletedOnboarding \u5DF2\u8BBE\u7F6E")),!0):(t.hasCompletedOnboarding=!0,lt(e,t),console.log(E.green(" \u2713 \u5DF2\u8BBE\u7F6E hasCompletedOnboarding: true")),!0)}catch(t){return console.error(E.red(` \u2717 \u8BBE\u7F6E hasCompletedOnboarding \u5931\u8D25: ${t}`)),!1}},eo=()=>{let e=Se();try{Ue();let t=ct(e);(!t.env||typeof t.env!="object")&&(t.env={});let o=t.env,s={DISABLE_BUG_COMMAND:"1",DISABLE_ERROR_REPORTING:"1",DISABLE_TELEMETRY:"1"},n=!1;for(let[i,a]of Object.entries(s))o[i]!==a&&(o[i]=a,n=!0);return n?(lt(e,t),console.log(E.green(" \u2713 \u5DF2\u914D\u7F6E\u73AF\u5883\u53D8\u91CF:")),console.log(E.gray(" DISABLE_BUG_COMMAND=1")),console.log(E.gray(" DISABLE_ERROR_REPORTING=1")),console.log(E.gray(" DISABLE_TELEMETRY=1")),!0):(console.log(E.gray(" \u2713 \u73AF\u5883\u53D8\u91CF\u5DF2\u914D\u7F6E")),!0)}catch(t){return console.error(E.red(` \u2717 \u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF\u5931\u8D25: ${t}`)),!1}},to=()=>new Promise(e=>{console.log(E.cyan(" \u2192 \u6B63\u5728\u6DFB\u52A0 chrome-devtools MCP \u5DE5\u5177..."));let t=Qt("claude",["mcp","add","chrome-devtools","npx","chrome-devtools-mcp@latest","--scope","user"],{stdio:"pipe",shell:!0}),o="",s="";t.stdout?.on("data",n=>{o+=n.toString()}),t.stderr?.on("data",n=>{s+=n.toString()}),t.on("exit",n=>{n===0?(console.log(E.green(" \u2713 \u5DF2\u6DFB\u52A0 chrome-devtools MCP \u5DE5\u5177")),e(!0)):s.includes("already exists")||o.includes("already exists")?(console.log(E.gray(" \u2713 chrome-devtools MCP \u5DE5\u5177\u5DF2\u5B58\u5728")),e(!0)):(console.error(E.red(` \u2717 \u6DFB\u52A0 MCP \u5DE5\u5177\u5931\u8D25 (code: ${n})`)),s&&console.error(E.gray(` ${s.trim()}`)),e(!1))}),t.on("error",n=>{console.error(E.red(` \u2717 \u6267\u884C claude \u547D\u4EE4\u5931\u8D25: ${n.message}`)),console.log(E.yellow(" \u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 Claude Code CLI")),e(!1)})}),dt=async()=>{console.log(E.bold(`
|
|
12
13
|
\u{1F527} Claude Code \u521D\u59CB\u5316\u8BBE\u7F6E
|
|
13
|
-
`)),console.log(
|
|
14
|
-
2. \u914D\u7F6E\u9690\u79C1\u8BBE\u7F6E`));let t=eo();console.log(
|
|
15
|
-
3. \u5B89\u88C5 MCP \u5DE5\u5177`));let o=await to();console.log(""),console.log(e&&t&&o?
|
|
16
|
-
\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E:`)),console.log(
|
|
14
|
+
`)),console.log(E.cyan("1. \u8BBE\u7F6E onboarding \u72B6\u6001"));let e=Zt();console.log(E.cyan(`
|
|
15
|
+
2. \u914D\u7F6E\u9690\u79C1\u8BBE\u7F6E`));let t=eo();console.log(E.cyan(`
|
|
16
|
+
3. \u5B89\u88C5 MCP \u5DE5\u5177`));let o=await to();console.log(""),console.log(e&&t&&o?E.green.bold("\u2705 \u521D\u59CB\u5316\u5B8C\u6210\uFF01"):E.yellow.bold("\u26A0\uFE0F \u90E8\u5206\u6B65\u9AA4\u672A\u5B8C\u6210\uFF0C\u8BF7\u68C0\u67E5\u4E0A\u8FF0\u9519\u8BEF")),console.log(E.gray(`
|
|
17
|
+
\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E:`)),console.log(E.gray(` - ${Pe()}`)),console.log(E.gray(` - ${Se()}`)),console.log("")};import{execSync as se}from"child_process";import*as C from"fs";import*as H from"path";import L from"chalk";var pt={official:{label:"\u5B98\u65B9",icon:"\u{1F3E2}"},featured:{label:"\u7CBE\u9009",icon:"\u2B50"},others:{label:"\u5176\u4ED6",icon:"\u{1F4E6}"}},ut=[{name:"frontend-design",description:"\u521B\u5EFA\u9AD8\u8D28\u91CF\u524D\u7AEF\u754C\u9762\u8BBE\u8BA1",group:"official",install:{type:"preset",name:"frontend-design"}},{name:"skill-creator",description:"\u521B\u5EFA\u65B0\u7684 Claude Code skills",group:"official",install:{type:"preset",name:"skill-creator"}},{name:"web-artifacts-builder",description:"\u6784\u5EFA\u53EF\u4EA4\u4E92\u7684 Web \u7EC4\u4EF6",group:"official",install:{type:"preset",name:"web-artifacts-builder"}},{name:"canvas-design",description:"Canvas \u7ED8\u56FE\u8BBE\u8BA1",group:"official",install:{type:"preset",name:"canvas-design"}},{name:"algorithmic-art",description:"\u7B97\u6CD5\u827A\u672F\u751F\u6210",group:"official",install:{type:"preset",name:"algorithmic-art"}},{name:"theme-factory",description:"\u4E3B\u9898\u5DE5\u5382 - \u521B\u5EFA UI \u4E3B\u9898",group:"official",install:{type:"preset",name:"theme-factory"}},{name:"mcp-builder",description:"\u6784\u5EFA MCP \u670D\u52A1\u5668",group:"official",install:{type:"preset",name:"mcp-builder"}},{name:"webapp-testing",description:"Web \u5E94\u7528\u6D4B\u8BD5",group:"official",install:{type:"preset",name:"webapp-testing"}},{name:"pdf",description:"PDF \u6587\u6863\u5904\u7406",group:"official",install:{type:"preset",name:"pdf"}},{name:"docx",description:"Word \u6587\u6863\u5904\u7406",group:"official",install:{type:"preset",name:"docx"}},{name:"pptx",description:"PowerPoint \u6F14\u793A\u6587\u7A3F\u5904\u7406",group:"official",install:{type:"preset",name:"pptx"}},{name:"xlsx",description:"Excel \u8868\u683C\u5904\u7406",group:"official",install:{type:"preset",name:"xlsx"}},{name:"brand-guidelines",description:"\u54C1\u724C\u6307\u5357\u751F\u6210",group:"official",install:{type:"preset",name:"brand-guidelines"}},{name:"doc-coauthoring",description:"\u6587\u6863\u534F\u4F5C\u7F16\u5199",group:"official",install:{type:"preset",name:"doc-coauthoring"}},{name:"internal-comms",description:"\u5185\u90E8\u901A\u4FE1\u6587\u6863",group:"official",install:{type:"preset",name:"internal-comms"}},{name:"slack-gif-creator",description:"Slack GIF \u521B\u5EFA\u5668",group:"official",install:{type:"preset",name:"slack-gif-creator"}},{name:"superpowers",description:"Claude Code Plan\u6A21\u5F0F\u5347\u7EA7\u7248\uFF0C\u8FDE\u7EED\u8FFD\u95EE\u8BA8\u8BBA\u786E\u5B9A\u5F00\u53D1\u65B9\u6848",group:"featured",install:{type:"plugin",marketplace:"obra/superpowers-marketplace",package:"superpowers@superpowers-marketplace"}},{name:"ui-ux-pro-max",description:"\u4E13\u4E1A UI/UX \u8BBE\u8BA1",group:"featured",install:{type:"github",url:"https://github.com/nextlevelbuilder/ui-ux-pro-max-skill/tree/main/.claude/skills/ui-ux-pro-max"}},{name:"Humanizer-zh",description:"\u53BB\u9664\u6587\u672C\u4E2D AI \u751F\u6210\u75D5\u8FF9\uFF0C\u6539\u5199\u5F97\u66F4\u81EA\u7136\u3001\u66F4\u50CF\u4EBA\u7C7B\u4E66\u5199",group:"featured",install:{type:"github",url:"https://github.com/op7418/Humanizer-zh"}},{name:"skill-writer",description:"\u6307\u5BFC\u7528\u6237\u4E3A Claude Code \u521B\u5EFA\u4EE3\u7406\u6280\u80FD",group:"others",install:{type:"github",url:"https://github.com/pytorch/pytorch/tree/main/.claude/skills/skill-writer"}}];function gt(e){return ut.filter(t=>t.group===e)}function ft(){return["official","featured","others"]}function mt(e){if(/^[\w-]+\/[\w-]+$/.test(e)){let[a,l]=e.split("/");return{owner:a,repo:l,branch:"main",path:""}}let t=e.match(/github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)(?:\/(.*))?)?/);if(!t)return null;let[,o,s,n="main",i=""]=t;return{owner:o,repo:s.replace(/\.git$/,""),branch:n,path:i}}function Ne(){return H.join(process.cwd(),".claude","skills")}function oo(){let e=Ne();return C.existsSync(e)?no(e):C.mkdirSync(e,{recursive:!0}),e}function no(e){try{let t=C.readdirSync(e,{withFileTypes:!0});for(let o of t)if(o.isDirectory()&&o.name.startsWith(".tmp-")){let s=H.join(e,o.name);C.rmSync(s,{recursive:!0})}}catch{}}function fe(e,t,o,s,n){let i=oo(),a=H.join(i,n);C.existsSync(a)&&(console.log(L.yellow(`Skill "${n}" already exists. Updating...`)),C.rmSync(a,{recursive:!0}));let l=`https://github.com/${e}/${t}.git`,c=H.join(i,`.tmp-${Date.now()}`);try{C.mkdirSync(c,{recursive:!0}),se("git init",{cwd:c,stdio:"pipe"}),se(`git remote add origin ${l}`,{cwd:c,stdio:"pipe"}),se("git config core.sparseCheckout true",{cwd:c,stdio:"pipe"});let m=H.join(c,".git","info","sparse-checkout");C.writeFileSync(m,s?`${s}/
|
|
17
18
|
`:`*
|
|
18
|
-
`),
|
|
19
|
-
Loaded ${
|
|
20
|
-
Run 'ccem ls' to see all environments.`))};var yo=ho(import.meta.url),_o=
|
|
21
|
-
To apply to current shell immediately, run:`)),console.log(
|
|
22
|
-
Or manually export:`)),
|
|
23
|
-
\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u5F0F: ccem setup default-mode --dev`)),console.log(
|
|
19
|
+
`),se(`git pull --depth=1 origin ${o}`,{cwd:c,stdio:"pipe"});let u=s?H.join(c,s):c;if(!C.existsSync(u))throw new Error(`Path "${s}" not found in repository`);return ht(u,a),console.log(L.green(`Successfully installed skill "${n}"`)),!0}catch(m){let u=m instanceof Error?m.message:String(m);return console.error(L.red(`Failed to download skill: ${u}`)),!1}finally{C.existsSync(c)&&C.rmSync(c,{recursive:!0})}}function ht(e,t){C.mkdirSync(t,{recursive:!0});let o=C.readdirSync(e,{withFileTypes:!0});for(let s of o){if(s.name===".git")continue;let n=H.join(e,s.name),i=H.join(t,s.name);s.isDirectory()?ht(n,i):C.copyFileSync(n,i)}}function he(e){let t=ut.find(n=>n.name===e);if(t){if(t.install.type==="preset")return fe("anthropics","skills","main",`skills/${t.install.name}`,t.name);if(t.install.type==="github"){let n=mt(t.install.url);return n?fe(n.owner,n.repo,n.branch,n.path,t.name):(console.error(L.red(`Invalid GitHub URL in preset: ${t.install.url}`)),!1)}else if(t.install.type==="plugin")return console.error(L.yellow(`Plugin installation not yet supported for "${t.name}"`)),console.log(L.gray(`Marketplace: ${t.install.marketplace}`)),console.log(L.gray(`Package: ${t.install.package}`)),!1}let o=mt(e);if(!o)return console.error(L.red("Invalid GitHub URL or preset name")),console.log(L.gray("Examples:")),console.log(L.gray(" ccem skill add frontend-design")),console.log(L.gray(" ccem skill add https://github.com/owner/repo")),console.log(L.gray(" ccem skill add https://github.com/owner/repo/tree/main/path")),!1;let s;return o.path?s=H.basename(o.path):s=o.repo,fe(o.owner,o.repo,o.branch,o.path,s)}function yt(){let e=Ne();return C.existsSync(e)?C.readdirSync(e,{withFileTypes:!0}).filter(o=>o.isDirectory()&&!o.name.startsWith(".")).map(o=>({name:o.name,path:H.join(e,o.name)})):[]}function _t(e){let t=Ne(),o=H.join(t,e);return C.existsSync(o)?(C.rmSync(o,{recursive:!0}),console.log(L.green(`Removed skill "${e}"`)),!0):(console.error(L.red(`Skill "${e}" not found`)),!1)}function so(e,t){try{return console.log(L.cyan(`Adding marketplace: ${e}...`)),se(`claude plugin marketplace add ${e}`,{stdio:"inherit"}),console.log(L.cyan(`Installing package: ${t}...`)),se(`claude plugin install ${t}`,{stdio:"inherit"}),console.log(L.green(`Successfully installed ${t}`)),!0}catch(o){let s=o instanceof Error?o.message:String(o);return console.error(L.red(`Failed to install from marketplace: ${s}`)),!1}}function At(e){switch(console.log(L.cyan(`Installing ${e.name}...`)),e.install.type){case"preset":let t={repo:"anthropics/skills",path:`skills/${e.install.name}`,branch:"main"},[o,s]=t.repo.split("/");return fe(o,s,t.branch,t.path,e.name);case"github":return he(e.install.url);case"plugin":return so(e.install.marketplace,e.install.package)}}import{render as co}from"ink";import{useState as Pt,useEffect as ro}from"react";import{Box as W,Text as X,useInput as io,useApp as ao}from"ink";import{jsx as F,jsxs as ie}from"react/jsx-runtime";function St({onSelect:e,onCancel:t}){let{exit:o}=ao(),s=ft(),[n,i]=Pt(0),[a,l]=Pt(0),c=s[n],u=[...gt(c),null],A=u.length-1;return ro(()=>{l(0)},[n]),io((g,_)=>{if(_.tab&&!_.shift){i(y=>(y+1)%s.length);return}if(_.tab&&_.shift){i(y=>(y-1+s.length)%s.length);return}if(_.upArrow){l(y=>Math.max(0,y-1));return}if(_.downArrow){l(y=>Math.min(A,y+1));return}if(_.return){let y=u[a];e(y===null?"custom":y);return}if(_.escape||g==="q"){t(),o();return}}),ie(W,{flexDirection:"column",children:[F(W,{marginBottom:1,children:s.map((g,_)=>{let y=pt[g],f=_===n;return F(W,{marginRight:2,children:ie(X,{bold:f,color:f?"cyan":"gray",inverse:f,children:[" ",y.icon," ",y.label," "]})},g)})}),F(W,{marginBottom:1,children:F(X,{color:"gray",children:"\u2500".repeat(50)})}),F(W,{flexDirection:"column",children:u.map((g,_)=>{let y=_===a,f=y?"\u276F ":" ";return g===null?F(W,{children:ie(X,{color:y?"yellow":"gray",children:[f,"\u8F93\u5165\u81EA\u5B9A\u4E49 GitHub URL"]})},"custom"):F(W,{children:ie(X,{color:y?"cyan":void 0,children:[f,F(X,{bold:y,children:g.name}),ie(X,{color:"gray",children:[" - ",g.description]})]})},g.name)})}),F(W,{marginTop:1,children:F(X,{color:"gray",children:"Tab \u5207\u6362\u5206\u7EC4 | \u2191\u2193 \u9009\u62E9 | Enter \u786E\u8BA4 | Esc \u53D6\u6D88"})})]})}import{jsx as lo}from"react/jsx-runtime";async function Rt(){return new Promise(e=>{let t=!1,{unmount:o,waitUntilExit:s}=co(lo(St,{onSelect:n=>{t||(t=!0,o(),e(n==="custom"?{type:"custom"}:{type:"skill",skill:n}))},onCancel:()=>{t||(t=!0,o(),e({type:"cancelled"}))}}));s().then(()=>{t||e({type:"cancelled"})})})}import Et from"crypto";import x from"chalk";import mo from"conf";var Tt=new mo({projectName:"claude-code-env-manager"}),po=(e,t)=>{let o=Et.scryptSync(t,"ccem-salt",32),s=Buffer.from(e,"base64"),n=s.subarray(0,16),i=s.subarray(16).toString("hex"),a=Et.createDecipheriv("aes-256-cbc",o,n),l=a.update(i,"hex","utf8");return l+=a.final("utf8"),l},uo=(e,t)=>{if(!t.has(e))return e;let o=1,s=`${e}-remote`;for(;t.has(s);)o++,s=`${e}-remote-${o}`;return s},Ct=async(e,t)=>{console.log(x.gray("Fetching from remote..."));let o;try{o=await fetch(e)}catch(c){console.error(x.red("Error: Failed to connect to server")),console.error(x.gray(c.message)),process.exit(1)}o.status===401&&(console.error(x.red("Error: Invalid key (HTTP 401)")),process.exit(1)),o.status===429&&(console.error(x.red("Error: Too many requests, please try again later")),process.exit(1)),o.ok||(console.error(x.red(`Error: Server returned HTTP ${o.status}`)),process.exit(1));let s;try{s=await o.json()}catch{console.error(x.red("Error: Invalid response format from server")),process.exit(1)}s.encrypted||(console.error(x.red("Error: Invalid response format from server")),process.exit(1));let n;try{let c=po(s.encrypted,t);n=JSON.parse(c)}catch{console.error(x.red("Error: Decryption failed, check your --secret")),process.exit(1)}(!n.environments||typeof n.environments!="object")&&(console.error(x.red("Error: Invalid response format from server")),process.exit(1));let i=Tt.get("registries"),a=new Set(Object.keys(i)),l=[];for(let[c,m]of Object.entries(n.environments)){let u=uo(c,a),A=u!==c,g={...m};g.ANTHROPIC_API_KEY&&(g.ANTHROPIC_API_KEY=J(g.ANTHROPIC_API_KEY)),i[u]=g,a.add(u),l.push({name:u,originalName:c,renamed:A})}Tt.set("registries",i),console.log(x.green(`
|
|
20
|
+
Loaded ${l.length} environment(s) from remote:`));for(let c of l)c.renamed?console.log(x.yellow(` + ${c.originalName} \u2192 ${c.name} (renamed, local exists)`)):console.log(x.green(` + ${c.name} (new)`));console.log(x.gray(`
|
|
21
|
+
Run 'ccem ls' to see all environments.`))};var yo=ho(import.meta.url),_o=ye.dirname(yo),Ao=ye.resolve(_o,"..","package.json"),Po=JSON.parse(wt.readFileSync(Ao,"utf-8")),N=new go,h=new fo({projectName:"claude-code-env-manager",defaults:{registries:{official:{ANTHROPIC_BASE_URL:"https://api.anthropic.com",ANTHROPIC_MODEL:"claude-sonnet-4-5-20250929",ANTHROPIC_SMALL_FAST_MODEL:"claude-haiku-4-5-20251001"}},current:"official",defaultMode:null}}),ce=["yolo","dev","readonly","safe","ci","audit"],Q=null,ae=!0;var Z=null,So=e=>{let t=Ke();t?(Q=t,ae=!1):e&&Je(e),Z&&Z.abort(),Z=new AbortController;let o=Z.signal;Ye(o).then(s=>{if(o.aborted)return;let n=ae||Q&&s&&Q.lastUpdated!==s.lastUpdated;Q=s,ae=!1,ue(),n&&e&&e()}).catch(s=>{s.message!=="Aborted"&&(ae=!1,ue())})};N.name("ccem").description("Claude Code Environment Manager - \u7BA1\u7406 Claude Code \u73AF\u5883\u53D8\u91CF\u548C\u6743\u9650").version(Po.version).option("--mode","\u67E5\u770B\u5F53\u524D\u6743\u9650\u6A21\u5F0F").option("--list-modes","\u5217\u51FA\u6240\u6709\u53EF\u7528\u6743\u9650\u6A21\u5F0F");ce.forEach(e=>{let t=w[e];N.command(e).description(`\u4E34\u65F6\u5E94\u7528 ${t.name}\uFF0C\u9000\u51FA\u540E\u8FD8\u539F`).action(async()=>{let o=h.get("registries"),s=h.get("current"),n=o[s];await ge(e,n)})});var Lt=(e,t)=>{if(!process.stdout.isTTY)return;let o=h.get("current"),n=h.get("registries")[o],i=h.get("defaultMode");n&&(console.log(qe(o,{ANTHROPIC_BASE_URL:n.ANTHROPIC_BASE_URL,ANTHROPIC_API_KEY:n.ANTHROPIC_API_KEY?Y(n.ANTHROPIC_API_KEY):void 0,ANTHROPIC_MODEL:n.ANTHROPIC_MODEL,ANTHROPIC_SMALL_FAST_MODEL:n.ANTHROPIC_SMALL_FAST_MODEL},i)),console.log(""),console.log(Ve(e,t)),console.log(Xe()),console.log(""))},Ro=async e=>{let t=h.get("registries");if(!t[e]){console.error(p.red(`Environment '${e}' not found.`));return}h.set("current",e),process.stdout.isTTY?console.log(p.green(`Switched to environment '${e}'`)):console.error(p.green(`Switched to environment '${e}'`)),Lt(null,!1);let o=t[e],s=[];o.ANTHROPIC_BASE_URL&&s.push(`export ANTHROPIC_BASE_URL="${o.ANTHROPIC_BASE_URL}"`),o.ANTHROPIC_API_KEY&&s.push(`export ANTHROPIC_API_KEY="${Y(o.ANTHROPIC_API_KEY)}"`),o.ANTHROPIC_MODEL&&s.push(`export ANTHROPIC_MODEL="${o.ANTHROPIC_MODEL}"`),o.ANTHROPIC_SMALL_FAST_MODEL&&s.push(`export ANTHROPIC_SMALL_FAST_MODEL="${o.ANTHROPIC_SMALL_FAST_MODEL}"`),process.stdout.isTTY?(console.log(p.yellow(`
|
|
22
|
+
To apply to current shell immediately, run:`)),console.log(p.cyan("eval $(ccem env)")),console.log(p.yellow(`
|
|
23
|
+
Or manually export:`)),s.forEach(n=>console.log(n))):s.forEach(n=>console.log(n))};N.command("ls").description("List all configured environments").action(()=>{let e=h.get("registries"),t=h.get("current"),o=new Ot({head:["Name","Base URL","Model"],style:{head:["cyan"]}});Object.keys(e).forEach(s=>{let n=e[s],i=s===t?p.green("* "):" ";o.push([i+s,n.ANTHROPIC_BASE_URL||"-",n.ANTHROPIC_MODEL||"-"])}),console.log(o.toString())});N.command("use <name>").description("Switch to a specific environment").action(async e=>{await Ro(e)});N.command("add <name>").description("Add a new environment configuration").action(async e=>{let t=h.get("registries");if(t[e]){console.log(p.red(`Environment '${e}' already exists.`));return}let{usePreset:o}=await M.prompt([{type:"confirm",name:"usePreset",message:"Do you want to use a preset configuration?",default:!0}]),s={};if(o){let{presetName:i}=await M.prompt([{type:"list",name:"presetName",message:"Select a preset:",choices:Object.keys(Re)}]);s=Re[i]}let n=await M.prompt([{type:"input",name:"ANTHROPIC_BASE_URL",message:"Enter ANTHROPIC_BASE_URL:",default:s.ANTHROPIC_BASE_URL||"https://api.anthropic.com"},{type:"password",name:"ANTHROPIC_API_KEY",message:"Enter ANTHROPIC_API_KEY:"},{type:"input",name:"ANTHROPIC_MODEL",message:"Enter ANTHROPIC_MODEL:",default:s.ANTHROPIC_MODEL||"claude-sonnet-4-5-20250929"},{type:"input",name:"ANTHROPIC_SMALL_FAST_MODEL",message:"Enter ANTHROPIC_SMALL_FAST_MODEL:",default:s.ANTHROPIC_SMALL_FAST_MODEL||"claude-haiku-4-5-20251001"}]);n.ANTHROPIC_API_KEY&&(n.ANTHROPIC_API_KEY=J(n.ANTHROPIC_API_KEY)),t[e]=n,h.set("registries",t),console.log(p.green(`Environment '${e}' added successfully.`))});N.command("del <name>").description("Delete an environment configuration").action(e=>{let t=h.get("registries");if(!t[e]){console.log(p.red(`Environment '${e}' not found.`));return}if(e==="official"){console.log(p.red("Cannot delete default 'official' environment."));return}delete t[e],h.set("registries",t),h.get("current")===e&&(h.set("current","official"),console.log(p.yellow("Deleted current environment. Switched back to 'official'."))),console.log(p.green(`Environment '${e}' deleted.`))});N.command("rename <old> <new>").description("Rename an environment configuration").action((e,t)=>{let o=h.get("registries");if(!o[e]){console.log(p.red(`Environment '${e}' not found.`));return}if(o[t]){console.log(p.red(`Environment '${t}' already exists.`));return}if(e==="official"){console.log(p.red("Cannot rename default 'official' environment."));return}o[t]=o[e],delete o[e],h.set("registries",o),h.get("current")===e&&h.set("current",t),console.log(p.green(`Environment '${e}' renamed to '${t}'.`))});N.command("cp <source> <target>").description("Copy an environment configuration").action(async(e,t)=>{let o=h.get("registries");if(!o[e]){console.log(p.red(`Environment '${e}' not found.`));return}if(o[t]){console.log(p.red(`Environment '${t}' already exists.`));return}o[t]={...o[e]},h.set("registries",o),console.log(p.green(`Environment '${e}' copied to '${t}'.`));let{modify:s}=await M.prompt([{type:"confirm",name:"modify",message:"Do you want to modify the configuration?",default:!1}]);if(s){let n=o[t],i=await M.prompt([{type:"input",name:"ANTHROPIC_BASE_URL",message:"ANTHROPIC_BASE_URL:",default:n.ANTHROPIC_BASE_URL},{type:"password",name:"ANTHROPIC_API_KEY",message:"ANTHROPIC_API_KEY (leave empty to keep current):"},{type:"input",name:"ANTHROPIC_MODEL",message:"ANTHROPIC_MODEL:",default:n.ANTHROPIC_MODEL},{type:"input",name:"ANTHROPIC_SMALL_FAST_MODEL",message:"ANTHROPIC_SMALL_FAST_MODEL:",default:n.ANTHROPIC_SMALL_FAST_MODEL}]);i.ANTHROPIC_BASE_URL&&(n.ANTHROPIC_BASE_URL=i.ANTHROPIC_BASE_URL),i.ANTHROPIC_API_KEY&&(n.ANTHROPIC_API_KEY=J(i.ANTHROPIC_API_KEY)),i.ANTHROPIC_MODEL&&(n.ANTHROPIC_MODEL=i.ANTHROPIC_MODEL),i.ANTHROPIC_SMALL_FAST_MODEL&&(n.ANTHROPIC_SMALL_FAST_MODEL=i.ANTHROPIC_SMALL_FAST_MODEL),o[t]=n,h.set("registries",o),console.log(p.green(`Environment '${t}' updated.`))}});N.command("current").description("Show current environment name").action(()=>{let e=h.get("current");console.log(p.green(e))});N.command("env").description("Output environment variables for shell eval").option("--json","Output as JSON").action(e=>{let t=h.get("registries"),o=h.get("current"),s=t[o];if(!s)return;let n={...s};n.ANTHROPIC_API_KEY&&(n.ANTHROPIC_API_KEY=Y(n.ANTHROPIC_API_KEY)),e.json?console.log(JSON.stringify(n,null,2)):(n.ANTHROPIC_BASE_URL&&console.log(`export ANTHROPIC_BASE_URL="${n.ANTHROPIC_BASE_URL}"`),n.ANTHROPIC_API_KEY&&console.log(`export ANTHROPIC_API_KEY="${n.ANTHROPIC_API_KEY}"`),n.ANTHROPIC_MODEL&&console.log(`export ANTHROPIC_MODEL="${n.ANTHROPIC_MODEL}"`),n.ANTHROPIC_SMALL_FAST_MODEL&&console.log(`export ANTHROPIC_SMALL_FAST_MODEL="${n.ANTHROPIC_SMALL_FAST_MODEL}"`))});N.command("run <command...>").description("Run a command with the current environment variables").action(e=>{let t=h.get("registries"),o=h.get("current"),s=t[o];s||(console.error(p.red("No environment configuration found.")),process.exit(1));let n={...process.env};s.ANTHROPIC_BASE_URL&&(n.ANTHROPIC_BASE_URL=s.ANTHROPIC_BASE_URL),s.ANTHROPIC_API_KEY&&(n.ANTHROPIC_API_KEY=Y(s.ANTHROPIC_API_KEY||"")),s.ANTHROPIC_MODEL&&(n.ANTHROPIC_MODEL=s.ANTHROPIC_MODEL),s.ANTHROPIC_SMALL_FAST_MODEL&&(n.ANTHROPIC_SMALL_FAST_MODEL=s.ANTHROPIC_SMALL_FAST_MODEL);let[i,...a]=e;It(i,a,{env:n,stdio:"inherit",shell:!0}).on("exit",c=>{process.exit(c??0)})});var xe=N.command("setup").description("Setup commands for permanent configurations");xe.command("perms").description("\u6C38\u4E45\u914D\u7F6E\u6743\u9650\u6A21\u5F0F").option("--yolo","\u5E94\u7528 YOLO \u6A21\u5F0F\uFF08\u5168\u90E8\u653E\u5F00\uFF09").option("--dev","\u5E94\u7528\u5F00\u53D1\u6A21\u5F0F").option("--readonly","\u5E94\u7528\u53EA\u8BFB\u6A21\u5F0F").option("--safe","\u5E94\u7528\u5B89\u5168\u6A21\u5F0F").option("--ci","\u5E94\u7528 CI/CD \u6A21\u5F0F").option("--audit","\u5E94\u7528\u5BA1\u8BA1\u6A21\u5F0F").option("--reset","\u91CD\u7F6E\u6743\u9650\u914D\u7F6E").action(function(){let e=this.opts();if(e.reset){rt();return}for(let t of ce)if(e[t]){st(t);return}console.log(p.yellow("\u8BF7\u6307\u5B9A\u4E00\u4E2A\u6743\u9650\u6A21\u5F0F\uFF0C\u4F8B\u5982: ccem setup perms --dev")),console.log(p.gray("\u53EF\u7528\u6A21\u5F0F: "+ce.join(", "))),console.log(p.gray("\u91CD\u7F6E\u6743\u9650: ccem setup perms --reset"))});xe.command("default-mode").description("\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F").option("--yolo","YOLO \u6A21\u5F0F").option("--dev","\u5F00\u53D1\u6A21\u5F0F").option("--readonly","\u53EA\u8BFB\u6A21\u5F0F").option("--safe","\u5B89\u5168\u6A21\u5F0F").option("--ci","CI/CD \u6A21\u5F0F").option("--audit","\u5BA1\u8BA1\u6A21\u5F0F").option("--reset","\u6E05\u9664\u9ED8\u8BA4\u6A21\u5F0F").action(function(){let e=this.opts();if(e.reset){h.set("defaultMode",null),console.log(p.green("\u5DF2\u6E05\u9664\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F"));return}for(let o of ce)if(e[o]){h.set("defaultMode",o),console.log(p.green(`\u5DF2\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F: ${w[o].name}`)),console.log(p.gray("\u4E0B\u6B21\u542F\u52A8 ccem \u65F6\u5C06\u9ED8\u8BA4\u4F7F\u7528\u6B64\u6A21\u5F0F"));return}let t=h.get("defaultMode");t&&w[t]?console.log(p.green(`\u5F53\u524D\u9ED8\u8BA4\u6A21\u5F0F: ${w[t].name}`)):console.log(p.yellow("\u672A\u8BBE\u7F6E\u9ED8\u8BA4\u6743\u9650\u6A21\u5F0F")),console.log(p.gray(`
|
|
24
|
+
\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u5F0F: ccem setup default-mode --dev`)),console.log(p.gray("\u6E05\u9664\u9ED8\u8BA4\u6A21\u5F0F: ccem setup default-mode --reset")),console.log(p.gray("\u53EF\u7528\u6A21\u5F0F: "+ce.join(", ")))});xe.command("init").description("\u521D\u59CB\u5316 Claude Code \u5168\u5C40\u914D\u7F6E\uFF08\u8DF3\u8FC7 onboarding\u3001\u7981\u7528\u9065\u6D4B\u3001\u5B89\u88C5 MCP \u5DE5\u5177\uFF09").action(async()=>{await dt()});var be=N.command("skill").description("\u7BA1\u7406 Claude Code skills");be.command("add [url]").description("\u6DFB\u52A0 skill\uFF08\u4ECE\u5B98\u65B9\u9884\u8BBE\u6216 GitHub URL\uFF09").action(async e=>{if(e)he(e);else{let t=await Rt();if(t.type==="cancelled"){console.log(p.yellow("\u5DF2\u53D6\u6D88"));return}if(t.type==="custom"){let{customUrl:o}=await M.prompt([{type:"input",name:"customUrl",message:"\u8F93\u5165 GitHub URL:",validate:s=>s.trim()?!s.includes("github.com")&&!/^[\w-]+\/[\w-]+$/.test(s)?"\u8BF7\u8F93\u5165\u6709\u6548\u7684 GitHub URL \u6216 owner/repo \u683C\u5F0F":!0:"\u8BF7\u8F93\u5165\u6709\u6548\u7684 URL"}]);he(o)}else t.skill&&At(t.skill)}});be.command("ls").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 skills").action(()=>{let e=yt();if(e.length===0){console.log(p.yellow("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u5B89\u88C5\u4EFB\u4F55 skill")),console.log(p.gray("\u4F7F\u7528 ccem skill add \u6DFB\u52A0 skills"));return}let t=new Ot({head:["Name","Path"],style:{head:["cyan"]}});e.forEach(o=>{t.push([p.green(o.name),p.gray(o.path)])}),console.log(t.toString())});be.command("rm <name>").description("\u5220\u9664\u5DF2\u5B89\u88C5\u7684 skill").action(e=>{_t(e)});N.command("load <url>").description("\u4ECE\u8FDC\u7A0B\u670D\u52A1\u5668\u52A0\u8F7D\u73AF\u5883\u914D\u7F6E").requiredOption("--secret <secret>","\u89E3\u5BC6\u5BC6\u94A5").action(async(e,t)=>{await Ct(e,t.secret)});N.action(async e=>{if(e.mode){it();return}if(e.listModes){at();return}for(So();;){console.clear(),Lt(Q,ae),console.log("");let t=h.get("defaultMode"),o=h.get("registries"),s=h.get("current"),n=o[s],{action:i}=await M.prompt([{type:"list",name:"action",message:p.gray("Select action"),choices:Qe(t),prefix:p.gray("?")}]);if(i==="start"){if(Z&&(Z.abort(),Z=null),ue(),n||(b.error("No environment configuration found."),process.exit(1)),t&&w[t])await ge(t,n);else{let a={...process.env};n.ANTHROPIC_BASE_URL&&(a.ANTHROPIC_BASE_URL=n.ANTHROPIC_BASE_URL),n.ANTHROPIC_API_KEY&&(a.ANTHROPIC_API_KEY=Y(n.ANTHROPIC_API_KEY||"")),n.ANTHROPIC_MODEL&&(a.ANTHROPIC_MODEL=n.ANTHROPIC_MODEL),n.ANTHROPIC_SMALL_FAST_MODEL&&(a.ANTHROPIC_SMALL_FAST_MODEL=n.ANTHROPIC_SMALL_FAST_MODEL),console.log(Ze()),It("claude",[],{env:a,stdio:"inherit",shell:!0}).on("exit",c=>{process.exit(c??0)})}return}else if(i==="usage")console.clear(),Q?console.log(et(Q)):b.warning("No usage data available"),await M.prompt([{type:"input",name:"continue",message:p.gray("Press Enter to continue..."),prefix:""}]);else if(i==="switch"){let a=await tt(o,s);if(a.action==="select")h.set("current",a.name);else if(a.action==="edit"){let l=o[a.name];console.log(p.yellow(`
|
|
25
|
+
Editing environment '${a.name}'`));let c=await M.prompt([{type:"input",name:"ANTHROPIC_BASE_URL",message:"ANTHROPIC_BASE_URL:",default:l.ANTHROPIC_BASE_URL},{type:"password",name:"ANTHROPIC_API_KEY",message:"ANTHROPIC_API_KEY (leave empty to keep current):"},{type:"input",name:"ANTHROPIC_MODEL",message:"ANTHROPIC_MODEL:",default:l.ANTHROPIC_MODEL},{type:"input",name:"ANTHROPIC_SMALL_FAST_MODEL",message:"ANTHROPIC_SMALL_FAST_MODEL:",default:l.ANTHROPIC_SMALL_FAST_MODEL}]);c.ANTHROPIC_BASE_URL&&(l.ANTHROPIC_BASE_URL=c.ANTHROPIC_BASE_URL),c.ANTHROPIC_API_KEY&&(l.ANTHROPIC_API_KEY=J(c.ANTHROPIC_API_KEY)),c.ANTHROPIC_MODEL&&(l.ANTHROPIC_MODEL=c.ANTHROPIC_MODEL),c.ANTHROPIC_SMALL_FAST_MODEL&&(l.ANTHROPIC_SMALL_FAST_MODEL=c.ANTHROPIC_SMALL_FAST_MODEL),o[a.name]=l,h.set("registries",o),b.success(`Environment '${a.name}' updated.`),await new Promise(m=>setTimeout(m,800))}else if(a.action==="rename")if(a.name==="official")b.error("Cannot rename default 'official' environment."),await new Promise(l=>setTimeout(l,800));else{let{newName:l}=await M.prompt([{type:"input",name:"newName",message:`Rename '${a.name}' to:`,validate:c=>c.trim()?o[c]?`Environment '${c}' already exists`:!0:"Name cannot be empty"}]);o[l]=o[a.name],delete o[a.name],h.set("registries",o),s===a.name&&h.set("current",l),b.success(`Environment '${a.name}' renamed to '${l}'.`),await new Promise(c=>setTimeout(c,800))}else if(a.action==="copy"){let{targetName:l}=await M.prompt([{type:"input",name:"targetName",message:`Copy '${a.name}' to:`,validate:m=>m.trim()?o[m]?`Environment '${m}' already exists`:!0:"Name cannot be empty"}]);o[l]={...o[a.name]},h.set("registries",o),b.success(`Environment '${a.name}' copied to '${l}'.`);let{modify:c}=await M.prompt([{type:"confirm",name:"modify",message:"Do you want to modify the configuration?",default:!1}]);if(c){let m=o[l],u=await M.prompt([{type:"input",name:"ANTHROPIC_BASE_URL",message:"ANTHROPIC_BASE_URL:",default:m.ANTHROPIC_BASE_URL},{type:"password",name:"ANTHROPIC_API_KEY",message:"ANTHROPIC_API_KEY (leave empty to keep current):"},{type:"input",name:"ANTHROPIC_MODEL",message:"ANTHROPIC_MODEL:",default:m.ANTHROPIC_MODEL},{type:"input",name:"ANTHROPIC_SMALL_FAST_MODEL",message:"ANTHROPIC_SMALL_FAST_MODEL:",default:m.ANTHROPIC_SMALL_FAST_MODEL}]);u.ANTHROPIC_BASE_URL&&(m.ANTHROPIC_BASE_URL=u.ANTHROPIC_BASE_URL),u.ANTHROPIC_API_KEY&&(m.ANTHROPIC_API_KEY=J(u.ANTHROPIC_API_KEY)),u.ANTHROPIC_MODEL&&(m.ANTHROPIC_MODEL=u.ANTHROPIC_MODEL),u.ANTHROPIC_SMALL_FAST_MODEL&&(m.ANTHROPIC_SMALL_FAST_MODEL=u.ANTHROPIC_SMALL_FAST_MODEL),o[l]=m,h.set("registries",o),b.success(`Environment '${l}' updated.`)}await new Promise(m=>setTimeout(m,800))}else if(a.action==="delete")if(a.name==="official")b.error("Cannot delete default 'official' environment."),await new Promise(l=>setTimeout(l,800));else{let{confirm:l}=await M.prompt([{type:"confirm",name:"confirm",message:`Are you sure you want to delete '${a.name}'?`,default:!1}]);l&&(delete o[a.name],h.set("registries",o),s===a.name?(h.set("current","official"),b.warning("Deleted current environment. Switched back to 'official'.")):b.success(`Environment '${a.name}' deleted.`)),await new Promise(c=>setTimeout(c,800))}}else if(i==="perm"){let{permMode:a}=await M.prompt([{type:"list",name:"permMode",message:p.gray("Select permission mode"),choices:Le(null,!1),prefix:p.gray("?")}]);if(a!=="back"){await ge(a,n);return}}else if(i==="setDefault"){let a=h.get("defaultMode"),{selectedMode:l}=await M.prompt([{type:"list",name:"selectedMode",message:p.gray("Set default permission mode"),choices:Le(a,!0),prefix:p.gray("?")}]);l==="clear"?(h.set("defaultMode",null),b.success("Default mode cleared"),await new Promise(c=>setTimeout(c,800))):l!=="back"&&(h.set("defaultMode",l),b.success(`Default mode set: ${w[l].name}`),await new Promise(c=>setTimeout(c,800)))}else process.exit(0)}});N.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccem",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code Environment Manager",
|
|
6
6
|
"author": {
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"@types/inquirer": "^9.0.7",
|
|
25
25
|
"@types/node": "^20.11.24",
|
|
26
26
|
"@types/react": "^19.2.9",
|
|
27
|
+
"asciify-image": "^0.1.10",
|
|
28
|
+
"sharp": "^0.34.5",
|
|
29
|
+
"terminal-image": "^4.2.0",
|
|
27
30
|
"tsup": "^8.0.2",
|
|
28
31
|
"typescript": "^5.3.3"
|
|
29
32
|
},
|