ccman 3.0.14 → 3.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +180 -255
  2. package/dist/index.js +340 -68
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,371 +1,296 @@
1
1
  # ccman
2
2
 
3
- > Codex 和 Claude Code 的 API 服务商配置管理工具
3
+ > Codex 和 Claude Code 的 API 服务商配置管理工具,一键切换 Codex 和 Claude Code 的 API 服务商配置。
4
4
 
5
- ## ✨ 特性
5
+ ---
6
6
 
7
- - 🔄 **快速切换**:一键切换不同的 API 服务商
8
- - 📦 **预设模板**:内置 7 个常用服务商模板,只需填写 API Key
9
- - 🛠️ **灵活配置**:支持自定义服务商配置
10
- - 🔒 **安全存储**:API Key 只存储在本地,权限保护
7
+ ## 核心特性
8
+
9
+ - 🔄 **一键切换**:一条命令切换服务商,自动修改配置文件
10
+ - 📦 **内置预设**:7 个常用服务商模板,只需填写 API Key
11
+ - 🛠️ **自定义配置**:支持添加任意第三方服务商
12
+ - 🔐 **零破坏性**:只修改管理的字段,写入前备份,失败回滚
11
13
  - 🎯 **双工具支持**:同时管理 Codex 和 Claude Code
14
+ - 📱 **双界面**:提供 CLI(命令行)和 Desktop(图形界面)
15
+ - 🔁 **克隆功能**:快速复制配置,管理多个 API Key
16
+ - ☁️ **WebDAV 同步**(可选):同步配置到你自己的 WebDAV 服务器(iCloud/Dropbox)
17
+ - 🔒 **无第三方上传**:不会上传到我们的服务器,配置权限 `0600`
18
+ - 🎨 **交互式菜单**:无需记忆命令,跟随提示操作
12
19
 
13
- ## 📦 安装
20
+ ---
14
21
 
15
- ```bash
16
- npm install -g ccman
17
- ```
22
+ ## 也许你不需要 ccman
18
23
 
19
- **系统要求**:Node.js >= 18.0.0
24
+ ccman 的核心功能是自动化配置文件的切换:
20
25
 
21
- ## 🚀 快速开始
26
+ - **Codex**:自动修改 `~/.codex/config.toml` 和 `~/.codex/auth.json`
27
+ - **Claude Code**:自动修改 `~/.claude/settings.json`
28
+ - **备份机制**:写入前自动备份,失败时自动回滚
22
29
 
23
- ### 管理 Codex 服务商
30
+ 如果你更喜欢手动编辑配置文件,或者只使用一个服务商从不切换,可能不需要这个工具。
24
31
 
25
- ```bash
26
- # 1. 添加服务商(交互式)
27
- ccman cx add
32
+ 📖 更多信息:[GitHub 仓库](https://github.com/2ue/ccm) - 包含手动配置方式和详细对比分析
28
33
 
29
- # 2. 查看所有服务商
30
- ccman cx list
34
+ ---
31
35
 
32
- # 3. 切换服务商
33
- ccman cx use
34
-
35
- # 4. 查看当前服务商
36
- ccman cx current
37
- ```
38
-
39
- ### 管理 Claude Code 服务商
36
+ ## 安装
40
37
 
41
38
  ```bash
42
- # 1. 添加服务商(交互式)
43
- ccman cc add
39
+ npm install -g ccman
40
+ ```
44
41
 
45
- # 2. 查看所有服务商
46
- ccman cc list
42
+ **要求**:Node.js >= 18.0.0
47
43
 
48
- # 3. 切换服务商
49
- ccman cc use
44
+ **Desktop 版本**:如果你更喜欢图形界面,可以从 [GitHub Releases](https://github.com/2ue/ccm/releases) 下载 Desktop 应用(macOS/Windows/Linux)。
50
45
 
51
- # 4. 查看当前服务商
52
- ccman cc current
53
- ```
46
+ ---
54
47
 
55
- ### 交互式菜单
48
+ ## 快速开始
56
49
 
57
- 直接运行 `ccman` 进入交互式主菜单:
50
+ ### 查看帮助
58
51
 
59
52
  ```bash
60
- ccman # 主菜单(选择 Codex 或 Claude Code)
61
- ccman cx # Codex 菜单
62
- ccman cc # Claude Code 菜单
63
- ```
53
+ $ ccman --help
64
54
 
65
- ## 📚 命令详解
55
+ Usage: ccman [options] [command]
66
56
 
67
- ### Codex 命令(`ccman cx`)
57
+ Codex/Claude Code API 服务商配置管理工具
68
58
 
69
- #### `ccman cx add`
59
+ Options:
60
+ -V, --version output the version number
61
+ -h, --help display help for command
70
62
 
71
- 添加新的 Codex 服务商。
63
+ Commands:
64
+ cx 管理 Codex 服务商
65
+ cc 管理 Claude 服务商
66
+ sync WebDAV 同步配置
67
+ help [command] display help for command
68
+ ```
72
69
 
73
- **交互流程**:
70
+ ### 管理 Codex 服务商
74
71
 
75
72
  ```bash
76
- $ ccman cx add
77
-
78
- ? 选择配置方式
79
- 使用预设模板
80
- 自定义配置
81
-
82
- # 选择 "使用预设模板"
83
- ? 选择预设 ›
84
- ❯ Anthropic Official
85
- AnyRouter
86
- PackyCode
87
- CoordCode
88
- 88Code
89
- BigModel
90
- ModelScope
91
-
92
- # 填写信息
93
- ? 服务商名称 › Anthropic Official
94
- ? API Key › sk-ant-••••••••••••••••••••••
95
-
96
- ✅ 添加成功!
97
- 💡 切换到此服务商: ccman cx use "Anthropic Official"
73
+ ccman cx # 进入交互式菜单
74
+ ccman cx add # 添加服务商
75
+ ccman cx use # 切换服务商
76
+ ccman cx list # 查看所有服务商
98
77
  ```
99
78
 
100
- #### `ccman cx list`
101
-
102
- 列出所有 Codex 服务商,`●` 表示当前使用的服务商。
79
+ **示例输出**:
103
80
 
104
81
  ```bash
105
82
  $ ccman cx list
106
83
 
107
- 📋 Codex 服务商列表 (2 个)
84
+ 📋 Codex 服务商 (2 个)
108
85
 
109
- Anthropic Official
110
- https://api.anthropic.com
86
+ Anthropic Official [当前]
87
+ https://api.anthropic.com
111
88
 
112
- PackyCode
113
- https://api.packycode.com
89
+ 88Code
90
+ https://www.88code.org/api
114
91
  ```
115
92
 
116
- #### `ccman cx use [name]`
117
-
118
- 切换 Codex 服务商。如果不提供名称,会显示交互式选择器。
119
-
120
93
  ```bash
121
- # 交互式选择
122
- $ ccman cx use
123
-
124
- ? 选择要使用的服务商 ›
125
- Anthropic Official (当前)
126
- ❯ PackyCode
94
+ $ ccman cx current
127
95
 
128
- 已切换到: PackyCode
96
+ 📍 当前 Codex 服务商
129
97
 
130
- # 直接指定名称
131
- $ ccman cx use PackyCode
132
- 已切换到: PackyCode
98
+ Anthropic Official
99
+ ID: codex-1760178741529-hbgbad
100
+ URL: https://api.anthropic.com
101
+ 最后使用: 2025/10/11 18:32:25
133
102
  ```
134
103
 
135
- **说明**:切换后会自动更新 `~/.codex/` 中的配置文件,无需重启 Codex。
136
-
137
- #### `ccman cx current`
138
-
139
- 显示当前使用的 Codex 服务商。
104
+ ### 管理 Claude Code 服务商
140
105
 
141
106
  ```bash
142
- $ ccman cx current
143
-
144
- 当前 Codex 服务商
145
-
146
- 名称: PackyCode
147
- Base URL: https://api.packycode.com
148
- API Key: sk-••••••••••••••••••••••••••••••••
107
+ ccman cc # 进入交互式菜单
108
+ ccman cc add # 添加服务商
109
+ ccman cc use # 切换服务商
110
+ ccman cc list # 查看所有服务商
149
111
  ```
150
112
 
151
- #### `ccman cx edit [name]`
152
-
153
- 编辑 Codex 服务商配置。
113
+ **示例输出**:
154
114
 
155
115
  ```bash
156
- $ ccman cx edit
116
+ $ ccman cc list
157
117
 
158
- ? 选择要编辑的服务商
159
- ❯ Anthropic Official
160
- PackyCode
118
+ 📋 Claude Code 服务商 (2 个)
161
119
 
162
- # 选择后可以修改
163
- ? 服务商名称 › Anthropic Official
164
- ? Base URL › https://api.anthropic.com
165
- ? API Key › (留空保持不变)
120
+ ○ AnyRouter
121
+ https://anyrouter.top
166
122
 
167
- 编辑成功!
123
+ ● PackyCode [当前]
124
+ https://api.packycode.com
168
125
  ```
169
126
 
170
- #### `ccman cx remove [name]`
171
-
172
- 删除 Codex 服务商。
173
-
174
127
  ```bash
175
- $ ccman cx remove
176
-
177
- ? 选择要删除的服务商 ›
178
- Anthropic Official (当前使用中)
179
- ❯ PackyCode
128
+ $ ccman cc current
180
129
 
181
- ? 确认删除 "PackyCode"? (y/N)
130
+ 📍 当前 Claude Code 服务商
182
131
 
183
- ✅ 已删除: PackyCode
132
+ PackyCode
133
+ ID: claude-1760182672751-unh2bp
134
+ URL: https://api.packycode.com
135
+ 最后使用: 2025/10/11 20:14:08
184
136
  ```
185
137
 
186
- #### `ccman cx clone [source]`
138
+ ---
187
139
 
188
- 克隆 Codex 服务商配置(复制 Base URL,修改名称和 API Key)。
140
+ ## 完整命令
189
141
 
190
- ```bash
191
- $ ccman cx clone
142
+ | 命令 | 说明 |
143
+ |------|------|
144
+ | `ccman` | 主菜单(选择 Codex 或 Claude Code) |
145
+ | `ccman cx` | Codex 交互式菜单 |
146
+ | `ccman cx add` | 添加 Codex 服务商(支持预设模板和自定义) |
147
+ | `ccman cx list` | 列出所有 Codex 服务商 |
148
+ | `ccman cx use [name]` | 切换 Codex 服务商 |
149
+ | `ccman cx current` | 查看当前 Codex 服务商 |
150
+ | `ccman cx edit [name]` | 编辑 Codex 服务商 |
151
+ | `ccman cx remove [name]` | 删除 Codex 服务商 |
152
+ | `ccman cx clone [source]` | 克隆 Codex 服务商(复制配置,改名称和 Key) |
153
+ | `ccman cc` | Claude Code 交互式菜单 |
154
+ | `ccman cc add` | 添加 Claude Code 服务商 |
155
+ | `ccman cc list` | 列出所有 Claude Code 服务商 |
156
+ | `ccman cc use [name]` | 切换 Claude Code 服务商 |
157
+ | `ccman cc current` | 查看当前 Claude Code 服务商 |
158
+ | `ccman cc edit [name]` | 编辑 Claude Code 服务商 |
159
+ | `ccman cc remove [name]` | 删除 Claude Code 服务商 |
160
+ | `ccman cc clone [source]` | 克隆 Claude Code 服务商 |
161
+ | `ccman sync` | WebDAV 同步配置(备份/恢复/合并) |
192
162
 
193
- ? 选择要克隆的服务商 ›
194
- ❯ Anthropic Official
195
- PackyCode
163
+ ---
196
164
 
197
- ? 新服务商名称 › Anthropic Test
198
- ? API Key › sk-ant-test-••••••••••••••••••
165
+ ## 内置预设
199
166
 
200
- 克隆成功!
201
- 💡 切换到此服务商: ccman cx use "Anthropic Test"
202
- ```
203
-
204
- **使用场景**:
205
- - 同一服务商有多个 API Key(测试/生产环境)
206
- - 快速创建类似配置的服务商
207
-
208
- ### Claude Code 命令(`ccman cc`)
167
+ 添加服务商时可以选择"使用预设模板",只需填写 API Key:
209
168
 
210
- Claude Code 的命令与 Codex 完全相同:
169
+ | 预设名称 | Base URL |
170
+ |---------|----------|
171
+ | Anthropic Official | `https://api.anthropic.com` |
172
+ | AnyRouter | `https://anyrouter.top` |
173
+ | PackyCode | `https://api.packycode.com` |
174
+ | CoordCode | `https://api.coordcode.com/api` |
175
+ | 88Code | `https://www.88code.org/api` |
176
+ | BigModel | `https://open.bigmodel.cn/api/anthropic` |
177
+ | ModelScope | `https://api-inference.modelscope.cn/v1/chat/completions` |
211
178
 
212
- - `ccman cc add` - 添加服务商
213
- - `ccman cc list` - 列出服务商
214
- - `ccman cc use [name]` - 切换服务商
215
- - `ccman cc current` - 查看当前服务商
216
- - `ccman cc edit [name]` - 编辑服务商
217
- - `ccman cc remove [name]` - 删除服务商
218
- - `ccman cc clone [source]` - 克隆服务商
179
+ 也可以选择"自定义配置",手动填写 Base URL。
219
180
 
220
- **说明**:切换后会自动更新 `~/.claude/settings.json`,无需重启 Claude Code。
181
+ ---
221
182
 
222
- ## 🎨 内置预设模板
183
+ ## 完整使用示例
223
184
 
224
- 两个工具都支持以下预设(只需填写 API Key):
185
+ ### 场景 1:添加并切换服务商
225
186
 
226
- | 预设名称 | Base URL | 说明 |
227
- |---------|----------|------|
228
- | Anthropic Official | `https://api.anthropic.com` | Anthropic 官方 API |
229
- | AnyRouter | `https://anyrouter.top` | AnyRouter API 服务 |
230
- | PackyCode | `https://api.packycode.com` | PackyCode API 服务 |
231
- | CoordCode | `https://api.coordcode.com/api` | CoordCode API 服务 |
232
- | 88Code | `https://www.88code.org/api` | 88Code API 服务 |
233
- | BigModel | `https://open.bigmodel.cn/api/anthropic` | 智谱 BigModel API |
234
- | ModelScope | `https://api-inference.modelscope.cn/v1/chat/completions` | 阿里云 ModelScope API |
187
+ ```bash
188
+ # 1. 查看当前列表
189
+ $ ccman cx list
235
190
 
236
- ## 💡 使用示例
191
+ 📋 Codex 服务商 (1 个)
237
192
 
238
- ### 示例 1:添加并切换到新服务商
193
+ ● Anthropic Official [当前]
194
+ https://api.anthropic.com
239
195
 
240
- ```bash
241
- # 1. 添加 PackyCode
196
+ # 2. 添加 PackyCode(交互式)
242
197
  $ ccman cx add
243
- # 选择 "使用预设模板" → "PackyCode" → 输入 API Key
198
+ ? 选择配置方式 使用预设模板
199
+ ? 选择预设 › PackyCode
200
+ ? 服务商名称 › PackyCode
201
+ ? API Key › ••••••••••••••••••••
202
+ ✅ 添加成功!
203
+ 💡 切换到此服务商: ccman cx use PackyCode
244
204
 
245
- # 2. 切换到 PackyCode
205
+ # 3. 切换到 PackyCode
246
206
  $ ccman cx use PackyCode
247
207
  ✅ 已切换到: PackyCode
248
208
 
249
- # 3. 确认
209
+ # 4. 确认当前服务商
250
210
  $ ccman cx current
251
- ✅ 当前 Codex 服务商
252
- 名称: PackyCode
211
+
212
+ 📍 当前 Codex 服务商
213
+
214
+ PackyCode
215
+ ID: codex-1760178741529-abc123
216
+ URL: https://api.packycode.com
217
+ 最后使用: 2025/10/11 18:32:25
253
218
  ```
254
219
 
255
- ### 示例 2:管理多个 API Key
220
+ ### 场景 2:克隆服务商(管理多个 Key
256
221
 
257
222
  ```bash
258
- # 场景:同一服务商有测试和生产两个 API Key
259
-
260
223
  # 1. 添加生产配置
261
224
  $ ccman cc add
262
- # 名称: Claude Production
263
- # API Key: sk-ant-prod-xxx
225
+ ? 选择配置方式 自定义配置
226
+ ? 服务商名称 Claude Production
227
+ ? Base URL › https://api.anthropic.com
228
+ ? API Key › ••••••••••••••••••••
229
+ ✅ 添加成功!
264
230
 
265
231
  # 2. 克隆创建测试配置
266
232
  $ ccman cc clone
267
- # 选择 "Claude Production"
268
- # 新名称: Claude Test
269
- # API Key: sk-ant-test-xxx
233
+ ? 选择要克隆的服务商 Claude Production
234
+ ? 新服务商名称 Claude Test
235
+ ? API Key › ••••••••••••••••••••
236
+ ✅ 克隆成功!
237
+ 💡 切换到此服务商: ccman cc use "Claude Test"
270
238
 
271
239
  # 3. 查看列表
272
240
  $ ccman cc list
273
- 📋 Claude Code 服务商列表 (共 2 个)
274
- ● Claude Production (当前)
275
- ○ Claude Test
276
241
 
277
- # 4. 快速切换
278
- $ ccman cc use "Claude Test"
279
- ✅ 已切换到: Claude Test
280
- ```
242
+ 📋 Claude Code 服务商 (2 个)
281
243
 
282
- ### 示例 3:自定义服务商配置
244
+ ● Claude Production [当前]
245
+ https://api.anthropic.com
283
246
 
284
- ```bash
285
- $ ccman cx add
286
-
287
- ? 选择配置方式 › 自定义配置
288
- ? 服务商名称 › My Custom Provider
289
- ? Base URL › https://api.example.com/v1
290
- ? API Key › my-secret-key
291
-
292
- ✅ 添加成功!
293
- ```
247
+ ○ Claude Test
248
+ https://api.anthropic.com
294
249
 
295
- ## 📂 配置文件
296
-
297
- ### ccman 配置
298
-
299
- **位置**:`~/.ccman/config.json`
300
-
301
- **结构**:
302
-
303
- ```json
304
- {
305
- "providers": [
306
- {
307
- "id": "codex-1234567890-abc123",
308
- "name": "PackyCode",
309
- "type": "codex",
310
- "baseUrl": "https://api.packycode.com",
311
- "apiKey": "sk-xxx",
312
- "createdAt": 1234567890000,
313
- "lastUsedAt": 1234567900000
314
- }
315
- ],
316
- "currentCodexProvider": "codex-1234567890-abc123",
317
- "currentClaudeProvider": null
318
- }
250
+ # 4. 快速切换
251
+ $ ccman cc use "Claude Test"
252
+ 已切换到: Claude Test
319
253
  ```
320
254
 
321
- ### Codex 配置
322
-
323
- ccman 会自动修改以下文件:
324
-
325
- - `~/.codex/config.toml` - Codex 主配置
326
- - `~/.codex/auth.json` - 认证信息
327
-
328
- ### Claude Code 配置
329
-
330
- ccman 会自动修改以下文件:
331
-
332
- - `~/.claude/settings.json` - Claude Code 设置(只修改 `env.ANTHROPIC_AUTH_TOKEN` 和 `env.ANTHROPIC_BASE_URL`,不影响其他配置)
333
-
334
- ## 🔒 安全性
335
-
336
- - ✅ API Key 只存储在本地,不会联网上传
337
- - ✅ 配置文件设置为 `0600` 权限(仅当前用户可读写)
338
- - ✅ 终端输入 API Key 时自动隐藏(显示为 `••••`)
339
- - ✅ 列表显示时 API Key 自动脱敏
255
+ ---
340
256
 
341
- ## ❓ 常见问题
257
+ ## 配置文件
342
258
 
343
- ### Q: 如何切换回之前的服务商?
259
+ **ccman 配置**:`~/.ccman/config.json`
344
260
 
345
- A: 使用 `ccman cx use` 或 `ccman cc use` 选择之前的服务商即可。
261
+ **Codex 配置**(ccman 会自动修改):
262
+ - `~/.codex/config.toml`
263
+ - `~/.codex/auth.json`
346
264
 
347
- ### Q: 删除服务商后还能恢复吗?
265
+ **Claude Code 配置**(ccman 会自动修改):
266
+ - `~/.claude/settings.json`
348
267
 
349
- A: 不能。删除操作不可逆,建议删除前确认。
268
+ **零破坏性承诺**:
269
+ - 只修改管理的字段,保留其他所有配置
270
+ - 写入前备份,失败时自动回滚
271
+ - API Key 存储在本地,权限 `0600`
350
272
 
351
- ### Q: 可以同时使用 CLI 和 Desktop 吗?
273
+ ---
352
274
 
353
- A: 可以。未来会推出 Desktop 应用,CLI 和 Desktop 共享同一套配置文件(`~/.ccman/`),修改会实时同步。
275
+ ## 常见问题
354
276
 
355
- ### Q: 切换服务商后需要重启 Codex/Claude Code 吗?
277
+ **Q: 支持配置导入/导出吗?**
278
+ A: 当前版本不支持。如需备份,手动复制 `~/.ccman/` 目录。
356
279
 
357
- A: 不需要。ccman 会直接修改配置文件,工具会自动重新加载配置。
280
+ **Q: WebDAV 同步是什么?**
281
+ A: 将 ccman 配置同步到 WebDAV 服务器(如 iCloud/Dropbox),多设备共享配置。详见 `ccman sync --help`。
358
282
 
359
- ### Q: 支持导入/导出配置吗?
283
+ ---
360
284
 
361
- A: 当前版本不支持。如需备份配置,可以手动复制 `~/.ccman/` 目录。
285
+ ## 许可证
362
286
 
363
- ## 📄 许可证
287
+ MIT
364
288
 
365
- MIT © [2ue](https://github.com/2ue)
289
+ ---
366
290
 
367
- ## 🔗 相关链接
291
+ ## 相关链接
368
292
 
369
293
  - [GitHub 仓库](https://github.com/2ue/ccm)
370
294
  - [问题反馈](https://github.com/2ue/ccm/issues)
371
295
  - [更新日志](https://github.com/2ue/ccm/blob/main/CHANGELOG.md)
296
+ - [Desktop 版本](https://github.com/2ue/ccm/releases)(图形界面)
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ var init_package = __esm({
39
39
  "../core/package.json"() {
40
40
  package_default = {
41
41
  name: "@ccman/core",
42
- version: "3.0.14",
42
+ version: "3.0.16",
43
43
  type: "module",
44
44
  description: "Core business logic for ccman",
45
45
  main: "./dist/index.js",
@@ -3001,22 +3001,22 @@ var require_url_parse = __commonJS({
3001
3001
  }
3002
3002
  function resolve(relative, base) {
3003
3003
  if (relative === "") return base;
3004
- var path8 = (base || "/").split("/").slice(0, -1).concat(relative.split("/")), i2 = path8.length, last = path8[i2 - 1], unshift = false, up = 0;
3004
+ var path11 = (base || "/").split("/").slice(0, -1).concat(relative.split("/")), i2 = path11.length, last = path11[i2 - 1], unshift = false, up = 0;
3005
3005
  while (i2--) {
3006
- if (path8[i2] === ".") {
3007
- path8.splice(i2, 1);
3008
- } else if (path8[i2] === "..") {
3009
- path8.splice(i2, 1);
3006
+ if (path11[i2] === ".") {
3007
+ path11.splice(i2, 1);
3008
+ } else if (path11[i2] === "..") {
3009
+ path11.splice(i2, 1);
3010
3010
  up++;
3011
3011
  } else if (up) {
3012
3012
  if (i2 === 0) unshift = true;
3013
- path8.splice(i2, 1);
3013
+ path11.splice(i2, 1);
3014
3014
  up--;
3015
3015
  }
3016
3016
  }
3017
- if (unshift) path8.unshift("");
3018
- if (last === "." || last === "..") path8.push("");
3019
- return path8.join("/");
3017
+ if (unshift) path11.unshift("");
3018
+ if (last === "." || last === "..") path11.push("");
3019
+ return path11.join("/");
3020
3020
  }
3021
3021
  function Url(address, location, parser) {
3022
3022
  address = trimLeft(address);
@@ -3442,14 +3442,14 @@ var require_path_posix = __commonJS({
3442
3442
  posix.resolve = function() {
3443
3443
  var resolvedPath = "", resolvedAbsolute = false;
3444
3444
  for (var i2 = arguments.length - 1; i2 >= -1 && !resolvedAbsolute; i2--) {
3445
- var path8 = i2 >= 0 ? arguments[i2] : process.cwd();
3446
- if (!isString(path8)) {
3445
+ var path11 = i2 >= 0 ? arguments[i2] : process.cwd();
3446
+ if (!isString(path11)) {
3447
3447
  throw new TypeError("Arguments to path.resolve must be strings");
3448
- } else if (!path8) {
3448
+ } else if (!path11) {
3449
3449
  continue;
3450
3450
  }
3451
- resolvedPath = path8 + "/" + resolvedPath;
3452
- resolvedAbsolute = path8.charAt(0) === "/";
3451
+ resolvedPath = path11 + "/" + resolvedPath;
3452
+ resolvedAbsolute = path11.charAt(0) === "/";
3453
3453
  }
3454
3454
  resolvedPath = normalizeArray(
3455
3455
  resolvedPath.split("/"),
@@ -3457,36 +3457,36 @@ var require_path_posix = __commonJS({
3457
3457
  ).join("/");
3458
3458
  return (resolvedAbsolute ? "/" : "") + resolvedPath || ".";
3459
3459
  };
3460
- posix.normalize = function(path8) {
3461
- var isAbsolute = posix.isAbsolute(path8), trailingSlash = path8.substr(-1) === "/";
3462
- path8 = normalizeArray(path8.split("/"), !isAbsolute).join("/");
3463
- if (!path8 && !isAbsolute) {
3464
- path8 = ".";
3460
+ posix.normalize = function(path11) {
3461
+ var isAbsolute = posix.isAbsolute(path11), trailingSlash = path11.substr(-1) === "/";
3462
+ path11 = normalizeArray(path11.split("/"), !isAbsolute).join("/");
3463
+ if (!path11 && !isAbsolute) {
3464
+ path11 = ".";
3465
3465
  }
3466
- if (path8 && trailingSlash) {
3467
- path8 += "/";
3466
+ if (path11 && trailingSlash) {
3467
+ path11 += "/";
3468
3468
  }
3469
- return (isAbsolute ? "/" : "") + path8;
3469
+ return (isAbsolute ? "/" : "") + path11;
3470
3470
  };
3471
- posix.isAbsolute = function(path8) {
3472
- return path8.charAt(0) === "/";
3471
+ posix.isAbsolute = function(path11) {
3472
+ return path11.charAt(0) === "/";
3473
3473
  };
3474
3474
  posix.join = function() {
3475
- var path8 = "";
3475
+ var path11 = "";
3476
3476
  for (var i2 = 0; i2 < arguments.length; i2++) {
3477
3477
  var segment = arguments[i2];
3478
3478
  if (!isString(segment)) {
3479
3479
  throw new TypeError("Arguments to path.join must be strings");
3480
3480
  }
3481
3481
  if (segment) {
3482
- if (!path8) {
3483
- path8 += segment;
3482
+ if (!path11) {
3483
+ path11 += segment;
3484
3484
  } else {
3485
- path8 += "/" + segment;
3485
+ path11 += "/" + segment;
3486
3486
  }
3487
3487
  }
3488
3488
  }
3489
- return posix.normalize(path8);
3489
+ return posix.normalize(path11);
3490
3490
  };
3491
3491
  posix.relative = function(from, to) {
3492
3492
  from = posix.resolve(from).substr(1);
@@ -3520,11 +3520,11 @@ var require_path_posix = __commonJS({
3520
3520
  outputParts = outputParts.concat(toParts.slice(samePartsLength));
3521
3521
  return outputParts.join("/");
3522
3522
  };
3523
- posix._makeLong = function(path8) {
3524
- return path8;
3523
+ posix._makeLong = function(path11) {
3524
+ return path11;
3525
3525
  };
3526
- posix.dirname = function(path8) {
3527
- var result = posixSplitPath(path8), root = result[0], dir = result[1];
3526
+ posix.dirname = function(path11) {
3527
+ var result = posixSplitPath(path11), root = result[0], dir = result[1];
3528
3528
  if (!root && !dir) {
3529
3529
  return ".";
3530
3530
  }
@@ -3533,15 +3533,15 @@ var require_path_posix = __commonJS({
3533
3533
  }
3534
3534
  return root + dir;
3535
3535
  };
3536
- posix.basename = function(path8, ext2) {
3537
- var f3 = posixSplitPath(path8)[2];
3536
+ posix.basename = function(path11, ext2) {
3537
+ var f3 = posixSplitPath(path11)[2];
3538
3538
  if (ext2 && f3.substr(-1 * ext2.length) === ext2) {
3539
3539
  f3 = f3.substr(0, f3.length - ext2.length);
3540
3540
  }
3541
3541
  return f3;
3542
3542
  };
3543
- posix.extname = function(path8) {
3544
- return posixSplitPath(path8)[3];
3543
+ posix.extname = function(path11) {
3544
+ return posixSplitPath(path11)[3];
3545
3545
  };
3546
3546
  posix.format = function(pathObject) {
3547
3547
  if (!util.isObject(pathObject)) {
@@ -14939,10 +14939,10 @@ var require_nested_property = __commonJS({
14939
14939
  return false;
14940
14940
  }
14941
14941
  }
14942
- function traverse(object, path8) {
14942
+ function traverse(object, path11) {
14943
14943
  var callback = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : function() {
14944
14944
  };
14945
- var segments = path8.split(PATH_DELIMITER);
14945
+ var segments = path11.split(PATH_DELIMITER);
14946
14946
  var length = segments.length;
14947
14947
  var _loop = function _loop2(idx2) {
14948
14948
  var currentSegment = segments[idx2];
@@ -15184,11 +15184,11 @@ async function createDirectory(context, dirPath, options = {}) {
15184
15184
  const response = await request(requestOptions, context);
15185
15185
  handleResponseCode(context, response);
15186
15186
  }
15187
- function ensureCollectionPath(path8) {
15188
- if (!path8.endsWith("/")) {
15189
- return path8 + "/";
15187
+ function ensureCollectionPath(path11) {
15188
+ if (!path11.endsWith("/")) {
15189
+ return path11 + "/";
15190
15190
  }
15191
- return path8;
15191
+ return path11;
15192
15192
  }
15193
15193
  async function createDirectoryRecursively(context, dirPath, options = {}) {
15194
15194
  const paths = getAllDirectories(normalisePath(dirPath));
@@ -15563,7 +15563,7 @@ var init_xml = __esm({
15563
15563
  });
15564
15564
 
15565
15565
  // ../../node_modules/.pnpm/webdav@5.8.0/node_modules/webdav/dist/node/operations/lock.js
15566
- async function lock(context, path8, options = {}) {
15566
+ async function lock(context, path11, options = {}) {
15567
15567
  const { refreshToken, timeout = DEFAULT_TIMEOUT } = options;
15568
15568
  const headers = {
15569
15569
  Accept: "text/plain,application/xml",
@@ -15573,7 +15573,7 @@ async function lock(context, path8, options = {}) {
15573
15573
  headers.If = refreshToken;
15574
15574
  }
15575
15575
  const requestOptions = prepareRequestOptions({
15576
- url: joinURL(context.remoteURL, encodePath(path8)),
15576
+ url: joinURL(context.remoteURL, encodePath(path11)),
15577
15577
  method: "LOCK",
15578
15578
  headers,
15579
15579
  data: generateLockXML(context.contactHref)
@@ -15593,9 +15593,9 @@ async function lock(context, path8, options = {}) {
15593
15593
  serverTimeout
15594
15594
  };
15595
15595
  }
15596
- async function unlock(context, path8, token, options = {}) {
15596
+ async function unlock(context, path11, token, options = {}) {
15597
15597
  const requestOptions = prepareRequestOptions({
15598
- url: joinURL(context.remoteURL, encodePath(path8)),
15598
+ url: joinURL(context.remoteURL, encodePath(path11)),
15599
15599
  method: "UNLOCK",
15600
15600
  headers: {
15601
15601
  "Lock-Token": token
@@ -15645,9 +15645,9 @@ var init_quota = __esm({
15645
15645
 
15646
15646
  // ../../node_modules/.pnpm/webdav@5.8.0/node_modules/webdav/dist/node/operations/getQuota.js
15647
15647
  async function getQuota(context, options = {}) {
15648
- const path8 = options.path || "/";
15648
+ const path11 = options.path || "/";
15649
15649
  const requestOptions = prepareRequestOptions({
15650
- url: joinURL(context.remoteURL, path8),
15650
+ url: joinURL(context.remoteURL, path11),
15651
15651
  method: "PROPFIND",
15652
15652
  headers: {
15653
15653
  Accept: "text/plain,application/xml",
@@ -15987,29 +15987,29 @@ function createClient(remoteURL, options = {}) {
15987
15987
  setupAuth(context, username, password, token, ha1);
15988
15988
  return {
15989
15989
  copyFile: (filename, destination, options2) => copyFile(context, filename, destination, options2),
15990
- createDirectory: (path8, options2) => createDirectory(context, path8, options2),
15990
+ createDirectory: (path11, options2) => createDirectory(context, path11, options2),
15991
15991
  createReadStream: (filename, options2) => createReadStream2(context, filename, options2),
15992
15992
  createWriteStream: (filename, options2, callback) => createWriteStream(context, filename, options2, callback),
15993
- customRequest: (path8, requestOptions) => customRequest(context, path8, requestOptions),
15993
+ customRequest: (path11, requestOptions) => customRequest(context, path11, requestOptions),
15994
15994
  deleteFile: (filename, options2) => deleteFile(context, filename, options2),
15995
- exists: (path8, options2) => exists(context, path8, options2),
15996
- getDirectoryContents: (path8, options2) => getDirectoryContents(context, path8, options2),
15995
+ exists: (path11, options2) => exists(context, path11, options2),
15996
+ getDirectoryContents: (path11, options2) => getDirectoryContents(context, path11, options2),
15997
15997
  getFileContents: (filename, options2) => getFileContents(context, filename, options2),
15998
15998
  getFileDownloadLink: (filename) => getFileDownloadLink(context, filename),
15999
15999
  getFileUploadLink: (filename) => getFileUploadLink(context, filename),
16000
16000
  getHeaders: () => Object.assign({}, context.headers),
16001
16001
  getQuota: (options2) => getQuota(context, options2),
16002
- lock: (path8, options2) => lock(context, path8, options2),
16002
+ lock: (path11, options2) => lock(context, path11, options2),
16003
16003
  moveFile: (filename, destinationFilename, options2) => moveFile(context, filename, destinationFilename, options2),
16004
16004
  putFileContents: (filename, data, options2) => putFileContents(context, filename, data, options2),
16005
16005
  partialUpdateFileContents: (filePath, start, end, data, options2) => partialUpdateFileContents(context, filePath, start, end, data, options2),
16006
- getDAVCompliance: (path8) => getDAVCompliance(context, path8),
16007
- search: (path8, options2) => getSearch2(context, path8, options2),
16006
+ getDAVCompliance: (path11) => getDAVCompliance(context, path11),
16007
+ search: (path11, options2) => getSearch2(context, path11, options2),
16008
16008
  setHeaders: (headers2) => {
16009
16009
  context.headers = Object.assign({}, headers2);
16010
16010
  },
16011
- stat: (path8, options2) => getStat(context, path8, options2),
16012
- unlock: (path8, token2, options2) => unlock(context, path8, token2, options2)
16011
+ stat: (path11, options2) => getStat(context, path11, options2),
16012
+ unlock: (path11, token2, options2) => unlock(context, path11, token2, options2)
16013
16013
  };
16014
16014
  }
16015
16015
  var DEFAULT_CONTACT_HREF;
@@ -16570,6 +16570,149 @@ var init_sync_v2 = __esm({
16570
16570
  }
16571
16571
  });
16572
16572
 
16573
+ // ../core/dist/export.js
16574
+ function validateExport() {
16575
+ const ccmanDir2 = getCcmanDir();
16576
+ const codexPath = path8.join(ccmanDir2, CODEX_CONFIG_FILE);
16577
+ const claudePath = path8.join(ccmanDir2, CLAUDE_CONFIG_FILE);
16578
+ const missingFiles = [];
16579
+ if (!fileExists(codexPath)) {
16580
+ missingFiles.push(CODEX_CONFIG_FILE);
16581
+ }
16582
+ if (!fileExists(claudePath)) {
16583
+ missingFiles.push(CLAUDE_CONFIG_FILE);
16584
+ }
16585
+ if (missingFiles.length > 0) {
16586
+ return {
16587
+ valid: false,
16588
+ message: `\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${missingFiles.join(", ")}`,
16589
+ missingFiles
16590
+ };
16591
+ }
16592
+ return { valid: true };
16593
+ }
16594
+ function validateImportDir(sourceDir) {
16595
+ if (!fileExists(sourceDir)) {
16596
+ return {
16597
+ valid: false,
16598
+ message: `\u76EE\u5F55\u4E0D\u5B58\u5728: ${sourceDir}`,
16599
+ foundFiles: []
16600
+ };
16601
+ }
16602
+ const stats = fs8.statSync(sourceDir);
16603
+ if (!stats.isDirectory()) {
16604
+ return {
16605
+ valid: false,
16606
+ message: `\u4E0D\u662F\u6709\u6548\u7684\u76EE\u5F55: ${sourceDir}`,
16607
+ foundFiles: []
16608
+ };
16609
+ }
16610
+ const codexPath = path8.join(sourceDir, CODEX_CONFIG_FILE);
16611
+ const claudePath = path8.join(sourceDir, CLAUDE_CONFIG_FILE);
16612
+ const foundFiles = [];
16613
+ if (fileExists(codexPath)) {
16614
+ foundFiles.push(CODEX_CONFIG_FILE);
16615
+ }
16616
+ if (fileExists(claudePath)) {
16617
+ foundFiles.push(CLAUDE_CONFIG_FILE);
16618
+ }
16619
+ if (foundFiles.length === 0) {
16620
+ return {
16621
+ valid: false,
16622
+ message: `\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6 (${CODEX_CONFIG_FILE} \u6216 ${CLAUDE_CONFIG_FILE})`,
16623
+ foundFiles: []
16624
+ };
16625
+ }
16626
+ return {
16627
+ valid: true,
16628
+ foundFiles
16629
+ };
16630
+ }
16631
+ function exportConfig(targetDir) {
16632
+ const validation = validateExport();
16633
+ if (!validation.valid) {
16634
+ throw new Error(validation.message);
16635
+ }
16636
+ ensureDir(targetDir);
16637
+ const ccmanDir2 = getCcmanDir();
16638
+ const exportedFiles = [];
16639
+ const codexSrc = path8.join(ccmanDir2, CODEX_CONFIG_FILE);
16640
+ const codexDst = path8.join(targetDir, CODEX_CONFIG_FILE);
16641
+ if (fileExists(codexSrc)) {
16642
+ fs8.copyFileSync(codexSrc, codexDst);
16643
+ exportedFiles.push(CODEX_CONFIG_FILE);
16644
+ }
16645
+ const claudeSrc = path8.join(ccmanDir2, CLAUDE_CONFIG_FILE);
16646
+ const claudeDst = path8.join(targetDir, CLAUDE_CONFIG_FILE);
16647
+ if (fileExists(claudeSrc)) {
16648
+ fs8.copyFileSync(claudeSrc, claudeDst);
16649
+ exportedFiles.push(CLAUDE_CONFIG_FILE);
16650
+ }
16651
+ return {
16652
+ success: true,
16653
+ targetDir,
16654
+ exportedFiles
16655
+ };
16656
+ }
16657
+ function importConfig(sourceDir) {
16658
+ const validation = validateImportDir(sourceDir);
16659
+ if (!validation.valid) {
16660
+ throw new Error(validation.message);
16661
+ }
16662
+ const ccmanDir2 = getCcmanDir();
16663
+ const backupPaths = [];
16664
+ const importedFiles = [];
16665
+ ensureDir(ccmanDir2);
16666
+ try {
16667
+ if (validation.foundFiles.includes(CODEX_CONFIG_FILE)) {
16668
+ const codexDst = path8.join(ccmanDir2, CODEX_CONFIG_FILE);
16669
+ if (fileExists(codexDst)) {
16670
+ const backupPath = backupConfig(codexDst);
16671
+ backupPaths.push(backupPath);
16672
+ }
16673
+ const codexSrc = path8.join(sourceDir, CODEX_CONFIG_FILE);
16674
+ fs8.copyFileSync(codexSrc, codexDst);
16675
+ importedFiles.push(CODEX_CONFIG_FILE);
16676
+ }
16677
+ if (validation.foundFiles.includes(CLAUDE_CONFIG_FILE)) {
16678
+ const claudeDst = path8.join(ccmanDir2, CLAUDE_CONFIG_FILE);
16679
+ if (fileExists(claudeDst)) {
16680
+ const backupPath = backupConfig(claudeDst);
16681
+ backupPaths.push(backupPath);
16682
+ }
16683
+ const claudeSrc = path8.join(sourceDir, CLAUDE_CONFIG_FILE);
16684
+ fs8.copyFileSync(claudeSrc, claudeDst);
16685
+ importedFiles.push(CLAUDE_CONFIG_FILE);
16686
+ }
16687
+ return {
16688
+ success: true,
16689
+ backupPaths,
16690
+ importedFiles
16691
+ };
16692
+ } catch (error) {
16693
+ for (const backupPath of backupPaths) {
16694
+ const originalPath = backupPath.replace(/\.backup\.\d+$/, "");
16695
+ if (fileExists(backupPath)) {
16696
+ fs8.copyFileSync(backupPath, originalPath);
16697
+ }
16698
+ }
16699
+ throw new Error(`\u5BFC\u5165\u5931\u8D25\uFF0C\u5DF2\u6062\u590D\u5907\u4EFD: ${error.message}`);
16700
+ }
16701
+ }
16702
+ var fs8, path8, CODEX_CONFIG_FILE, CLAUDE_CONFIG_FILE;
16703
+ var init_export = __esm({
16704
+ "../core/dist/export.js"() {
16705
+ "use strict";
16706
+ fs8 = __toESM(require("fs"), 1);
16707
+ path8 = __toESM(require("path"), 1);
16708
+ init_paths();
16709
+ init_file();
16710
+ init_merge2();
16711
+ CODEX_CONFIG_FILE = "codex.json";
16712
+ CLAUDE_CONFIG_FILE = "claude.json";
16713
+ }
16714
+ });
16715
+
16573
16716
  // ../core/dist/index.js
16574
16717
  var VERSION;
16575
16718
  var init_dist4 = __esm({
@@ -16587,6 +16730,7 @@ var init_dist4 = __esm({
16587
16730
  init_sync_v2();
16588
16731
  init_crypto2();
16589
16732
  init_merge_advanced();
16733
+ init_export();
16590
16734
  VERSION = package_default.version;
16591
16735
  }
16592
16736
  });
@@ -17038,8 +17182,8 @@ function downloadCommand(program2) {
17038
17182
  console.log();
17039
17183
  if (backupPaths.length > 0) {
17040
17184
  console.log(import_chalk6.default.gray("\u672C\u5730\u5907\u4EFD:"));
17041
- backupPaths.forEach((path8) => {
17042
- console.log(import_chalk6.default.gray(` ${path8}`));
17185
+ backupPaths.forEach((path11) => {
17186
+ console.log(import_chalk6.default.gray(` ${path11}`));
17043
17187
  });
17044
17188
  console.log();
17045
17189
  }
@@ -17103,8 +17247,8 @@ function mergeCommand(program2) {
17103
17247
  console.log();
17104
17248
  if (result.backupPaths.length > 0) {
17105
17249
  console.log(import_chalk7.default.gray("\u5907\u4EFD:"));
17106
- result.backupPaths.forEach((path8) => {
17107
- console.log(import_chalk7.default.gray(` ${path8}`));
17250
+ result.backupPaths.forEach((path11) => {
17251
+ console.log(import_chalk7.default.gray(` ${path11}`));
17108
17252
  });
17109
17253
  console.log();
17110
17254
  }
@@ -17303,7 +17447,7 @@ var init_sync2 = __esm({
17303
17447
 
17304
17448
  // src/index.ts
17305
17449
  var import_commander2 = require("commander");
17306
- var import_chalk25 = __toESM(require("chalk"));
17450
+ var import_chalk27 = __toESM(require("chalk"));
17307
17451
 
17308
17452
  // src/utils/logo.ts
17309
17453
  var import_chalk = __toESM(require("chalk"));
@@ -18743,12 +18887,138 @@ function createClaudeCommands(program2) {
18743
18887
 
18744
18888
  // src/index.ts
18745
18889
  init_sync2();
18890
+
18891
+ // src/commands/export.ts
18892
+ var import_chalk25 = __toESM(require("chalk"));
18893
+ var import_path18 = __toESM(require("path"));
18894
+ init_dist4();
18895
+ function exportCommand(program2) {
18896
+ program2.command("export <\u76EE\u6807\u76EE\u5F55>").description("\u5BFC\u51FA\u914D\u7F6E\u5230\u672C\u5730\u76EE\u5F55\uFF08\u5305\u542B API Key\uFF09").action(async (targetDir) => {
18897
+ try {
18898
+ console.log(import_chalk25.default.bold("\n\u{1F4E6} \u5BFC\u51FA\u914D\u7F6E\n"));
18899
+ const validation = validateExport();
18900
+ if (!validation.valid) {
18901
+ console.log(import_chalk25.default.red(`\u274C ${validation.message}
18902
+ `));
18903
+ process.exit(1);
18904
+ }
18905
+ const resolvedPath = targetDir.startsWith("~") ? import_path18.default.join(process.env.HOME || "", targetDir.slice(1)) : import_path18.default.resolve(targetDir);
18906
+ console.log("\u5BFC\u51FA\u6587\u4EF6:");
18907
+ console.log(` ${import_chalk25.default.cyan("codex.json")} - Codex \u914D\u7F6E`);
18908
+ console.log(` ${import_chalk25.default.cyan("claude.json")} - Claude \u914D\u7F6E`);
18909
+ console.log();
18910
+ console.log(`\u76EE\u6807\u76EE\u5F55: ${import_chalk25.default.cyan(resolvedPath)}`);
18911
+ console.log();
18912
+ console.log(import_chalk25.default.yellow("\u26A0\uFE0F \u5BFC\u51FA\u6587\u4EF6\u5305\u542B API Key\uFF0C\u8BF7\u59A5\u5584\u4FDD\u7BA1"));
18913
+ console.log();
18914
+ const result = exportConfig(resolvedPath);
18915
+ console.log(import_chalk25.default.green("\u2705 \u5BFC\u51FA\u6210\u529F"));
18916
+ console.log();
18917
+ console.log("\u5DF2\u5BFC\u51FA\u6587\u4EF6:");
18918
+ for (const file of result.exportedFiles) {
18919
+ console.log(` ${import_chalk25.default.cyan("\u2713")} ${file}`);
18920
+ }
18921
+ console.log();
18922
+ console.log(import_chalk25.default.blue(`\u{1F4A1} \u5BFC\u5165\u547D\u4EE4: ccman import ${resolvedPath}
18923
+ `));
18924
+ } catch (error) {
18925
+ console.error(import_chalk25.default.red(`
18926
+ \u274C ${error.message}
18927
+ `));
18928
+ process.exit(1);
18929
+ }
18930
+ });
18931
+ }
18932
+
18933
+ // src/commands/import.ts
18934
+ var import_chalk26 = __toESM(require("chalk"));
18935
+ var import_inquirer17 = __toESM(require("inquirer"));
18936
+ var import_path19 = __toESM(require("path"));
18937
+ init_dist4();
18938
+ function importCommand(program2) {
18939
+ program2.command("import <\u6E90\u76EE\u5F55>").description("\u4ECE\u672C\u5730\u76EE\u5F55\u5BFC\u5165\u914D\u7F6E\uFF08\u4F1A\u8986\u76D6\u5F53\u524D\u914D\u7F6E\uFF09").action(async (sourceDir) => {
18940
+ try {
18941
+ const resolvedPath = sourceDir.startsWith("~") ? import_path19.default.join(process.env.HOME || "", sourceDir.slice(1)) : import_path19.default.resolve(sourceDir);
18942
+ console.log(import_chalk26.default.bold("\n\u{1F4E5} \u5BFC\u5165\u914D\u7F6E\n"));
18943
+ const validation = validateImportDir(resolvedPath);
18944
+ if (!validation.valid) {
18945
+ console.log(import_chalk26.default.red(`\u274C ${validation.message}
18946
+ `));
18947
+ process.exit(1);
18948
+ }
18949
+ console.log(import_chalk26.default.yellow("\u26A0\uFE0F \u8B66\u544A\uFF1A\u5BFC\u5165\u5C06\u8986\u76D6\u5F53\u524D\u914D\u7F6E\n"));
18950
+ console.log(`\u6E90\u76EE\u5F55: ${import_chalk26.default.cyan(resolvedPath)}`);
18951
+ console.log();
18952
+ console.log("\u627E\u5230\u914D\u7F6E\u6587\u4EF6:");
18953
+ for (const file of validation.foundFiles) {
18954
+ console.log(` ${import_chalk26.default.cyan("\u2713")} ${file}`);
18955
+ }
18956
+ console.log();
18957
+ console.log(import_chalk26.default.gray("\u5F53\u524D\u914D\u7F6E\u5C06\u88AB\u8986\u76D6\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09"));
18958
+ console.log();
18959
+ const { confirmFirst } = await import_inquirer17.default.prompt([
18960
+ {
18961
+ type: "confirm",
18962
+ name: "confirmFirst",
18963
+ message: "\u786E\u8BA4\u5BFC\u5165\uFF1F",
18964
+ default: false
18965
+ }
18966
+ ]);
18967
+ if (!confirmFirst) {
18968
+ console.log(import_chalk26.default.gray("\n\u274C \u5DF2\u53D6\u6D88\n"));
18969
+ return;
18970
+ }
18971
+ console.log();
18972
+ console.log(import_chalk26.default.red.bold("\u26A0\uFE0F \u6700\u540E\u786E\u8BA4\uFF1A\u6B64\u64CD\u4F5C\u5C06\u8986\u76D6\u6240\u6709\u5F53\u524D\u914D\u7F6E\uFF01"));
18973
+ console.log();
18974
+ const { confirmSecond } = await import_inquirer17.default.prompt([
18975
+ {
18976
+ type: "confirm",
18977
+ name: "confirmSecond",
18978
+ message: "\u771F\u7684\u8981\u7EE7\u7EED\u5417\uFF1F",
18979
+ default: false
18980
+ }
18981
+ ]);
18982
+ if (!confirmSecond) {
18983
+ console.log(import_chalk26.default.gray("\n\u274C \u5DF2\u53D6\u6D88\n"));
18984
+ return;
18985
+ }
18986
+ console.log();
18987
+ console.log(import_chalk26.default.gray("\u{1F4BE} \u5907\u4EFD\u5F53\u524D\u914D\u7F6E..."));
18988
+ console.log(import_chalk26.default.gray("\u{1F4E5} \u5BFC\u5165\u65B0\u914D\u7F6E..."));
18989
+ const result = importConfig(resolvedPath);
18990
+ console.log();
18991
+ console.log(import_chalk26.default.green("\u2705 \u5BFC\u5165\u6210\u529F"));
18992
+ console.log();
18993
+ if (result.backupPaths.length > 0) {
18994
+ console.log("\u5907\u4EFD\u6587\u4EF6:");
18995
+ for (const backupPath of result.backupPaths) {
18996
+ console.log(` ${import_chalk26.default.gray(backupPath)}`);
18997
+ }
18998
+ console.log();
18999
+ }
19000
+ console.log("\u5DF2\u5BFC\u5165\u6587\u4EF6:");
19001
+ for (const file of result.importedFiles) {
19002
+ console.log(` ${import_chalk26.default.cyan("\u2713")} ${file}`);
19003
+ }
19004
+ console.log();
19005
+ console.log(import_chalk26.default.blue("\u{1F4A1} \u8BF7\u4F7F\u7528 'ccman cx use' \u6216 'ccman cc use' \u5207\u6362\u670D\u52A1\u5546\n"));
19006
+ } catch (error) {
19007
+ console.error(import_chalk26.default.red(`
19008
+ \u274C ${error.message}
19009
+ `));
19010
+ process.exit(1);
19011
+ }
19012
+ });
19013
+ }
19014
+
19015
+ // src/index.ts
18746
19016
  init_dist4();
18747
19017
  if (process.env.NODE_ENV === "development") {
18748
- console.log(import_chalk25.default.gray("\n[\u5F00\u53D1\u6A21\u5F0F] \u914D\u7F6E\u76EE\u5F55:"));
18749
- console.log(import_chalk25.default.gray(` ccman: ${getCcmanDir()}`));
18750
- console.log(import_chalk25.default.gray(` codex: ${getCodexDir()}`));
18751
- console.log(import_chalk25.default.gray(` claude: ${getClaudeDir()}`));
19018
+ console.log(import_chalk27.default.gray("\n[\u5F00\u53D1\u6A21\u5F0F] \u914D\u7F6E\u76EE\u5F55:"));
19019
+ console.log(import_chalk27.default.gray(` ccman: ${getCcmanDir()}`));
19020
+ console.log(import_chalk27.default.gray(` codex: ${getCodexDir()}`));
19021
+ console.log(import_chalk27.default.gray(` claude: ${getClaudeDir()}`));
18752
19022
  console.log();
18753
19023
  }
18754
19024
  var program = new import_commander2.Command();
@@ -18776,6 +19046,8 @@ sync.action(async () => {
18776
19046
  printLogo();
18777
19047
  await startSyncMenu();
18778
19048
  });
19049
+ exportCommand(program);
19050
+ importCommand(program);
18779
19051
  (async () => {
18780
19052
  if (!process.argv.slice(2).length) {
18781
19053
  printLogo();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccman",
3
- "version": "3.0.14",
3
+ "version": "3.0.16",
4
4
  "description": "Manage Codex and Claude Code API service provider configurations",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {