@mk-co/neox-cli 2.2.16 → 2.4.7-test
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/neox +1 -1
- package/cli-wrapper.cjs +97 -60
- package/install.cjs +21 -51
- package/package.json +4 -4
package/bin/neox
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
require('../cli-wrapper.cjs');
|
|
2
|
+
require('../cli-wrapper.cjs').run();
|
package/cli-wrapper.cjs
CHANGED
|
@@ -1,83 +1,120 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* @mk-co/neox-cli · cli-wrapper
|
|
3
|
+
* @mk-co/neox-cli · cli-wrapper (launcher)
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* ★ Windows EBUSY 根治 ★
|
|
6
|
+
* 以前: bin/neox = symlink/copy → node_modules 里的平台二进制, 直接跑它 → 运行中的 .exe 被
|
|
7
|
+
* Windows 锁死 → `npm i -g` 要覆盖 node_modules\...\neox.exe → EBUSY (Claude Code 是纯 JS
|
|
8
|
+
* 跑在 node 上, .js 不被锁, 所以没这问题)。
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* 2. 用户 install 时 postinstall 失败 (e.g. --no-optional), bin/neox 是
|
|
15
|
-
* placeholder, 直接跑 `neox` 时 placeholder script 调本 wrapper 退出
|
|
16
|
-
* + 友好提示.
|
|
17
|
-
*
|
|
18
|
-
* binary 跑起来后, 这个 wrapper 完全不在 critical path, 所以零业务逻辑.
|
|
10
|
+
* 现在: bin/neox 永远是这个 JS launcher (node 跑, 不被锁)。它把平台二进制【copy 到缓存】
|
|
11
|
+
* `~/.neox/bin/neox-<版本>(.exe)` (在 node_modules 之外), 跑【缓存】那一份。daemon / workers
|
|
12
|
+
* 都用 process.execPath = 缓存二进制起, 也跑在缓存。
|
|
13
|
+
* → `npm i -g` 只覆盖 node_modules (JS launcher + 源二进制, 都没在运行) → 永不 EBUSY。
|
|
14
|
+
* 缓存按版本命名, 升级时换新名字 (旧的在跑也不冲突), 用完清理旧的。
|
|
19
15
|
*/
|
|
20
16
|
|
|
21
17
|
'use strict';
|
|
22
18
|
|
|
23
19
|
const fs = require('node:fs');
|
|
24
20
|
const path = require('node:path');
|
|
21
|
+
const os = require('node:os');
|
|
22
|
+
const { spawn } = require('node:child_process');
|
|
25
23
|
|
|
26
24
|
const SUPPORTED = {
|
|
27
|
-
'darwin-arm64':
|
|
28
|
-
'darwin-x64':
|
|
29
|
-
'linux-x64':
|
|
30
|
-
'linux-arm64':
|
|
31
|
-
'win32-x64':
|
|
25
|
+
'darwin-arm64': '@mk-co/neox-cli-darwin-arm64',
|
|
26
|
+
'darwin-x64': '@mk-co/neox-cli-darwin-x64',
|
|
27
|
+
'linux-x64': '@mk-co/neox-cli-linux-x64',
|
|
28
|
+
'linux-arm64': '@mk-co/neox-cli-linux-arm64',
|
|
29
|
+
'win32-x64': '@mk-co/neox-cli-win32-x64',
|
|
32
30
|
};
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
32
|
+
const EXE = process.platform === 'win32' ? 'neox.exe' : 'neox';
|
|
33
|
+
|
|
34
|
+
/** 解析 node_modules 里的平台二进制 (源) + 其版本号。失败返回 { error }。 */
|
|
35
|
+
function resolveSource() {
|
|
36
|
+
const key = `${process.platform}-${process.arch}`;
|
|
37
|
+
const pkg = SUPPORTED[key];
|
|
38
|
+
if (!pkg) return { error: `不支持当前平台 ${key} (支持: ${Object.keys(SUPPORTED).join(', ')})` };
|
|
42
39
|
try {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
const root = path.dirname(require.resolve(`${pkg}/package.json`));
|
|
41
|
+
const bin = path.join(root, EXE);
|
|
42
|
+
if (fs.existsSync(bin)) {
|
|
43
|
+
let version = '0';
|
|
44
|
+
try { version = require(`${pkg}/package.json`).version || '0'; } catch { /* keep 0 */ }
|
|
45
|
+
return { bin, version };
|
|
46
|
+
}
|
|
47
47
|
} catch { /* fall through */ }
|
|
48
|
+
return { error: `平台子包 ${pkg} 未安装 (可能用了 --no-optional)。修复: npm install -g ${pkg}` };
|
|
49
|
+
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
/**
|
|
52
|
+
* 把源二进制 copy 到缓存 ~/.neox/bin/neox-<版本>(.exe), 返回缓存路径。
|
|
53
|
+
* · 已存在且大小一致 → 跳过 copy
|
|
54
|
+
* · 原子: 临时文件 + rename (rename 失败说明被同版本 daemon 锁着, 即已存在, 直接用)
|
|
55
|
+
* · 清理旧版本缓存 (best-effort; 锁着就跳过)
|
|
56
|
+
* · 任何失败 → 回落直接返回源路径 (退化成旧行为, 至少能跑)
|
|
57
|
+
*/
|
|
58
|
+
function ensureCached(src, version) {
|
|
59
|
+
const cacheDir = path.join(os.homedir(), '.neox', 'bin');
|
|
60
|
+
const name = process.platform === 'win32' ? `neox-${version}.exe` : `neox-${version}`;
|
|
61
|
+
const dest = path.join(cacheDir, name);
|
|
62
|
+
try {
|
|
63
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
64
|
+
let need = true;
|
|
65
|
+
try {
|
|
66
|
+
if (fs.statSync(dest).size === fs.statSync(src).size) need = false; // 已缓存好
|
|
67
|
+
} catch { /* dest 不存在 → need=true */ }
|
|
68
|
+
if (need) {
|
|
69
|
+
const tmp = path.join(cacheDir, `.tmp-${process.pid}-${Date.now()}-${name}`);
|
|
70
|
+
fs.copyFileSync(src, tmp);
|
|
71
|
+
if (process.platform !== 'win32') { try { fs.chmodSync(tmp, 0o755); } catch { /* ignore */ } }
|
|
72
|
+
try {
|
|
73
|
+
fs.renameSync(tmp, dest);
|
|
74
|
+
} catch {
|
|
75
|
+
// dest 被同版本运行中的 daemon 锁住 → 它已存在且就是对的, 删临时文件用现有的
|
|
76
|
+
try { fs.rmSync(tmp, { force: true }); } catch { /* ignore */ }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// 清理旧版本缓存 (锁着的旧 daemon 二进制跳过, 下次再清)
|
|
80
|
+
try {
|
|
81
|
+
for (const f of fs.readdirSync(cacheDir)) {
|
|
82
|
+
if (f === name || f.startsWith('.tmp-') || !f.startsWith('neox-')) continue;
|
|
83
|
+
try { fs.rmSync(path.join(cacheDir, f), { force: true }); } catch { /* 锁着, 跳过 */ }
|
|
84
|
+
}
|
|
85
|
+
} catch { /* ignore */ }
|
|
86
|
+
if (fs.existsSync(dest)) return dest;
|
|
87
|
+
} catch { /* 缓存整体失败 → 回落源路径 */ }
|
|
88
|
+
return src;
|
|
52
89
|
}
|
|
53
90
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
91
|
+
/** 安装期预热: 把当前平台二进制 copy 进缓存, 让首次 `neox` 不用现 copy。失败静默。 */
|
|
92
|
+
function prewarm() {
|
|
93
|
+
try {
|
|
94
|
+
const s = resolveSource();
|
|
95
|
+
if (s.bin) ensureCached(s.bin, s.version);
|
|
96
|
+
} catch { /* best-effort */ }
|
|
97
|
+
}
|
|
61
98
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
process.
|
|
66
|
-
|
|
67
|
-
process.on('SIGHUP', passSignal('SIGHUP'));
|
|
99
|
+
/** 真正启动: 解析源 → 确保缓存 → 跑缓存二进制 (透传 argv / stdio / signals)。 */
|
|
100
|
+
function run() {
|
|
101
|
+
const s = resolveSource();
|
|
102
|
+
if (s.error) { process.stderr.write(`neox: ${s.error}\n`); process.exit(1); return; }
|
|
103
|
+
const target = ensureCached(s.bin, s.version);
|
|
68
104
|
|
|
69
|
-
child.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
105
|
+
const child = spawn(target, process.argv.slice(2), { stdio: 'inherit', windowsHide: false });
|
|
106
|
+
const pass = (sig) => () => { try { child.kill(sig); } catch { /* ignore */ } };
|
|
107
|
+
process.on('SIGINT', pass('SIGINT'));
|
|
108
|
+
process.on('SIGTERM', pass('SIGTERM'));
|
|
109
|
+
process.on('SIGHUP', pass('SIGHUP'));
|
|
110
|
+
child.on('exit', (code, sig) => {
|
|
111
|
+
if (sig) process.kill(process.pid, sig); // 让 parent 也以同 signal 死, shell 能正确 detect Ctrl-C
|
|
112
|
+
else process.exit(code ?? 0);
|
|
113
|
+
});
|
|
114
|
+
child.on('error', (e) => { process.stderr.write(`neox: 无法启动 binary: ${e.message}\n`); process.exit(2); });
|
|
115
|
+
}
|
|
77
116
|
|
|
78
|
-
|
|
79
|
-
process.stderr.write(`neox: 无法启动 binary: ${e.message}\n`);
|
|
80
|
-
process.exit(2);
|
|
81
|
-
});
|
|
117
|
+
module.exports = { run, prewarm, resolveSource, ensureCached };
|
|
82
118
|
|
|
83
|
-
|
|
119
|
+
/* 作为 binary 直接跑 (bin/neox → require(this).run())。被 require 当 module 用时不自动跑。 */
|
|
120
|
+
if (require.main === module) run();
|
package/install.cjs
CHANGED
|
@@ -3,40 +3,30 @@
|
|
|
3
3
|
* @mk-co/neox-cli · postinstall
|
|
4
4
|
*
|
|
5
5
|
* npm 装完主包 + 当前 platform 的 optional dep 之后跑这里:
|
|
6
|
-
* ·
|
|
7
|
-
* ·
|
|
8
|
-
* · symlink (Unix) / copy (Windows) 到 ./bin/neox 给 npm bin shim 用
|
|
6
|
+
* · 校验当前 platform 受支持 + 平台子包的二进制在位
|
|
7
|
+
* · 预热: 把二进制 copy 进缓存 ~/.neox/bin/neox-<版本>(.exe), 让首次 `neox` 不用现 copy
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* ★ 不再把 bin/neox 替换成指向 node_modules 二进制的 symlink/copy ★
|
|
10
|
+
* bin/neox 永远是 JS launcher (cli-wrapper)。真正运行的二进制在缓存里 (node_modules 之外),
|
|
11
|
+
* 所以 `npm i -g` 重装/升级时 npm 不会去覆盖一个【正在运行 / 被锁】的文件 → 根治 Windows EBUSY。
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
* · platform 不支持 → 退 1 + 友好提示 (列出我们支持的 platforms)
|
|
15
|
-
* · platform 包没装 → 友好提示用户 manual install (常见: 用了 --no-optional)
|
|
16
|
-
*
|
|
17
|
-
* 不抛 throw, 因为 npm postinstall 抛了会让整个 install 显红, 但其实没 fatal.
|
|
13
|
+
* 不抛 throw — postinstall 抛了会让整个 install 显红, 但其实没 fatal (跑 neox 时 launcher 会兜底)。
|
|
18
14
|
*/
|
|
19
15
|
|
|
20
16
|
'use strict';
|
|
21
17
|
|
|
22
18
|
const fs = require('node:fs');
|
|
23
19
|
const path = require('node:path');
|
|
24
|
-
const os = require('node:os');
|
|
25
20
|
|
|
26
21
|
const SUPPORTED = {
|
|
27
|
-
'darwin-arm64':
|
|
28
|
-
'linux-x64':
|
|
29
|
-
'win32-x64':
|
|
30
|
-
// darwin-x64
|
|
31
|
-
// "platform 不支持" 友好提示, 待加回时同步 build-cli-binaries PLATFORMS + package.json.tpl.
|
|
22
|
+
'darwin-arm64': '@mk-co/neox-cli-darwin-arm64',
|
|
23
|
+
'linux-x64': '@mk-co/neox-cli-linux-x64',
|
|
24
|
+
'win32-x64': '@mk-co/neox-cli-win32-x64',
|
|
25
|
+
// darwin-x64 / linux-arm64 本版未发布; 落到"平台不支持"友好提示。
|
|
32
26
|
};
|
|
33
27
|
|
|
34
|
-
function log(msg) {
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
function warn(msg) {
|
|
38
|
-
process.stderr.write(`[neox-cli postinstall] ⚠ ${msg}\n`);
|
|
39
|
-
}
|
|
28
|
+
function log(msg) { process.stdout.write(`[neox-cli postinstall] ${msg}\n`); }
|
|
29
|
+
function warn(msg) { process.stderr.write(`[neox-cli postinstall] ⚠ ${msg}\n`); }
|
|
40
30
|
|
|
41
31
|
function main() {
|
|
42
32
|
const platformKey = `${process.platform}-${process.arch}`;
|
|
@@ -45,18 +35,15 @@ function main() {
|
|
|
45
35
|
if (!pkgName) {
|
|
46
36
|
warn(`不支持当前平台 ${platformKey}.`);
|
|
47
37
|
warn(`支持的平台: ${Object.keys(SUPPORTED).join(', ')}`);
|
|
48
|
-
warn(`你的 neox
|
|
49
|
-
return; /* 不退 1
|
|
38
|
+
warn(`你的 neox 命令会找不到二进制, 请联系 MK-CO 支持其他平台.`);
|
|
39
|
+
return; /* 不退 1 */
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
/*
|
|
53
|
-
* (-g vs 项目级), 用 require.resolve 让 npm 自己决定. */
|
|
54
|
-
let binarySrc;
|
|
42
|
+
/* 校验平台子包二进制在位 */
|
|
55
43
|
try {
|
|
56
|
-
/* 期待: node_modules/@mk-co/neox-cli-${platform}/neox (Unix) or neox.exe (Win) */
|
|
57
44
|
const pkgRoot = path.dirname(require.resolve(`${pkgName}/package.json`));
|
|
58
45
|
const exeName = process.platform === 'win32' ? 'neox.exe' : 'neox';
|
|
59
|
-
binarySrc = path.join(pkgRoot, exeName);
|
|
46
|
+
const binarySrc = path.join(pkgRoot, exeName);
|
|
60
47
|
if (!fs.existsSync(binarySrc)) {
|
|
61
48
|
warn(`平台子包已装但 binary 不在 ${binarySrc} — 可能 publish 出错.`);
|
|
62
49
|
return;
|
|
@@ -68,30 +55,13 @@ function main() {
|
|
|
68
55
|
return;
|
|
69
56
|
}
|
|
70
57
|
|
|
71
|
-
/*
|
|
72
|
-
const binDir = path.join(__dirname, 'bin');
|
|
73
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
74
|
-
const exeName = process.platform === 'win32' ? 'neox.exe' : 'neox';
|
|
75
|
-
const binDest = path.join(binDir, exeName);
|
|
76
|
-
|
|
58
|
+
/* 预热缓存 (copy 二进制到 ~/.neox/bin/neox-<版本>)。失败不致命 — launcher 首次跑时会兜底 copy。 */
|
|
77
59
|
try {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
} catch { /* ignore */ }
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
if (process.platform === 'win32') {
|
|
85
|
-
/* Windows: 不能 symlink (要管理员), 直接 copy */
|
|
86
|
-
fs.copyFileSync(binarySrc, binDest);
|
|
87
|
-
} else {
|
|
88
|
-
fs.symlinkSync(binarySrc, binDest);
|
|
89
|
-
fs.chmodSync(binDest, 0o755);
|
|
90
|
-
}
|
|
91
|
-
log(`installed ${platformKey}: ${path.relative(process.cwd(), binDest)} → ${path.relative(process.cwd(), binarySrc)}`);
|
|
60
|
+
const { prewarm } = require('./cli-wrapper.cjs');
|
|
61
|
+
prewarm();
|
|
62
|
+
log(`ready (${platformKey}) — 二进制已缓存到 ~/.neox/bin, 跑 neox 即可。`);
|
|
92
63
|
} catch (e) {
|
|
93
|
-
warn(
|
|
94
|
-
warn(`手动 fix: ${process.platform === 'win32' ? 'copy' : 'ln -s'} "${binarySrc}" "${binDest}"`);
|
|
64
|
+
warn(`缓存预热失败 (非致命, 首次跑 neox 时会自动补): ${e?.message || e}`);
|
|
95
65
|
}
|
|
96
66
|
}
|
|
97
67
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mk-co/neox-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.7-test",
|
|
4
4
|
"description": "Neox CLI · Professional AI code assistant",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"engines": {
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"postinstall": "node install.cjs"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
|
-
"@mk-co/neox-cli-darwin-arm64": "2.
|
|
25
|
-
"@mk-co/neox-cli-linux-x64": "2.
|
|
26
|
-
"@mk-co/neox-cli-win32-x64": "2.
|
|
24
|
+
"@mk-co/neox-cli-darwin-arm64": "2.4.7-test",
|
|
25
|
+
"@mk-co/neox-cli-linux-x64": "2.4.7-test",
|
|
26
|
+
"@mk-co/neox-cli-win32-x64": "2.4.7-test"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|