fogact 1.1.5 → 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/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");
@@ -8,51 +10,243 @@ const { runTestCommand } = require("./commands/test");
8
10
  const { runRestoreCommand } = require("./commands/restore");
9
11
  const { runActivationWizard } = require("./services/activation-orchestrator");
10
12
 
11
- function printBanner() {
12
- const version = packageJson.version;
13
- const title = `FogAct 激活器 v${version}`;
14
- const displayWidth = (value) => Array.from(value).reduce((width, char) => {
13
+ const MENU_CHOICES = [
14
+ { title: "1. 激活服务", value: "activate" },
15
+ { title: "2. 测试节点", value: "test" },
16
+ { title: "3. 恢复备份", value: "restore" },
17
+ { title: "4. 退出", value: "exit" },
18
+ ];
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
+
134
+ function displayWidth(value) {
135
+ return Array.from(value).reduce((width, char) => {
15
136
  return width + (char.charCodeAt(0) > 0xff ? 2 : 1);
16
137
  }, 0);
17
- const padLine = (value, width) => {
18
- const padding = Math.max(0, width - displayWidth(value));
19
- const left = Math.floor(padding / 2);
20
- const right = padding - left;
21
- return `${" ".repeat(left)}${value}${" ".repeat(right)}`;
22
- };
23
- console.log("");
24
- console.log(" ╭─────────────────────────────────────╮");
25
- console.log(` │${padLine(title, 37)}│`);
26
- console.log(" │ Claude Code / Codex 配置工具 │");
27
- console.log(" ╰─────────────────────────────────────╯");
28
- console.log("");
138
+ }
139
+
140
+ function padLine(value, width) {
141
+ const padding = Math.max(0, width - displayWidth(value));
142
+ const left = Math.floor(padding / 2);
143
+ const right = padding - left;
144
+ return `${" ".repeat(left)}${value}${" ".repeat(right)}`;
145
+ }
146
+
147
+ function renderMenu(cursor = 0) {
148
+ const version = packageJson.version;
149
+ const title = `FogAct 激活器 v${version}`;
150
+ const lines = [
151
+ "",
152
+ " ╭─────────────────────────────────────╮",
153
+ ` │${padLine(title, 37)}│`,
154
+ " │ Claude Code / Codex 配置工具 │",
155
+ " ╰─────────────────────────────────────╯",
156
+ "",
157
+ "? 请选择操作:",
158
+ ];
159
+
160
+ MENU_CHOICES.forEach((choice, index) => {
161
+ lines.push(`${index === cursor ? "❯" : " "} ${choice.title}`);
162
+ });
163
+
164
+ lines.push("");
165
+ lines.push("↑↓ navigate • ⏎ select");
166
+ return lines.join("\n");
167
+ }
168
+
169
+ function printBanner() {
170
+ console.log(renderMenu(0));
29
171
  }
30
172
 
31
173
  async function runInteractiveMenu() {
32
174
  await runActivationWizard();
33
175
  }
34
176
 
177
+ function selectMenuAction() {
178
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
179
+ printBanner();
180
+ return Promise.resolve(null);
181
+ }
182
+
183
+ return new Promise((resolve) => {
184
+ let cursor = 0;
185
+ let renderedLines = 0;
186
+ const stdin = process.stdin;
187
+
188
+ const render = () => {
189
+ if (renderedLines > 0) {
190
+ process.stdout.write(`\x1b[${renderedLines}A`);
191
+ process.stdout.write("\x1b[J");
192
+ }
193
+ const output = renderMenu(cursor);
194
+ renderedLines = output.split("\n").length;
195
+ process.stdout.write(output);
196
+ };
197
+
198
+ const cleanup = () => {
199
+ stdin.off("data", onData);
200
+ if (stdin.isRaw) stdin.setRawMode(false);
201
+ stdin.pause();
202
+ process.stdout.write("\x1b[?25h\n");
203
+ };
204
+
205
+ const submit = () => {
206
+ const choice = MENU_CHOICES[cursor];
207
+ cleanup();
208
+ resolve(choice.value);
209
+ };
210
+
211
+ const cancel = () => {
212
+ cleanup();
213
+ resolve("exit");
214
+ };
215
+
216
+ const onData = (data) => {
217
+ const key = data.toString("utf8");
218
+ if (key === "\u0003" || key === "\u001b") {
219
+ cancel();
220
+ return;
221
+ }
222
+ if (key === "\r" || key === "\n") {
223
+ submit();
224
+ return;
225
+ }
226
+ if (key === "\u001b[A") {
227
+ cursor = cursor === 0 ? MENU_CHOICES.length - 1 : cursor - 1;
228
+ render();
229
+ return;
230
+ }
231
+ if (key === "\u001b[B") {
232
+ cursor = cursor === MENU_CHOICES.length - 1 ? 0 : cursor + 1;
233
+ render();
234
+ }
235
+ };
236
+
237
+ process.stdout.write("\x1b[?25l");
238
+ render();
239
+ stdin.resume();
240
+ stdin.setEncoding("utf8");
241
+ stdin.setRawMode(true);
242
+ stdin.on("data", onData);
243
+ });
244
+ }
245
+
35
246
  async function runToolsMenu() {
36
- printBanner();
37
-
38
- const response = await prompts(
39
- {
40
- type: "select",
41
- name: "action",
42
- message: "请选择操作:",
43
- hint: "↑↓ 选择,回车确认",
44
- choices: [
45
- { title: "1. 激活服务", value: "activate" },
46
- { title: "2. 测试节点", value: "test" },
47
- { title: "3. 恢复备份", value: "restore" },
48
- { title: "4. 退出", value: "exit" },
49
- ],
50
- initial: 0,
51
- },
52
- { onCancel: () => false }
53
- );
247
+ const action = await selectMenuAction();
54
248
 
55
- switch (response.action) {
249
+ switch (action) {
56
250
  case "activate":
57
251
  await runActivationWizard();
58
252
  break;
@@ -152,6 +346,8 @@ function buildProgram() {
152
346
  }
153
347
 
154
348
  async function runCli(argv = process.argv) {
349
+ await ensureLatestVersion(argv);
350
+
155
351
  const args = argv.slice(2).filter((arg) => arg !== "--help" && arg !== "-h");
156
352
 
157
353
  if (args.length === 0) {
@@ -165,6 +361,8 @@ async function runCli(argv = process.argv) {
165
361
 
166
362
  module.exports = {
167
363
  buildProgram,
364
+ ensureLatestVersion,
365
+ isNewerVersion,
168
366
  runCli,
169
367
  runInteractiveMenu,
170
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.5",
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.5",
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.5",
3
+ "version": "1.1.7",
4
4
  "description": "FogAct activation helper for Claude Code and Codex",
5
5
  "keywords": [
6
6
  "fogact",