neoctl-web 0.1.0 → 0.1.2
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 +244 -238
- package/bin/neow.mjs +139 -123
- package/core-runtime.mjs +55 -55
- package/cpa-quota.mjs +209 -209
- package/dist/assets/{index-H7num-0s.js → index-B6NPbDJ9.js} +1 -1
- package/dist/assets/{index-BSFq6wfd.css → index-DG8NYKAP.css} +1 -1
- package/dist/favicon.svg +3 -3
- package/dist/index.html +2 -2
- package/memory-monitor.mjs +150 -150
- package/package.json +63 -62
- package/platform-paths.mjs +40 -0
- package/plugin-settings.mjs +67 -67
- package/plugins/downloads/downloads.mjs +147 -147
- package/plugins/downloads/index.mjs +19 -19
- package/plugins/downloads/neo-plugin.json +9 -9
- package/plugins/xhs-artifact/artifacts.mjs +388 -388
- package/plugins/xhs-artifact/editor-page.mjs +73 -73
- package/plugins/xhs-artifact/index.mjs +48 -48
- package/plugins/xhs-artifact/neo-plugin.json +9 -9
- package/plugins.mjs +111 -111
- package/runtime-router-cleanup.mjs +152 -152
- package/runtime-workspaces.mjs +515 -515
- package/server.mjs +447 -445
- package/tool-settings.mjs +80 -80
package/bin/neow.mjs
CHANGED
|
@@ -1,137 +1,153 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
3
|
import net from 'node:net';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
+
import { realpathSync } from 'node:fs';
|
|
5
6
|
import { readFile } from 'node:fs/promises';
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
|
|
9
|
-
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
-
const packageJson = JSON.parse(await readFile(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
11
|
-
|
|
12
|
-
export function parseArgs(argv) {
|
|
13
|
-
const options = {
|
|
14
|
-
host: '127.0.0.1',
|
|
15
|
-
port: 5173,
|
|
16
|
-
runtimeHost: '127.0.0.1',
|
|
17
|
-
runtimePort: 3101,
|
|
18
|
-
open: true,
|
|
19
|
-
};
|
|
20
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
21
|
-
const argument = argv[index];
|
|
22
|
-
if (argument === '-h' || argument === '--help') options.help = true;
|
|
23
|
-
else if (argument === '-v' || argument === '--version') options.version = true;
|
|
24
|
-
else if (argument === '--no-open') options.open = false;
|
|
25
|
-
else if (argument === '--host') options.host = readValue(argv, ++index, argument);
|
|
26
|
-
else if (argument === '--port' || argument === '-p') options.port = readPort(argv, ++index, argument);
|
|
27
|
-
else if (argument === '--runtime-port') options.runtimePort = readPort(argv, ++index, argument);
|
|
28
|
-
else throw new Error(`未知参数:${argument}`);
|
|
29
|
-
}
|
|
30
|
-
return options;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function findAvailablePort(startPort, host, excluded = new Set()) {
|
|
34
|
-
for (let port = startPort; port <= 65535; port += 1) {
|
|
35
|
-
if (excluded.has(port)) continue;
|
|
36
|
-
if (await canListen(port, host)) return port;
|
|
37
|
-
}
|
|
38
|
-
throw new Error(`从 ${startPort} 开始没有可用端口`);
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
|
+
const packageJson = JSON.parse(await readFile(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
12
|
+
|
|
13
|
+
export function parseArgs(argv) {
|
|
14
|
+
const options = {
|
|
15
|
+
host: '127.0.0.1',
|
|
16
|
+
port: 5173,
|
|
17
|
+
runtimeHost: '127.0.0.1',
|
|
18
|
+
runtimePort: 3101,
|
|
19
|
+
open: true,
|
|
20
|
+
};
|
|
21
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
22
|
+
const argument = argv[index];
|
|
23
|
+
if (argument === '-h' || argument === '--help') options.help = true;
|
|
24
|
+
else if (argument === '-v' || argument === '--version') options.version = true;
|
|
25
|
+
else if (argument === '--no-open') options.open = false;
|
|
26
|
+
else if (argument === '--host') options.host = readValue(argv, ++index, argument);
|
|
27
|
+
else if (argument === '--port' || argument === '-p') options.port = readPort(argv, ++index, argument);
|
|
28
|
+
else if (argument === '--runtime-port') options.runtimePort = readPort(argv, ++index, argument);
|
|
29
|
+
else throw new Error(`未知参数:${argument}`);
|
|
30
|
+
}
|
|
31
|
+
return options;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function findAvailablePort(startPort, host, excluded = new Set()) {
|
|
35
|
+
for (let port = startPort; port <= 65535; port += 1) {
|
|
36
|
+
if (excluded.has(port)) continue;
|
|
37
|
+
if (await canListen(port, host)) return port;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`从 ${startPort} 开始没有可用端口`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function helpText() {
|
|
43
|
+
return `neow ${packageJson.version}
|
|
44
|
+
|
|
45
|
+
启动 Neo Web、后台和核心运行时
|
|
46
|
+
|
|
47
|
+
用法:
|
|
48
|
+
neow [选项]
|
|
49
|
+
|
|
50
|
+
选项:
|
|
51
|
+
-h, --help 显示帮助
|
|
52
|
+
-v, --version 显示版本
|
|
53
|
+
-p, --port <端口> Web 起始端口,默认 5173
|
|
54
|
+
--runtime-port <端口> 核心起始端口,默认 3101
|
|
55
|
+
--host <地址> Web 监听地址,默认 127.0.0.1
|
|
56
|
+
--no-open 不自动打开浏览器
|
|
57
|
+
|
|
58
|
+
端口被占用时会从指定端口开始自动顺延。`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function main() {
|
|
62
|
+
let options;
|
|
63
|
+
try {
|
|
64
|
+
options = parseArgs(process.argv.slice(2));
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(error.message);
|
|
67
|
+
console.error('运行 neow --help 查看用法。');
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (options.help) return console.log(helpText());
|
|
72
|
+
if (options.version) return console.log(packageJson.version);
|
|
73
|
+
|
|
74
|
+
const runtimePort = await findAvailablePort(options.runtimePort, options.runtimeHost);
|
|
75
|
+
const webPort = await findAvailablePort(options.port, options.host, new Set([runtimePort]));
|
|
76
|
+
const browserHost = isWildcardHost(options.host) ? '127.0.0.1' : normalizeBrowserHost(options.host);
|
|
77
|
+
const url = `http://${browserHost}:${webPort}`;
|
|
78
|
+
|
|
79
|
+
process.env.APP_HOST = options.host;
|
|
80
|
+
process.env.APP_PORT = String(webPort);
|
|
81
|
+
process.env.NEO_RUNTIME_TARGET = `http://${options.runtimeHost}:${runtimePort}`;
|
|
82
|
+
process.env.NEO_EMBED_RUNTIME = 'true';
|
|
83
|
+
process.env.NEO_CORE_SOURCE = 'package';
|
|
84
|
+
|
|
85
|
+
if (runtimePort !== options.runtimePort) console.log(`核心端口 ${options.runtimePort} 已占用,使用 ${runtimePort}`);
|
|
86
|
+
if (webPort !== options.port) console.log(`Web 端口 ${options.port} 已占用,使用 ${webPort}`);
|
|
87
|
+
await import('../server.mjs');
|
|
88
|
+
console.log(`Neo Web 已启动:${url}`);
|
|
89
|
+
if (options.open) openBrowser(url);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function readValue(argv, index, flag) {
|
|
93
|
+
const value = argv[index];
|
|
94
|
+
if (!value || value.startsWith('-')) throw new Error(`${flag} 缺少值`);
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function readPort(argv, index, flag) {
|
|
99
|
+
const value = Number(readValue(argv, index, flag));
|
|
100
|
+
if (!Number.isInteger(value) || value < 1 || value > 65535) throw new Error(`${flag} 必须是 1-65535 的端口`);
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function canListen(port, host) {
|
|
105
|
+
return new Promise((resolve) => {
|
|
106
|
+
const probe = net.createServer();
|
|
107
|
+
probe.unref();
|
|
108
|
+
probe.once('error', () => resolve(false));
|
|
109
|
+
probe.listen({ port, host }, () => probe.close(() => resolve(true)));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isWildcardHost(host) {
|
|
114
|
+
return host === '0.0.0.0' || host === '::';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizeBrowserHost(host) {
|
|
118
|
+
return host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function openBrowser(url) {
|
|
122
|
+
const command = process.platform === 'win32'
|
|
123
|
+
? ['explorer.exe', [url]]
|
|
124
|
+
: process.platform === 'darwin'
|
|
125
|
+
? ['open', [url]]
|
|
126
|
+
: ['xdg-open', [url]];
|
|
127
|
+
const child = spawn(command[0], command[1], { detached: true, stdio: 'ignore', windowsHide: true });
|
|
128
|
+
child.once('error', () => {});
|
|
129
|
+
child.unref();
|
|
39
130
|
}
|
|
40
131
|
|
|
41
|
-
export function
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
启动 Neo Web、后台和核心运行时
|
|
45
|
-
|
|
46
|
-
用法:
|
|
47
|
-
neow [选项]
|
|
48
|
-
|
|
49
|
-
选项:
|
|
50
|
-
-h, --help 显示帮助
|
|
51
|
-
-v, --version 显示版本
|
|
52
|
-
-p, --port <端口> Web 起始端口,默认 5173
|
|
53
|
-
--runtime-port <端口> 核心起始端口,默认 3101
|
|
54
|
-
--host <地址> Web 监听地址,默认 127.0.0.1
|
|
55
|
-
--no-open 不自动打开浏览器
|
|
56
|
-
|
|
57
|
-
端口被占用时会从指定端口开始自动顺延。`;
|
|
132
|
+
export function isMainModule(invokedPath, moduleUrl) {
|
|
133
|
+
if (!invokedPath) return false;
|
|
134
|
+
return comparablePath(invokedPath) === comparablePath(fileURLToPath(moduleUrl));
|
|
58
135
|
}
|
|
59
136
|
|
|
60
|
-
|
|
61
|
-
|
|
137
|
+
function comparablePath(value) {
|
|
138
|
+
const absolutePath = path.resolve(value);
|
|
139
|
+
let resolvedPath = absolutePath;
|
|
62
140
|
try {
|
|
63
|
-
|
|
64
|
-
} catch
|
|
65
|
-
|
|
66
|
-
console.error('运行 neow --help 查看用法。');
|
|
67
|
-
process.exitCode = 1;
|
|
68
|
-
return;
|
|
141
|
+
resolvedPath = realpathSync.native(absolutePath);
|
|
142
|
+
} catch {
|
|
143
|
+
// Keep the absolute path so invalid invocations still fail in main().
|
|
69
144
|
}
|
|
70
|
-
|
|
71
|
-
if (options.version) return console.log(packageJson.version);
|
|
72
|
-
|
|
73
|
-
const runtimePort = await findAvailablePort(options.runtimePort, options.runtimeHost);
|
|
74
|
-
const webPort = await findAvailablePort(options.port, options.host, new Set([runtimePort]));
|
|
75
|
-
const browserHost = isWildcardHost(options.host) ? '127.0.0.1' : normalizeBrowserHost(options.host);
|
|
76
|
-
const url = `http://${browserHost}:${webPort}`;
|
|
77
|
-
|
|
78
|
-
process.env.APP_HOST = options.host;
|
|
79
|
-
process.env.APP_PORT = String(webPort);
|
|
80
|
-
process.env.NEO_RUNTIME_TARGET = `http://${options.runtimeHost}:${runtimePort}`;
|
|
81
|
-
process.env.NEO_EMBED_RUNTIME = 'true';
|
|
82
|
-
process.env.NEO_CORE_SOURCE = 'package';
|
|
83
|
-
|
|
84
|
-
if (runtimePort !== options.runtimePort) console.log(`核心端口 ${options.runtimePort} 已占用,使用 ${runtimePort}`);
|
|
85
|
-
if (webPort !== options.port) console.log(`Web 端口 ${options.port} 已占用,使用 ${webPort}`);
|
|
86
|
-
await import('../server.mjs');
|
|
87
|
-
console.log(`Neo Web 已启动:${url}`);
|
|
88
|
-
if (options.open) openBrowser(url);
|
|
145
|
+
return process.platform === 'win32' ? resolvedPath.toLowerCase() : resolvedPath;
|
|
89
146
|
}
|
|
90
147
|
|
|
91
|
-
|
|
92
|
-
const value = argv[index];
|
|
93
|
-
if (!value || value.startsWith('-')) throw new Error(`${flag} 缺少值`);
|
|
94
|
-
return value;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function readPort(argv, index, flag) {
|
|
98
|
-
const value = Number(readValue(argv, index, flag));
|
|
99
|
-
if (!Number.isInteger(value) || value < 1 || value > 65535) throw new Error(`${flag} 必须是 1-65535 的端口`);
|
|
100
|
-
return value;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function canListen(port, host) {
|
|
104
|
-
return new Promise((resolve) => {
|
|
105
|
-
const probe = net.createServer();
|
|
106
|
-
probe.unref();
|
|
107
|
-
probe.once('error', () => resolve(false));
|
|
108
|
-
probe.listen({ port, host }, () => probe.close(() => resolve(true)));
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function isWildcardHost(host) {
|
|
113
|
-
return host === '0.0.0.0' || host === '::';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function normalizeBrowserHost(host) {
|
|
117
|
-
return host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function openBrowser(url) {
|
|
121
|
-
const command = process.platform === 'win32'
|
|
122
|
-
? ['explorer.exe', [url]]
|
|
123
|
-
: process.platform === 'darwin'
|
|
124
|
-
? ['open', [url]]
|
|
125
|
-
: ['xdg-open', [url]];
|
|
126
|
-
const child = spawn(command[0], command[1], { detached: true, stdio: 'ignore', windowsHide: true });
|
|
127
|
-
child.once('error', () => {});
|
|
128
|
-
child.unref();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : '';
|
|
132
|
-
if (invokedPath === fileURLToPath(import.meta.url)) {
|
|
148
|
+
if (isMainModule(process.argv[1], import.meta.url)) {
|
|
133
149
|
main().catch((error) => {
|
|
134
|
-
console.error(`neow 启动失败:${error instanceof Error ? error.message : String(error)}`);
|
|
135
|
-
process.exitCode = 1;
|
|
136
|
-
});
|
|
137
|
-
}
|
|
150
|
+
console.error(`neow 启动失败:${error instanceof Error ? error.message : String(error)}`);
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
});
|
|
153
|
+
}
|
package/core-runtime.mjs
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const source = resolveCoreSource();
|
|
7
|
-
const localEngineRoot = path.resolve(process.env.NEO_LOCAL_ENGINE_ROOT || path.join(webRoot, '..', 'engine'));
|
|
8
|
-
const coreSpecifier = source === 'local'
|
|
9
|
-
? pathToFileURL(path.join(localEngineRoot, 'dist', 'index.js')).href
|
|
10
|
-
: 'neoctl';
|
|
11
|
-
const webSpecifier = source === 'local'
|
|
12
|
-
? pathToFileURL(path.join(localEngineRoot, 'dist', 'web', 'index.js')).href
|
|
13
|
-
: 'neoctl/web/index.js';
|
|
14
|
-
|
|
15
|
-
const [coreModule, webModule] = await Promise.all([
|
|
16
|
-
importCore(coreSpecifier),
|
|
17
|
-
importCore(webSpecifier),
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
export const QueryEngine = coreModule.QueryEngine;
|
|
21
|
-
export const loadNeoPlugins = coreModule.loadNeoPlugins;
|
|
22
|
-
export const WebRepl = webModule.WebRepl;
|
|
23
|
-
export const WebRuntimeRouter = webModule.WebRuntimeRouter;
|
|
24
|
-
export const createWebRuntime = webModule.createWebRuntime;
|
|
25
|
-
export const runWebServer = webModule.runWebServer;
|
|
26
|
-
export const coreRuntimeInfo = Object.freeze({
|
|
27
|
-
source,
|
|
28
|
-
version: await readCoreVersion(),
|
|
29
|
-
location: source === 'local' ? localEngineRoot : 'neoctl',
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
function resolveCoreSource() {
|
|
33
|
-
const argumentIndex = process.argv.indexOf('--core');
|
|
34
|
-
const argument = argumentIndex >= 0 ? process.argv[argumentIndex + 1] : undefined;
|
|
35
|
-
const value = String(argument || process.env.NEO_CORE_SOURCE || 'package').trim().toLowerCase();
|
|
36
|
-
if (value === 'local' || value === 'package') return value;
|
|
37
|
-
throw new Error(`Invalid Core source: ${value}. Use local or package.`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function importCore(specifier) {
|
|
41
|
-
try {
|
|
42
|
-
return await import(specifier);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (source !== 'local') throw error;
|
|
45
|
-
throw new Error('Local Engine is not built. Run npm --prefix ../engine run build first.', { cause: error });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function readCoreVersion() {
|
|
50
|
-
const packageFile = source === 'local'
|
|
51
|
-
? path.join(localEngineRoot, 'package.json')
|
|
52
|
-
: path.join(webRoot, 'node_modules', 'neoctl', 'package.json');
|
|
53
|
-
const value = JSON.parse(await readFile(packageFile, 'utf8'));
|
|
54
|
-
return String(value.version || 'unknown');
|
|
55
|
-
}
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const webRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const source = resolveCoreSource();
|
|
7
|
+
const localEngineRoot = path.resolve(process.env.NEO_LOCAL_ENGINE_ROOT || path.join(webRoot, '..', 'engine'));
|
|
8
|
+
const coreSpecifier = source === 'local'
|
|
9
|
+
? pathToFileURL(path.join(localEngineRoot, 'dist', 'index.js')).href
|
|
10
|
+
: 'neoctl';
|
|
11
|
+
const webSpecifier = source === 'local'
|
|
12
|
+
? pathToFileURL(path.join(localEngineRoot, 'dist', 'web', 'index.js')).href
|
|
13
|
+
: 'neoctl/web/index.js';
|
|
14
|
+
|
|
15
|
+
const [coreModule, webModule] = await Promise.all([
|
|
16
|
+
importCore(coreSpecifier),
|
|
17
|
+
importCore(webSpecifier),
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export const QueryEngine = coreModule.QueryEngine;
|
|
21
|
+
export const loadNeoPlugins = coreModule.loadNeoPlugins;
|
|
22
|
+
export const WebRepl = webModule.WebRepl;
|
|
23
|
+
export const WebRuntimeRouter = webModule.WebRuntimeRouter;
|
|
24
|
+
export const createWebRuntime = webModule.createWebRuntime;
|
|
25
|
+
export const runWebServer = webModule.runWebServer;
|
|
26
|
+
export const coreRuntimeInfo = Object.freeze({
|
|
27
|
+
source,
|
|
28
|
+
version: await readCoreVersion(),
|
|
29
|
+
location: source === 'local' ? localEngineRoot : 'neoctl',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function resolveCoreSource() {
|
|
33
|
+
const argumentIndex = process.argv.indexOf('--core');
|
|
34
|
+
const argument = argumentIndex >= 0 ? process.argv[argumentIndex + 1] : undefined;
|
|
35
|
+
const value = String(argument || process.env.NEO_CORE_SOURCE || 'package').trim().toLowerCase();
|
|
36
|
+
if (value === 'local' || value === 'package') return value;
|
|
37
|
+
throw new Error(`Invalid Core source: ${value}. Use local or package.`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function importCore(specifier) {
|
|
41
|
+
try {
|
|
42
|
+
return await import(specifier);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (source !== 'local') throw error;
|
|
45
|
+
throw new Error('Local Engine is not built. Run npm --prefix ../engine run build first.', { cause: error });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function readCoreVersion() {
|
|
50
|
+
const packageFile = source === 'local'
|
|
51
|
+
? path.join(localEngineRoot, 'package.json')
|
|
52
|
+
: path.join(webRoot, 'node_modules', 'neoctl', 'package.json');
|
|
53
|
+
const value = JSON.parse(await readFile(packageFile, 'utf8'));
|
|
54
|
+
return String(value.version || 'unknown');
|
|
55
|
+
}
|