@2api-store/activate 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -5
- package/activate.mjs +0 -3
- package/lib/cmds.mjs +3 -249
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,10 +37,8 @@ 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
|
-
| `reconfig` | 重新配置:输入已有 `sk-` API Key → 重拉最新模板/模型 → 覆盖写入配置(写前备份)。同激活流程但不创建账号 |
|
|
41
40
|
| `refresh-groups` | 查看可用分组:`GET /cli/groups` → 彩色表格展示。只读,不改 key/配置 |
|
|
42
41
|
|
|
43
|
-
所有子命令支持 `--api-base <url>` 覆盖端点;`restore` / `reset` / `reconfig` 支持 `--dry-run` 预演。
|
|
44
42
|
|
|
45
43
|
### restore — 从备份恢复
|
|
46
44
|
|
|
@@ -65,11 +63,8 @@ npx @2api-store/activate reset
|
|
|
65
63
|
- 删除后尝试从最新常规备份恢复(回到激活前的官方状态)
|
|
66
64
|
- 需要显式确认(默认 `N`,输入 `y` 才执行)
|
|
67
65
|
|
|
68
|
-
### reconfig — 重新配置
|
|
69
66
|
|
|
70
67
|
```bash
|
|
71
|
-
npx @2api-store/activate reconfig
|
|
72
|
-
npx @2api-store/activate reconfig --api-base http://127.0.0.1:5566
|
|
73
68
|
```
|
|
74
69
|
|
|
75
70
|
- 输入现有 `sk-` API Key(不接受兑换码)
|
package/activate.mjs
CHANGED
|
@@ -6,7 +6,6 @@ process.env.NODE_NO_WARNINGS = '1'
|
|
|
6
6
|
* @2api-store/activate — TokenShop CLI 一键激活(v0.3.0+)
|
|
7
7
|
*
|
|
8
8
|
* 入口(argv[2] 分发):
|
|
9
|
-
* - 子命令(restore / reset / reconfigure / refresh-groups)→ 分发到 lib/cmds.mjs
|
|
10
9
|
* - 无子命令 / 选项(--help / --version / --dry-run / --platform / --api-base)
|
|
11
10
|
* → 走默认激活流程 main()
|
|
12
11
|
*
|
|
@@ -100,7 +99,6 @@ ${SUB_HELP}
|
|
|
100
99
|
npx @2api-store/activate --platform openai # 只激活 openai
|
|
101
100
|
npx @2api-store/activate restore # 从备份恢复
|
|
102
101
|
npx @2api-store/activate reset # 恢复官方默认
|
|
103
|
-
npx @2api-store/activate reconfigure # 用已有 API Key 重新配置
|
|
104
102
|
npx @2api-store/activate refresh-groups # 查看可用分组
|
|
105
103
|
ACTIVATE_API_BASE=http://127.0.0.1:5566 npx @2api-store/activate
|
|
106
104
|
|
|
@@ -142,7 +140,6 @@ function inferModelPlatforms(models) {
|
|
|
142
140
|
// ============================================================
|
|
143
141
|
|
|
144
142
|
async function main() {
|
|
145
|
-
// === 子命令分发:argv[2] 是子命令(restore/reset/reconfigure/refresh-groups)===
|
|
146
143
|
// 注意:必须在 parseArgs 之前判断,因为子命令有自己的 argv 解析(--api-base / --dry-run)
|
|
147
144
|
const firstArg = process.argv[2]
|
|
148
145
|
if (firstArg && !firstArg.startsWith('-') && SUBCOMMAND_NAMES.has(firstArg)) {
|
package/lib/cmds.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 子命令实现:restore / reset /
|
|
2
|
+
* 子命令实现:restore / reset / refresh-groups
|
|
3
3
|
*
|
|
4
4
|
* 设计原则:
|
|
5
5
|
* - 每个子命令是独立 async 函数,接收 argv 数组(不含子命令名本身)
|
|
6
6
|
* - 返回退出码(0 成功 / 1 输入错误 / 2 权限 / 3 写入失败)
|
|
7
7
|
* - 自包含:解析自己的 argv + 走完整流程
|
|
8
|
-
* - 操作配置文件前都备份(reset
|
|
8
|
+
* - 操作配置文件前都备份(reset 删前备份)
|
|
9
9
|
* - 不复用 activate.mjs 的 main()(避免耦合,main 包含 redeem/CLI 多选/安装等复杂流程)
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -126,10 +126,9 @@ const SUB_HELP = `
|
|
|
126
126
|
子命令:
|
|
127
127
|
restore 从备份恢复(扫描 ~/.codex / ~/.claude / ~/.gemini 下的 .bak.* 文件)
|
|
128
128
|
reset 恢复官方默认(删 2api.store 写入的配置,并从最新备份恢复)
|
|
129
|
-
reconfig 重新配置(输入已有 sk- API Key → 重拉模板/模型 → 覆盖写入)
|
|
130
129
|
refresh-groups 刷新分组列表(只展示,不改 key/配置)
|
|
131
130
|
|
|
132
|
-
所有子命令都支持 --api-base <url> 覆盖端点,restore/reset
|
|
131
|
+
所有子命令都支持 --api-base <url> 覆盖端点,restore/reset 支持 --dry-run。
|
|
133
132
|
`.trim()
|
|
134
133
|
|
|
135
134
|
// ============================================================
|
|
@@ -466,250 +465,6 @@ async function cmdReset(argv) {
|
|
|
466
465
|
return deleted.length > 0 ? 0 : 1
|
|
467
466
|
}
|
|
468
467
|
|
|
469
|
-
// ============================================================
|
|
470
|
-
// reconfig:重新配置
|
|
471
|
-
// ============================================================
|
|
472
|
-
|
|
473
|
-
async function cmdReconfig(argv) {
|
|
474
|
-
const args = parseSubArgs(argv)
|
|
475
|
-
if (args.help) {
|
|
476
|
-
console.log(`npx @2api-store/activate reconfig [options]\n\n${SUB_HELP}`)
|
|
477
|
-
return 0
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
banner()
|
|
481
|
-
const env = detectAll()
|
|
482
|
-
const osLabel = env.os.isMac
|
|
483
|
-
? 'macOS'
|
|
484
|
-
: env.os.isLinux
|
|
485
|
-
? 'Linux'
|
|
486
|
-
: env.os.isWindows
|
|
487
|
-
? 'Windows'
|
|
488
|
-
: env.os.platform
|
|
489
|
-
info(`环境:${osLabel} · node v${env.node.version}`)
|
|
490
|
-
if (!env.node.supported) {
|
|
491
|
-
fail(`需要 node >= 18,当前 ${env.node.version}`)
|
|
492
|
-
return 1
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
const apiBase = args.apiBase || process.env.ACTIVATE_API_BASE || DEFAULT_API_BASE
|
|
496
|
-
info(`API 端点:${apiBase}`)
|
|
497
|
-
if (args.dryRun) warn('【试运行模式 --dry-run】不会真写配置')
|
|
498
|
-
console.log()
|
|
499
|
-
|
|
500
|
-
// 输入 API Key
|
|
501
|
-
const apiKey = (await askSecret('请输入现有的 API Key (sk-...)')).trim()
|
|
502
|
-
if (!apiKey) {
|
|
503
|
-
fail('未输入 API Key')
|
|
504
|
-
return 1
|
|
505
|
-
}
|
|
506
|
-
if (!apiKey.startsWith('sk-')) {
|
|
507
|
-
fail('reconfig 仅支持 API Key 模式(sk- 前缀)。如需使用兑换码,请运行默认激活流程:npx @2api-store/activate')
|
|
508
|
-
return 1
|
|
509
|
-
}
|
|
510
|
-
info('识别为:API Key 模式(不创建账号,仅重拉模板写入配置)')
|
|
511
|
-
console.log()
|
|
512
|
-
|
|
513
|
-
// POST /cli/activate — 全平台,不问平台限制
|
|
514
|
-
step('请求激活服务(全量重拉模板)')
|
|
515
|
-
let bundle
|
|
516
|
-
try {
|
|
517
|
-
bundle = await cliActivate({ credential: apiKey, apiBase })
|
|
518
|
-
} catch (err) {
|
|
519
|
-
fail(`请求失败:${err.message}`)
|
|
520
|
-
if (err.statusCode) fail(` HTTP ${err.statusCode}`)
|
|
521
|
-
return 1
|
|
522
|
-
}
|
|
523
|
-
if (bundle.mode && bundle.mode !== 'key') {
|
|
524
|
-
warn(`服务端返回模式:${bundle.mode}(预期 key 模式,但继续)`)
|
|
525
|
-
}
|
|
526
|
-
ok('已获取最新配置模板')
|
|
527
|
-
if (bundle.base_url) info(`base_url:${bundle.base_url}`)
|
|
528
|
-
console.log()
|
|
529
|
-
|
|
530
|
-
// 过滤配置 — 按模型平台推断 + OS 过滤(与默认激活流程一致)
|
|
531
|
-
const allowedPlatforms = new Set()
|
|
532
|
-
for (const m of bundle.models || []) {
|
|
533
|
-
const id = (typeof m === 'string' ? m : m?.id || '').toLowerCase()
|
|
534
|
-
if (/^(gpt|o1|o3|codex|glm|minimax|deepseek)/.test(id)) allowedPlatforms.add('openai')
|
|
535
|
-
else if (id.startsWith('claude')) allowedPlatforms.add('anthropic')
|
|
536
|
-
else if (id.startsWith('gemini')) allowedPlatforms.add('gemini')
|
|
537
|
-
else if (id.startsWith('grok')) allowedPlatforms.add('grok')
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
const osConfigs = (bundle.configs || []).filter((c) => {
|
|
541
|
-
if (!c) return false
|
|
542
|
-
if (allowedPlatforms.size > 0 && !allowedPlatforms.has(c.platform)) return false
|
|
543
|
-
if (env.os.isWindows) {
|
|
544
|
-
return c.os_type === 'windows' || c.os_type === 'all'
|
|
545
|
-
}
|
|
546
|
-
return c.os_type === 'unix' || c.os_type === 'all'
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
if (osConfigs.length === 0) {
|
|
550
|
-
fail('服务端未返回适配当前系统的配置模板')
|
|
551
|
-
return 1
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// reconfig: 全选主 CLI 配置(跳过独立 CLI 如 opencode)
|
|
555
|
-
const seen = new Set()
|
|
556
|
-
const selectedItems = []
|
|
557
|
-
for (const c of osConfigs) {
|
|
558
|
-
if (!isDefaultSelected(c.client_type)) continue
|
|
559
|
-
const key = `${c.platform}/${c.client_type}`
|
|
560
|
-
if (seen.has(key)) continue
|
|
561
|
-
seen.add(key)
|
|
562
|
-
const meta = getCliMeta(c.client_type)
|
|
563
|
-
const displayName = c.display_name || c.displayName || meta?.name || `${c.platform} ${c.client_type}`
|
|
564
|
-
selectedItems.push({
|
|
565
|
-
config: c,
|
|
566
|
-
clientType: c.client_type,
|
|
567
|
-
meta,
|
|
568
|
-
})
|
|
569
|
-
}
|
|
570
|
-
ok(`将覆盖 ${selectedItems.length} 个 CLI 配置`)
|
|
571
|
-
console.log()
|
|
572
|
-
|
|
573
|
-
// 选默认模型
|
|
574
|
-
const modelItems = listModels(bundle.models || [])
|
|
575
|
-
let defaultModelId = ''
|
|
576
|
-
if (modelItems.length > 0) {
|
|
577
|
-
step('选择默认模型')
|
|
578
|
-
const TOP_N = 10
|
|
579
|
-
const topItems = modelItems.slice(0, TOP_N)
|
|
580
|
-
const hasMore = modelItems.length > TOP_N
|
|
581
|
-
const selectItems = [
|
|
582
|
-
...topItems.map((m) => ({ label: `${m.label} (${m.id})` })),
|
|
583
|
-
...(hasMore ? [{ label: chalk.dim('其他(手动输入)') }] : []),
|
|
584
|
-
]
|
|
585
|
-
const idx = await select('选择默认模型', selectItems, { defaultIndex: 0 })
|
|
586
|
-
if (hasMore && idx === topItems.length) {
|
|
587
|
-
defaultModelId = (await ask('输入模型 ID')).trim() || topItems[0].id
|
|
588
|
-
} else {
|
|
589
|
-
defaultModelId = topItems[idx].id
|
|
590
|
-
}
|
|
591
|
-
ok(`已选默认模型:${defaultModelId}`)
|
|
592
|
-
} else {
|
|
593
|
-
warn('服务端未返回模型列表,{{defaultModel}} 将留空')
|
|
594
|
-
}
|
|
595
|
-
console.log()
|
|
596
|
-
|
|
597
|
-
// 渲染
|
|
598
|
-
const baseUrl = bundle.base_url || ''
|
|
599
|
-
const baseUrlBeta = bundle.base_url_beta || baseUrl
|
|
600
|
-
const renderValues = {
|
|
601
|
-
apiKey: bundle.api_key || apiKey,
|
|
602
|
-
baseUrl,
|
|
603
|
-
apiBase: baseUrl,
|
|
604
|
-
apiBaseBeta: baseUrlBeta,
|
|
605
|
-
antigravityApiBase: `${baseUrl}/antigravity`,
|
|
606
|
-
antigravityApiBaseBeta: `${baseUrlBeta}/antigravity`,
|
|
607
|
-
defaultModel: defaultModelId,
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// 备份 + 写入
|
|
611
|
-
step('备份并写入配置')
|
|
612
|
-
const report = []
|
|
613
|
-
let firstPermissionError = null
|
|
614
|
-
|
|
615
|
-
for (const item of selectedItems) {
|
|
616
|
-
const displayName =
|
|
617
|
-
item.config.display_name || item.config.displayName || item.meta?.name || item.clientType
|
|
618
|
-
plain(`── ${displayName} ──`)
|
|
619
|
-
const files = item.config.files || []
|
|
620
|
-
for (const f of files) {
|
|
621
|
-
const rawPath = f.path || ''
|
|
622
|
-
const isShellOnly = rawPath === 'Terminal' || rawPath === 'PowerShell'
|
|
623
|
-
const targetPath = isShellOnly
|
|
624
|
-
? rawPath
|
|
625
|
-
: expandPath(rawPath, { isWindows: env.os.isWindows })
|
|
626
|
-
const rendered = renderContent(f.content || '', renderValues)
|
|
627
|
-
|
|
628
|
-
if (isShellOnly) {
|
|
629
|
-
plain(' [shell 命令,请手动执行]')
|
|
630
|
-
plain(rendered.split('\n').map((l) => ' ' + l).join('\n'))
|
|
631
|
-
report.push({ cli: displayName, path: rawPath, action: 'shell-only' })
|
|
632
|
-
continue
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
plain(` 目标:${targetPath}`)
|
|
636
|
-
|
|
637
|
-
if (args.dryRun) {
|
|
638
|
-
warn(' [试运行] 不真实写入')
|
|
639
|
-
report.push({ cli: displayName, path: targetPath, action: 'dry-run' })
|
|
640
|
-
console.log()
|
|
641
|
-
continue
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
let backupPath = null
|
|
645
|
-
try {
|
|
646
|
-
backupPath = backupFile(targetPath)
|
|
647
|
-
if (backupPath) plain(` 备份:${backupPath}`)
|
|
648
|
-
} catch (err) {
|
|
649
|
-
fail(`备份失败:${err.message}(跳过此文件避免无备份覆盖)`)
|
|
650
|
-
report.push({ cli: displayName, path: targetPath, action: 'backup-failed', error: err.message })
|
|
651
|
-
continue
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
try {
|
|
655
|
-
writeFileWithPermission(targetPath, rendered, { isWindows: env.os.isWindows })
|
|
656
|
-
ok('已写入')
|
|
657
|
-
report.push({ cli: displayName, path: targetPath, action: 'written', backup: backupPath })
|
|
658
|
-
} catch (err) {
|
|
659
|
-
if (err instanceof PermissionDeniedError) {
|
|
660
|
-
if (!firstPermissionError) firstPermissionError = err
|
|
661
|
-
fail(err.message)
|
|
662
|
-
report.push({ cli: displayName, path: targetPath, action: 'permission-denied', error: err.message })
|
|
663
|
-
} else {
|
|
664
|
-
fail(`写入失败:${err.message}`)
|
|
665
|
-
report.push({ cli: displayName, path: targetPath, action: 'failed', error: err.message })
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
console.log()
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// 报告
|
|
673
|
-
step('重配报告')
|
|
674
|
-
const written = report.filter((r) => r.action === 'written')
|
|
675
|
-
const failed = report.filter(
|
|
676
|
-
(r) => r.action === 'failed' || r.action === 'permission-denied' || r.action === 'backup-failed',
|
|
677
|
-
)
|
|
678
|
-
if (args.dryRun) {
|
|
679
|
-
plain(`[试运行] 将写入 ${report.filter((r) => r.action === 'dry-run').length} 个文件`)
|
|
680
|
-
} else {
|
|
681
|
-
ok(`已写入 ${written.length} 个文件`)
|
|
682
|
-
}
|
|
683
|
-
if (failed.length > 0) {
|
|
684
|
-
warn(`失败 ${failed.length} 项:`)
|
|
685
|
-
failed.forEach((f) => warn(` - ${f.cli} ${f.path}`))
|
|
686
|
-
}
|
|
687
|
-
const backups = report.filter((r) => r.backup).map((r) => r.backup)
|
|
688
|
-
if (backups.length > 0) {
|
|
689
|
-
info('备份位置:')
|
|
690
|
-
backups.forEach((b) => plain(` - ${b}`))
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
if (firstPermissionError) {
|
|
694
|
-
console.log()
|
|
695
|
-
fail('部分文件权限不足,请按提示用 sudo / 管理员 PowerShell 重跑:')
|
|
696
|
-
fail(' sudo npx -y @2api-store/activate reconfig')
|
|
697
|
-
return 2
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// 打印启动/使用指南(与默认激活一致)
|
|
701
|
-
const guideItems = selectedItems
|
|
702
|
-
.filter((item) => item.meta)
|
|
703
|
-
.map((item) => ({
|
|
704
|
-
name: item.meta.name || item.clientType,
|
|
705
|
-
startCmd: item.meta.startCmd || '',
|
|
706
|
-
usage: item.meta.usage || '',
|
|
707
|
-
}))
|
|
708
|
-
guide(guideItems)
|
|
709
|
-
|
|
710
|
-
return failed.length > 0 ? 3 : 0
|
|
711
|
-
}
|
|
712
|
-
|
|
713
468
|
// ============================================================
|
|
714
469
|
// refresh-groups:刷新分组列表(只展示)
|
|
715
470
|
// ============================================================
|
|
@@ -781,7 +536,6 @@ async function cmdRefreshGroups(argv) {
|
|
|
781
536
|
export const SUBCOMMANDS = {
|
|
782
537
|
restore: { handler: cmdRestore, description: '从备份恢复配置' },
|
|
783
538
|
reset: { handler: cmdReset, description: '恢复官方默认(删 2api.store 配置并从备份恢复)' },
|
|
784
|
-
reconfig: { handler: cmdReconfig, description: '用已有 API Key 重新拉取并写入配置' },
|
|
785
539
|
'refresh-groups': { handler: cmdRefreshGroups, description: '查看可用分组(不改配置)' },
|
|
786
540
|
}
|
|
787
541
|
|