fogact 1.1.6 → 1.1.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/README.md CHANGED
@@ -27,6 +27,8 @@ That is the user-facing command. Running it opens the interactive menu:
27
27
 
28
28
  Do not run `npm fogact`; npm treats that as an npm subcommand. Use `npx fogact`.
29
29
 
30
+ FogAct checks npm for the latest version before opening the menu. If an older installed version is detected, it updates first and then continues. Set `FOGACT_SKIP_UPDATE=1` to skip this check.
31
+
30
32
  ## Clean VPS
31
33
 
32
34
  If the machine does not have Node.js/npm yet, use the bootstrap installer first:
package/README.zh-CN.md CHANGED
@@ -27,6 +27,8 @@ npx fogact
27
27
 
28
28
  不要运行 `npm fogact`;npm 会把它当成 npm 子命令。正确方式是 `npx fogact`。
29
29
 
30
+ FogAct 启动时会自动检查 npm 最新版本;检测到旧版本会先更新,再进入菜单。需要跳过检查时可设置 `FOGACT_SKIP_UPDATE=1`。
31
+
30
32
  ## 干净 VPS
31
33
 
32
34
  如果机器还没有 Node.js/npm,先用 bootstrap 安装:
@@ -731,6 +731,35 @@ body.market-dark .market-auth-card::before {
731
731
  color: var(--market-primary);
732
732
  }
733
733
 
734
+ .market-quick-links {
735
+ display: grid;
736
+ grid-template-columns: repeat(2, minmax(0, 1fr));
737
+ gap: 8px;
738
+ margin-top: 16px;
739
+ }
740
+
741
+ .market-quick-links a {
742
+ display: inline-flex;
743
+ align-items: center;
744
+ justify-content: center;
745
+ min-height: 36px;
746
+ border: 1px solid var(--market-line);
747
+ border-radius: 14px;
748
+ color: var(--market-ink);
749
+ background: var(--market-panel-muted);
750
+ text-decoration: none;
751
+ font-size: 13px;
752
+ font-weight: 800;
753
+ transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease, color 0.18s ease;
754
+ }
755
+
756
+ .market-quick-links a:hover {
757
+ transform: translateY(-1px);
758
+ border-color: color-mix(in srgb, var(--market-primary) 42%, var(--market-line));
759
+ color: var(--market-primary);
760
+ background: color-mix(in srgb, var(--market-primary) 10%, var(--market-panel-muted));
761
+ }
762
+
734
763
  .market-result-list {
735
764
  display: grid;
736
765
  gap: 10px;
@@ -1190,7 +1219,7 @@ body.market-user {
1190
1219
  width: min(48vw, 440px);
1191
1220
  height: min(48vw, 440px);
1192
1221
  pointer-events: none;
1193
- opacity: 0.95;
1222
+ opacity: 0.98;
1194
1223
  }
1195
1224
 
1196
1225
  .market-orbit-stage::before,
@@ -1200,69 +1229,139 @@ body.market-user {
1200
1229
  inset: 12%;
1201
1230
  border: 1px solid color-mix(in srgb, var(--market-ink) 12%, transparent);
1202
1231
  border-radius: 999px;
1203
- animation: orbitSpin 18s linear infinite;
1232
+ background:
1233
+ radial-gradient(circle at center, color-mix(in srgb, var(--market-primary) 7%, transparent), transparent 62%);
1234
+ animation: orbitSpin 22s linear infinite;
1204
1235
  }
1205
1236
 
1206
1237
  .market-orbit-stage::after {
1207
1238
  inset: 25%;
1208
1239
  border-style: dashed;
1209
- animation-duration: 12s;
1240
+ animation-duration: 16s;
1210
1241
  animation-direction: reverse;
1211
1242
  opacity: 0.72;
1212
1243
  }
1213
1244
 
1214
- .market-orbit-node {
1245
+ .market-orbit-core {
1215
1246
  position: absolute;
1247
+ left: 50%;
1248
+ top: 50%;
1249
+ z-index: 2;
1250
+ display: grid;
1251
+ place-items: center;
1252
+ width: 132px;
1253
+ height: 132px;
1254
+ padding: 18px;
1255
+ border: 1px solid color-mix(in srgb, var(--market-primary) 30%, var(--market-line));
1256
+ border-radius: 36px;
1257
+ color: var(--market-ink);
1258
+ background:
1259
+ linear-gradient(145deg, color-mix(in srgb, var(--market-panel-strong) 92%, transparent), var(--market-panel-muted)),
1260
+ radial-gradient(circle at 30% 20%, color-mix(in srgb, var(--market-primary) 24%, transparent), transparent 58%);
1261
+ box-shadow: 0 24px 60px color-mix(in srgb, var(--market-primary) 20%, transparent);
1262
+ transform: translate(-50%, -50%) rotate(-2deg);
1263
+ }
1264
+
1265
+ .market-orbit-core .material-symbols-outlined {
1216
1266
  display: inline-flex;
1217
1267
  align-items: center;
1218
1268
  justify-content: center;
1219
- width: 74px;
1220
- height: 74px;
1221
- border: 1px solid var(--market-line-strong);
1222
- border-radius: 24px;
1269
+ width: 42px;
1270
+ height: 42px;
1271
+ border-radius: 16px;
1223
1272
  color: #fff;
1224
- background: linear-gradient(135deg, var(--node-a, var(--market-primary)), var(--node-b, var(--market-primary-2)));
1225
- box-shadow: 0 20px 50px color-mix(in srgb, var(--node-a, var(--market-primary)) 28%, transparent);
1226
- font-size: 28px;
1273
+ background: linear-gradient(135deg, var(--market-primary), var(--market-primary-2));
1274
+ box-shadow: 0 14px 32px color-mix(in srgb, var(--market-primary) 28%, transparent);
1275
+ }
1276
+
1277
+ .market-orbit-core strong {
1278
+ margin-top: 8px;
1279
+ font-family: var(--market-headline);
1280
+ font-size: 18px;
1281
+ letter-spacing: -0.04em;
1282
+ }
1283
+
1284
+ .market-orbit-core small {
1285
+ color: var(--market-muted);
1286
+ font-size: 12px;
1287
+ font-weight: 800;
1288
+ }
1289
+
1290
+ .market-orbit-node {
1291
+ position: absolute;
1292
+ z-index: 3;
1293
+ display: inline-flex;
1294
+ align-items: center;
1295
+ gap: 8px;
1296
+ min-width: 118px;
1297
+ min-height: 54px;
1298
+ padding: 10px 12px;
1299
+ border: 1px solid color-mix(in srgb, var(--node-a, var(--market-primary)) 30%, var(--market-line));
1300
+ border-radius: 20px;
1301
+ color: var(--market-ink);
1302
+ background:
1303
+ linear-gradient(145deg, color-mix(in srgb, var(--market-panel-strong) 94%, transparent), var(--market-panel-muted)),
1304
+ radial-gradient(circle at 15% 0%, color-mix(in srgb, var(--node-a, var(--market-primary)) 24%, transparent), transparent 64%);
1305
+ box-shadow: 0 18px 44px color-mix(in srgb, var(--node-a, var(--market-primary)) 18%, transparent);
1227
1306
  transform: translate3d(0, 0, 0) rotate(var(--tilt, 0deg));
1228
1307
  animation: nodeFloat var(--speed, 6s) ease-in-out infinite;
1229
1308
  }
1230
1309
 
1231
- .market-orbit-node:nth-child(1) {
1310
+ .market-orbit-node .material-symbols-outlined {
1311
+ display: inline-flex;
1312
+ align-items: center;
1313
+ justify-content: center;
1314
+ flex: 0 0 auto;
1315
+ width: 32px;
1316
+ height: 32px;
1317
+ border-radius: 13px;
1318
+ color: #fff;
1319
+ background: linear-gradient(135deg, var(--node-a, var(--market-primary)), var(--node-b, var(--market-primary-2)));
1320
+ font-size: 18px;
1321
+ }
1322
+
1323
+ .market-orbit-node span:last-child {
1324
+ color: var(--market-ink);
1325
+ font-size: 13px;
1326
+ font-weight: 900;
1327
+ white-space: nowrap;
1328
+ }
1329
+
1330
+ .market-orbit-node-cli {
1232
1331
  --node-a: #6d5dfc;
1233
1332
  --node-b: #b86bff;
1234
- --tilt: -10deg;
1333
+ --tilt: -8deg;
1235
1334
  --speed: 6.5s;
1236
- left: 6%;
1335
+ left: 1%;
1237
1336
  top: 18%;
1238
1337
  }
1239
1338
 
1240
- .market-orbit-node:nth-child(2) {
1339
+ .market-orbit-node-cdk {
1241
1340
  --node-a: #ff7a59;
1242
1341
  --node-b: #ffd166;
1243
- --tilt: 9deg;
1342
+ --tilt: 7deg;
1244
1343
  --speed: 7.5s;
1245
- right: 11%;
1246
- top: 7%;
1344
+ right: 2%;
1345
+ top: 9%;
1247
1346
  animation-delay: -1.8s;
1248
1347
  }
1249
1348
 
1250
- .market-orbit-node:nth-child(3) {
1349
+ .market-orbit-node-quota {
1251
1350
  --node-a: #17c3b2;
1252
1351
  --node-b: #5eead4;
1253
- --tilt: 12deg;
1352
+ --tilt: 8deg;
1254
1353
  --speed: 8s;
1255
- right: 5%;
1256
- bottom: 21%;
1354
+ right: 0;
1355
+ bottom: 20%;
1257
1356
  animation-delay: -3s;
1258
1357
  }
1259
1358
 
1260
- .market-orbit-node:nth-child(4) {
1261
- --node-a: #101014;
1359
+ .market-orbit-node-sync {
1360
+ --node-a: #111827;
1262
1361
  --node-b: #6d5dfc;
1263
- --tilt: -8deg;
1362
+ --tilt: -7deg;
1264
1363
  --speed: 7s;
1265
- left: 19%;
1364
+ left: 7%;
1266
1365
  bottom: 8%;
1267
1366
  animation-delay: -4.3s;
1268
1367
  }
@@ -1279,6 +1378,11 @@ body.market-user {
1279
1378
  overflow: hidden;
1280
1379
  }
1281
1380
 
1381
+ .market-signal-line-alt {
1382
+ transform: rotate(36deg);
1383
+ opacity: 0.42;
1384
+ }
1385
+
1282
1386
  .market-signal-line::after {
1283
1387
  content: "";
1284
1388
  position: absolute;
@@ -1288,6 +1392,10 @@ body.market-user {
1288
1392
  animation: signalSweep 2.6s cubic-bezier(0.16, 1, 0.3, 1) infinite;
1289
1393
  }
1290
1394
 
1395
+ .market-signal-line-alt::after {
1396
+ animation-delay: -1.1s;
1397
+ }
1398
+
1291
1399
  .market-card-lab {
1292
1400
  min-height: 318px;
1293
1401
  padding: 0;
@@ -1880,11 +1988,26 @@ body.is-pointer-active .market-mouse-light {
1880
1988
  height: 240px;
1881
1989
  }
1882
1990
 
1991
+ .market-orbit-core {
1992
+ width: 104px;
1993
+ height: 104px;
1994
+ border-radius: 28px;
1995
+ }
1996
+
1997
+ .market-orbit-core strong {
1998
+ font-size: 16px;
1999
+ }
2000
+
1883
2001
  .market-orbit-node {
1884
- width: 58px;
1885
- height: 58px;
2002
+ min-width: 58px;
2003
+ min-height: 58px;
2004
+ padding: 8px;
1886
2005
  border-radius: 20px;
1887
- font-size: 22px;
2006
+ justify-content: center;
2007
+ }
2008
+
2009
+ .market-orbit-node span:last-child {
2010
+ display: none;
1888
2011
  }
1889
2012
 
1890
2013
  .market-mouse-light {
@@ -73,12 +73,30 @@
73
73
  <span class="market-ambient-item"><span class="material-symbols-outlined">query_stats</span></span>
74
74
  </div>
75
75
 
76
- <div class="market-orbit-stage" aria-hidden="true">
77
- <span class="market-orbit-node material-symbols-outlined">code</span>
78
- <span class="market-orbit-node material-symbols-outlined">vpn_key</span>
79
- <span class="market-orbit-node material-symbols-outlined">monitoring</span>
80
- <span class="market-orbit-node material-symbols-outlined">hub</span>
81
- <span class="market-signal-line"></span>
76
+ <div class="market-orbit-stage" aria-label="FogAct 服务流程拓扑">
77
+ <div class="market-orbit-core">
78
+ <span class="material-symbols-outlined">hub</span>
79
+ <strong>FogAct</strong>
80
+ <small>统一入口</small>
81
+ </div>
82
+ <span class="market-orbit-node market-orbit-node-cli">
83
+ <span class="material-symbols-outlined">terminal</span>
84
+ <span>CLI 接入</span>
85
+ </span>
86
+ <span class="market-orbit-node market-orbit-node-cdk">
87
+ <span class="material-symbols-outlined">vpn_key</span>
88
+ <span>CDK 校验</span>
89
+ </span>
90
+ <span class="market-orbit-node market-orbit-node-quota">
91
+ <span class="material-symbols-outlined">monitoring</span>
92
+ <span>配额监控</span>
93
+ </span>
94
+ <span class="market-orbit-node market-orbit-node-sync">
95
+ <span class="material-symbols-outlined">cloud_sync</span>
96
+ <span>上游同步</span>
97
+ </span>
98
+ <span class="market-signal-line market-signal-line-main"></span>
99
+ <span class="market-signal-line market-signal-line-alt"></span>
82
100
  </div>
83
101
  </div>
84
102
 
@@ -112,12 +130,18 @@
112
130
 
113
131
  <div class="market-panel" style="border-radius: 26px; padding: 18px;">
114
132
  <div class="market-card-footer">
115
- <span class="market-tag">Local server</span>
133
+ <span class="market-tag">本地服务</span>
116
134
  <span class="market-status-pill"><span class="market-kicker-dot"></span> 34020</span>
117
135
  </div>
118
136
  <p class="market-mini-copy" style="margin-top: 14px; font-size: 14px; line-height: 1.7;">
119
137
  本地 Web 服务默认运行在 34020 端口,提供首页、用户中心、管理中心和激活入口。
120
138
  </p>
139
+ <div class="market-quick-links" aria-label="本地服务快捷入口">
140
+ <a href="/">首页</a>
141
+ <a href="/user/">用户中心</a>
142
+ <a href="/admin/">管理中心</a>
143
+ <a href="/activate.html">激活入口</a>
144
+ </div>
121
145
  </div>
122
146
  </aside>
123
147
  </section>
package/lib/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const https = require("https");
4
+ const { spawnSync } = require("child_process");
3
5
  const { Command } = require("commander");
4
6
  const prompts = require("prompts");
5
7
  const packageJson = require("../package.json");
@@ -15,6 +17,120 @@ const MENU_CHOICES = [
15
17
  { title: "4. 退出", value: "exit" },
16
18
  ];
17
19
 
20
+ const UPDATE_TIMEOUT_MS = 2500;
21
+
22
+ function parseVersion(version) {
23
+ return String(version || "")
24
+ .split("-")[0]
25
+ .split(".")
26
+ .map((part) => Number.parseInt(part, 10) || 0);
27
+ }
28
+
29
+ function isNewerVersion(latest, current) {
30
+ const latestParts = parseVersion(latest);
31
+ const currentParts = parseVersion(current);
32
+ const length = Math.max(latestParts.length, currentParts.length);
33
+ for (let index = 0; index < length; index += 1) {
34
+ const latestPart = latestParts[index] || 0;
35
+ const currentPart = currentParts[index] || 0;
36
+ if (latestPart > currentPart) return true;
37
+ if (latestPart < currentPart) return false;
38
+ }
39
+ return false;
40
+ }
41
+
42
+ function fetchLatestVersion() {
43
+ return new Promise((resolve) => {
44
+ const request = https.get(
45
+ "https://registry.npmjs.org/fogact/latest",
46
+ {
47
+ timeout: UPDATE_TIMEOUT_MS,
48
+ headers: {
49
+ Accept: "application/json",
50
+ "User-Agent": `fogact/${packageJson.version}`,
51
+ },
52
+ },
53
+ (response) => {
54
+ if (response.statusCode !== 200) {
55
+ response.resume();
56
+ resolve(null);
57
+ return;
58
+ }
59
+
60
+ let body = "";
61
+ response.setEncoding("utf8");
62
+ response.on("data", (chunk) => {
63
+ body += chunk;
64
+ });
65
+ response.on("end", () => {
66
+ try {
67
+ const metadata = JSON.parse(body);
68
+ resolve(metadata.version || null);
69
+ } catch (_error) {
70
+ resolve(null);
71
+ }
72
+ });
73
+ }
74
+ );
75
+
76
+ request.on("timeout", () => {
77
+ request.destroy();
78
+ resolve(null);
79
+ });
80
+ request.on("error", () => resolve(null));
81
+ });
82
+ }
83
+
84
+ function isNpmExecRun() {
85
+ const argvPath = process.argv[1] || "";
86
+ const npmCommand = process.env.npm_command || "";
87
+ const npmExecPath = process.env.npm_execpath || "";
88
+ return argvPath.includes("/_npx/") || npmCommand === "exec" || npmCommand === "x" || npmExecPath.includes("npx-cli");
89
+ }
90
+
91
+ function runLatestWithNpmExec(latestVersion, args, env) {
92
+ return spawnSync(
93
+ "npm",
94
+ ["exec", "--yes", "--package", `fogact@${latestVersion}`, "--", "fogact", ...args],
95
+ { stdio: "inherit", env }
96
+ );
97
+ }
98
+
99
+ async function ensureLatestVersion(argv = process.argv) {
100
+ if (process.env.FOGACT_SKIP_UPDATE === "1" || process.env.FOGACT_NO_UPDATE === "1") {
101
+ return false;
102
+ }
103
+
104
+ const latestVersion = await fetchLatestVersion();
105
+ if (!latestVersion || !isNewerVersion(latestVersion, packageJson.version)) {
106
+ return false;
107
+ }
108
+
109
+ const args = argv.slice(2);
110
+ const env = { ...process.env, FOGACT_SKIP_UPDATE: "1" };
111
+ console.log(`检测到新版本 v${latestVersion},正在自动更新...`);
112
+
113
+ if (isNpmExecRun()) {
114
+ const result = runLatestWithNpmExec(latestVersion, args, env);
115
+ process.exit(result.status === null ? 1 : result.status);
116
+ }
117
+
118
+ const update = spawnSync("npm", ["install", "-g", `fogact@${latestVersion}`], {
119
+ stdio: "inherit",
120
+ env,
121
+ });
122
+
123
+ if (update.status === 0) {
124
+ console.log("更新完成,正在重新启动...");
125
+ const restart = spawnSync(process.execPath, argv.slice(1), { stdio: "inherit", env });
126
+ process.exit(restart.status === null ? 1 : restart.status);
127
+ }
128
+
129
+ console.log("自动更新失败,正在尝试直接运行最新版...");
130
+ const result = runLatestWithNpmExec(latestVersion, args, env);
131
+ process.exit(result.status === null ? 1 : result.status);
132
+ }
133
+
18
134
  function displayWidth(value) {
19
135
  return Array.from(value).reduce((width, char) => {
20
136
  return width + (char.charCodeAt(0) > 0xff ? 2 : 1);
@@ -230,6 +346,8 @@ function buildProgram() {
230
346
  }
231
347
 
232
348
  async function runCli(argv = process.argv) {
349
+ await ensureLatestVersion(argv);
350
+
233
351
  const args = argv.slice(2).filter((arg) => arg !== "--help" && arg !== "-h");
234
352
 
235
353
  if (args.length === 0) {
@@ -243,6 +361,8 @@ async function runCli(argv = process.argv) {
243
361
 
244
362
  module.exports = {
245
363
  buildProgram,
364
+ ensureLatestVersion,
365
+ isNewerVersion,
246
366
  runCli,
247
367
  runInteractiveMenu,
248
368
  runToolsMenu,
@@ -19,7 +19,7 @@ function makeRequest(path, options = {}) {
19
19
  method: options.method || "GET",
20
20
  headers: {
21
21
  "Content-Type": "application/json",
22
- "User-Agent": "fogact/1.1.6",
22
+ "User-Agent": "fogact/1.1.7",
23
23
  ...options.headers,
24
24
  },
25
25
  };
@@ -58,7 +58,7 @@ async function verifyNewApiKey(config, apiKey) {
58
58
  headers: {
59
59
  Authorization: `Bearer ${apiKey}`,
60
60
  Accept: "application/json",
61
- "User-Agent": "fogact/1.1.6",
61
+ "User-Agent": "fogact/1.1.7",
62
62
  },
63
63
  });
64
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fogact",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "FogAct activation helper for Claude Code and Codex",
5
5
  "keywords": [
6
6
  "fogact",