glasspane-install 1.16.4 → 1.19.0
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/cli.js +33 -25
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* 流程:terminal onboarding 横幅 → 环境预检(node/swift/git/open)→ 定位项目
|
|
6
6
|
* 根目录 → npm 依赖(有 package-lock.json 走 npm ci,见 npmDepsCommand)→ 编译 TS
|
|
7
|
-
* (kernel
|
|
7
|
+
* (mcp-shell;内核是已发布的 `iterate-kernel` 依赖,捆进它的 bundle,不再是仓内 workspace)→ swift
|
|
8
8
|
* build -c release(engine)→ make-app.sh 打包**并签名**两个 .app → 安置到
|
|
9
9
|
* ~/Applications → 以 bundle 内可执行注册 launchd 自启并启动 daemon → 打开
|
|
10
10
|
* GlassPane.app 设置面板进入权限引导(授权对象是 daemon,见 P1 v1.2 §11)→
|
|
@@ -70,7 +70,7 @@ export const INSTALL_SH_URL =
|
|
|
70
70
|
/** 发布线锚点(版本真源 = 根 package.json,由 scripts/set-version.mjs 统一改写,
|
|
71
71
|
* 勿手改):npx 形态下本地没有仓库时,引导 clone 的就是这个 tag,与 install.sh
|
|
72
72
|
* 的 `GLASSPANE_RELEASE` 同值——两条一键入口必须拿到同一份源码。 */
|
|
73
|
-
export const RELEASE_VERSION = '1.
|
|
73
|
+
export const RELEASE_VERSION = '1.19.0'
|
|
74
74
|
/** 发布 ref(tag 名)。GLASSPANE_REF 环境变量可覆盖(追主干用 `main`)。 */
|
|
75
75
|
export const REPO_REF = `v${RELEASE_VERSION}`
|
|
76
76
|
|
|
@@ -324,22 +324,24 @@ export function checkEnvironment({ nodePath = process.execPath } = {}) {
|
|
|
324
324
|
/** 汇总预检结果:缺项按严重程度排序返回可读描述(空数组 = 全部通过)。 */
|
|
325
325
|
export function preflightIssues(env) {
|
|
326
326
|
const issues = []
|
|
327
|
+
// 每一行都要带上"下一步能跑的动作":`未找到 npm` 单独一句是把诊断交给读者,而预检失败
|
|
328
|
+
// 时汇总只补一句通用的"修复后重新运行即可"——四行里只有 swift 那条说得出该装什么。
|
|
327
329
|
if (env.nodeMajor === 0) {
|
|
328
|
-
issues.push(
|
|
330
|
+
issues.push('无法解析 Node.js 版本(`node --version` 跑不起来):装一个 >= 18 的 Node(brew install node,或 nvm install --lts 后 nvm use)再重跑')
|
|
329
331
|
} else if (!env.nodeOk) {
|
|
330
|
-
issues.push(`Node.js 需要 >= ${MIN_NODE_MAJOR},当前为 ${env.nodeMajor}
|
|
332
|
+
issues.push(`Node.js 需要 >= ${MIN_NODE_MAJOR},当前为 ${env.nodeMajor}:换够格的 Node 版本再重跑(brew upgrade node 或 nvm use 22)`)
|
|
331
333
|
}
|
|
332
334
|
if (!env.npm) {
|
|
333
|
-
issues.push('未找到 npm')
|
|
335
|
+
issues.push('未找到 npm(它随 Node 一起装:brew install node)')
|
|
334
336
|
}
|
|
335
337
|
if (!env.swift) {
|
|
336
338
|
issues.push('未找到 swift(需安装 Xcode Command Line Tools:xcode-select --install)')
|
|
337
339
|
}
|
|
338
340
|
if (!env.git) {
|
|
339
|
-
issues.push('未找到 git')
|
|
341
|
+
issues.push('未找到 git(xcode-select --install 会带上它,或 brew install git)')
|
|
340
342
|
}
|
|
341
343
|
if (!env.open) {
|
|
342
|
-
issues.push('未找到 open(本安装器面向 macOS
|
|
344
|
+
issues.push('未找到 open(本安装器面向 macOS:Linux/Windows 上没有可装的东西,请在 macOS 14+ 上重跑)')
|
|
343
345
|
}
|
|
344
346
|
return issues
|
|
345
347
|
}
|
|
@@ -481,7 +483,7 @@ export function classifyKillResult({ status, stderr = '', error = null } = {}) {
|
|
|
481
483
|
}
|
|
482
484
|
|
|
483
485
|
/** 这个 pid 是不是**僵尸**(已死、等父进程回收):`kill -0` 对僵尸回 0,光靠信号探测会把
|
|
484
|
-
* 一个已经不服务 socket 的进程报成"
|
|
486
|
+
* 一个已经不服务 socket 的进程报成"还活着、请你去收尾"。状态读不出来时保守回 false
|
|
485
487
|
* (宁可多报一个幸存者,也不放走真的还在跑的进程)。 */
|
|
486
488
|
export function isZombiePid(pid, { spawn = spawnSync } = {}) {
|
|
487
489
|
try {
|
|
@@ -534,13 +536,13 @@ export function terminatePids(pids, { spawn = spawnSync, sleepMs = 1000, reprobe
|
|
|
534
536
|
let after = state(pid)
|
|
535
537
|
if (after !== 'gone') {
|
|
536
538
|
// 刚被 SIGKILL 的进程要等它的父进程回收(父进程正 `wait` 着的话是毫秒级);
|
|
537
|
-
// 立刻探一次会把它读成"还活着"
|
|
539
|
+
// 立刻探一次会把它读成"还活着",于是红字让人去收尾一个已经没了的 pid。
|
|
538
540
|
// 给一次**有界**的再探窗口,只有窗口过后还在的才算幸存者。
|
|
539
541
|
if (sleep) sleep(reprobeMs)
|
|
540
542
|
else spawn('sleep', [String(reprobeMs / 1000)], { encoding: 'utf8' })
|
|
541
543
|
after = state(pid)
|
|
542
544
|
// 还"活着"但状态是 Z = 它已经死了,只差父进程 wait() 收尸:这种东西不持有 socket、
|
|
543
|
-
//
|
|
545
|
+
// 不需要人去做任何收尾动作,报成幸存者就是把人支到一条没用的命令上。
|
|
544
546
|
if (after === 'alive' && isZombiePid(pid, { spawn })) after = 'gone'
|
|
545
547
|
}
|
|
546
548
|
if (after === 'gone') {
|
|
@@ -554,11 +556,15 @@ export function terminatePids(pids, { spawn = spawnSync, sleepMs = 1000, reprobe
|
|
|
554
556
|
return { terminated, forced, survivors }
|
|
555
557
|
}
|
|
556
558
|
|
|
557
|
-
/**
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
559
|
+
/** 幸存者(安装器收不掉的进程)的红字(finding 5,纯函数)。
|
|
560
|
+
* 这里给的必须是**能直接执行**的动作,而不是"请检查权限";而本产品的红线是:
|
|
561
|
+
* 补救文案一律不发提权命令。发信号这条本来就分两档,两档都如实写出来——
|
|
562
|
+
* ① 当前账号自己就能做的 `/bin/kill -9 <pid>`(安装器已经试过 TERM 与 KILL,
|
|
563
|
+
* 再报一次只是给人一个能手敲的等价动作,它不越权);
|
|
564
|
+
* ② 它被 EPERM 拒掉 = 这些 pid 不属于当前账号,那一步**只有坐在键盘前的这个人
|
|
565
|
+
* 能做**。这一步归人,不归脚本:本产品保留给真人的就是这种物理动作。
|
|
566
|
+
* socket 还挂在这些 pid 上,所以本轮的产物从未接管——收尾那次版本核对会把这件
|
|
567
|
+
* 事再钉一次。退出码契约不变(调用方仍按失败说)。 */
|
|
562
568
|
export function survivorText(survivors, { socketPath } = {}) {
|
|
563
569
|
const pids = survivors.map((entry) => entry.pid).join(' ')
|
|
564
570
|
return [
|
|
@@ -566,8 +572,10 @@ export function survivorText(survivors, { socketPath } = {}) {
|
|
|
566
572
|
+ survivors.map((entry) => `pid ${entry.pid} — ${entry.reason}`).join(';'),
|
|
567
573
|
`安装器无权向这些进程发信号(它们不属于当前账号,典型是 root 拉起的旧 daemon)。`
|
|
568
574
|
+ `上面"已结束"那句只对确实已经不在的 pid 成立,不含这些。`,
|
|
569
|
-
|
|
570
|
-
+
|
|
575
|
+
`先试当前账号自己就能做的那条(不提权):\`/bin/kill -9 ${pids}\`。`
|
|
576
|
+
+ `它若仍被拒,就只能由坐在键盘前的人来结束这些进程——本安装器不发放提权命令,`
|
|
577
|
+
+ `这一步归人。先看清是谁起的 \`ps -o pid=,user=,lstart=,command= -p ${pids}\`,`
|
|
578
|
+
+ `由本人结束它们,然后重跑本安装器并加 --replace-daemon。`,
|
|
571
579
|
].join('\n')
|
|
572
580
|
}
|
|
573
581
|
|
|
@@ -1776,8 +1784,11 @@ export function usageText() {
|
|
|
1776
1784
|
' (--no-daemon 且 --no-launchd)',
|
|
1777
1785
|
' 1 收尾未通过 hello 校验(daemon 没起来/不应答),或构建/产物/launchd 出错',
|
|
1778
1786
|
' 2 命令行参数错误',
|
|
1779
|
-
' 注:install.sh 用 exec
|
|
1780
|
-
' $?
|
|
1787
|
+
' 注:install.sh 用 exec 委托本文件,所以**脚本自己跑完了**之后,退出码原样透传。',
|
|
1788
|
+
' 但 `curl … | sh` 的 $? 只在 curl 成功时才是这里的结论:curl 取不到脚本时 sh 读到的',
|
|
1789
|
+
' 是空输入,直接 EOF 退 0(本机实测 curl 退 7、管道退 0);中途断流时 sh 只执行了',
|
|
1790
|
+
' 收到的前半截,回的是最后一条命令的状态。要拿退出码当结论就分两步跑:',
|
|
1791
|
+
' curl -fsSL <install.sh> -o /tmp/gp-install.sh && sh /tmp/gp-install.sh',
|
|
1781
1792
|
'',
|
|
1782
1793
|
].join('\n')
|
|
1783
1794
|
}
|
|
@@ -1862,7 +1873,7 @@ export function nextStepsText({
|
|
|
1862
1873
|
` 作业被 bootout(等授权期间常发生)后的恢复**不需要手敲 launchctl**:`,
|
|
1863
1874
|
` node "${path.join(rootDir, 'installer', 'cli.js')}" --restore-launchd`,
|
|
1864
1875
|
` (检测→bootstrap→hello 校验自报席位;勾完 TCC 框重跑即自动换进程复验)`,
|
|
1865
|
-
`4.
|
|
1876
|
+
`4. 主体名核对:设置面板「Daemon 状态 → 主体」应与上面「${entryName}」一致;不一致说明连到了别的构建实例。`,
|
|
1866
1877
|
'5. registry 发布形态:MCP 服务器已可独立安装(`npm i -g glasspane-mcp`,命令名',
|
|
1867
1878
|
' glasspane-mcp);但它只是转发层,daemon 与权限仍由本次安装产出,缺 daemon 时',
|
|
1868
1879
|
' 工具调用会返回带补救步骤的结构化错误。',
|
|
@@ -2372,13 +2383,10 @@ export async function install({
|
|
|
2372
2383
|
const hasLockfile = fs.existsSync(path.join(rootDir, 'package-lock.json'))
|
|
2373
2384
|
const depsCmd = npmDepsCommand({ hasLockfile })
|
|
2374
2385
|
printStep(hasLockfile
|
|
2375
|
-
? '安装 npm 依赖(workspaces:
|
|
2376
|
-
: '安装 npm 依赖(workspaces:
|
|
2386
|
+
? '安装 npm 依赖(workspaces: mcp-shell / installer / updater)——按提交过的 package-lock.json 走 npm ci……'
|
|
2387
|
+
: '安装 npm 依赖(workspaces: mcp-shell / installer / updater)——该树没有 package-lock.json,退回 npm install(依赖树未经 CI 那一份 lock 的 attest)……')
|
|
2377
2388
|
await run('npm', depsCmd, { cwd: rootDir })
|
|
2378
2389
|
|
|
2379
|
-
printStep('编译 kernel……')
|
|
2380
|
-
await run('npm', ['run', 'build', '--workspace', '@iterate/kernel'], { cwd: rootDir })
|
|
2381
|
-
|
|
2382
2390
|
printStep('编译 mcp-shell……')
|
|
2383
2391
|
await run('npm', ['run', 'build', '--workspace', 'glasspane-mcp'], { cwd: rootDir })
|
|
2384
2392
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glasspane-install",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "GlassPane one-command installer: terminal onboarding → install deps → build TS + Swift engine → start daemon → open settings GUI for permission guide",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|