fogact 1.1.10 → 1.2.2
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/web-server.js +303 -97
- package/frontend/admin/admin-panel-v2.js +8 -5
- package/frontend/assets/market-ui.css +11 -84
- package/frontend/user/assets/Dashboard-rPsmltm5.js +1 -1
- package/frontend/user/assets/DashboardLayout-DDkxHYFj.js +2 -2
- package/frontend/user/assets/Welcome-Dtfp6oER.js +1 -1
- package/frontend/user/assets/announcement-35mOnjRL.js +1 -1
- package/frontend/user/assets/index-Da98HOxL.js +3 -3
- package/frontend/user/index.html +5 -5
- package/lib/commands/activate.js +1 -8
- package/lib/commands/restore.js +41 -38
- package/lib/commands/test.js +15 -21
- package/lib/config/claude.js +1 -0
- package/lib/config/codex.js +1 -1
- package/lib/config/upstream.js +3 -0
- package/lib/index.js +77 -35
- package/lib/platforms/openclaw.js +4 -4
- package/lib/platforms/opencode.js +3 -3
- package/lib/services/activation-orchestrator.js +170 -246
- package/lib/services/backup-service.js +65 -13
- package/lib/services/fogact-api.js +40 -26
- package/lib/services/node-service.js +85 -14
- package/package.json +1 -1
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const prompts = require("prompts");
|
|
4
4
|
const { detectPlatforms, getPlatforms } = require("../platforms");
|
|
5
|
-
const { loadUpstreamConfig } = require("../config/upstream");
|
|
6
5
|
const { createActivationBackup } = require("./backup-service");
|
|
7
|
-
const { inspectActivationCode,
|
|
8
|
-
const { maskKey
|
|
6
|
+
const { getNodes, inspectActivationCode, testNode, verifyActivationCode } = require("./fogact-api");
|
|
7
|
+
const { maskKey } = require("./newapi");
|
|
9
8
|
|
|
10
9
|
const SUPPORTED_SERVICES = ["codex", "claude"];
|
|
11
10
|
const SERVICE_LABELS = {
|
|
@@ -149,6 +148,9 @@ function normalizeEntitlement(raw = {}, fallbackServices = []) {
|
|
|
149
148
|
function extractApiKeyFromEntitlement(entitlement, fallback) {
|
|
150
149
|
const raw = entitlement && entitlement.raw ? entitlement.raw : {};
|
|
151
150
|
const credential = raw.credential || raw.credentials || {};
|
|
151
|
+
if (raw.proxy === true) {
|
|
152
|
+
return String(raw.apiKey || raw.key || raw.token || fallback || "").trim();
|
|
153
|
+
}
|
|
152
154
|
return String(
|
|
153
155
|
raw.apiKey ||
|
|
154
156
|
raw.key ||
|
|
@@ -176,6 +178,16 @@ function extractBaseUrlFromEntitlement(entitlement) {
|
|
|
176
178
|
).trim().replace(/\/+$/, "");
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
function extractProxyBaseUrlFromEntitlement(entitlement) {
|
|
182
|
+
const raw = entitlement && entitlement.raw ? entitlement.raw : {};
|
|
183
|
+
const service = raw.serviceKey || (entitlement.services && entitlement.services[0]);
|
|
184
|
+
return String(
|
|
185
|
+
raw.baseUrl ||
|
|
186
|
+
(raw.publicBaseUrl && service === "codex" ? `${String(raw.publicBaseUrl).replace(/\/+$/, "")}/v1` : raw.publicBaseUrl) ||
|
|
187
|
+
""
|
|
188
|
+
).trim().replace(/\/+$/, "");
|
|
189
|
+
}
|
|
190
|
+
|
|
179
191
|
function isServiceAllowed(entitlement, service) {
|
|
180
192
|
return !entitlement.services.length || entitlement.services.includes(service);
|
|
181
193
|
}
|
|
@@ -219,21 +231,17 @@ async function promptService(defaultService, entitlement = normalizeEntitlement(
|
|
|
219
231
|
if (!isServiceAllowed(entitlement, normalized)) {
|
|
220
232
|
throw new Error(`当前激活码不支持 ${getServiceLabel(normalized)}`);
|
|
221
233
|
}
|
|
222
|
-
console.log(`能力范围: ${getServiceLabel(normalized)}`);
|
|
223
234
|
return normalized;
|
|
224
235
|
}
|
|
225
236
|
|
|
226
237
|
const allowedServices = entitlement.services.length ? entitlement.services : [];
|
|
227
238
|
if (allowedServices.length === 1) {
|
|
228
|
-
console.log(`能力范围: ${getServiceLabel(allowedServices[0])}`);
|
|
229
239
|
return allowedServices[0];
|
|
230
240
|
}
|
|
231
241
|
|
|
232
242
|
if (!allowPrompt) {
|
|
233
243
|
if (allowedServices.length > 1) {
|
|
234
244
|
const service = allowedServices[0];
|
|
235
|
-
const labels = allowedServices.map(getServiceLabel).join(" / ");
|
|
236
|
-
console.log(`能力范围: ${labels},本次自动激活 ${getServiceLabel(service)}`);
|
|
237
245
|
return service;
|
|
238
246
|
}
|
|
239
247
|
console.log("✗ 激活码没有返回 Codex / Claude 能力,无法自动识别。请联系管理员重新生成激活码。");
|
|
@@ -253,75 +261,22 @@ async function promptService(defaultService, entitlement = normalizeEntitlement(
|
|
|
253
261
|
return response.service || null;
|
|
254
262
|
}
|
|
255
263
|
|
|
256
|
-
async function promptApiKey(defaultApiKey) {
|
|
257
|
-
if (defaultApiKey) {
|
|
258
|
-
return defaultApiKey;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const response = await prompts({
|
|
262
|
-
type: "password",
|
|
263
|
-
name: "apiKey",
|
|
264
|
-
message: "请输入 NewAPI API Key",
|
|
265
|
-
validate: (value) => value && value.trim() ? true : "API Key 不能为空",
|
|
266
|
-
}, { onCancel: () => false });
|
|
267
|
-
|
|
268
|
-
return response.apiKey ? response.apiKey.trim() : null;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async function promptBaseUrl(defaultBaseUrl) {
|
|
272
|
-
if (defaultBaseUrl) {
|
|
273
|
-
return defaultBaseUrl;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const response = await prompts({
|
|
277
|
-
type: "text",
|
|
278
|
-
name: "baseUrl",
|
|
279
|
-
message: "请输入 NewAPI Base URL",
|
|
280
|
-
validate: (value) => value && value.trim() ? true : "Base URL 不能为空",
|
|
281
|
-
}, { onCancel: () => false });
|
|
282
|
-
|
|
283
|
-
return response.baseUrl ? response.baseUrl.trim().replace(/\/+$/, "") : null;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
264
|
async function promptActivationCode(defaultCode) {
|
|
287
265
|
if (defaultCode) {
|
|
288
266
|
return defaultCode;
|
|
289
267
|
}
|
|
290
268
|
|
|
291
269
|
const response = await prompts({
|
|
292
|
-
type: "
|
|
270
|
+
type: "password",
|
|
293
271
|
name: "code",
|
|
294
|
-
message: "
|
|
272
|
+
message: "请输入激活码:",
|
|
295
273
|
validate: (value) => value && value.trim() ? true : "激活码不能为空",
|
|
296
274
|
}, { onCancel: () => false });
|
|
297
275
|
|
|
298
276
|
return response.code ? response.code.trim() : null;
|
|
299
277
|
}
|
|
300
278
|
|
|
301
|
-
async function
|
|
302
|
-
if (options.code) {
|
|
303
|
-
return "code";
|
|
304
|
-
}
|
|
305
|
-
if (options.apiKey || upstream.apiKey) {
|
|
306
|
-
return "api-key";
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const response = await prompts({
|
|
310
|
-
type: "select",
|
|
311
|
-
name: "credentialType",
|
|
312
|
-
message: "请选择激活方式",
|
|
313
|
-
hint: "↑↓ 选择,回车确认",
|
|
314
|
-
choices: [
|
|
315
|
-
{ title: "输入激活码 / 兑换码", value: "code" },
|
|
316
|
-
{ title: "输入 NewAPI API Key", value: "api-key" },
|
|
317
|
-
],
|
|
318
|
-
initial: 0,
|
|
319
|
-
}, { onCancel: () => false });
|
|
320
|
-
|
|
321
|
-
return response.credentialType || null;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async function confirmActivation(yes) {
|
|
279
|
+
async function confirmActivation(yes, service) {
|
|
325
280
|
if (yes) {
|
|
326
281
|
return true;
|
|
327
282
|
}
|
|
@@ -329,7 +284,7 @@ async function confirmActivation(yes) {
|
|
|
329
284
|
const response = await prompts({
|
|
330
285
|
type: "confirm",
|
|
331
286
|
name: "confirmed",
|
|
332
|
-
message:
|
|
287
|
+
message: `确认激活 ${getServiceLabel(service)} 配置?`,
|
|
333
288
|
initial: true,
|
|
334
289
|
}, { onCancel: () => false });
|
|
335
290
|
|
|
@@ -353,6 +308,10 @@ function getBackupPaths(targets) {
|
|
|
353
308
|
return targets.flatMap(({ detection }) => detection.paths || []);
|
|
354
309
|
}
|
|
355
310
|
|
|
311
|
+
function divider(width = 37) {
|
|
312
|
+
return ` ${"─".repeat(width)}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
356
315
|
function printBanner() {
|
|
357
316
|
console.log("");
|
|
358
317
|
console.log("╭────────────────────────────────────────╮");
|
|
@@ -362,65 +321,109 @@ function printBanner() {
|
|
|
362
321
|
console.log("");
|
|
363
322
|
}
|
|
364
323
|
|
|
365
|
-
function
|
|
366
|
-
console.log("检测结果:");
|
|
367
|
-
console.log(` 当前能力: ${getServiceLabel(service)}`);
|
|
368
|
-
for (const { platform, detection } of detectedPlatforms) {
|
|
369
|
-
const mark = canSelectPlatform(platform, detection) ? "✓" : "-";
|
|
370
|
-
console.log(` ${mark} ${platform.name}:${getStatusLabel(platform, detection)}`);
|
|
371
|
-
}
|
|
372
|
-
for (const { platform } of blockedPlatforms) {
|
|
373
|
-
console.log(` - ${platform.name}:当前激活码能力不包含`);
|
|
374
|
-
}
|
|
324
|
+
function printCredentialProfile(service, upstream, apiKey, entitlement) {
|
|
375
325
|
console.log("");
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
console.log(
|
|
380
|
-
console.log(`
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
console.log(`
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
|
|
326
|
+
console.log(" 账号信息");
|
|
327
|
+
console.log(divider());
|
|
328
|
+
console.log(` 服务类型: ${getServiceLabel(service)}`);
|
|
329
|
+
console.log(` 接入地址: ${upstream.baseUrl}`);
|
|
330
|
+
console.log(` 激活码: ${maskKey(apiKey)}`);
|
|
331
|
+
if (entitlement.planName) {
|
|
332
|
+
console.log(` 套餐名称: ${entitlement.planName}`);
|
|
333
|
+
}
|
|
334
|
+
if (entitlement.raw && entitlement.raw.expiresAt) {
|
|
335
|
+
console.log(` 到期时间: ${entitlement.raw.expiresAt}`);
|
|
336
|
+
}
|
|
337
|
+
const quota = entitlement.raw && entitlement.raw.quota;
|
|
338
|
+
if (quota && typeof quota === "object") {
|
|
339
|
+
const total = quota.total ?? quota.total_quota ?? quota.dailyLimit ?? quota.daily;
|
|
340
|
+
const used = quota.used ?? quota.used_quota ?? quota.dailyUsed;
|
|
341
|
+
if (total !== undefined) console.log(` 总配额: ${total}`);
|
|
342
|
+
if (used !== undefined) console.log(` 已使用: ${used}`);
|
|
389
343
|
}
|
|
344
|
+
console.log("");
|
|
390
345
|
}
|
|
391
346
|
|
|
392
|
-
function printResultSummary(service,
|
|
347
|
+
function printResultSummary(service, backupPath, results, redeemResult) {
|
|
393
348
|
const succeeded = results.filter(({ result }) => result.success);
|
|
394
349
|
const skipped = results.filter(({ result }) => !result.success && result.skipped);
|
|
395
350
|
const failed = results.filter(({ result }) => !result.success && !result.skipped);
|
|
351
|
+
const byId = new Map(results.map((entry) => [entry.platform.id, entry]));
|
|
396
352
|
|
|
397
353
|
console.log("");
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
354
|
+
if (backupPath) {
|
|
355
|
+
console.log(" ✓ 备份已创建");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const printConfigured = (entry, label) => {
|
|
359
|
+
if (!entry) return false;
|
|
360
|
+
if (entry.result.success) {
|
|
361
|
+
console.log(` ✓ ${label} 已激活`);
|
|
362
|
+
const files = entry.result.files || [];
|
|
363
|
+
if (files.length) console.log(` 配置: ${files.join(", ")}`);
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
if (!entry.result.skipped) {
|
|
367
|
+
console.log(` ✗ ${label} 激活失败: ${entry.result.error || entry.result.message || "未知错误"}`);
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
if (service === "codex") {
|
|
374
|
+
printConfigured(byId.get("codex-cli"), "Codex CLI");
|
|
375
|
+
} else {
|
|
376
|
+
printConfigured(byId.get("claude-code"), "Claude Code");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const opencode = byId.get("opencode");
|
|
380
|
+
if (!printConfigured(opencode, "OpenCode")) {
|
|
381
|
+
console.log("");
|
|
382
|
+
console.log(" ℹ 已跳过 OpenCode 配置(未检测到安装)");
|
|
383
|
+
console.log(" 如需使用,请先运行一次 opencode 初始化后重新激活");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const openclaw = byId.get("openclaw");
|
|
387
|
+
if (!printConfigured(openclaw, "OpenClaw")) {
|
|
388
|
+
console.log("");
|
|
389
|
+
console.log(" ℹ 已跳过 OpenClaw 配置(未检测到安装)");
|
|
390
|
+
console.log(" 如需使用,请先运行一次 openclaw 初始化后重新激活");
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const extensionResults = [byId.get("vscode-codex-plugin"), byId.get("cursor-codex-plugin")]
|
|
394
|
+
.filter(Boolean)
|
|
395
|
+
.filter((entry) => entry.result.success || !entry.result.skipped);
|
|
396
|
+
if (extensionResults.length) {
|
|
397
|
+
for (const entry of extensionResults) {
|
|
398
|
+
if (entry.result.success) {
|
|
399
|
+
console.log("");
|
|
400
|
+
console.log(` ✓ ${entry.platform.name} 已激活`);
|
|
401
|
+
for (const file of entry.result.files || []) console.log(` 目录: ${file}`);
|
|
402
|
+
} else {
|
|
403
|
+
console.log("");
|
|
404
|
+
console.log(` ⚠ ${entry.platform.name}: ${entry.result.error || entry.result.message || "无法激活"}`);
|
|
404
405
|
}
|
|
405
|
-
} else if (result.skipped) {
|
|
406
|
-
console.log(` - ${platform.name}: ${result.message || "已跳过"}`);
|
|
407
|
-
} else {
|
|
408
|
-
console.log(` ✗ ${platform.name}: ${result.error || result.message || "失败"}`);
|
|
409
406
|
}
|
|
407
|
+
} else if (service === "codex") {
|
|
408
|
+
console.log("");
|
|
409
|
+
console.log(" ℹ 已跳过编辑器插件配置(未检测到 Codex 插件)");
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
console.log("");
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
413
|
+
if (failed.length) {
|
|
414
|
+
console.log(` 激活完成:${succeeded.length} 成功,${failed.length} 失败,${skipped.length} 跳过`);
|
|
415
|
+
}
|
|
416
|
+
if (service === "claude") {
|
|
417
|
+
const tools = ["Claude Code"];
|
|
418
|
+
if (byId.get("opencode")?.result.success) tools.push("OpenCode");
|
|
419
|
+
if (byId.get("openclaw")?.result.success) tools.push("OpenClaw");
|
|
420
|
+
console.log(` 请重启相关工具(${tools.join("/")})以应用新配置`);
|
|
421
|
+
} else {
|
|
422
|
+
const tools = ["Codex", "VSCode", "Cursor"];
|
|
423
|
+
if (byId.get("opencode")?.result.success) tools.push("OpenCode");
|
|
424
|
+
if (byId.get("openclaw")?.result.success) tools.push("OpenClaw");
|
|
425
|
+
console.log(` 请重启相关工具(${tools.join("/")})以应用新配置`);
|
|
426
|
+
}
|
|
424
427
|
console.log("");
|
|
425
428
|
}
|
|
426
429
|
|
|
@@ -431,6 +434,28 @@ async function selectPlatforms(detectedPlatforms, options = {}) {
|
|
|
431
434
|
return getActivationTargets(detectedPlatforms, Boolean(options.all));
|
|
432
435
|
}
|
|
433
436
|
|
|
437
|
+
async function verifyPrimaryNode() {
|
|
438
|
+
const nodes = await getNodes("codex");
|
|
439
|
+
const primary = nodes[0];
|
|
440
|
+
if (!primary) return null;
|
|
441
|
+
|
|
442
|
+
console.log("");
|
|
443
|
+
console.log(" 正在验证节点...");
|
|
444
|
+
const result = await testNode(primary.url);
|
|
445
|
+
if (result.available) {
|
|
446
|
+
console.log(` ✓ ${primary.name || "FogAct"} 已连接`);
|
|
447
|
+
console.log(` 延迟: ${result.latency}ms`);
|
|
448
|
+
console.log(` 地址: ${primary.url}`);
|
|
449
|
+
console.log("");
|
|
450
|
+
return { node: primary, latency: result.latency };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
console.log(` ✗ ${primary.name || "FogAct"} 连接失败`);
|
|
454
|
+
console.log(` 地址: ${primary.url}`);
|
|
455
|
+
console.log("");
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
|
|
434
459
|
async function resolveCodeCredential(options, upstream) {
|
|
435
460
|
const code = await promptActivationCode(options.code);
|
|
436
461
|
if (!code) {
|
|
@@ -438,7 +463,7 @@ async function resolveCodeCredential(options, upstream) {
|
|
|
438
463
|
}
|
|
439
464
|
|
|
440
465
|
console.log("");
|
|
441
|
-
console.log("
|
|
466
|
+
console.log("正在验证激活码...");
|
|
442
467
|
const inspection = await inspectActivationCode(code);
|
|
443
468
|
if (!inspection.valid) {
|
|
444
469
|
console.log(`✗ 无法读取激活码能力: ${inspection.error || "接口未返回有效信息"}`);
|
|
@@ -447,199 +472,98 @@ async function resolveCodeCredential(options, upstream) {
|
|
|
447
472
|
|
|
448
473
|
const entitlement = normalizeEntitlement(inspection, options.service ? [options.service] : []);
|
|
449
474
|
const apiKey = extractApiKeyFromEntitlement(entitlement, code);
|
|
450
|
-
const entitlementBaseUrl =
|
|
475
|
+
const entitlementBaseUrl = extractProxyBaseUrlFromEntitlement(entitlement);
|
|
476
|
+
if (!apiKey || !entitlementBaseUrl || entitlement.raw.proxy !== true) {
|
|
477
|
+
console.log("✗ 激活接口没有返回 FogAct 中转配置,请联系管理员检查服务端代理设置。");
|
|
478
|
+
return { cancelled: true };
|
|
479
|
+
}
|
|
451
480
|
return {
|
|
452
481
|
activationCode: code,
|
|
453
482
|
apiKey,
|
|
454
483
|
entitlement,
|
|
455
|
-
upstream:
|
|
484
|
+
upstream: { services: {}, baseUrl: entitlementBaseUrl, proxy: true },
|
|
456
485
|
inspection,
|
|
457
486
|
};
|
|
458
487
|
}
|
|
459
488
|
|
|
460
|
-
async function resolveApiKeyCredential(options, upstream) {
|
|
461
|
-
const apiKey = await promptApiKey(options.apiKey || upstream.apiKey);
|
|
462
|
-
if (!apiKey) {
|
|
463
|
-
return { cancelled: true };
|
|
464
|
-
}
|
|
465
|
-
return {
|
|
466
|
-
apiKey,
|
|
467
|
-
entitlement: normalizeEntitlement({}, options.service ? [options.service] : []),
|
|
468
|
-
upstream,
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
489
|
async function activateTargets({ service, upstream, apiKey, targets, activationCode, options = {} }) {
|
|
473
|
-
console.log("");
|
|
474
|
-
console.log("正在创建备份...");
|
|
475
490
|
const backupPath = createActivationBackup(service, getBackupPaths(targets), {
|
|
476
491
|
upstream: upstream.baseUrl,
|
|
477
492
|
targets: targets.map(({ platform }) => platform.id),
|
|
478
493
|
});
|
|
479
|
-
if (backupPath) {
|
|
480
|
-
console.log(`✓ 备份完成: ${backupPath}`);
|
|
481
|
-
} else {
|
|
482
|
-
console.log("ℹ 没有旧配置需要备份");
|
|
483
|
-
}
|
|
484
494
|
|
|
485
|
-
console.log("");
|
|
486
|
-
console.log("正在激活平台...");
|
|
487
495
|
const results = [];
|
|
488
496
|
for (const { platform, detection } of targets) {
|
|
497
|
+
if (!canSelectPlatform(platform, detection)) {
|
|
498
|
+
results.push({ platform, result: { success: false, skipped: true, message: "未安装" } });
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
489
501
|
try {
|
|
490
|
-
const result = platform.activate({ service, upstream, apiKey, detection });
|
|
502
|
+
const result = platform.activate({ service, upstream, apiKey, options, detection });
|
|
491
503
|
results.push({ platform, result });
|
|
492
|
-
if (result.success) {
|
|
493
|
-
console.log(`✓ ${platform.name}`);
|
|
494
|
-
} else {
|
|
495
|
-
console.log(`⚠ ${platform.name}: ${result.message || "已跳过"}`);
|
|
496
|
-
}
|
|
497
504
|
} catch (err) {
|
|
498
505
|
results.push({ platform, result: { success: false, error: err.message } });
|
|
499
|
-
console.log(`✗ ${platform.name}: ${err.message}`);
|
|
500
506
|
}
|
|
501
507
|
}
|
|
502
508
|
|
|
503
|
-
let
|
|
509
|
+
let activationRecord = null;
|
|
504
510
|
const failures = results.filter(({ result }) => !result.success && !result.skipped);
|
|
505
|
-
if (activationCode && failures.length === 0
|
|
506
|
-
|
|
507
|
-
console.log("正在完成兑换记录...");
|
|
508
|
-
redeemResult = await redeemActivationCode(activationCode, service);
|
|
509
|
-
if (redeemResult.valid) {
|
|
510
|
-
console.log("✓ 兑换记录已完成");
|
|
511
|
-
} else {
|
|
512
|
-
console.log(`⚠ 兑换记录未完成: ${redeemResult.error || "接口不可用"}`);
|
|
513
|
-
}
|
|
511
|
+
if (activationCode && failures.length === 0) {
|
|
512
|
+
activationRecord = await verifyActivationCode(activationCode, service);
|
|
514
513
|
}
|
|
515
514
|
|
|
516
|
-
return { backupPath, results, redeemResult };
|
|
515
|
+
return { backupPath, results, redeemResult: null, activationRecord };
|
|
517
516
|
}
|
|
518
517
|
|
|
519
|
-
async function verifyCredential(upstream, apiKey, options = {}) {
|
|
520
|
-
if (options.skipVerify) {
|
|
521
|
-
console.log("跳过 NewAPI 连通性验证。");
|
|
522
|
-
return { valid: true, skipped: true };
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
console.log("正在验证 NewAPI Key...");
|
|
526
|
-
const verification = await verifyNewApiKey(upstream, apiKey);
|
|
527
|
-
if (!verification.valid) {
|
|
528
|
-
console.log(`✗ NewAPI 验证失败: ${verification.error}`);
|
|
529
|
-
return verification;
|
|
530
|
-
}
|
|
531
|
-
console.log(`✓ NewAPI 验证通过(可见 ${verification.models.length} 个模型)`);
|
|
532
|
-
return verification;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
async function runNewApiActivation(options = {}) {
|
|
536
|
-
const baseUpstream = loadUpstreamConfig({ configPath: options.upstreamConfig });
|
|
537
|
-
const upstream = { ...baseUpstream };
|
|
538
|
-
upstream.baseUrl = await promptBaseUrl(upstream.baseUrl);
|
|
539
|
-
if (!upstream.baseUrl) {
|
|
540
|
-
console.log("Activation cancelled.");
|
|
541
|
-
return { success: false, cancelled: true };
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
const apiKey = await promptApiKey(options.apiKey || upstream.apiKey);
|
|
545
|
-
if (!apiKey) {
|
|
546
|
-
console.log("Activation cancelled.");
|
|
547
|
-
return { success: false, cancelled: true };
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const entitlement = normalizeEntitlement({}, options.service ? [options.service] : []);
|
|
551
|
-
const service = await promptService(options.service, entitlement);
|
|
552
|
-
if (!service) {
|
|
553
|
-
console.log("Activation cancelled.");
|
|
554
|
-
return { success: false, cancelled: true };
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
console.log("");
|
|
558
|
-
const verification = await verifyCredential(upstream, apiKey, options);
|
|
559
|
-
if (!verification.valid) {
|
|
560
|
-
return { success: false, verification };
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const detectedPlatforms = detectPlatforms(service);
|
|
564
|
-
const selectedPlatformIds = options.platforms ? parsePlatformIds(options.platforms) : null;
|
|
565
|
-
const targets = getActivationTargets(detectedPlatforms, Boolean(options.all), selectedPlatformIds);
|
|
566
|
-
const skipped = detectedPlatforms.filter((entry) => !targets.includes(entry));
|
|
567
|
-
|
|
568
|
-
console.log("");
|
|
569
|
-
printPlan(service, upstream, apiKey, targets, skipped);
|
|
570
|
-
|
|
571
|
-
if (!(await confirmActivation(Boolean(options.yes || options.auto)))) {
|
|
572
|
-
console.log("Activation cancelled.");
|
|
573
|
-
return { success: false, cancelled: true };
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const activation = await activateTargets({ service, upstream, apiKey, targets, options });
|
|
577
|
-
const failures = activation.results.filter(({ result }) => !result.success && !result.skipped);
|
|
578
|
-
printResultSummary(service, upstream, activation.backupPath, activation.results, activation.redeemResult);
|
|
579
|
-
|
|
580
|
-
return {
|
|
581
|
-
success: failures.length === 0,
|
|
582
|
-
backupPath: activation.backupPath,
|
|
583
|
-
results: activation.results,
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
518
|
|
|
587
519
|
async function runActivationWizard(options = {}) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
520
|
+
if (!options.noNodeCheck) {
|
|
521
|
+
const node = await verifyPrimaryNode();
|
|
522
|
+
if (!node) {
|
|
523
|
+
return { success: false, cancelled: true };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
591
526
|
|
|
592
|
-
const credential =
|
|
593
|
-
? await resolveCodeCredential(options, baseUpstream)
|
|
594
|
-
: await resolveApiKeyCredential(options, baseUpstream);
|
|
527
|
+
const credential = await resolveCodeCredential(options, {});
|
|
595
528
|
if (credential.cancelled) {
|
|
596
529
|
console.log("已取消。");
|
|
597
530
|
return { success: false, cancelled: true };
|
|
598
531
|
}
|
|
599
532
|
|
|
600
533
|
const upstream = { ...credential.upstream };
|
|
601
|
-
upstream.baseUrl = await promptBaseUrl(upstream.baseUrl);
|
|
602
534
|
if (!upstream.baseUrl) {
|
|
603
535
|
console.log("已取消。");
|
|
604
536
|
return { success: false, cancelled: true };
|
|
605
537
|
}
|
|
606
538
|
|
|
607
|
-
const service = await promptService(options.service, credential.entitlement, { allowPrompt:
|
|
539
|
+
const service = await promptService(options.service, credential.entitlement, { allowPrompt: false });
|
|
608
540
|
if (!service) {
|
|
609
541
|
console.log("已取消。");
|
|
610
542
|
return { success: false, cancelled: true };
|
|
611
543
|
}
|
|
612
544
|
|
|
613
|
-
console.log("");
|
|
614
|
-
if (credentialType === "api-key") {
|
|
615
|
-
const verification = await verifyCredential(upstream, credential.apiKey, options);
|
|
616
|
-
if (!verification.valid) {
|
|
617
|
-
return { success: false, verification };
|
|
618
|
-
}
|
|
619
|
-
} else {
|
|
620
|
-
console.log("✓ 已按激活码能力限制可选平台");
|
|
621
|
-
}
|
|
622
545
|
|
|
623
546
|
const allDetectedPlatforms = detectPlatforms(service);
|
|
624
547
|
const allowedPlatforms = allDetectedPlatforms.filter((entry) => isPlatformAllowed(entry, credential.entitlement, service));
|
|
625
|
-
const blockedPlatforms = allDetectedPlatforms.filter((entry) => !allowedPlatforms.includes(entry));
|
|
626
|
-
|
|
627
|
-
console.log("");
|
|
628
|
-
printDetection(service, allowedPlatforms, blockedPlatforms);
|
|
629
548
|
|
|
630
549
|
const targets = await selectPlatforms(allowedPlatforms, options);
|
|
631
550
|
if (targets.length === 0) {
|
|
632
|
-
console.log("
|
|
551
|
+
console.log("");
|
|
552
|
+
console.log(" ✗ 当前环境没有可激活目标");
|
|
553
|
+
console.log("");
|
|
633
554
|
return { success: false, cancelled: true };
|
|
634
555
|
}
|
|
635
|
-
const skipped = allowedPlatforms.filter((entry) => !targets.includes(entry)).concat(blockedPlatforms);
|
|
636
556
|
|
|
637
|
-
|
|
638
|
-
if (!(await confirmActivation(Boolean(options.yes || options.auto)))) {
|
|
639
|
-
console.log("
|
|
557
|
+
printCredentialProfile(service, upstream, credential.apiKey, credential.entitlement);
|
|
558
|
+
if (!(await confirmActivation(Boolean(options.yes || options.auto), service))) {
|
|
559
|
+
console.log("");
|
|
560
|
+
console.log(" 已取消");
|
|
561
|
+
console.log("");
|
|
640
562
|
return { success: false, cancelled: true };
|
|
641
563
|
}
|
|
642
564
|
|
|
565
|
+
console.log("");
|
|
566
|
+
console.log(" 正在写入配置...");
|
|
643
567
|
const activation = await activateTargets({
|
|
644
568
|
service,
|
|
645
569
|
upstream,
|
|
@@ -649,7 +573,8 @@ async function runActivationWizard(options = {}) {
|
|
|
649
573
|
options,
|
|
650
574
|
});
|
|
651
575
|
const failures = activation.results.filter(({ result }) => !result.success && !result.skipped);
|
|
652
|
-
|
|
576
|
+
console.log(" 配置完成");
|
|
577
|
+
printResultSummary(service, activation.backupPath, activation.results, activation.redeemResult);
|
|
653
578
|
|
|
654
579
|
return {
|
|
655
580
|
success: failures.length === 0,
|
|
@@ -667,5 +592,4 @@ module.exports = {
|
|
|
667
592
|
normalizeServices,
|
|
668
593
|
parsePlatformIds,
|
|
669
594
|
runActivationWizard,
|
|
670
|
-
runNewApiActivation,
|
|
671
595
|
};
|