@2api-store/activate 0.3.6 → 0.3.7
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/activate.mjs +22 -3
- package/lib/api.mjs +66 -2
- package/package.json +1 -1
package/activate.mjs
CHANGED
|
@@ -18,7 +18,7 @@ process.env.NODE_NO_WARNINGS = '1'
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { detectAll } from './lib/detect.mjs'
|
|
21
|
-
import { cliActivate, verifyApiKey } from './lib/api.mjs'
|
|
21
|
+
import { cliActivate, listGroups, verifyApiKey } from './lib/api.mjs'
|
|
22
22
|
import { renderContent, expandPath, listModels } from './lib/render.mjs'
|
|
23
23
|
import { backupFile } from './lib/backup.mjs'
|
|
24
24
|
import { writeFileWithPermission, PermissionDeniedError } from './lib/write.mjs'
|
|
@@ -186,21 +186,40 @@ async function main() {
|
|
|
186
186
|
}
|
|
187
187
|
info(`识别为:${kind === 'key' ? 'API Key' : '兑换码'}模式`)
|
|
188
188
|
|
|
189
|
-
// === 3. 兑换码 → 邮箱 ===
|
|
189
|
+
// === 3. 兑换码 → 邮箱 + 选组 ===
|
|
190
190
|
let email = ''
|
|
191
|
+
let groupId = null
|
|
191
192
|
if (kind === 'redeem') {
|
|
192
193
|
email = (await ask('请输入邮箱(用于兑换码绑定 / 新账号创建)')).trim()
|
|
193
194
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
194
195
|
fail('邮箱格式不正确')
|
|
195
196
|
return 1
|
|
196
197
|
}
|
|
198
|
+
|
|
199
|
+
// 选组:兑换码模式都走选组流程(balance 类型码用 group_id 绑定 + 加余额 + 建 key;
|
|
200
|
+
// api_key 类型码若码已绑组则后端忽略传入的 group_id,但 CLI 选组无害,统一流程)
|
|
201
|
+
step('获取可用分组')
|
|
202
|
+
const groups = await listGroups({ apiBase })
|
|
203
|
+
if (groups.length === 0) {
|
|
204
|
+
fail('未查询到可用分组。请联系管理员开通分组后再激活。')
|
|
205
|
+
return 1
|
|
206
|
+
}
|
|
207
|
+
const groupItems = groups.map((g) => ({ label: `${g.name}`, value: g.id }))
|
|
208
|
+
const groupIdx = await select('选择绑定的分组', groupItems, { defaultIndex: 0 })
|
|
209
|
+
groupId = groups[groupIdx]?.id
|
|
210
|
+
if (!groupId) {
|
|
211
|
+
fail('未选择分组')
|
|
212
|
+
return 1
|
|
213
|
+
}
|
|
214
|
+
ok(`已选分组:${groups[groupIdx].name}(ID ${groupId})`)
|
|
215
|
+
console.log()
|
|
197
216
|
}
|
|
198
217
|
|
|
199
218
|
// === 4. POST /cli/activate ===
|
|
200
219
|
step('请求激活服务')
|
|
201
220
|
let bundle
|
|
202
221
|
try {
|
|
203
|
-
bundle = await cliActivate({ credential, email, platform: args.platform, apiBase })
|
|
222
|
+
bundle = await cliActivate({ credential, email, platform: args.platform, groupId, apiBase })
|
|
204
223
|
} catch (err) {
|
|
205
224
|
fail(`激活失败:${err.message}`)
|
|
206
225
|
if (err.statusCode) fail(` HTTP ${err.statusCode}`)
|
package/lib/api.mjs
CHANGED
|
@@ -6,10 +6,10 @@ const DEFAULT_TIMEOUT_MS = 30000
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 发送 POST /api/v1/cli/activate 请求
|
|
9
|
-
* @param {{credential: string, email?: string, platform?: string, apiBase: string}} opts
|
|
9
|
+
* @param {{credential: string, email?: string, platform?: string, group_id?: number, apiBase: string}} opts
|
|
10
10
|
* @returns {Promise<object>} 解析后的 JSON 响应
|
|
11
11
|
*/
|
|
12
|
-
export function cliActivate({ credential, email, platform, apiBase }) {
|
|
12
|
+
export function cliActivate({ credential, email, platform, groupId, apiBase }) {
|
|
13
13
|
return new Promise((resolve, reject) => {
|
|
14
14
|
let url
|
|
15
15
|
try {
|
|
@@ -24,6 +24,7 @@ export function cliActivate({ credential, email, platform, apiBase }) {
|
|
|
24
24
|
credential,
|
|
25
25
|
...(email ? { email } : {}),
|
|
26
26
|
...(platform ? { platform } : {}),
|
|
27
|
+
...(groupId ? { group_id: groupId } : {}),
|
|
27
28
|
}
|
|
28
29
|
const body = JSON.stringify(payload)
|
|
29
30
|
|
|
@@ -76,6 +77,69 @@ export function cliActivate({ credential, email, platform, apiBase }) {
|
|
|
76
77
|
})
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/**
|
|
81
|
+
* 获取可用分组列表:GET /api/v1/cli/groups
|
|
82
|
+
* 兑换码模式(balance 类型码无 group_id)CLI 选组用。
|
|
83
|
+
* @param {{apiBase: string}} opts
|
|
84
|
+
* @returns {Promise<Array<{id: number, name: string}>>} 分组列表(精简,仅 id+name)
|
|
85
|
+
*/
|
|
86
|
+
export function listGroups({ apiBase }) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
let url
|
|
89
|
+
try {
|
|
90
|
+
url = new URL('/api/v1/cli/groups', apiBase)
|
|
91
|
+
} catch (err) {
|
|
92
|
+
reject(new Error(`无效的 apiBase:${apiBase}(${err.message})`))
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const lib = url.protocol === 'https:' ? https : http
|
|
97
|
+
const req = lib.request(
|
|
98
|
+
url,
|
|
99
|
+
{
|
|
100
|
+
method: 'GET',
|
|
101
|
+
headers: {
|
|
102
|
+
'User-Agent': USER_AGENT,
|
|
103
|
+
Accept: 'application/json',
|
|
104
|
+
},
|
|
105
|
+
timeout: DEFAULT_TIMEOUT_MS,
|
|
106
|
+
},
|
|
107
|
+
(res) => {
|
|
108
|
+
const chunks = []
|
|
109
|
+
res.on('data', (c) => chunks.push(c))
|
|
110
|
+
res.on('end', () => {
|
|
111
|
+
const text = Buffer.concat(chunks).toString('utf8')
|
|
112
|
+
let json
|
|
113
|
+
try {
|
|
114
|
+
json = text ? JSON.parse(text) : {}
|
|
115
|
+
} catch {
|
|
116
|
+
// 响应非 JSON,退化为空列表(CLI 兜底)
|
|
117
|
+
resolve([])
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
121
|
+
// 后端返回 {groups: [{id,name}]};兼容直接返回数组的形式
|
|
122
|
+
const arr = Array.isArray(json) ? json : json?.groups
|
|
123
|
+
resolve(Array.isArray(arr) ? arr : [])
|
|
124
|
+
} else {
|
|
125
|
+
// 失败退化:返回空列表(不阻塞激活主流程,让用户看到「无组可选」提示)
|
|
126
|
+
resolve([])
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
req.on('timeout', () => {
|
|
133
|
+
req.destroy(new Error(`请求超时(${DEFAULT_TIMEOUT_MS / 1000}s)— ${url.href}`))
|
|
134
|
+
})
|
|
135
|
+
req.on('error', (err) => {
|
|
136
|
+
// 网络错误同样退化为空列表(CLI 兜底)
|
|
137
|
+
resolve([])
|
|
138
|
+
})
|
|
139
|
+
req.end()
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
79
143
|
/**
|
|
80
144
|
* 验证 API Key:GET {baseUrl}/v1/models(Bearer api_key),期望 200
|
|
81
145
|
* @param {{apiKey: string, baseUrl: string}} opts
|