ccman 3.0.14 → 3.0.15
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 +180 -255
- package/dist/index.js +340 -68
- 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
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
-
|
|
16
|
-
npm install -g ccman
|
|
17
|
-
```
|
|
22
|
+
## 也许你不需要 ccman
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
ccman 的核心功能是自动化配置文件的切换:
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
- **Codex**:自动修改 `~/.codex/config.toml` 和 `~/.codex/auth.json`
|
|
27
|
+
- **Claude Code**:自动修改 `~/.claude/settings.json`
|
|
28
|
+
- **备份机制**:写入前自动备份,失败时自动回滚
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
如果你更喜欢手动编辑配置文件,或者只使用一个服务商从不切换,可能不需要这个工具。
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
# 1. 添加服务商(交互式)
|
|
27
|
-
ccman cx add
|
|
32
|
+
📖 更多信息:[GitHub 仓库](https://github.com/2ue/ccm) - 包含手动配置方式和详细对比分析
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
ccman cx list
|
|
34
|
+
---
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
ccman cx use
|
|
34
|
-
|
|
35
|
-
# 4. 查看当前服务商
|
|
36
|
-
ccman cx current
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### 管理 Claude Code 服务商
|
|
36
|
+
## 安装
|
|
40
37
|
|
|
41
38
|
```bash
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
npm install -g ccman
|
|
40
|
+
```
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
ccman cc list
|
|
42
|
+
**要求**:Node.js >= 18.0.0
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
ccman cc use
|
|
44
|
+
**Desktop 版本**:如果你更喜欢图形界面,可以从 [GitHub Releases](https://github.com/2ue/ccm/releases) 下载 Desktop 应用(macOS/Windows/Linux)。
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
ccman cc current
|
|
53
|
-
```
|
|
46
|
+
---
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
## 快速开始
|
|
56
49
|
|
|
57
|
-
|
|
50
|
+
### 查看帮助
|
|
58
51
|
|
|
59
52
|
```bash
|
|
60
|
-
ccman
|
|
61
|
-
ccman cx # Codex 菜单
|
|
62
|
-
ccman cc # Claude Code 菜单
|
|
63
|
-
```
|
|
53
|
+
$ ccman --help
|
|
64
54
|
|
|
65
|
-
|
|
55
|
+
Usage: ccman [options] [command]
|
|
66
56
|
|
|
67
|
-
|
|
57
|
+
Codex/Claude Code API 服务商配置管理工具
|
|
68
58
|
|
|
69
|
-
|
|
59
|
+
Options:
|
|
60
|
+
-V, --version output the version number
|
|
61
|
+
-h, --help display help for command
|
|
70
62
|
|
|
71
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
列出所有 Codex 服务商,`●` 表示当前使用的服务商。
|
|
79
|
+
**示例输出**:
|
|
103
80
|
|
|
104
81
|
```bash
|
|
105
82
|
$ ccman cx list
|
|
106
83
|
|
|
107
|
-
📋 Codex
|
|
84
|
+
📋 Codex 服务商 (2 个)
|
|
108
85
|
|
|
109
|
-
●
|
|
110
|
-
|
|
86
|
+
● Anthropic Official [当前]
|
|
87
|
+
https://api.anthropic.com
|
|
111
88
|
|
|
112
|
-
○
|
|
113
|
-
|
|
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
|
-
|
|
96
|
+
📍 当前 Codex 服务商
|
|
129
97
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
#### `ccman cx current`
|
|
138
|
-
|
|
139
|
-
显示当前使用的 Codex 服务商。
|
|
104
|
+
### 管理 Claude Code 服务商
|
|
140
105
|
|
|
141
106
|
```bash
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
编辑 Codex 服务商配置。
|
|
113
|
+
**示例输出**:
|
|
154
114
|
|
|
155
115
|
```bash
|
|
156
|
-
$ ccman
|
|
116
|
+
$ ccman cc list
|
|
157
117
|
|
|
158
|
-
|
|
159
|
-
❯ Anthropic Official
|
|
160
|
-
PackyCode
|
|
118
|
+
📋 Claude Code 服务商 (2 个)
|
|
161
119
|
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
176
|
-
|
|
177
|
-
? 选择要删除的服务商 ›
|
|
178
|
-
Anthropic Official (当前使用中)
|
|
179
|
-
❯ PackyCode
|
|
128
|
+
$ ccman cc current
|
|
180
129
|
|
|
181
|
-
|
|
130
|
+
📍 当前 Claude Code 服务商
|
|
182
131
|
|
|
183
|
-
|
|
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
|
-
|
|
138
|
+
---
|
|
187
139
|
|
|
188
|
-
|
|
140
|
+
## 完整命令
|
|
189
141
|
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
+
---
|
|
221
182
|
|
|
222
|
-
##
|
|
183
|
+
## 完整使用示例
|
|
223
184
|
|
|
224
|
-
|
|
185
|
+
### 场景 1:添加并切换服务商
|
|
225
186
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
193
|
+
● Anthropic Official [当前]
|
|
194
|
+
https://api.anthropic.com
|
|
239
195
|
|
|
240
|
-
|
|
241
|
-
# 1. 添加 PackyCode
|
|
196
|
+
# 2. 添加 PackyCode(交互式)
|
|
242
197
|
$ ccman cx add
|
|
243
|
-
|
|
198
|
+
? 选择配置方式 › 使用预设模板
|
|
199
|
+
? 选择预设 › PackyCode
|
|
200
|
+
? 服务商名称 › PackyCode
|
|
201
|
+
? API Key › ••••••••••••••••••••
|
|
202
|
+
✅ 添加成功!
|
|
203
|
+
💡 切换到此服务商: ccman cx use PackyCode
|
|
244
204
|
|
|
245
|
-
#
|
|
205
|
+
# 3. 切换到 PackyCode
|
|
246
206
|
$ ccman cx use PackyCode
|
|
247
207
|
✅ 已切换到: PackyCode
|
|
248
208
|
|
|
249
|
-
#
|
|
209
|
+
# 4. 确认当前服务商
|
|
250
210
|
$ ccman cx current
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
###
|
|
220
|
+
### 场景 2:克隆服务商(管理多个 Key)
|
|
256
221
|
|
|
257
222
|
```bash
|
|
258
|
-
# 场景:同一服务商有测试和生产两个 API Key
|
|
259
|
-
|
|
260
223
|
# 1. 添加生产配置
|
|
261
224
|
$ ccman cc add
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
278
|
-
$ ccman cc use "Claude Test"
|
|
279
|
-
✅ 已切换到: Claude Test
|
|
280
|
-
```
|
|
242
|
+
📋 Claude Code 服务商 (2 个)
|
|
281
243
|
|
|
282
|
-
|
|
244
|
+
● Claude Production [当前]
|
|
245
|
+
https://api.anthropic.com
|
|
283
246
|
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
259
|
+
**ccman 配置**:`~/.ccman/config.json`
|
|
344
260
|
|
|
345
|
-
|
|
261
|
+
**Codex 配置**(ccman 会自动修改):
|
|
262
|
+
- `~/.codex/config.toml`
|
|
263
|
+
- `~/.codex/auth.json`
|
|
346
264
|
|
|
347
|
-
|
|
265
|
+
**Claude Code 配置**(ccman 会自动修改):
|
|
266
|
+
- `~/.claude/settings.json`
|
|
348
267
|
|
|
349
|
-
|
|
268
|
+
**零破坏性承诺**:
|
|
269
|
+
- 只修改管理的字段,保留其他所有配置
|
|
270
|
+
- 写入前备份,失败时自动回滚
|
|
271
|
+
- API Key 存储在本地,权限 `0600`
|
|
350
272
|
|
|
351
|
-
|
|
273
|
+
---
|
|
352
274
|
|
|
353
|
-
|
|
275
|
+
## 常见问题
|
|
354
276
|
|
|
355
|
-
|
|
277
|
+
**Q: 支持配置导入/导出吗?**
|
|
278
|
+
A: 当前版本不支持。如需备份,手动复制 `~/.ccman/` 目录。
|
|
356
279
|
|
|
357
|
-
|
|
280
|
+
**Q: WebDAV 同步是什么?**
|
|
281
|
+
A: 将 ccman 配置同步到 WebDAV 服务器(如 iCloud/Dropbox),多设备共享配置。详见 `ccman sync --help`。
|
|
358
282
|
|
|
359
|
-
|
|
283
|
+
---
|
|
360
284
|
|
|
361
|
-
|
|
285
|
+
## 许可证
|
|
362
286
|
|
|
363
|
-
|
|
287
|
+
MIT
|
|
364
288
|
|
|
365
|
-
|
|
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.
|
|
42
|
+
version: "3.0.15",
|
|
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
|
|
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 (
|
|
3007
|
-
|
|
3008
|
-
} else if (
|
|
3009
|
-
|
|
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
|
-
|
|
3013
|
+
path11.splice(i2, 1);
|
|
3014
3014
|
up--;
|
|
3015
3015
|
}
|
|
3016
3016
|
}
|
|
3017
|
-
if (unshift)
|
|
3018
|
-
if (last === "." || last === "..")
|
|
3019
|
-
return
|
|
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
|
|
3446
|
-
if (!isString(
|
|
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 (!
|
|
3448
|
+
} else if (!path11) {
|
|
3449
3449
|
continue;
|
|
3450
3450
|
}
|
|
3451
|
-
resolvedPath =
|
|
3452
|
-
resolvedAbsolute =
|
|
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(
|
|
3461
|
-
var isAbsolute = posix.isAbsolute(
|
|
3462
|
-
|
|
3463
|
-
if (!
|
|
3464
|
-
|
|
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 (
|
|
3467
|
-
|
|
3466
|
+
if (path11 && trailingSlash) {
|
|
3467
|
+
path11 += "/";
|
|
3468
3468
|
}
|
|
3469
|
-
return (isAbsolute ? "/" : "") +
|
|
3469
|
+
return (isAbsolute ? "/" : "") + path11;
|
|
3470
3470
|
};
|
|
3471
|
-
posix.isAbsolute = function(
|
|
3472
|
-
return
|
|
3471
|
+
posix.isAbsolute = function(path11) {
|
|
3472
|
+
return path11.charAt(0) === "/";
|
|
3473
3473
|
};
|
|
3474
3474
|
posix.join = function() {
|
|
3475
|
-
var
|
|
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 (!
|
|
3483
|
-
|
|
3482
|
+
if (!path11) {
|
|
3483
|
+
path11 += segment;
|
|
3484
3484
|
} else {
|
|
3485
|
-
|
|
3485
|
+
path11 += "/" + segment;
|
|
3486
3486
|
}
|
|
3487
3487
|
}
|
|
3488
3488
|
}
|
|
3489
|
-
return posix.normalize(
|
|
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(
|
|
3524
|
-
return
|
|
3523
|
+
posix._makeLong = function(path11) {
|
|
3524
|
+
return path11;
|
|
3525
3525
|
};
|
|
3526
|
-
posix.dirname = function(
|
|
3527
|
-
var result = posixSplitPath(
|
|
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(
|
|
3537
|
-
var f3 = posixSplitPath(
|
|
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(
|
|
3544
|
-
return posixSplitPath(
|
|
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,
|
|
14942
|
+
function traverse(object, path11) {
|
|
14943
14943
|
var callback = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : function() {
|
|
14944
14944
|
};
|
|
14945
|
-
var segments =
|
|
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(
|
|
15188
|
-
if (!
|
|
15189
|
-
return
|
|
15187
|
+
function ensureCollectionPath(path11) {
|
|
15188
|
+
if (!path11.endsWith("/")) {
|
|
15189
|
+
return path11 + "/";
|
|
15190
15190
|
}
|
|
15191
|
-
return
|
|
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,
|
|
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(
|
|
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,
|
|
15596
|
+
async function unlock(context, path11, token, options = {}) {
|
|
15597
15597
|
const requestOptions = prepareRequestOptions({
|
|
15598
|
-
url: joinURL(context.remoteURL, encodePath(
|
|
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
|
|
15648
|
+
const path11 = options.path || "/";
|
|
15649
15649
|
const requestOptions = prepareRequestOptions({
|
|
15650
|
-
url: joinURL(context.remoteURL,
|
|
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: (
|
|
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: (
|
|
15993
|
+
customRequest: (path11, requestOptions) => customRequest(context, path11, requestOptions),
|
|
15994
15994
|
deleteFile: (filename, options2) => deleteFile(context, filename, options2),
|
|
15995
|
-
exists: (
|
|
15996
|
-
getDirectoryContents: (
|
|
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: (
|
|
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: (
|
|
16007
|
-
search: (
|
|
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: (
|
|
16012
|
-
unlock: (
|
|
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((
|
|
17042
|
-
console.log(import_chalk6.default.gray(` ${
|
|
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((
|
|
17107
|
-
console.log(import_chalk7.default.gray(` ${
|
|
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
|
|
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(
|
|
18749
|
-
console.log(
|
|
18750
|
-
console.log(
|
|
18751
|
-
console.log(
|
|
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();
|