fogact 1.1.3
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/LICENSE +21 -0
- package/README.md +244 -0
- package/README.zh-CN.md +244 -0
- package/bin/cli.js +9 -0
- package/bin/web-server.js +1434 -0
- package/config/upstream.example.json +14 -0
- package/frontend/activate.html +249 -0
- package/frontend/admin/admin-panel-v2.js +1899 -0
- package/frontend/admin/index.html +705 -0
- package/frontend/assets/market-ui.css +1876 -0
- package/frontend/color-test.html +136 -0
- package/frontend/index.html +191 -0
- package/frontend/user/assets/AnnouncementDetail-Dvxmwz0A.js +12 -0
- package/frontend/user/assets/Announcements-CS1tF2mx.js +11 -0
- package/frontend/user/assets/CardBind-CsCxihhP.js +21 -0
- package/frontend/user/assets/CardContent.vue_vue_type_script_setup_true_lang-D2L-uqSl.js +1 -0
- package/frontend/user/assets/CardDescription.vue_vue_type_script_setup_true_lang-D-v5Pl7F.js +1 -0
- package/frontend/user/assets/CardTitle.vue_vue_type_script_setup_true_lang-a0CCN6D5.js +1 -0
- package/frontend/user/assets/Dashboard-rPsmltm5.js +51 -0
- package/frontend/user/assets/DashboardLayout-BUCWGlXC.css +1 -0
- package/frontend/user/assets/DashboardLayout-DDkxHYFj.js +80 -0
- package/frontend/user/assets/Input.vue_vue_type_script_setup_true_lang-B0SyPmYb.js +6 -0
- package/frontend/user/assets/Label.vue_vue_type_script_setup_true_lang-CxYORSgN.js +1 -0
- package/frontend/user/assets/Progress.vue_vue_type_script_setup_true_lang-2_QbPsEQ.js +1 -0
- package/frontend/user/assets/QuotaPack-B_tJ7Psm.js +6 -0
- package/frontend/user/assets/Renewal-BSDhDmwv.js +6 -0
- package/frontend/user/assets/ScrollArea.vue_vue_type_script_setup_true_lang-DMYwcfpz.js +1 -0
- package/frontend/user/assets/Separator.vue_vue_type_script_setup_true_lang-Ckg8EXj_.js +1 -0
- package/frontend/user/assets/Settings-CBdAa3lw.js +11 -0
- package/frontend/user/assets/TooltipTrigger.vue_vue_type_script_setup_true_lang-DtSBjzGo.js +16 -0
- package/frontend/user/assets/Welcome-7IfzEli4.css +1 -0
- package/frontend/user/assets/Welcome-Dtfp6oER.js +1 -0
- package/frontend/user/assets/_plugin-vue_export-helper-5cjT4u0R.js +16 -0
- package/frontend/user/assets/activity-wYWtyqTJ.js +6 -0
- package/frontend/user/assets/announcement-35mOnjRL.js +16 -0
- package/frontend/user/assets/calendar-BFNuCata.js +6 -0
- package/frontend/user/assets/chart-vendor-CULJE59K.js +37 -0
- package/frontend/user/assets/chevron-down-kDbuU1Py.js +6 -0
- package/frontend/user/assets/chevron-right-BayASIm0.js +6 -0
- package/frontend/user/assets/eye-CY62vip0.js +6 -0
- package/frontend/user/assets/gauge-C5NQ-mV8.js +6 -0
- package/frontend/user/assets/index-B8QSyYhS.css +1 -0
- package/frontend/user/assets/index-Da98HOxL.js +91 -0
- package/frontend/user/assets/link-2-DT5R5nGO.js +6 -0
- package/frontend/user/assets/package-rUbExUEn.js +6 -0
- package/frontend/user/assets/plus-CQc6C8wG.js +11 -0
- package/frontend/user/assets/refresh-cw-Y9hCloPL.js +6 -0
- package/frontend/user/assets/useUserPageRefresh-BYZvpNR9.js +1 -0
- package/frontend/user/assets/zap-l5zbZqrM.js +11 -0
- package/frontend/user/index.html +67 -0
- package/install.sh +402 -0
- package/lib/commands/activate.js +144 -0
- package/lib/commands/restore.js +102 -0
- package/lib/commands/test.js +40 -0
- package/lib/config/claude.js +81 -0
- package/lib/config/codex.js +164 -0
- package/lib/config/upstream.js +79 -0
- package/lib/index.js +164 -0
- package/lib/platforms/claude-code.js +35 -0
- package/lib/platforms/codex-cli.js +35 -0
- package/lib/platforms/editor-codex.js +138 -0
- package/lib/platforms/index.js +32 -0
- package/lib/platforms/openclaw.js +118 -0
- package/lib/platforms/opencode.js +89 -0
- package/lib/services/activation-orchestrator.js +666 -0
- package/lib/services/backup-service.js +162 -0
- package/lib/services/cliproxy-api.js +174 -0
- package/lib/services/database.js +461 -0
- package/lib/services/newapi.js +97 -0
- package/lib/services/node-service.js +49 -0
- package/lib/utils/json-file.js +33 -0
- package/package.json +53 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const prompts = require("prompts");
|
|
4
|
+
const { detectPlatforms } = require("../platforms");
|
|
5
|
+
const { loadUpstreamConfig } = require("../config/upstream");
|
|
6
|
+
const { createActivationBackup } = require("./backup-service");
|
|
7
|
+
const { inspectActivationCode, redeemActivationCode } = require("./cliproxy-api");
|
|
8
|
+
const { maskKey, verifyNewApiKey } = require("./newapi");
|
|
9
|
+
|
|
10
|
+
const SUPPORTED_SERVICES = ["codex", "claude"];
|
|
11
|
+
const SERVICE_LABELS = {
|
|
12
|
+
claude: "Claude Code",
|
|
13
|
+
codex: "Codex",
|
|
14
|
+
};
|
|
15
|
+
const CREATABLE_PLATFORM_IDS = new Set(["claude-code", "codex-cli", "opencode", "openclaw"]);
|
|
16
|
+
|
|
17
|
+
function normalizeService(service) {
|
|
18
|
+
const value = String(service || "").trim().toLowerCase();
|
|
19
|
+
if (["claude", "claude-code", "claude code", "anthropic"].includes(value)) {
|
|
20
|
+
return "claude";
|
|
21
|
+
}
|
|
22
|
+
if (["codex", "codex-cli", "codex cli", "openai", "gpt"].includes(value)) {
|
|
23
|
+
return "codex";
|
|
24
|
+
}
|
|
25
|
+
if (value.includes("claude")) {
|
|
26
|
+
return "claude";
|
|
27
|
+
}
|
|
28
|
+
if (value.includes("codex") || value.includes("openai") || value.includes("gpt")) {
|
|
29
|
+
return "codex";
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getServiceLabel(service) {
|
|
35
|
+
return SERVICE_LABELS[service] || service;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function flattenValues(values) {
|
|
39
|
+
const result = [];
|
|
40
|
+
for (const value of values) {
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
result.push(...flattenValues(value));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (value && typeof value === "object") {
|
|
46
|
+
result.push(...flattenValues(Object.values(value)));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
50
|
+
result.push(...value.split(","));
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
54
|
+
result.push(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeServices(...values) {
|
|
61
|
+
const services = [];
|
|
62
|
+
for (const value of flattenValues(values)) {
|
|
63
|
+
const normalized = normalizeService(value);
|
|
64
|
+
if (SUPPORTED_SERVICES.includes(normalized) && !services.includes(normalized)) {
|
|
65
|
+
services.push(normalized);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return services;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizePlatformIds(...values) {
|
|
72
|
+
const ids = [];
|
|
73
|
+
for (const value of flattenValues(values)) {
|
|
74
|
+
const id = String(value).trim().toLowerCase().replace(/\s+/g, "-");
|
|
75
|
+
if (id && !SUPPORTED_SERVICES.includes(id) && !ids.includes(id)) {
|
|
76
|
+
ids.push(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return ids;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeEntitlement(raw = {}, fallbackServices = []) {
|
|
83
|
+
const source = raw && typeof raw === "object" ? raw : {};
|
|
84
|
+
const data = source.data && typeof source.data === "object" ? source.data : source;
|
|
85
|
+
const capabilities = data.capabilities || data.capability || data.entitlement || data.ability || {};
|
|
86
|
+
const services = normalizeServices(
|
|
87
|
+
data.services,
|
|
88
|
+
data.service,
|
|
89
|
+
data.products,
|
|
90
|
+
data.product,
|
|
91
|
+
data.scopes,
|
|
92
|
+
data.scope,
|
|
93
|
+
data.abilities,
|
|
94
|
+
capabilities.services,
|
|
95
|
+
capabilities.service,
|
|
96
|
+
capabilities.products,
|
|
97
|
+
capabilities.product,
|
|
98
|
+
capabilities.scopes,
|
|
99
|
+
capabilities.scope,
|
|
100
|
+
capabilities.abilities,
|
|
101
|
+
fallbackServices
|
|
102
|
+
);
|
|
103
|
+
const platforms = normalizePlatformIds(
|
|
104
|
+
data.platforms,
|
|
105
|
+
data.platform,
|
|
106
|
+
data.targets,
|
|
107
|
+
data.target,
|
|
108
|
+
capabilities.platforms,
|
|
109
|
+
capabilities.platform,
|
|
110
|
+
capabilities.targets,
|
|
111
|
+
capabilities.target
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
services,
|
|
116
|
+
platforms,
|
|
117
|
+
planName: data.planName || data.plan || data.name || capabilities.planName || capabilities.name || null,
|
|
118
|
+
raw: data,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function extractApiKeyFromEntitlement(entitlement, fallback) {
|
|
123
|
+
const raw = entitlement && entitlement.raw ? entitlement.raw : {};
|
|
124
|
+
const credential = raw.credential || raw.credentials || {};
|
|
125
|
+
return String(
|
|
126
|
+
raw.apiKey ||
|
|
127
|
+
raw.key ||
|
|
128
|
+
raw.token ||
|
|
129
|
+
raw.accessToken ||
|
|
130
|
+
credential.apiKey ||
|
|
131
|
+
credential.key ||
|
|
132
|
+
credential.token ||
|
|
133
|
+
fallback ||
|
|
134
|
+
""
|
|
135
|
+
).trim();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractBaseUrlFromEntitlement(entitlement) {
|
|
139
|
+
const raw = entitlement && entitlement.raw ? entitlement.raw : {};
|
|
140
|
+
const upstream = raw.upstream || raw.endpoint || {};
|
|
141
|
+
return String(
|
|
142
|
+
raw.baseUrl ||
|
|
143
|
+
raw.baseURL ||
|
|
144
|
+
raw.url ||
|
|
145
|
+
upstream.baseUrl ||
|
|
146
|
+
upstream.baseURL ||
|
|
147
|
+
upstream.url ||
|
|
148
|
+
""
|
|
149
|
+
).trim().replace(/\/+$/, "");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function isServiceAllowed(entitlement, service) {
|
|
153
|
+
return !entitlement.services.length || entitlement.services.includes(service);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function isPlatformAllowed(entry, entitlement, service) {
|
|
157
|
+
if (!isServiceAllowed(entitlement, service)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (entitlement.platforms.length && !entitlement.platforms.includes(entry.platform.id)) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return entry.platform.services.includes(service);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getStatusLabel(platform, detection) {
|
|
167
|
+
if (detection.installed) {
|
|
168
|
+
return "已检测到";
|
|
169
|
+
}
|
|
170
|
+
if (CREATABLE_PLATFORM_IDS.has(platform.id)) {
|
|
171
|
+
return platform.required ? "将创建配置" : "未安装,可创建配置";
|
|
172
|
+
}
|
|
173
|
+
return "未安装,将跳过";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function canSelectPlatform(platform, detection) {
|
|
177
|
+
return detection.installed || CREATABLE_PLATFORM_IDS.has(platform.id);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function parsePlatformIds(value) {
|
|
181
|
+
return normalizePlatformIds(value);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function promptService(defaultService, entitlement = normalizeEntitlement()) {
|
|
185
|
+
if (defaultService) {
|
|
186
|
+
const normalized = normalizeService(defaultService);
|
|
187
|
+
if (!SUPPORTED_SERVICES.includes(normalized)) {
|
|
188
|
+
throw new Error("Service must be claude or codex");
|
|
189
|
+
}
|
|
190
|
+
if (!isServiceAllowed(entitlement, normalized)) {
|
|
191
|
+
throw new Error(`当前激活码不支持 ${getServiceLabel(normalized)}`);
|
|
192
|
+
}
|
|
193
|
+
return normalized;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const allowedServices = entitlement.services.length ? entitlement.services : SUPPORTED_SERVICES;
|
|
197
|
+
if (allowedServices.length === 1) {
|
|
198
|
+
console.log(`能力范围: ${getServiceLabel(allowedServices[0])}`);
|
|
199
|
+
return allowedServices[0];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const response = await prompts({
|
|
203
|
+
type: "select",
|
|
204
|
+
name: "service",
|
|
205
|
+
message: "请选择要激活的能力",
|
|
206
|
+
choices: allowedServices.map((service) => ({ title: getServiceLabel(service), value: service })),
|
|
207
|
+
initial: 0,
|
|
208
|
+
}, { onCancel: () => false });
|
|
209
|
+
|
|
210
|
+
return response.service || null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function promptApiKey(defaultApiKey) {
|
|
214
|
+
if (defaultApiKey) {
|
|
215
|
+
return defaultApiKey;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const response = await prompts({
|
|
219
|
+
type: "password",
|
|
220
|
+
name: "apiKey",
|
|
221
|
+
message: "请输入 NewAPI API Key",
|
|
222
|
+
validate: (value) => value && value.trim() ? true : "API Key 不能为空",
|
|
223
|
+
}, { onCancel: () => false });
|
|
224
|
+
|
|
225
|
+
return response.apiKey ? response.apiKey.trim() : null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function promptBaseUrl(defaultBaseUrl) {
|
|
229
|
+
if (defaultBaseUrl) {
|
|
230
|
+
return defaultBaseUrl;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const response = await prompts({
|
|
234
|
+
type: "text",
|
|
235
|
+
name: "baseUrl",
|
|
236
|
+
message: "请输入 NewAPI Base URL",
|
|
237
|
+
validate: (value) => value && value.trim() ? true : "Base URL 不能为空",
|
|
238
|
+
}, { onCancel: () => false });
|
|
239
|
+
|
|
240
|
+
return response.baseUrl ? response.baseUrl.trim().replace(/\/+$/, "") : null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function promptActivationCode(defaultCode) {
|
|
244
|
+
if (defaultCode) {
|
|
245
|
+
return defaultCode;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const response = await prompts({
|
|
249
|
+
type: "text",
|
|
250
|
+
name: "code",
|
|
251
|
+
message: "请输入激活码 / 兑换码",
|
|
252
|
+
validate: (value) => value && value.trim() ? true : "激活码不能为空",
|
|
253
|
+
}, { onCancel: () => false });
|
|
254
|
+
|
|
255
|
+
return response.code ? response.code.trim() : null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function promptCredentialType(options, upstream) {
|
|
259
|
+
if (options.code) {
|
|
260
|
+
return "code";
|
|
261
|
+
}
|
|
262
|
+
if (options.apiKey || upstream.apiKey) {
|
|
263
|
+
return "api-key";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const response = await prompts({
|
|
267
|
+
type: "select",
|
|
268
|
+
name: "credentialType",
|
|
269
|
+
message: "请选择激活方式",
|
|
270
|
+
choices: [
|
|
271
|
+
{ title: "输入 NewAPI API Key", value: "api-key" },
|
|
272
|
+
{ title: "输入激活码 / 兑换码", value: "code" },
|
|
273
|
+
],
|
|
274
|
+
initial: 0,
|
|
275
|
+
}, { onCancel: () => false });
|
|
276
|
+
|
|
277
|
+
return response.credentialType || null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function confirmActivation(yes) {
|
|
281
|
+
if (yes) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const response = await prompts({
|
|
286
|
+
type: "confirm",
|
|
287
|
+
name: "confirmed",
|
|
288
|
+
message: "确认开始激活?",
|
|
289
|
+
initial: true,
|
|
290
|
+
}, { onCancel: () => false });
|
|
291
|
+
|
|
292
|
+
return Boolean(response.confirmed);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function getActivationTargets(detectedPlatforms, includeAll = false, selectedPlatformIds = null) {
|
|
296
|
+
const selectedIds = selectedPlatformIds ? parsePlatformIds(selectedPlatformIds) : [];
|
|
297
|
+
return detectedPlatforms.filter(({ platform, detection }) => {
|
|
298
|
+
if (selectedIds.length) {
|
|
299
|
+
return selectedIds.includes(platform.id);
|
|
300
|
+
}
|
|
301
|
+
if (platform.required) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
return detection.installed || (includeAll && canSelectPlatform(platform, detection));
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function getBackupPaths(targets) {
|
|
309
|
+
return targets.flatMap(({ detection }) => detection.paths || []);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function printBanner() {
|
|
313
|
+
console.log("");
|
|
314
|
+
console.log("╭────────────────────────────────────────╮");
|
|
315
|
+
console.log("│ FogIDC 多平台激活器 │");
|
|
316
|
+
console.log("│ 一次检测,按能力激活 Codex / Claude │");
|
|
317
|
+
console.log("╰────────────────────────────────────────╯");
|
|
318
|
+
console.log("");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function printDetection(service, detectedPlatforms, blockedPlatforms = []) {
|
|
322
|
+
console.log("检测结果:");
|
|
323
|
+
console.log(` 当前能力: ${getServiceLabel(service)}`);
|
|
324
|
+
for (const { platform, detection } of detectedPlatforms) {
|
|
325
|
+
const mark = canSelectPlatform(platform, detection) ? "✓" : "-";
|
|
326
|
+
console.log(` ${mark} ${platform.name}:${getStatusLabel(platform, detection)}`);
|
|
327
|
+
}
|
|
328
|
+
for (const { platform } of blockedPlatforms) {
|
|
329
|
+
console.log(` - ${platform.name}:当前激活码能力不包含`);
|
|
330
|
+
}
|
|
331
|
+
console.log("");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function printPlan(service, upstream, apiKey, targets, skipped = []) {
|
|
335
|
+
console.log("激活计划:");
|
|
336
|
+
console.log(` 能力: ${getServiceLabel(service)}`);
|
|
337
|
+
console.log(` 上游: ${upstream.baseUrl}`);
|
|
338
|
+
console.log(` 密钥: ${maskKey(apiKey)}`);
|
|
339
|
+
console.log(" 平台:");
|
|
340
|
+
for (const { platform, detection } of targets) {
|
|
341
|
+
console.log(` ✓ ${platform.name} (${getStatusLabel(platform, detection)})`);
|
|
342
|
+
}
|
|
343
|
+
for (const { platform } of skipped) {
|
|
344
|
+
console.log(` - ${platform.name} (未选择)`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function printResultSummary(service, upstream, backupPath, results, redeemResult) {
|
|
349
|
+
const succeeded = results.filter(({ result }) => result.success);
|
|
350
|
+
const skipped = results.filter(({ result }) => !result.success && result.skipped);
|
|
351
|
+
const failed = results.filter(({ result }) => !result.success && !result.skipped);
|
|
352
|
+
|
|
353
|
+
console.log("");
|
|
354
|
+
console.log("激活结果:");
|
|
355
|
+
for (const { platform, result } of results) {
|
|
356
|
+
if (result.success) {
|
|
357
|
+
console.log(` ✓ ${platform.name}`);
|
|
358
|
+
for (const file of result.files || []) {
|
|
359
|
+
console.log(` ${file}`);
|
|
360
|
+
}
|
|
361
|
+
} else if (result.skipped) {
|
|
362
|
+
console.log(` - ${platform.name}: ${result.message || "已跳过"}`);
|
|
363
|
+
} else {
|
|
364
|
+
console.log(` ✗ ${platform.name}: ${result.error || result.message || "失败"}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
console.log("");
|
|
369
|
+
console.log("汇总:");
|
|
370
|
+
console.log(` 能力: ${getServiceLabel(service)}`);
|
|
371
|
+
console.log(` 上游: ${upstream.baseUrl}`);
|
|
372
|
+
console.log(` 成功: ${succeeded.length}`);
|
|
373
|
+
console.log(` 跳过: ${skipped.length}`);
|
|
374
|
+
console.log(` 失败: ${failed.length}`);
|
|
375
|
+
console.log(` 备份: ${backupPath || "无旧配置需要备份"}`);
|
|
376
|
+
if (redeemResult) {
|
|
377
|
+
console.log(` 兑换: ${redeemResult.valid ? "已完成" : `未完成(${redeemResult.error || "接口不可用"})`}`);
|
|
378
|
+
}
|
|
379
|
+
console.log(" 提示: 重启相关工具后生效");
|
|
380
|
+
console.log("");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function selectPlatforms(detectedPlatforms, options = {}) {
|
|
384
|
+
if (options.platforms) {
|
|
385
|
+
return getActivationTargets(detectedPlatforms, false, options.platforms);
|
|
386
|
+
}
|
|
387
|
+
if (options.yes || options.auto) {
|
|
388
|
+
return getActivationTargets(detectedPlatforms, Boolean(options.all));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const choices = detectedPlatforms.map(({ platform, detection }) => {
|
|
392
|
+
const selectable = canSelectPlatform(platform, detection);
|
|
393
|
+
return {
|
|
394
|
+
title: `${platform.name}(${getStatusLabel(platform, detection)})`,
|
|
395
|
+
value: platform.id,
|
|
396
|
+
selected: platform.required || detection.installed,
|
|
397
|
+
disabled: selectable ? false : "未安装,无法自动配置",
|
|
398
|
+
};
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const response = await prompts({
|
|
402
|
+
type: "multiselect",
|
|
403
|
+
name: "platformIds",
|
|
404
|
+
message: "请选择要激活的平台",
|
|
405
|
+
choices,
|
|
406
|
+
min: 1,
|
|
407
|
+
hint: "空格选择,回车确认",
|
|
408
|
+
}, { onCancel: () => false });
|
|
409
|
+
|
|
410
|
+
const selectedIds = response.platformIds || [];
|
|
411
|
+
return getActivationTargets(detectedPlatforms, false, selectedIds);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function resolveCodeCredential(options, upstream) {
|
|
415
|
+
const code = await promptActivationCode(options.code);
|
|
416
|
+
if (!code) {
|
|
417
|
+
return { cancelled: true };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
console.log("");
|
|
421
|
+
console.log("正在读取激活码能力...");
|
|
422
|
+
const inspection = await inspectActivationCode(code);
|
|
423
|
+
if (!inspection.valid) {
|
|
424
|
+
console.log(`⚠ 无法读取激活码能力: ${inspection.error || "接口未返回有效信息"}`);
|
|
425
|
+
if (options.yes || options.auto) {
|
|
426
|
+
return { cancelled: true };
|
|
427
|
+
}
|
|
428
|
+
const response = await prompts({
|
|
429
|
+
type: "confirm",
|
|
430
|
+
name: "fallback",
|
|
431
|
+
message: "是否按手动能力选择继续?",
|
|
432
|
+
initial: false,
|
|
433
|
+
}, { onCancel: () => false });
|
|
434
|
+
if (!response.fallback) {
|
|
435
|
+
return { cancelled: true };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const entitlement = normalizeEntitlement(inspection, options.service ? [options.service] : []);
|
|
440
|
+
const apiKey = extractApiKeyFromEntitlement(entitlement, code);
|
|
441
|
+
const entitlementBaseUrl = extractBaseUrlFromEntitlement(entitlement);
|
|
442
|
+
return {
|
|
443
|
+
activationCode: code,
|
|
444
|
+
apiKey,
|
|
445
|
+
entitlement,
|
|
446
|
+
upstream: entitlementBaseUrl ? { ...upstream, baseUrl: entitlementBaseUrl } : upstream,
|
|
447
|
+
inspection,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async function resolveApiKeyCredential(options, upstream) {
|
|
452
|
+
const apiKey = await promptApiKey(options.apiKey || upstream.apiKey);
|
|
453
|
+
if (!apiKey) {
|
|
454
|
+
return { cancelled: true };
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
apiKey,
|
|
458
|
+
entitlement: normalizeEntitlement({}, options.service ? [options.service] : []),
|
|
459
|
+
upstream,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async function activateTargets({ service, upstream, apiKey, targets, activationCode, options = {} }) {
|
|
464
|
+
console.log("");
|
|
465
|
+
console.log("正在创建备份...");
|
|
466
|
+
const backupPath = createActivationBackup(service, getBackupPaths(targets), {
|
|
467
|
+
upstream: upstream.baseUrl,
|
|
468
|
+
targets: targets.map(({ platform }) => platform.id),
|
|
469
|
+
});
|
|
470
|
+
if (backupPath) {
|
|
471
|
+
console.log(`✓ 备份完成: ${backupPath}`);
|
|
472
|
+
} else {
|
|
473
|
+
console.log("ℹ 没有旧配置需要备份");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
console.log("");
|
|
477
|
+
console.log("正在激活平台...");
|
|
478
|
+
const results = [];
|
|
479
|
+
for (const { platform, detection } of targets) {
|
|
480
|
+
try {
|
|
481
|
+
const result = platform.activate({ service, upstream, apiKey, detection });
|
|
482
|
+
results.push({ platform, result });
|
|
483
|
+
if (result.success) {
|
|
484
|
+
console.log(`✓ ${platform.name}`);
|
|
485
|
+
} else {
|
|
486
|
+
console.log(`⚠ ${platform.name}: ${result.message || "已跳过"}`);
|
|
487
|
+
}
|
|
488
|
+
} catch (err) {
|
|
489
|
+
results.push({ platform, result: { success: false, error: err.message } });
|
|
490
|
+
console.log(`✗ ${platform.name}: ${err.message}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
let redeemResult = null;
|
|
495
|
+
const failures = results.filter(({ result }) => !result.success && !result.skipped);
|
|
496
|
+
if (activationCode && failures.length === 0 && !options.noRedeem) {
|
|
497
|
+
console.log("");
|
|
498
|
+
console.log("正在完成兑换记录...");
|
|
499
|
+
redeemResult = await redeemActivationCode(activationCode, service);
|
|
500
|
+
if (redeemResult.valid) {
|
|
501
|
+
console.log("✓ 兑换记录已完成");
|
|
502
|
+
} else {
|
|
503
|
+
console.log(`⚠ 兑换记录未完成: ${redeemResult.error || "接口不可用"}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return { backupPath, results, redeemResult };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async function verifyCredential(upstream, apiKey, options = {}) {
|
|
511
|
+
if (options.skipVerify) {
|
|
512
|
+
console.log("跳过 NewAPI 连通性验证。");
|
|
513
|
+
return { valid: true, skipped: true };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
console.log("正在验证 NewAPI Key...");
|
|
517
|
+
const verification = await verifyNewApiKey(upstream, apiKey);
|
|
518
|
+
if (!verification.valid) {
|
|
519
|
+
console.log(`✗ NewAPI 验证失败: ${verification.error}`);
|
|
520
|
+
return verification;
|
|
521
|
+
}
|
|
522
|
+
console.log(`✓ NewAPI 验证通过(可见 ${verification.models.length} 个模型)`);
|
|
523
|
+
return verification;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function runNewApiActivation(options = {}) {
|
|
527
|
+
const baseUpstream = loadUpstreamConfig({ configPath: options.upstreamConfig });
|
|
528
|
+
const upstream = { ...baseUpstream };
|
|
529
|
+
upstream.baseUrl = await promptBaseUrl(upstream.baseUrl);
|
|
530
|
+
if (!upstream.baseUrl) {
|
|
531
|
+
console.log("Activation cancelled.");
|
|
532
|
+
return { success: false, cancelled: true };
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const apiKey = await promptApiKey(options.apiKey || upstream.apiKey);
|
|
536
|
+
if (!apiKey) {
|
|
537
|
+
console.log("Activation cancelled.");
|
|
538
|
+
return { success: false, cancelled: true };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const entitlement = normalizeEntitlement({}, options.service ? [options.service] : []);
|
|
542
|
+
const service = await promptService(options.service, entitlement);
|
|
543
|
+
if (!service) {
|
|
544
|
+
console.log("Activation cancelled.");
|
|
545
|
+
return { success: false, cancelled: true };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
console.log("");
|
|
549
|
+
const verification = await verifyCredential(upstream, apiKey, options);
|
|
550
|
+
if (!verification.valid) {
|
|
551
|
+
return { success: false, verification };
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const detectedPlatforms = detectPlatforms(service);
|
|
555
|
+
const selectedPlatformIds = options.platforms ? parsePlatformIds(options.platforms) : null;
|
|
556
|
+
const targets = getActivationTargets(detectedPlatforms, Boolean(options.all), selectedPlatformIds);
|
|
557
|
+
const skipped = detectedPlatforms.filter((entry) => !targets.includes(entry));
|
|
558
|
+
|
|
559
|
+
console.log("");
|
|
560
|
+
printPlan(service, upstream, apiKey, targets, skipped);
|
|
561
|
+
|
|
562
|
+
if (!(await confirmActivation(Boolean(options.yes || options.auto)))) {
|
|
563
|
+
console.log("Activation cancelled.");
|
|
564
|
+
return { success: false, cancelled: true };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const activation = await activateTargets({ service, upstream, apiKey, targets, options });
|
|
568
|
+
const failures = activation.results.filter(({ result }) => !result.success && !result.skipped);
|
|
569
|
+
printResultSummary(service, upstream, activation.backupPath, activation.results, activation.redeemResult);
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
success: failures.length === 0,
|
|
573
|
+
backupPath: activation.backupPath,
|
|
574
|
+
results: activation.results,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async function runActivationWizard(options = {}) {
|
|
579
|
+
printBanner();
|
|
580
|
+
const baseUpstream = loadUpstreamConfig({ configPath: options.upstreamConfig });
|
|
581
|
+
const credentialType = await promptCredentialType(options, baseUpstream);
|
|
582
|
+
if (!credentialType) {
|
|
583
|
+
console.log("已取消。");
|
|
584
|
+
return { success: false, cancelled: true };
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const credential = credentialType === "code"
|
|
588
|
+
? await resolveCodeCredential(options, baseUpstream)
|
|
589
|
+
: await resolveApiKeyCredential(options, baseUpstream);
|
|
590
|
+
if (credential.cancelled) {
|
|
591
|
+
console.log("已取消。");
|
|
592
|
+
return { success: false, cancelled: true };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const upstream = { ...credential.upstream };
|
|
596
|
+
upstream.baseUrl = await promptBaseUrl(upstream.baseUrl);
|
|
597
|
+
if (!upstream.baseUrl) {
|
|
598
|
+
console.log("已取消。");
|
|
599
|
+
return { success: false, cancelled: true };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const service = await promptService(options.service, credential.entitlement);
|
|
603
|
+
if (!service) {
|
|
604
|
+
console.log("已取消。");
|
|
605
|
+
return { success: false, cancelled: true };
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
console.log("");
|
|
609
|
+
if (credentialType === "api-key") {
|
|
610
|
+
const verification = await verifyCredential(upstream, credential.apiKey, options);
|
|
611
|
+
if (!verification.valid) {
|
|
612
|
+
return { success: false, verification };
|
|
613
|
+
}
|
|
614
|
+
} else {
|
|
615
|
+
console.log("✓ 已按激活码能力限制可选平台");
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const allDetectedPlatforms = detectPlatforms(service);
|
|
619
|
+
const allowedPlatforms = allDetectedPlatforms.filter((entry) => isPlatformAllowed(entry, credential.entitlement, service));
|
|
620
|
+
const blockedPlatforms = allDetectedPlatforms.filter((entry) => !allowedPlatforms.includes(entry));
|
|
621
|
+
|
|
622
|
+
console.log("");
|
|
623
|
+
printDetection(service, allowedPlatforms, blockedPlatforms);
|
|
624
|
+
|
|
625
|
+
const targets = await selectPlatforms(allowedPlatforms, options);
|
|
626
|
+
if (targets.length === 0) {
|
|
627
|
+
console.log("没有选择任何平台,已取消。");
|
|
628
|
+
return { success: false, cancelled: true };
|
|
629
|
+
}
|
|
630
|
+
const skipped = allowedPlatforms.filter((entry) => !targets.includes(entry));
|
|
631
|
+
|
|
632
|
+
printPlan(service, upstream, credential.apiKey, targets, skipped);
|
|
633
|
+
if (!(await confirmActivation(Boolean(options.yes || options.auto)))) {
|
|
634
|
+
console.log("已取消。");
|
|
635
|
+
return { success: false, cancelled: true };
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const activation = await activateTargets({
|
|
639
|
+
service,
|
|
640
|
+
upstream,
|
|
641
|
+
apiKey: credential.apiKey,
|
|
642
|
+
targets,
|
|
643
|
+
activationCode: credential.activationCode,
|
|
644
|
+
options,
|
|
645
|
+
});
|
|
646
|
+
const failures = activation.results.filter(({ result }) => !result.success && !result.skipped);
|
|
647
|
+
printResultSummary(service, upstream, activation.backupPath, activation.results, activation.redeemResult);
|
|
648
|
+
|
|
649
|
+
return {
|
|
650
|
+
success: failures.length === 0,
|
|
651
|
+
backupPath: activation.backupPath,
|
|
652
|
+
results: activation.results,
|
|
653
|
+
redeemResult: activation.redeemResult,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
module.exports = {
|
|
658
|
+
getActivationTargets,
|
|
659
|
+
isPlatformAllowed,
|
|
660
|
+
normalizeEntitlement,
|
|
661
|
+
normalizeService,
|
|
662
|
+
normalizeServices,
|
|
663
|
+
parsePlatformIds,
|
|
664
|
+
runActivationWizard,
|
|
665
|
+
runNewApiActivation,
|
|
666
|
+
};
|