@2api-store/activate 0.4.0 → 0.4.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 +5 -5
- package/lib/cmds.mjs +106 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,10 +37,10 @@ npx @2api-store/activate <subcommand> [options]
|
|
|
37
37
|
|--------|------|
|
|
38
38
|
| `restore` | 从备份恢复:扫描 `~/.codex/`、`~/.claude/`、`~/.gemini/` 下的 `.bak.*` 文件,选择一个恢复(覆盖当前) |
|
|
39
39
|
| `reset` | 恢复官方默认:删除 2api.store 写入的配置文件(`~/.codex/config.toml`、`~/.codex/auth.json`、`~/.claude/settings.json`、`~/.gemini/settings.json`),并尝试从最新备份恢复。**破坏性操作,需确认** |
|
|
40
|
-
| `
|
|
40
|
+
| `reconfig` | 重新配置:输入已有 `sk-` API Key → 重拉最新模板/模型 → 覆盖写入配置(写前备份)。同激活流程但不创建账号 |
|
|
41
41
|
| `refresh-groups` | 查看可用分组:`GET /cli/groups` → 彩色表格展示。只读,不改 key/配置 |
|
|
42
42
|
|
|
43
|
-
所有子命令支持 `--api-base <url>` 覆盖端点;`restore` / `reset` / `
|
|
43
|
+
所有子命令支持 `--api-base <url>` 覆盖端点;`restore` / `reset` / `reconfig` 支持 `--dry-run` 预演。
|
|
44
44
|
|
|
45
45
|
### restore — 从备份恢复
|
|
46
46
|
|
|
@@ -65,11 +65,11 @@ npx @2api-store/activate reset
|
|
|
65
65
|
- 删除后尝试从最新常规备份恢复(回到激活前的官方状态)
|
|
66
66
|
- 需要显式确认(默认 `N`,输入 `y` 才执行)
|
|
67
67
|
|
|
68
|
-
###
|
|
68
|
+
### reconfig — 重新配置
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
npx @2api-store/activate
|
|
72
|
-
npx @2api-store/activate
|
|
71
|
+
npx @2api-store/activate reconfig
|
|
72
|
+
npx @2api-store/activate reconfig --api-base http://127.0.0.1:5566
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
- 输入现有 `sk-` API Key(不接受兑换码)
|
package/lib/cmds.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 子命令实现:restore / reset /
|
|
2
|
+
* 子命令实现:restore / reset / reconfig / refresh-groups
|
|
3
3
|
*
|
|
4
4
|
* 设计原则:
|
|
5
5
|
* - 每个子命令是独立 async 函数,接收 argv 数组(不含子命令名本身)
|
|
6
6
|
* - 返回退出码(0 成功 / 1 输入错误 / 2 权限 / 3 写入失败)
|
|
7
7
|
* - 自包含:解析自己的 argv + 走完整流程
|
|
8
|
-
* - 操作配置文件前都备份(reset 删前备份;
|
|
8
|
+
* - 操作配置文件前都备份(reset 删前备份;reconfig 写前备份)
|
|
9
9
|
* - 不复用 activate.mjs 的 main()(避免耦合,main 包含 redeem/CLI 多选/安装等复杂流程)
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -126,10 +126,10 @@ const SUB_HELP = `
|
|
|
126
126
|
子命令:
|
|
127
127
|
restore 从备份恢复(扫描 ~/.codex / ~/.claude / ~/.gemini 下的 .bak.* 文件)
|
|
128
128
|
reset 恢复官方默认(删 2api.store 写入的配置,并从最新备份恢复)
|
|
129
|
-
|
|
129
|
+
reconfig 重新配置(输入已有 sk- API Key → 重拉模板/模型 → 覆盖写入)
|
|
130
130
|
refresh-groups 刷新分组列表(只展示,不改 key/配置)
|
|
131
131
|
|
|
132
|
-
所有子命令都支持 --api-base <url> 覆盖端点,restore/reset/
|
|
132
|
+
所有子命令都支持 --api-base <url> 覆盖端点,restore/reset/reconfig 支持 --dry-run。
|
|
133
133
|
`.trim()
|
|
134
134
|
|
|
135
135
|
// ============================================================
|
|
@@ -162,56 +162,118 @@ async function cmdRestore(argv) {
|
|
|
162
162
|
return 0
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
ok(`找到 ${backups.length}
|
|
166
|
-
console.log()
|
|
165
|
+
ok(`找到 ${backups.length} 个备份文件`)
|
|
167
166
|
|
|
168
|
-
//
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
167
|
+
// 按应用分组(根据 origPath 的父目录)
|
|
168
|
+
const APP_LABELS = {
|
|
169
|
+
'.codex': 'Codex CLI',
|
|
170
|
+
'.claude': 'Claude Code',
|
|
171
|
+
'.gemini': 'Gemini CLI',
|
|
172
|
+
}
|
|
173
|
+
function appLabel(origPath) {
|
|
174
|
+
const home = process.env.HOME || process.env.USERPROFILE || ''
|
|
175
|
+
const rel = path.relative(home, origPath).replace(/\\/g, '/')
|
|
176
|
+
const firstSeg = rel.split('/')[0]
|
|
177
|
+
return APP_LABELS[firstSeg] || firstSeg
|
|
178
|
+
}
|
|
179
|
+
function appDir(origPath) {
|
|
180
|
+
const home = process.env.HOME || process.env.USERPROFILE || ''
|
|
181
|
+
return path.dirname(origPath)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 按应用(目录)分组
|
|
185
|
+
const byApp = {}
|
|
186
|
+
for (const b of backups) {
|
|
187
|
+
const dir = appDir(b.origPath)
|
|
188
|
+
if (!byApp[dir]) byApp[dir] = []
|
|
189
|
+
byApp[dir].push(b)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const appDirs = Object.keys(byApp)
|
|
193
|
+
if (appDirs.length === 0) {
|
|
194
|
+
info('无可恢复的备份')
|
|
195
|
+
return 0
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log()
|
|
199
|
+
step('选择要恢复的应用')
|
|
200
|
+
const appItems = appDirs.map((dir) => {
|
|
201
|
+
const label = appLabel(path.join(dir, 'dummy'))
|
|
202
|
+
const count = byApp[dir].length
|
|
203
|
+
return { label: `${chalk.cyan(label)}(${count} 个备份)`, dir }
|
|
178
204
|
})
|
|
205
|
+
const appIdx = await select('选择应用(回车=第一个)', appItems, { defaultIndex: 0 })
|
|
206
|
+
if (appIdx < 0 || appIdx >= appItems.length) {
|
|
207
|
+
fail('未选择应用')
|
|
208
|
+
return 1
|
|
209
|
+
}
|
|
210
|
+
const chosenDir = appItems[appIdx].dir
|
|
211
|
+
const chosenAppLabel = appLabel(path.join(chosenDir, 'dummy'))
|
|
212
|
+
|
|
213
|
+
// 该应用的备份按时间戳分组(截取到秒:T08-23-03)
|
|
214
|
+
const appBackups = byApp[chosenDir].sort((a, b) => b.mtime - a.mtime)
|
|
215
|
+
const byTime = {}
|
|
216
|
+
const timeOrder = []
|
|
217
|
+
for (const b of appBackups) {
|
|
218
|
+
// 从 bakPath 提取时间戳前缀(到秒)
|
|
219
|
+
const m = path.basename(b.bakPath).match(/\.bak\.(.+)$/)
|
|
220
|
+
const fullTs = m ? m[1] : ''
|
|
221
|
+
// 截取到秒:去毫秒后缀(最后 4 字符:-XXXZ)
|
|
222
|
+
const tsKey = fullTs.replace(/-\d{3}Z$/, 'Z')
|
|
223
|
+
if (!byTime[tsKey]) {
|
|
224
|
+
byTime[tsKey] = []
|
|
225
|
+
timeOrder.push(tsKey)
|
|
226
|
+
}
|
|
227
|
+
byTime[tsKey].push(b)
|
|
228
|
+
}
|
|
179
229
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
230
|
+
console.log()
|
|
231
|
+
step(`选择恢复时间点(${chosenAppLabel})`)
|
|
232
|
+
const timeItems = timeOrder.map((ts) => {
|
|
233
|
+
const group = byTime[ts]
|
|
234
|
+
const files = group.map((b) => path.basename(b.origPath)).join(', ')
|
|
235
|
+
const tsDisplay = fmtTime(group[0].mtime)
|
|
236
|
+
return { label: `${chalk.dim(tsDisplay)}(${group.length} 文件:${files})`, ts, group }
|
|
237
|
+
})
|
|
238
|
+
const timeIdx = await select('选择时间点(回车=最新)', timeItems, { defaultIndex: 0 })
|
|
239
|
+
if (timeIdx < 0 || timeIdx >= timeItems.length) {
|
|
240
|
+
fail('未选择时间点')
|
|
184
241
|
return 1
|
|
185
242
|
}
|
|
186
|
-
const
|
|
243
|
+
const chosenGroup = timeItems[timeIdx].group
|
|
244
|
+
|
|
187
245
|
console.log()
|
|
188
|
-
info(
|
|
189
|
-
|
|
246
|
+
info(`将恢复 ${chosenAppLabel}(${chosenGroup.length} 个文件):`)
|
|
247
|
+
for (const b of chosenGroup) {
|
|
248
|
+
info(` ${path.basename(b.origPath)} ← ${path.basename(b.bakPath)}`)
|
|
249
|
+
}
|
|
190
250
|
|
|
191
251
|
if (args.dryRun) {
|
|
192
252
|
warn('[试运行] 不真实恢复')
|
|
193
253
|
return 0
|
|
194
254
|
}
|
|
195
255
|
|
|
196
|
-
//
|
|
197
|
-
|
|
256
|
+
// 恢复前安全备份当前文件(方便撤销)
|
|
257
|
+
let restored = 0
|
|
258
|
+
for (const b of chosenGroup) {
|
|
259
|
+
if (fs.existsSync(b.origPath)) {
|
|
260
|
+
try {
|
|
261
|
+
const safetyBak = backupFile(b.origPath)
|
|
262
|
+
if (safetyBak) info(`安全备份:${path.basename(b.origPath)} → ${path.basename(safetyBak)}`)
|
|
263
|
+
} catch {
|
|
264
|
+
// 安全备份失败不阻塞
|
|
265
|
+
}
|
|
266
|
+
}
|
|
198
267
|
try {
|
|
199
|
-
|
|
200
|
-
|
|
268
|
+
restoreBackup(b.bakPath, b.origPath)
|
|
269
|
+
ok(`已恢复:${b.origPath}`)
|
|
270
|
+
restored++
|
|
201
271
|
} catch (err) {
|
|
202
|
-
|
|
272
|
+
fail(`恢复失败:${b.origPath}(${err.message})`)
|
|
203
273
|
}
|
|
204
274
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
restoreBackup(chosen.bakPath, chosen.origPath)
|
|
208
|
-
ok(`已恢复:${chosen.origPath}`)
|
|
209
|
-
info('(原备份文件保留,可多次恢复)')
|
|
210
|
-
return 0
|
|
211
|
-
} catch (err) {
|
|
212
|
-
fail(`恢复失败:${err.message}`)
|
|
213
|
-
return 3
|
|
214
|
-
}
|
|
275
|
+
info(`共恢复 ${restored} 个文件`)
|
|
276
|
+
return restored > 0 ? 0 : 3
|
|
215
277
|
}
|
|
216
278
|
|
|
217
279
|
// ============================================================
|
|
@@ -359,13 +421,13 @@ async function cmdReset(argv) {
|
|
|
359
421
|
}
|
|
360
422
|
|
|
361
423
|
// ============================================================
|
|
362
|
-
//
|
|
424
|
+
// reconfig:重新配置
|
|
363
425
|
// ============================================================
|
|
364
426
|
|
|
365
|
-
async function
|
|
427
|
+
async function cmdReconfig(argv) {
|
|
366
428
|
const args = parseSubArgs(argv)
|
|
367
429
|
if (args.help) {
|
|
368
|
-
console.log(`npx @2api-store/activate
|
|
430
|
+
console.log(`npx @2api-store/activate reconfig [options]\n\n${SUB_HELP}`)
|
|
369
431
|
return 0
|
|
370
432
|
}
|
|
371
433
|
|
|
@@ -396,7 +458,7 @@ async function cmdReconfigure(argv) {
|
|
|
396
458
|
return 1
|
|
397
459
|
}
|
|
398
460
|
if (!apiKey.startsWith('sk-')) {
|
|
399
|
-
fail('
|
|
461
|
+
fail('reconfig 仅支持 API Key 模式(sk- 前缀)。如需使用兑换码,请运行默认激活流程:npx @2api-store/activate')
|
|
400
462
|
return 1
|
|
401
463
|
}
|
|
402
464
|
info('识别为:API Key 模式(不创建账号,仅重拉模板写入配置)')
|
|
@@ -613,7 +675,7 @@ async function cmdReconfigure(argv) {
|
|
|
613
675
|
if (firstPermissionError) {
|
|
614
676
|
console.log()
|
|
615
677
|
fail('部分文件权限不足,请按提示用 sudo / 管理员 PowerShell 重跑:')
|
|
616
|
-
fail(' sudo npx -y @2api-store/activate
|
|
678
|
+
fail(' sudo npx -y @2api-store/activate reconfig')
|
|
617
679
|
return 2
|
|
618
680
|
}
|
|
619
681
|
|
|
@@ -691,7 +753,7 @@ async function cmdRefreshGroups(argv) {
|
|
|
691
753
|
export const SUBCOMMANDS = {
|
|
692
754
|
restore: { handler: cmdRestore, description: '从备份恢复配置' },
|
|
693
755
|
reset: { handler: cmdReset, description: '恢复官方默认(删 2api.store 配置并从备份恢复)' },
|
|
694
|
-
|
|
756
|
+
reconfig: { handler: cmdReconfig, description: '用已有 API Key 重新拉取并写入配置' },
|
|
695
757
|
'refresh-groups': { handler: cmdRefreshGroups, description: '查看可用分组(不改配置)' },
|
|
696
758
|
}
|
|
697
759
|
|