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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +244 -0
  3. package/README.zh-CN.md +244 -0
  4. package/bin/cli.js +9 -0
  5. package/bin/web-server.js +1434 -0
  6. package/config/upstream.example.json +14 -0
  7. package/frontend/activate.html +249 -0
  8. package/frontend/admin/admin-panel-v2.js +1899 -0
  9. package/frontend/admin/index.html +705 -0
  10. package/frontend/assets/market-ui.css +1876 -0
  11. package/frontend/color-test.html +136 -0
  12. package/frontend/index.html +191 -0
  13. package/frontend/user/assets/AnnouncementDetail-Dvxmwz0A.js +12 -0
  14. package/frontend/user/assets/Announcements-CS1tF2mx.js +11 -0
  15. package/frontend/user/assets/CardBind-CsCxihhP.js +21 -0
  16. package/frontend/user/assets/CardContent.vue_vue_type_script_setup_true_lang-D2L-uqSl.js +1 -0
  17. package/frontend/user/assets/CardDescription.vue_vue_type_script_setup_true_lang-D-v5Pl7F.js +1 -0
  18. package/frontend/user/assets/CardTitle.vue_vue_type_script_setup_true_lang-a0CCN6D5.js +1 -0
  19. package/frontend/user/assets/Dashboard-rPsmltm5.js +51 -0
  20. package/frontend/user/assets/DashboardLayout-BUCWGlXC.css +1 -0
  21. package/frontend/user/assets/DashboardLayout-DDkxHYFj.js +80 -0
  22. package/frontend/user/assets/Input.vue_vue_type_script_setup_true_lang-B0SyPmYb.js +6 -0
  23. package/frontend/user/assets/Label.vue_vue_type_script_setup_true_lang-CxYORSgN.js +1 -0
  24. package/frontend/user/assets/Progress.vue_vue_type_script_setup_true_lang-2_QbPsEQ.js +1 -0
  25. package/frontend/user/assets/QuotaPack-B_tJ7Psm.js +6 -0
  26. package/frontend/user/assets/Renewal-BSDhDmwv.js +6 -0
  27. package/frontend/user/assets/ScrollArea.vue_vue_type_script_setup_true_lang-DMYwcfpz.js +1 -0
  28. package/frontend/user/assets/Separator.vue_vue_type_script_setup_true_lang-Ckg8EXj_.js +1 -0
  29. package/frontend/user/assets/Settings-CBdAa3lw.js +11 -0
  30. package/frontend/user/assets/TooltipTrigger.vue_vue_type_script_setup_true_lang-DtSBjzGo.js +16 -0
  31. package/frontend/user/assets/Welcome-7IfzEli4.css +1 -0
  32. package/frontend/user/assets/Welcome-Dtfp6oER.js +1 -0
  33. package/frontend/user/assets/_plugin-vue_export-helper-5cjT4u0R.js +16 -0
  34. package/frontend/user/assets/activity-wYWtyqTJ.js +6 -0
  35. package/frontend/user/assets/announcement-35mOnjRL.js +16 -0
  36. package/frontend/user/assets/calendar-BFNuCata.js +6 -0
  37. package/frontend/user/assets/chart-vendor-CULJE59K.js +37 -0
  38. package/frontend/user/assets/chevron-down-kDbuU1Py.js +6 -0
  39. package/frontend/user/assets/chevron-right-BayASIm0.js +6 -0
  40. package/frontend/user/assets/eye-CY62vip0.js +6 -0
  41. package/frontend/user/assets/gauge-C5NQ-mV8.js +6 -0
  42. package/frontend/user/assets/index-B8QSyYhS.css +1 -0
  43. package/frontend/user/assets/index-Da98HOxL.js +91 -0
  44. package/frontend/user/assets/link-2-DT5R5nGO.js +6 -0
  45. package/frontend/user/assets/package-rUbExUEn.js +6 -0
  46. package/frontend/user/assets/plus-CQc6C8wG.js +11 -0
  47. package/frontend/user/assets/refresh-cw-Y9hCloPL.js +6 -0
  48. package/frontend/user/assets/useUserPageRefresh-BYZvpNR9.js +1 -0
  49. package/frontend/user/assets/zap-l5zbZqrM.js +11 -0
  50. package/frontend/user/index.html +67 -0
  51. package/install.sh +402 -0
  52. package/lib/commands/activate.js +144 -0
  53. package/lib/commands/restore.js +102 -0
  54. package/lib/commands/test.js +40 -0
  55. package/lib/config/claude.js +81 -0
  56. package/lib/config/codex.js +164 -0
  57. package/lib/config/upstream.js +79 -0
  58. package/lib/index.js +164 -0
  59. package/lib/platforms/claude-code.js +35 -0
  60. package/lib/platforms/codex-cli.js +35 -0
  61. package/lib/platforms/editor-codex.js +138 -0
  62. package/lib/platforms/index.js +32 -0
  63. package/lib/platforms/openclaw.js +118 -0
  64. package/lib/platforms/opencode.js +89 -0
  65. package/lib/services/activation-orchestrator.js +666 -0
  66. package/lib/services/backup-service.js +162 -0
  67. package/lib/services/cliproxy-api.js +174 -0
  68. package/lib/services/database.js +461 -0
  69. package/lib/services/newapi.js +97 -0
  70. package/lib/services/node-service.js +49 -0
  71. package/lib/utils/json-file.js +33 -0
  72. package/package.json +53 -0
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+
7
+ const BACKUP_DIR = process.env.CLIPROXY_BACKUP_DIR ||
8
+ process.env.FOGIDC_BACKUP_DIR ||
9
+ path.join(os.homedir(), ".fogact", "backups");
10
+
11
+ function ensureBackupDir() {
12
+ if (!fs.existsSync(BACKUP_DIR)) {
13
+ fs.mkdirSync(BACKUP_DIR, { recursive: true });
14
+ }
15
+ }
16
+
17
+ function createBackup(service, configPath) {
18
+ ensureBackupDir();
19
+
20
+ if (!fs.existsSync(configPath)) {
21
+ return null;
22
+ }
23
+
24
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
25
+ const backupName = `${service}-${timestamp}.json`;
26
+ const backupPath = path.join(BACKUP_DIR, backupName);
27
+
28
+ const content = fs.readFileSync(configPath, "utf8");
29
+
30
+ const backup = {
31
+ service,
32
+ originalPath: configPath,
33
+ timestamp: new Date().toISOString(),
34
+ content,
35
+ };
36
+
37
+ fs.writeFileSync(backupPath, JSON.stringify(backup, null, 2));
38
+
39
+ return backupPath;
40
+ }
41
+
42
+ function copyRecursive(sourcePath, targetPath) {
43
+ const stat = fs.statSync(sourcePath);
44
+ if (stat.isDirectory()) {
45
+ fs.mkdirSync(targetPath, { recursive: true });
46
+ for (const entry of fs.readdirSync(sourcePath, { withFileTypes: true })) {
47
+ copyRecursive(path.join(sourcePath, entry.name), path.join(targetPath, entry.name));
48
+ }
49
+ return;
50
+ }
51
+
52
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
53
+ fs.copyFileSync(sourcePath, targetPath);
54
+ }
55
+
56
+ function createActivationBackup(service, filePaths, metadata = {}) {
57
+ ensureBackupDir();
58
+
59
+ const existingPaths = [...new Set(filePaths || [])]
60
+ .filter(Boolean)
61
+ .filter((filePath) => fs.existsSync(filePath));
62
+
63
+ if (existingPaths.length === 0) {
64
+ return null;
65
+ }
66
+
67
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
68
+ const backupRoot = path.join(BACKUP_DIR, `${service}-${timestamp}`);
69
+ fs.mkdirSync(backupRoot, { recursive: true });
70
+
71
+ const files = existingPaths.map((filePath, index) => {
72
+ const stat = fs.statSync(filePath);
73
+ const backupName = `${String(index + 1).padStart(2, "0")}-${path.basename(filePath)}`;
74
+ const backupPath = path.join(backupRoot, backupName);
75
+ copyRecursive(filePath, backupPath);
76
+ return {
77
+ originalPath: filePath,
78
+ backupName,
79
+ isDirectory: stat.isDirectory(),
80
+ size: stat.size,
81
+ };
82
+ });
83
+
84
+ const manifest = {
85
+ version: 1,
86
+ service,
87
+ timestamp: new Date().toISOString(),
88
+ metadata,
89
+ files,
90
+ };
91
+ fs.writeFileSync(path.join(backupRoot, "manifest.json"), JSON.stringify(manifest, null, 2));
92
+
93
+ return backupRoot;
94
+ }
95
+
96
+ function listBackups(service = null) {
97
+ ensureBackupDir();
98
+
99
+ const files = fs.readdirSync(BACKUP_DIR);
100
+ const backups = [];
101
+
102
+ for (const file of files) {
103
+ if (!file.endsWith(".json")) continue;
104
+
105
+ const filePath = path.join(BACKUP_DIR, file);
106
+
107
+ try {
108
+ const content = fs.readFileSync(filePath, "utf8");
109
+ const backup = JSON.parse(content);
110
+
111
+ if (!service || backup.service === service) {
112
+ backups.push({
113
+ file,
114
+ path: filePath,
115
+ ...backup,
116
+ });
117
+ }
118
+ } catch (err) {
119
+ // Skip invalid backup files
120
+ }
121
+ }
122
+
123
+ backups.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
124
+
125
+ return backups;
126
+ }
127
+
128
+ function restoreBackup(backupPath) {
129
+ if (!fs.existsSync(backupPath)) {
130
+ throw new Error("Backup file not found");
131
+ }
132
+
133
+ const content = fs.readFileSync(backupPath, "utf8");
134
+ const backup = JSON.parse(content);
135
+
136
+ const targetDir = path.dirname(backup.originalPath);
137
+ if (!fs.existsSync(targetDir)) {
138
+ fs.mkdirSync(targetDir, { recursive: true });
139
+ }
140
+
141
+ fs.writeFileSync(backup.originalPath, backup.content);
142
+
143
+ return backup.originalPath;
144
+ }
145
+
146
+ function clearBackups(service = null) {
147
+ const backups = listBackups(service);
148
+
149
+ for (const backup of backups) {
150
+ fs.unlinkSync(backup.path);
151
+ }
152
+
153
+ return backups.length;
154
+ }
155
+
156
+ module.exports = {
157
+ createActivationBackup,
158
+ createBackup,
159
+ listBackups,
160
+ restoreBackup,
161
+ clearBackups,
162
+ };
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+
3
+ const https = require("https");
4
+ const http = require("http");
5
+
6
+ // 支持环境变量配置 API 地址
7
+ const API_BASE = process.env.CLIPROXY_API_BASE || "http://localhost:34020";
8
+
9
+ function makeRequest(path, options = {}) {
10
+ return new Promise((resolve, reject) => {
11
+ const url = new URL(path, API_BASE);
12
+ const isHttps = url.protocol === "https:";
13
+ const client = isHttps ? https : http;
14
+
15
+ const reqOptions = {
16
+ hostname: url.hostname,
17
+ port: url.port || (isHttps ? 443 : 80),
18
+ path: url.pathname + url.search,
19
+ method: options.method || "GET",
20
+ headers: {
21
+ "Content-Type": "application/json",
22
+ "User-Agent": "fogact/1.1.3",
23
+ ...options.headers,
24
+ },
25
+ };
26
+
27
+ const req = client.request(reqOptions, (res) => {
28
+ let data = "";
29
+
30
+ res.on("data", (chunk) => {
31
+ data += chunk;
32
+ });
33
+
34
+ res.on("end", () => {
35
+ try {
36
+ const parsed = JSON.parse(data);
37
+ resolve({ status: res.statusCode, data: parsed });
38
+ } catch (err) {
39
+ resolve({ status: res.statusCode, data: data });
40
+ }
41
+ });
42
+ });
43
+
44
+ req.on("error", (err) => {
45
+ reject(err);
46
+ });
47
+
48
+ if (options.body) {
49
+ req.write(JSON.stringify(options.body));
50
+ }
51
+
52
+ req.end();
53
+ });
54
+ }
55
+
56
+ async function verifyActivationCode(code, service) {
57
+ try {
58
+ // 调用本地激活 API
59
+ const response = await makeRequest("/api/activate", {
60
+ method: "POST",
61
+ body: {
62
+ code,
63
+ service,
64
+ username: process.env.USER || process.env.USERNAME || "cli-user"
65
+ },
66
+ });
67
+
68
+ if (response.status === 200 && response.data.success) {
69
+ return {
70
+ valid: true,
71
+ service: response.data.data.service,
72
+ expiresAt: response.data.data.expiresAt,
73
+ quota: response.data.data.quota,
74
+ nodes: response.data.data.nodes || [],
75
+ };
76
+ }
77
+
78
+ return { valid: false, error: response.data.message || "Invalid code" };
79
+ } catch (err) {
80
+ return { valid: false, error: err.message };
81
+ }
82
+ }
83
+
84
+ function normalizeActivationResponse(response) {
85
+ const payload = response && response.data && typeof response.data === "object" ? response.data : {};
86
+ const data = payload.data && typeof payload.data === "object" ? payload.data : payload;
87
+ return {
88
+ valid: Boolean(payload.success || payload.valid),
89
+ message: payload.message,
90
+ data,
91
+ };
92
+ }
93
+
94
+ async function inspectActivationCode(code) {
95
+ try {
96
+ const response = await makeRequest("/api/verify", {
97
+ method: "POST",
98
+ body: { code },
99
+ });
100
+ const normalized = normalizeActivationResponse(response);
101
+
102
+ if (response.status >= 200 && response.status < 300 && normalized.valid) {
103
+ return {
104
+ valid: true,
105
+ ...normalized.data,
106
+ data: normalized.data,
107
+ };
108
+ }
109
+
110
+ return { valid: false, error: normalized.message || "Invalid activation code" };
111
+ } catch (err) {
112
+ return { valid: false, error: err.message };
113
+ }
114
+ }
115
+
116
+ async function redeemActivationCode(code, service) {
117
+ return verifyActivationCode(code, service);
118
+ }
119
+
120
+ async function getNodes(service) {
121
+ try {
122
+ const response = await makeRequest(`/api/nodes?service=${service}`);
123
+
124
+ if (response.status === 200 && Array.isArray(response.data.nodes)) {
125
+ return response.data.nodes;
126
+ }
127
+
128
+ // 返回默认节点
129
+ return [
130
+ { name: "Default Node", url: "https://yunyi.cfd", region: "Global" }
131
+ ];
132
+ } catch (err) {
133
+ console.error("Failed to fetch nodes:", err.message);
134
+ // 返回默认节点
135
+ return [
136
+ { name: "Default Node", url: "https://yunyi.cfd", region: "Global" }
137
+ ];
138
+ }
139
+ }
140
+
141
+ async function testNode(nodeUrl) {
142
+ const start = Date.now();
143
+
144
+ try {
145
+ const url = new URL("/health", nodeUrl);
146
+ const response = await makeRequest(url.pathname, {
147
+ method: "GET",
148
+ headers: { Host: url.hostname },
149
+ });
150
+
151
+ const latency = Date.now() - start;
152
+
153
+ return {
154
+ url: nodeUrl,
155
+ available: response.status === 200,
156
+ latency,
157
+ };
158
+ } catch (err) {
159
+ return {
160
+ url: nodeUrl,
161
+ available: false,
162
+ latency: -1,
163
+ error: err.message,
164
+ };
165
+ }
166
+ }
167
+
168
+ module.exports = {
169
+ inspectActivationCode,
170
+ redeemActivationCode,
171
+ verifyActivationCode,
172
+ getNodes,
173
+ testNode,
174
+ };