@fmode/studio 0.0.7 → 0.0.8
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/bin/fmode.js +1078 -22
- package/cli.js +1066 -0
- package/package.json +2 -4
package/bin/fmode.js
CHANGED
|
@@ -1,22 +1,1078 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ============================================================
|
|
4
|
+
// ROOTDIR FIX FOR NPM PACKAGE
|
|
5
|
+
// ============================================================
|
|
6
|
+
// In the npm package, the bin file is at: node_modules/@fmode/studio/bin/fmode.js
|
|
7
|
+
// The package.json is at: node_modules/@fmode/studio/package.json
|
|
8
|
+
// So we need to calculate rootDir relative to the bin file
|
|
9
|
+
import { fileURLToPath as _fileURLToPath } from 'url';
|
|
10
|
+
import { dirname as _dirname, join as _join } from 'path';
|
|
11
|
+
const _pkgRoot = _dirname(_dirname(_fileURLToPath(import.meta.url)));
|
|
12
|
+
// Override the rootDir variable used in the bundled code
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
var __defProp = Object.defineProperty;
|
|
15
|
+
var __export = (target, all) => {
|
|
16
|
+
for (var name in all)
|
|
17
|
+
__defProp(target, name, {
|
|
18
|
+
get: all[name],
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
set: (newValue) => all[name] = () => newValue
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
25
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
26
|
+
|
|
27
|
+
// cli/shared/utils.ts
|
|
28
|
+
import { spawn, exec } from "child_process";
|
|
29
|
+
import { join } from "path";
|
|
30
|
+
import { existsSync, mkdirSync, chmodSync, renameSync } from "fs";
|
|
31
|
+
import { createWriteStream } from "fs";
|
|
32
|
+
import { pipeline } from "stream/promises";
|
|
33
|
+
import { unlink } from "fs/promises";
|
|
34
|
+
import { promisify } from "util";
|
|
35
|
+
function getExecutableName() {
|
|
36
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
37
|
+
if (platform === "win32" && arch === "x64") {
|
|
38
|
+
return `fmode-win-x64${ext}`;
|
|
39
|
+
} else if (platform === "win32" && arch === "arm64") {
|
|
40
|
+
return `fmode-win-arm64${ext}`;
|
|
41
|
+
} else if (platform === "linux" && arch === "x64") {
|
|
42
|
+
return "fmode-linux-x64";
|
|
43
|
+
} else if (platform === "linux" && arch === "arm64") {
|
|
44
|
+
return "fmode-linux-arm64";
|
|
45
|
+
} else if (platform === "darwin" && arch === "x64") {
|
|
46
|
+
return "fmode-macos-x64";
|
|
47
|
+
} else if (platform === "darwin" && arch === "arm64") {
|
|
48
|
+
return "fmode-macos-arm64";
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function compareVersions(a, b) {
|
|
54
|
+
const partsA = a.split(".").map(Number);
|
|
55
|
+
const partsB = b.split(".").map(Number);
|
|
56
|
+
for (let i = 0;i < Math.max(partsA.length, partsB.length); i++) {
|
|
57
|
+
const partA = partsA[i] || 0;
|
|
58
|
+
const partB = partsB[i] || 0;
|
|
59
|
+
if (partA !== partB) {
|
|
60
|
+
return partA - partB;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
async function downloadBinary(url, executableName, cacheDir) {
|
|
66
|
+
const tempPath = join(cacheDir, `${executableName}.tmp`);
|
|
67
|
+
const finalPath = join(cacheDir, executableName);
|
|
68
|
+
console.log(`Downloading ${url}...`);
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(url);
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(`HTTP ${response.status}`);
|
|
73
|
+
}
|
|
74
|
+
if (!existsSync(cacheDir)) {
|
|
75
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
const fileStream = createWriteStream(tempPath);
|
|
78
|
+
await pipeline(response.body, fileStream);
|
|
79
|
+
if (platform !== "win32") {
|
|
80
|
+
chmodSync(tempPath, 493);
|
|
81
|
+
}
|
|
82
|
+
if (existsSync(finalPath)) {
|
|
83
|
+
await unlink(finalPath);
|
|
84
|
+
}
|
|
85
|
+
renameSync(tempPath, finalPath);
|
|
86
|
+
console.log(`Downloaded to ${finalPath}`);
|
|
87
|
+
return finalPath;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (existsSync(tempPath)) {
|
|
90
|
+
await unlink(tempPath).catch(() => {});
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function getBinaryVersion(executablePath) {
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
let output = "";
|
|
98
|
+
let timedOut = false;
|
|
99
|
+
const timeout = setTimeout(() => {
|
|
100
|
+
timedOut = true;
|
|
101
|
+
child.kill();
|
|
102
|
+
resolve(null);
|
|
103
|
+
}, 3000);
|
|
104
|
+
const child = spawn(executablePath, ["-vvv"], {
|
|
105
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
106
|
+
env: { ...process.env }
|
|
107
|
+
});
|
|
108
|
+
child.stdout.on("data", (data) => {
|
|
109
|
+
output += data.toString();
|
|
110
|
+
});
|
|
111
|
+
child.on("exit", (code) => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
if (timedOut)
|
|
114
|
+
return;
|
|
115
|
+
const match = output.match(/version\s+(\d+\.\d+\.\d+)/);
|
|
116
|
+
if (match) {
|
|
117
|
+
resolve(match[1]);
|
|
118
|
+
} else {
|
|
119
|
+
resolve(null);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
child.on("error", () => {
|
|
123
|
+
clearTimeout(timeout);
|
|
124
|
+
resolve(null);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async function isProcessRunning(pid) {
|
|
129
|
+
try {
|
|
130
|
+
if (platform === "win32") {
|
|
131
|
+
const { stdout } = await execAsync(`tasklist /FI "PID eq ${pid}" /FO CSV`);
|
|
132
|
+
return stdout.includes(String(pid));
|
|
133
|
+
} else {
|
|
134
|
+
process.kill(pid, 0);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function killProcess(pid) {
|
|
142
|
+
try {
|
|
143
|
+
if (platform === "win32") {
|
|
144
|
+
await execAsync(`taskkill /F /PID ${pid}`);
|
|
145
|
+
return true;
|
|
146
|
+
} else {
|
|
147
|
+
process.kill(pid);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function findProcessByPort(port) {
|
|
155
|
+
try {
|
|
156
|
+
let command;
|
|
157
|
+
if (platform === "win32") {
|
|
158
|
+
command = `netstat -ano | findstr :${port} | findstr LISTENING`;
|
|
159
|
+
} else {
|
|
160
|
+
command = `lsof -ti:${port}`;
|
|
161
|
+
}
|
|
162
|
+
const { stdout } = await execAsync(command);
|
|
163
|
+
if (platform === "win32") {
|
|
164
|
+
const lines = stdout.trim().split(`
|
|
165
|
+
`);
|
|
166
|
+
for (const line of lines) {
|
|
167
|
+
const parts = line.trim().split(/\s+/);
|
|
168
|
+
const pid = parseInt(parts[parts.length - 1], 10);
|
|
169
|
+
if (!isNaN(pid)) {
|
|
170
|
+
return pid;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
const pid = parseInt(stdout.trim(), 10);
|
|
175
|
+
if (!isNaN(pid)) {
|
|
176
|
+
return pid;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
} catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function findFmodeProcesses() {
|
|
185
|
+
try {
|
|
186
|
+
const command = platform === "win32" ? 'tasklist /FI "IMAGENAME eq fmode*" /FO CSV' : 'ps aux | grep -E "fmode|fmode-win|fmode-linux|fmode-macos" | grep -v grep';
|
|
187
|
+
const { stdout } = await execAsync(command);
|
|
188
|
+
return stdout;
|
|
189
|
+
} catch {
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
var execAsync, platform, arch;
|
|
194
|
+
var init_utils = __esm(() => {
|
|
195
|
+
execAsync = promisify(exec);
|
|
196
|
+
platform = process.platform;
|
|
197
|
+
arch = process.arch;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// cli/shared/repos.ts
|
|
201
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
202
|
+
import { join as join2 } from "path";
|
|
203
|
+
function getReposConfig(packageName) {
|
|
204
|
+
const baseUrl = `https://repos.fmode.cn/x/${packageName}`;
|
|
205
|
+
return {
|
|
206
|
+
baseUrl,
|
|
207
|
+
manifestUrl: `${baseUrl}/manifest.json`
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async function fetchRemoteManifest(packageName) {
|
|
211
|
+
const config = getReposConfig(packageName);
|
|
212
|
+
try {
|
|
213
|
+
const response = await fetch(config.manifestUrl);
|
|
214
|
+
if (!response.ok) {
|
|
215
|
+
throw new Error(`HTTP ${response.status}`);
|
|
216
|
+
}
|
|
217
|
+
return await response.json();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.warn(`Warning: Could not fetch remote manifest: ${error.message}`);
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function getLocalVersion(rootDir) {
|
|
224
|
+
const packageJson = join2(rootDir, "package.json");
|
|
225
|
+
try {
|
|
226
|
+
const content = await readFile2(packageJson, "utf-8");
|
|
227
|
+
const pkg = JSON.parse(content);
|
|
228
|
+
return pkg.version || null;
|
|
229
|
+
} catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
var init_repos = () => {};
|
|
234
|
+
|
|
235
|
+
// cli/server/start.ts
|
|
236
|
+
var exports_start = {};
|
|
237
|
+
__export(exports_start, {
|
|
238
|
+
run: () => run6
|
|
239
|
+
});
|
|
240
|
+
import { spawn as spawn4 } from "child_process";
|
|
241
|
+
import { join as join8, resolve } from "path";
|
|
242
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, chmodSync as chmodSync2, renameSync as renameSync2 } from "fs";
|
|
243
|
+
import { createWriteStream as createWriteStream2 } from "fs";
|
|
244
|
+
import { pipeline as pipeline2 } from "stream/promises";
|
|
245
|
+
import { unlink as unlink4 } from "fs/promises";
|
|
246
|
+
function getServerExecutableName() {
|
|
247
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
248
|
+
if (platform === "win32") {
|
|
249
|
+
return arch2 === "arm64" ? `fmode-server-win-arm64${ext}` : `fmode-server-win-x64${ext}`;
|
|
250
|
+
} else if (platform === "linux") {
|
|
251
|
+
return arch2 === "arm64" ? "fmode-server-linux-arm64" : "fmode-server-linux-x64";
|
|
252
|
+
} else if (platform === "darwin") {
|
|
253
|
+
return arch2 === "arm64" ? "fmode-server-macos-arm64" : "fmode-server-macos-x64";
|
|
254
|
+
}
|
|
255
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
256
|
+
}
|
|
257
|
+
function getCachedServerPath() {
|
|
258
|
+
const executableName = getServerExecutableName();
|
|
259
|
+
const cachedPath = join8(SERVER_CACHE_DIR, executableName);
|
|
260
|
+
return existsSync6(cachedPath) ? cachedPath : null;
|
|
261
|
+
}
|
|
262
|
+
async function downloadServerBinary(version) {
|
|
263
|
+
const executableName = getServerExecutableName();
|
|
264
|
+
const url = `${REPOS_BASE_URL3}/${version}/${executableName}`;
|
|
265
|
+
const tempPath = join8(SERVER_CACHE_DIR, `${executableName}.tmp`);
|
|
266
|
+
const finalPath = join8(SERVER_CACHE_DIR, executableName);
|
|
267
|
+
console.log(`Downloading fmode-server ${version}...`);
|
|
268
|
+
console.log(`URL: ${url}`);
|
|
269
|
+
try {
|
|
270
|
+
const response = await fetch(url);
|
|
271
|
+
if (!response.ok) {
|
|
272
|
+
throw new Error(`HTTP ${response.status}`);
|
|
273
|
+
}
|
|
274
|
+
if (!existsSync6(SERVER_CACHE_DIR)) {
|
|
275
|
+
mkdirSync3(SERVER_CACHE_DIR, { recursive: true });
|
|
276
|
+
}
|
|
277
|
+
const fileStream = createWriteStream2(tempPath);
|
|
278
|
+
await pipeline2(response.body, fileStream);
|
|
279
|
+
if (platform !== "win32") {
|
|
280
|
+
chmodSync2(tempPath, 493);
|
|
281
|
+
}
|
|
282
|
+
if (existsSync6(finalPath)) {
|
|
283
|
+
await unlink4(finalPath);
|
|
284
|
+
}
|
|
285
|
+
renameSync2(tempPath, finalPath);
|
|
286
|
+
console.log(`Downloaded to ${finalPath}`);
|
|
287
|
+
return finalPath;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (existsSync6(tempPath)) {
|
|
290
|
+
await unlink4(tempPath).catch(() => {});
|
|
291
|
+
}
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function getConfigPath(args) {
|
|
296
|
+
const configIndex = args.indexOf("--config");
|
|
297
|
+
if (configIndex !== -1 && args[configIndex + 1]) {
|
|
298
|
+
const configPath = args[configIndex + 1];
|
|
299
|
+
return resolve(process.cwd(), configPath);
|
|
300
|
+
}
|
|
301
|
+
return resolve(process.cwd(), "config.json");
|
|
302
|
+
}
|
|
303
|
+
async function run6(args) {
|
|
304
|
+
const configPath = getConfigPath(args);
|
|
305
|
+
if (!existsSync6(configPath)) {
|
|
306
|
+
console.error(`Config file not found: ${configPath}`);
|
|
307
|
+
console.error("Please create a config.json file or specify --config <path>");
|
|
308
|
+
console.error("");
|
|
309
|
+
console.error("Example config.json:");
|
|
310
|
+
console.error(JSON.stringify({
|
|
311
|
+
parse: {
|
|
312
|
+
appId: "your-app-id",
|
|
313
|
+
masterKey: "your-master-key",
|
|
314
|
+
databaseURI: "postgresql://localhost/dev",
|
|
315
|
+
serverURL: "http://localhost:3000/parse",
|
|
316
|
+
port: 3000,
|
|
317
|
+
allowClientClassCreation: true,
|
|
318
|
+
enableAnonymousUsers: true,
|
|
319
|
+
allowExpiredAuthDataToken: false,
|
|
320
|
+
revokeSessionOnPasswordReset: false,
|
|
321
|
+
cloud: "./cloud/main.js"
|
|
322
|
+
}
|
|
323
|
+
}, null, 2));
|
|
324
|
+
process.exit(1);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
console.log(`Using config: ${configPath}`);
|
|
328
|
+
let serverPath = getCachedServerPath();
|
|
329
|
+
if (!serverPath) {
|
|
330
|
+
console.log("fmode-server not found. Downloading...");
|
|
331
|
+
const manifest = await fetchRemoteManifest("fmode-server");
|
|
332
|
+
if (!manifest || !manifest.version) {
|
|
333
|
+
console.error("Could not fetch fmode-server version information.");
|
|
334
|
+
console.error("Please check your internet connection.");
|
|
335
|
+
process.exit(1);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
serverPath = await downloadServerBinary(manifest.version);
|
|
339
|
+
}
|
|
340
|
+
console.log(`Starting fmode-server...`);
|
|
341
|
+
const serverArgs = ["--config", configPath, ...args.filter((a) => a !== "--config" && !args[args.indexOf("--config") + 1]?.startsWith("./"))];
|
|
342
|
+
spawn4(serverPath, serverArgs, {
|
|
343
|
+
stdio: "inherit",
|
|
344
|
+
env: {
|
|
345
|
+
...process.env,
|
|
346
|
+
FORCE_COLOR: process.env.FORCE_COLOR || "1",
|
|
347
|
+
TERM: process.env.TERM || "xterm-256color"
|
|
348
|
+
}
|
|
349
|
+
}).on("exit", (code) => {
|
|
350
|
+
process.exit(code ?? 0);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
var REPOS_BASE_URL3 = "https://repos.fmode.cn/x/fmode-server", arch2;
|
|
354
|
+
var init_start = __esm(() => {
|
|
355
|
+
init_utils();
|
|
356
|
+
init_repos();
|
|
357
|
+
init_server();
|
|
358
|
+
arch2 = process.arch;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// cli/server/update.ts
|
|
362
|
+
var exports_update = {};
|
|
363
|
+
__export(exports_update, {
|
|
364
|
+
run: () => run7
|
|
365
|
+
});
|
|
366
|
+
import { join as join9 } from "path";
|
|
367
|
+
import { existsSync as existsSync7, renameSync as renameSync3, unlinkSync, mkdirSync as mkdirSync4 } from "fs";
|
|
368
|
+
import { exec as exec3 } from "child_process";
|
|
369
|
+
import { promisify as promisify2 } from "util";
|
|
370
|
+
import { createWriteStream as createWriteStream3 } from "fs";
|
|
371
|
+
import { pipeline as pipeline3 } from "stream/promises";
|
|
372
|
+
function getServerExecutableName2() {
|
|
373
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
374
|
+
if (platform === "win32") {
|
|
375
|
+
return process.arch === "arm64" ? `fmode-server-win-arm64${ext}` : `fmode-server-win-x64${ext}`;
|
|
376
|
+
} else if (platform === "linux") {
|
|
377
|
+
return process.arch === "arm64" ? "fmode-server-linux-arm64" : "fmode-server-linux-x64";
|
|
378
|
+
} else if (platform === "darwin") {
|
|
379
|
+
return process.arch === "arm64" ? "fmode-server-macos-arm64" : "fmode-server-macos-x64";
|
|
380
|
+
}
|
|
381
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
382
|
+
}
|
|
383
|
+
function getCachedServerPath2() {
|
|
384
|
+
const executableName = getServerExecutableName2();
|
|
385
|
+
const cachedPath = join9(SERVER_CACHE_DIR, executableName);
|
|
386
|
+
return existsSync7(cachedPath) ? cachedPath : null;
|
|
387
|
+
}
|
|
388
|
+
async function getLocalServerVersion(executablePath) {
|
|
389
|
+
return new Promise((resolve2) => {
|
|
390
|
+
let output = "";
|
|
391
|
+
let timedOut = false;
|
|
392
|
+
const timeout = setTimeout(() => {
|
|
393
|
+
timedOut = true;
|
|
394
|
+
child.kill();
|
|
395
|
+
resolve2(null);
|
|
396
|
+
}, 5000);
|
|
397
|
+
const child = exec3(`"${executablePath}" --version`, {
|
|
398
|
+
env: { ...process.env }
|
|
399
|
+
});
|
|
400
|
+
child.stdout?.on("data", (data) => {
|
|
401
|
+
output += data.toString();
|
|
402
|
+
});
|
|
403
|
+
child.on("exit", (code) => {
|
|
404
|
+
clearTimeout(timeout);
|
|
405
|
+
if (timedOut)
|
|
406
|
+
return;
|
|
407
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
408
|
+
if (match) {
|
|
409
|
+
resolve2(match[1]);
|
|
410
|
+
} else {
|
|
411
|
+
resolve2(null);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
child.on("error", () => {
|
|
415
|
+
clearTimeout(timeout);
|
|
416
|
+
resolve2(null);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
async function downloadServerBinary2(version) {
|
|
421
|
+
const executableName = getServerExecutableName2();
|
|
422
|
+
const url = `${REPOS_BASE_URL4}/${version}/${executableName}`;
|
|
423
|
+
const tempPath = join9(SERVER_CACHE_DIR, `${executableName}.tmp`);
|
|
424
|
+
const finalPath = join9(SERVER_CACHE_DIR, executableName);
|
|
425
|
+
console.log(`Downloading fmode-server ${version}...`);
|
|
426
|
+
try {
|
|
427
|
+
const response = await fetch(url);
|
|
428
|
+
if (!response.ok) {
|
|
429
|
+
throw new Error(`HTTP ${response.status}`);
|
|
430
|
+
}
|
|
431
|
+
if (!existsSync7(SERVER_CACHE_DIR)) {
|
|
432
|
+
mkdirSync4(SERVER_CACHE_DIR, { recursive: true });
|
|
433
|
+
}
|
|
434
|
+
const fileStream = createWriteStream3(tempPath);
|
|
435
|
+
await pipeline3(response.body, fileStream);
|
|
436
|
+
if (existsSync7(finalPath)) {
|
|
437
|
+
unlinkSync(finalPath);
|
|
438
|
+
}
|
|
439
|
+
renameSync3(tempPath, finalPath);
|
|
440
|
+
console.log(`Downloaded to ${finalPath}`);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
if (existsSync7(tempPath)) {
|
|
443
|
+
unlinkSync(tempPath);
|
|
444
|
+
}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async function run7(args) {
|
|
449
|
+
console.log(`Checking for fmode-server updates...
|
|
450
|
+
`);
|
|
451
|
+
const manifest = await fetchRemoteManifest("fmode-server");
|
|
452
|
+
if (!manifest || !manifest.version) {
|
|
453
|
+
console.log("Could not fetch remote version.");
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const remoteVersion = manifest.version;
|
|
457
|
+
console.log(`Remote version: ${remoteVersion}`);
|
|
458
|
+
const serverPath = getCachedServerPath2();
|
|
459
|
+
if (!serverPath) {
|
|
460
|
+
console.log("No fmode-server binary found. Downloading...");
|
|
461
|
+
await downloadServerBinary2(remoteVersion);
|
|
462
|
+
console.log(`
|
|
463
|
+
Update complete!`);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
console.log(`Local binary: ${serverPath}`);
|
|
467
|
+
console.log("Checking local binary version...");
|
|
468
|
+
const localVersion = await getLocalServerVersion(serverPath);
|
|
469
|
+
if (localVersion) {
|
|
470
|
+
console.log(`Local version: ${localVersion}`);
|
|
471
|
+
const comparison = compareVersions(remoteVersion, localVersion);
|
|
472
|
+
if (comparison > 0) {
|
|
473
|
+
console.log(`Update available: ${localVersion} -> ${remoteVersion}`);
|
|
474
|
+
console.log("Downloading...");
|
|
475
|
+
await downloadServerBinary2(remoteVersion);
|
|
476
|
+
console.log(`
|
|
477
|
+
Update complete!`);
|
|
478
|
+
} else if (comparison === 0) {
|
|
479
|
+
console.log("Already up to date.");
|
|
480
|
+
} else {
|
|
481
|
+
console.log("Local version is newer.");
|
|
482
|
+
}
|
|
483
|
+
} else {
|
|
484
|
+
console.log("Could not determine local version");
|
|
485
|
+
console.log("Downloading latest version...");
|
|
486
|
+
await downloadServerBinary2(remoteVersion);
|
|
487
|
+
console.log(`
|
|
488
|
+
Update complete!`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
var execAsync2, REPOS_BASE_URL4 = "https://repos.fmode.cn/x/fmode-server";
|
|
492
|
+
var init_update = __esm(() => {
|
|
493
|
+
init_utils();
|
|
494
|
+
init_repos();
|
|
495
|
+
init_server();
|
|
496
|
+
execAsync2 = promisify2(exec3);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// cli/server/version.ts
|
|
500
|
+
var exports_version = {};
|
|
501
|
+
__export(exports_version, {
|
|
502
|
+
run: () => run8
|
|
503
|
+
});
|
|
504
|
+
import { join as join10 } from "path";
|
|
505
|
+
import { existsSync as existsSync8 } from "fs";
|
|
506
|
+
function getServerExecutableName3() {
|
|
507
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
508
|
+
if (platform === "win32") {
|
|
509
|
+
return process.arch === "arm64" ? `fmode-server-win-arm64${ext}` : `fmode-server-win-x64${ext}`;
|
|
510
|
+
} else if (platform === "linux") {
|
|
511
|
+
return process.arch === "arm64" ? "fmode-server-linux-arm64" : "fmode-server-linux-x64";
|
|
512
|
+
} else if (platform === "darwin") {
|
|
513
|
+
return process.arch === "arm64" ? "fmode-server-macos-arm64" : "fmode-server-macos-x64";
|
|
514
|
+
}
|
|
515
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
516
|
+
}
|
|
517
|
+
function getCachedServerPath3() {
|
|
518
|
+
const executableName = getServerExecutableName3();
|
|
519
|
+
const cachedPath = join10(SERVER_CACHE_DIR, executableName);
|
|
520
|
+
return existsSync8(cachedPath) ? cachedPath : null;
|
|
521
|
+
}
|
|
522
|
+
async function getLocalServerVersion2(executablePath) {
|
|
523
|
+
return new Promise((resolve2) => {
|
|
524
|
+
let output = "";
|
|
525
|
+
let timedOut = false;
|
|
526
|
+
const { exec: exec4 } = __require("child_process");
|
|
527
|
+
const timeout = setTimeout(() => {
|
|
528
|
+
timedOut = true;
|
|
529
|
+
child.kill();
|
|
530
|
+
resolve2(null);
|
|
531
|
+
}, 5000);
|
|
532
|
+
const child = exec4(`"${executablePath}" --version`, {
|
|
533
|
+
env: { ...process.env }
|
|
534
|
+
});
|
|
535
|
+
child.stdout?.on("data", (data) => {
|
|
536
|
+
output += data.toString();
|
|
537
|
+
});
|
|
538
|
+
child.on("exit", (code) => {
|
|
539
|
+
clearTimeout(timeout);
|
|
540
|
+
if (timedOut)
|
|
541
|
+
return;
|
|
542
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
543
|
+
if (match) {
|
|
544
|
+
resolve2(match[1]);
|
|
545
|
+
} else {
|
|
546
|
+
resolve2(null);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
child.on("error", () => {
|
|
550
|
+
clearTimeout(timeout);
|
|
551
|
+
resolve2(null);
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
async function run8(args) {
|
|
556
|
+
const rootDir6 = join10(import.meta.dir, "..", "..");
|
|
557
|
+
const cliVersion = await getLocalVersion(rootDir6);
|
|
558
|
+
console.log("Fmode CLI Version Information:");
|
|
559
|
+
console.log("==============================");
|
|
560
|
+
console.log(`CLI Version: ${cliVersion || "unknown"}`);
|
|
561
|
+
const serverPath = getCachedServerPath3();
|
|
562
|
+
if (serverPath) {
|
|
563
|
+
console.log(`Server Binary: ${serverPath}`);
|
|
564
|
+
const serverVersion = await getLocalServerVersion2(serverPath);
|
|
565
|
+
console.log(`Server Version: ${serverVersion || "unknown"}`);
|
|
566
|
+
} else {
|
|
567
|
+
console.log('Server Binary: Not installed (run "fmode server update" to install)');
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
var init_version = __esm(() => {
|
|
571
|
+
init_utils();
|
|
572
|
+
init_repos();
|
|
573
|
+
init_server();
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// cli/server/index.ts
|
|
577
|
+
import { join as join11, dirname as dirname6 } from "path";
|
|
578
|
+
import { homedir as homedir3 } from "os";
|
|
579
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
580
|
+
function showHelp() {
|
|
581
|
+
console.log(`
|
|
582
|
+
Fmode-Server Management Commands
|
|
583
|
+
|
|
584
|
+
USAGE:
|
|
585
|
+
fmode server <COMMAND> [OPTIONS]
|
|
586
|
+
|
|
587
|
+
COMMANDS:
|
|
588
|
+
start Start fmode-server with config.json
|
|
589
|
+
update Update fmode-server binary
|
|
590
|
+
version Show CLI and fmode-server version information
|
|
591
|
+
|
|
592
|
+
OPTIONS:
|
|
593
|
+
--config <path> Path to config.json (default: ./config.json)
|
|
594
|
+
|
|
595
|
+
EXAMPLES:
|
|
596
|
+
fmode server start # Start with ./config.json
|
|
597
|
+
fmode server start --config ./server-config.json
|
|
598
|
+
fmode server update # Check and update fmode-server
|
|
599
|
+
fmode server version # Show version information
|
|
600
|
+
`);
|
|
601
|
+
}
|
|
602
|
+
async function run9(args) {
|
|
603
|
+
const subcommand = args[0];
|
|
604
|
+
switch (subcommand) {
|
|
605
|
+
case "start": {
|
|
606
|
+
const { run: run10 } = await Promise.resolve().then(() => (init_start(), exports_start));
|
|
607
|
+
await run10(args.slice(1));
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
case "update": {
|
|
611
|
+
const { run: run10 } = await Promise.resolve().then(() => (init_update(), exports_update));
|
|
612
|
+
await run10(args.slice(1));
|
|
613
|
+
process.exit(0);
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
case "-v":
|
|
617
|
+
case "-vvv":
|
|
618
|
+
case "-version":
|
|
619
|
+
case "--version":
|
|
620
|
+
case "version": {
|
|
621
|
+
const { run: run10 } = await Promise.resolve().then(() => (init_version(), exports_version));
|
|
622
|
+
await run10(args.slice(1));
|
|
623
|
+
process.exit(0);
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
case "--help":
|
|
627
|
+
case "-h":
|
|
628
|
+
case undefined:
|
|
629
|
+
showHelp();
|
|
630
|
+
break;
|
|
631
|
+
default:
|
|
632
|
+
console.error(`Unknown server command: ${subcommand}`);
|
|
633
|
+
console.log('Run "fmode server --help" for usage.');
|
|
634
|
+
process.exit(1);
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
var __filename7, __dirname7, SERVER_CACHE_DIR;
|
|
639
|
+
var init_server = __esm(() => {
|
|
640
|
+
__filename7 = fileURLToPath6(import.meta.url);
|
|
641
|
+
__dirname7 = dirname6(__filename7);
|
|
642
|
+
SERVER_CACHE_DIR = join11(homedir3(), ".fmode", "server", "bin");
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// cli/index.ts
|
|
646
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
647
|
+
import { dirname as dirname7 } from "path";
|
|
648
|
+
|
|
649
|
+
// cli/start/index.ts
|
|
650
|
+
init_utils();
|
|
651
|
+
init_repos();
|
|
652
|
+
import { spawn as spawn2 } from "child_process";
|
|
653
|
+
import { join as join3, dirname } from "path";
|
|
654
|
+
import { homedir } from "os";
|
|
655
|
+
import { existsSync as existsSync2 } from "fs";
|
|
656
|
+
import { fileURLToPath } from "url";
|
|
657
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
658
|
+
var __dirname2 = dirname(__filename2);
|
|
659
|
+
var rootDir = dirname(dirname(__dirname2));
|
|
660
|
+
var REPOS_BASE_URL = "https://repos.fmode.cn/x/fmode-studio";
|
|
661
|
+
var CACHE_DIR = join3(homedir(), ".fmode", "studio", "bin");
|
|
662
|
+
async function getExecutablePath() {
|
|
663
|
+
const executableName = getExecutableName();
|
|
664
|
+
const cachedPath = join3(CACHE_DIR, executableName);
|
|
665
|
+
if (existsSync2(cachedPath)) {
|
|
666
|
+
return cachedPath;
|
|
667
|
+
}
|
|
668
|
+
const bundledPath = join3(rootDir, "dist", "bin", executableName);
|
|
669
|
+
if (existsSync2(bundledPath)) {
|
|
670
|
+
return bundledPath;
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
async function downloadBinary2(version, executableName) {
|
|
675
|
+
const url = `${REPOS_BASE_URL}/${version}/${executableName}`;
|
|
676
|
+
return downloadBinary(url, executableName, CACHE_DIR);
|
|
677
|
+
}
|
|
678
|
+
async function run(args) {
|
|
679
|
+
const executablePath = await getExecutablePath();
|
|
680
|
+
let finalExecutablePath = executablePath;
|
|
681
|
+
if (!finalExecutablePath) {
|
|
682
|
+
console.log("No local binary found. Downloading...");
|
|
683
|
+
const manifest = await fetchRemoteManifest("fmode-studio");
|
|
684
|
+
if (manifest && manifest.version) {
|
|
685
|
+
finalExecutablePath = await downloadBinary2(manifest.version, getExecutableName());
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (!finalExecutablePath) {
|
|
689
|
+
console.error('No executable found and could not download. Please run "fmode upgrade" first.');
|
|
690
|
+
process.exit(1);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
spawn2(finalExecutablePath, args, {
|
|
694
|
+
stdio: "inherit",
|
|
695
|
+
env: {
|
|
696
|
+
...process.env,
|
|
697
|
+
FORCE_COLOR: process.env.FORCE_COLOR || "1",
|
|
698
|
+
TERM: process.env.TERM || "xterm-256color"
|
|
699
|
+
}
|
|
700
|
+
}).on("exit", (code) => {
|
|
701
|
+
process.exit(code ?? 0);
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// cli/stop/index.ts
|
|
706
|
+
init_utils();
|
|
707
|
+
import { unlink as unlink2, readFile as readFile3 } from "fs/promises";
|
|
708
|
+
import { join as join4, dirname as dirname2 } from "path";
|
|
709
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
710
|
+
var __filename3 = fileURLToPath2(import.meta.url);
|
|
711
|
+
var __dirname3 = dirname2(__filename3);
|
|
712
|
+
var rootDir2 = dirname2(dirname2(__dirname3));
|
|
713
|
+
var PID_DIR = join4(rootDir2, ".cache", "pids");
|
|
714
|
+
var PID_FILE = join4(PID_DIR, "fmode.pid");
|
|
715
|
+
function getPortFromArgs(args) {
|
|
716
|
+
const portIndex = args.indexOf("-p");
|
|
717
|
+
if (portIndex !== -1 && args[portIndex + 1]) {
|
|
718
|
+
return parseInt(args[portIndex + 1], 10);
|
|
719
|
+
}
|
|
720
|
+
const portIndex2 = args.indexOf("--port");
|
|
721
|
+
if (portIndex2 !== -1 && args[portIndex2 + 1]) {
|
|
722
|
+
return parseInt(args[portIndex2 + 1], 10);
|
|
723
|
+
}
|
|
724
|
+
return 16666;
|
|
725
|
+
}
|
|
726
|
+
async function readPidWithPort() {
|
|
727
|
+
try {
|
|
728
|
+
const content = await readFile3(PID_FILE, "utf-8");
|
|
729
|
+
const parts = content.trim().split(":");
|
|
730
|
+
if (parts.length === 2) {
|
|
731
|
+
return { pid: parseInt(parts[0], 10), port: parseInt(parts[1], 10) };
|
|
732
|
+
}
|
|
733
|
+
return { pid: parseInt(content.trim(), 10), port: null };
|
|
734
|
+
} catch {
|
|
735
|
+
return { pid: null, port: null };
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
async function run2(args) {
|
|
739
|
+
const port = getPortFromArgs(args);
|
|
740
|
+
const { pid: savedPid, port: savedPort } = await readPidWithPort();
|
|
741
|
+
const portPid = await findProcessByPort(port);
|
|
742
|
+
if (portPid && await isProcessRunning(portPid)) {
|
|
743
|
+
console.log(`Stopping Fmode Studio on port ${port} (PID: ${portPid})...`);
|
|
744
|
+
if (await killProcess(portPid)) {
|
|
745
|
+
console.log("Fmode Studio stopped.");
|
|
746
|
+
await unlink2(PID_FILE).catch(() => {});
|
|
747
|
+
return;
|
|
748
|
+
} else {
|
|
749
|
+
console.error("Failed to stop Fmode Studio.");
|
|
750
|
+
process.exit(1);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (savedPid && await isProcessRunning(savedPid)) {
|
|
755
|
+
console.log(`Stopping Fmode Studio (PID: ${savedPid})...`);
|
|
756
|
+
if (await killProcess(savedPid)) {
|
|
757
|
+
console.log("Fmode Studio stopped.");
|
|
758
|
+
await unlink2(PID_FILE).catch(() => {});
|
|
759
|
+
return;
|
|
760
|
+
} else {
|
|
761
|
+
console.error("Failed to stop Fmode Studio.");
|
|
762
|
+
process.exit(1);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (savedPid || portPid) {
|
|
767
|
+
console.log("Process not running. Cleaning up PID file...");
|
|
768
|
+
await unlink2(PID_FILE).catch(() => {});
|
|
769
|
+
}
|
|
770
|
+
console.log(`No process found on port ${port}. Searching for Fmode processes...`);
|
|
771
|
+
const processes = await findFmodeProcesses();
|
|
772
|
+
if (processes.trim()) {
|
|
773
|
+
console.log(`
|
|
774
|
+
Found Fmode processes:`);
|
|
775
|
+
console.log(processes);
|
|
776
|
+
console.log(`
|
|
777
|
+
Please manually kill the process or use:`);
|
|
778
|
+
console.log(platform === "win32" ? " taskkill /F /PID <pid>" : " kill -9 <pid>");
|
|
779
|
+
} else {
|
|
780
|
+
console.log("No Fmode Studio process found running.");
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// cli/log/index.ts
|
|
785
|
+
init_utils();
|
|
786
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
787
|
+
import { existsSync as existsSync3 } from "fs";
|
|
788
|
+
import { spawn as spawn3 } from "child_process";
|
|
789
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
790
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
791
|
+
var __filename4 = fileURLToPath3(import.meta.url);
|
|
792
|
+
var __dirname4 = dirname3(__filename4);
|
|
793
|
+
var rootDir3 = dirname3(dirname3(__dirname4));
|
|
794
|
+
var LOG_DIR = join5(rootDir3, ".cache", "logs");
|
|
795
|
+
var LOG_FILE = join5(LOG_DIR, "fmode.log");
|
|
796
|
+
async function showLogContent(numLines) {
|
|
797
|
+
const content = await readFile4(LOG_FILE, "utf-8");
|
|
798
|
+
const lines = content.split(`
|
|
799
|
+
`);
|
|
800
|
+
if (lines.length <= numLines) {
|
|
801
|
+
console.log(content);
|
|
802
|
+
} else {
|
|
803
|
+
console.log(lines.slice(-numLines).join(`
|
|
804
|
+
`));
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function run3(args) {
|
|
808
|
+
const followMode = args.includes("-f") || args.includes("--follow");
|
|
809
|
+
const linesIndex = args.indexOf("-n") !== -1 ? args.indexOf("-n") : args.indexOf("--lines");
|
|
810
|
+
const numLines = linesIndex !== -1 ? parseInt(args[linesIndex + 1] || "50", 10) : 50;
|
|
811
|
+
if (!existsSync3(LOG_FILE)) {
|
|
812
|
+
console.log("No log file found. Has the server been started?");
|
|
813
|
+
console.log(`Expected log file: ${LOG_FILE}`);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
if (followMode) {
|
|
817
|
+
console.log(`Following log file: ${LOG_FILE} (Ctrl+C to exit)`);
|
|
818
|
+
console.log(`${"=".repeat(60)}
|
|
819
|
+
`);
|
|
820
|
+
let tailCmd;
|
|
821
|
+
if (platform === "win32") {
|
|
822
|
+
tailCmd = spawn3("powershell", ["-Command", `Get-Content -Path "${LOG_FILE}" -Wait -Tail 50`], {
|
|
823
|
+
stdio: "inherit",
|
|
824
|
+
windowsHide: false
|
|
825
|
+
});
|
|
826
|
+
} else {
|
|
827
|
+
tailCmd = spawn3("tail", ["-n", String(numLines), "-f", LOG_FILE], {
|
|
828
|
+
stdio: "inherit"
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
tailCmd.on("error", (err) => {
|
|
832
|
+
console.log(`Follow mode not available: ${err.message}`);
|
|
833
|
+
console.log("Showing current log content:");
|
|
834
|
+
showLogContent(numLines);
|
|
835
|
+
});
|
|
836
|
+
await new Promise((resolve) => {
|
|
837
|
+
tailCmd.on("exit", resolve);
|
|
838
|
+
});
|
|
839
|
+
} else {
|
|
840
|
+
await showLogContent(numLines);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// cli/clear/index.ts
|
|
845
|
+
import { readdir, unlink as unlink3 } from "fs/promises";
|
|
846
|
+
import { existsSync as existsSync4 } from "fs";
|
|
847
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
848
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
849
|
+
var __filename5 = fileURLToPath4(import.meta.url);
|
|
850
|
+
var __dirname5 = dirname4(__filename5);
|
|
851
|
+
var rootDir4 = dirname4(dirname4(__dirname5));
|
|
852
|
+
var CACHE_DIR2 = join6(rootDir4, ".cache", "binaries");
|
|
853
|
+
async function run4(args) {
|
|
854
|
+
console.log("Clearing cached binaries...");
|
|
855
|
+
if (!existsSync4(CACHE_DIR2)) {
|
|
856
|
+
console.log("No cache directory found.");
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
try {
|
|
860
|
+
const files = await readdir(CACHE_DIR2);
|
|
861
|
+
let removedCount = 0;
|
|
862
|
+
for (const file of files) {
|
|
863
|
+
const filePath = join6(CACHE_DIR2, file);
|
|
864
|
+
try {
|
|
865
|
+
await unlink3(filePath);
|
|
866
|
+
console.log(` Removed: ${file}`);
|
|
867
|
+
removedCount++;
|
|
868
|
+
} catch (err) {
|
|
869
|
+
console.warn(` Skipped: ${file} (${err.message})`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
console.log(`
|
|
873
|
+
Cleared ${removedCount} file(s).`);
|
|
874
|
+
} catch (error) {
|
|
875
|
+
console.error(`Failed to clear cache: ${error.message}`);
|
|
876
|
+
process.exit(1);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// cli/upgrade/index.ts
|
|
881
|
+
init_utils();
|
|
882
|
+
init_repos();
|
|
883
|
+
import { join as join7, dirname as dirname5 } from "path";
|
|
884
|
+
import { homedir as homedir2 } from "os";
|
|
885
|
+
import { existsSync as existsSync5 } from "fs";
|
|
886
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
887
|
+
var __filename6 = fileURLToPath5(import.meta.url);
|
|
888
|
+
var __dirname6 = dirname5(__filename6);
|
|
889
|
+
var rootDir5 = dirname5(dirname5(__dirname6));
|
|
890
|
+
var REPOS_BASE_URL2 = "https://repos.fmode.cn/x/fmode-studio";
|
|
891
|
+
var CACHE_DIR3 = join7(homedir2(), ".fmode", "studio", "bin");
|
|
892
|
+
async function getExecutablePath2() {
|
|
893
|
+
const executableName = getExecutableName();
|
|
894
|
+
const cachedPath = join7(CACHE_DIR3, executableName);
|
|
895
|
+
if (existsSync5(cachedPath)) {
|
|
896
|
+
return cachedPath;
|
|
897
|
+
}
|
|
898
|
+
const bundledPath = join7(rootDir5, "dist", "bin", executableName);
|
|
899
|
+
if (existsSync5(bundledPath)) {
|
|
900
|
+
return bundledPath;
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
async function downloadBinary3(version, executableName) {
|
|
905
|
+
const url = `${REPOS_BASE_URL2}/${version}/${executableName}`;
|
|
906
|
+
return downloadBinary(url, executableName, CACHE_DIR3);
|
|
907
|
+
}
|
|
908
|
+
async function run5(args) {
|
|
909
|
+
console.log(`Checking for updates...
|
|
910
|
+
`);
|
|
911
|
+
const manifest = await fetchRemoteManifest("fmode-studio");
|
|
912
|
+
if (!manifest || !manifest.version) {
|
|
913
|
+
console.log("Could not fetch remote version.");
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const remoteVersion = manifest.version;
|
|
917
|
+
console.log(`Remote version: ${remoteVersion}`);
|
|
918
|
+
const executablePath = await getExecutablePath2();
|
|
919
|
+
if (!executablePath) {
|
|
920
|
+
console.log("No binary found. Downloading latest version...");
|
|
921
|
+
await downloadBinary3(remoteVersion, getExecutableName());
|
|
922
|
+
console.log(`
|
|
923
|
+
Upgrade complete!`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
console.log(`Local binary: ${executablePath}`);
|
|
927
|
+
console.log("Checking local binary version...");
|
|
928
|
+
const localVersion = await getBinaryVersion(executablePath);
|
|
929
|
+
if (localVersion) {
|
|
930
|
+
console.log(`Local version: ${localVersion}`);
|
|
931
|
+
const comparison = compareVersions(remoteVersion, localVersion);
|
|
932
|
+
if (comparison > 0) {
|
|
933
|
+
console.log(`Update available: ${localVersion} -> ${remoteVersion}`);
|
|
934
|
+
console.log("Downloading...");
|
|
935
|
+
await downloadBinary3(remoteVersion, getExecutableName());
|
|
936
|
+
console.log(`
|
|
937
|
+
Upgrade complete!`);
|
|
938
|
+
} else if (comparison === 0) {
|
|
939
|
+
console.log("Already up to date.");
|
|
940
|
+
} else {
|
|
941
|
+
console.log("Local version is newer.");
|
|
942
|
+
}
|
|
943
|
+
} else {
|
|
944
|
+
console.log("Could not determine local version (timeout or error)");
|
|
945
|
+
console.log("Downloading latest version to ensure compatibility...");
|
|
946
|
+
await downloadBinary3(remoteVersion, getExecutableName());
|
|
947
|
+
console.log(`
|
|
948
|
+
Upgrade complete!`);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// cli/index.ts
|
|
953
|
+
init_server();
|
|
954
|
+
init_repos();
|
|
955
|
+
var __filename8 = fileURLToPath7(import.meta.url);
|
|
956
|
+
var __dirname8 = dirname7(__filename8);
|
|
957
|
+
var rootDir6 = _pkgRoot;
|
|
958
|
+
async function showHelp2() {
|
|
959
|
+
const localVersion = await getLocalVersion(rootDir6);
|
|
960
|
+
console.log(`
|
|
961
|
+
Fmode Code UI Server${localVersion ? ` v${localVersion}` : ""}
|
|
962
|
+
|
|
963
|
+
USAGE:
|
|
964
|
+
fmode <COMMAND> [OPTIONS]
|
|
965
|
+
npx @fmode/studio <COMMAND> [OPTIONS]
|
|
966
|
+
|
|
967
|
+
COMMANDS:
|
|
968
|
+
start Start the Fmode Studio server (default)
|
|
969
|
+
stop Stop the running Fmode Studio server
|
|
970
|
+
log View server logs
|
|
971
|
+
clear Clear cached binary files
|
|
972
|
+
upgrade / update Upgrade to the latest version
|
|
973
|
+
|
|
974
|
+
server Manage fmode-server
|
|
975
|
+
server start Start fmode-server with config.json
|
|
976
|
+
server update Update fmode-server binary
|
|
977
|
+
server version Show CLI and fmode-server version information
|
|
978
|
+
|
|
979
|
+
OPTIONS:
|
|
980
|
+
-p, --port <PORT> Set the server port (default: 16666)
|
|
981
|
+
-h, --host <HOST> Set the server host (default: 0.0.0.0)
|
|
982
|
+
--help Show this help message
|
|
983
|
+
--version, -v Show version information
|
|
984
|
+
|
|
985
|
+
LOG OPTIONS:
|
|
986
|
+
-f, --follow Follow log output (like tail -f)
|
|
987
|
+
-n, --lines <NUM> Number of lines to show (default: 50)
|
|
988
|
+
|
|
989
|
+
EXAMPLES:
|
|
990
|
+
fmode # Start with default settings
|
|
991
|
+
fmode start # Explicitly start the server
|
|
992
|
+
fmode start --port 8080 # Start on port 8080
|
|
993
|
+
fmode --port 8080 # Start on port 8080 (same as above)
|
|
994
|
+
fmode stop # Stop the server
|
|
995
|
+
fmode stop --port 8080 # Stop server on port 8080
|
|
996
|
+
fmode log # Show recent logs
|
|
997
|
+
fmode log -f # Follow logs in real-time
|
|
998
|
+
fmode log -n 100 # Show last 100 log lines
|
|
999
|
+
fmode clear # Clear cached binaries
|
|
1000
|
+
fmode upgrade # Upgrade to latest version
|
|
1001
|
+
fmode server start # Start fmode-server
|
|
1002
|
+
fmode server update # Update fmode-server
|
|
1003
|
+
fmode server version # Show version information
|
|
1004
|
+
|
|
1005
|
+
UPGRADE MECHANISM:
|
|
1006
|
+
The CLI automatically downloads the latest binary from repos.fmode.cn.
|
|
1007
|
+
Use 'fmode upgrade' to manually upgrade.
|
|
1008
|
+
`);
|
|
1009
|
+
}
|
|
1010
|
+
async function showVersion() {
|
|
1011
|
+
const localVersion = await getLocalVersion(rootDir6);
|
|
1012
|
+
if (localVersion) {
|
|
1013
|
+
console.log(`@fmode/studio v${localVersion}`);
|
|
1014
|
+
} else {
|
|
1015
|
+
console.log("@fmode/studio (version unknown)");
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
async function runCli() {
|
|
1019
|
+
const args = process.argv.slice(2);
|
|
1020
|
+
const yesIndex = args.indexOf("-y");
|
|
1021
|
+
if (yesIndex !== -1) {
|
|
1022
|
+
args.splice(yesIndex, 1);
|
|
1023
|
+
}
|
|
1024
|
+
const command = args[0];
|
|
1025
|
+
const commandArgs = args.slice(1);
|
|
1026
|
+
if (args.includes("version") || args.includes("--version") || args.includes("-version") || args.includes("-v") || args.includes("-vvv")) {
|
|
1027
|
+
if (command !== "server") {
|
|
1028
|
+
await showVersion();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
if (args.includes("--help") || args.includes("-h") && !args.includes("--host")) {
|
|
1033
|
+
if (!command || command !== "server") {
|
|
1034
|
+
await showHelp2();
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
switch (command) {
|
|
1039
|
+
case "start":
|
|
1040
|
+
await run(commandArgs);
|
|
1041
|
+
break;
|
|
1042
|
+
case "stop":
|
|
1043
|
+
await run2(commandArgs);
|
|
1044
|
+
process.exit(0);
|
|
1045
|
+
break;
|
|
1046
|
+
case "log":
|
|
1047
|
+
await run3(commandArgs);
|
|
1048
|
+
process.exit(0);
|
|
1049
|
+
break;
|
|
1050
|
+
case "clear":
|
|
1051
|
+
await run4(commandArgs);
|
|
1052
|
+
process.exit(0);
|
|
1053
|
+
break;
|
|
1054
|
+
case "upgrade":
|
|
1055
|
+
case "update":
|
|
1056
|
+
await run5(commandArgs);
|
|
1057
|
+
process.exit(0);
|
|
1058
|
+
break;
|
|
1059
|
+
case "server":
|
|
1060
|
+
await run9(commandArgs);
|
|
1061
|
+
break;
|
|
1062
|
+
default:
|
|
1063
|
+
await run(args);
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
var isMainModule = process.argv[1] && import.meta.url === `file:///${process.argv[1].replace(/\\/g, "/")}`;
|
|
1068
|
+
if (isMainModule) {
|
|
1069
|
+
runCli().catch((error) => {
|
|
1070
|
+
console.error("Fatal error:", error);
|
|
1071
|
+
process.exit(1);
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
export {
|
|
1075
|
+
showVersion,
|
|
1076
|
+
showHelp2 as showHelp,
|
|
1077
|
+
runCli
|
|
1078
|
+
};
|