From 31e249af237c5a5a3f276ff9fe32f21dde25693e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B3=E6=99=97?= Date: Mon, 7 Sep 2026 05:18:17 -0700 Subject: [PATCH] =?UTF-8?q?chore(pm2):=20=E6=B8=85=E7=90=86=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=90=8E=E9=9B=B6=E8=B0=83=E7=94=A8=E6=96=B9=E7=9A=84?= =?UTF-8?q?=20PM2=20spawn=20=E5=91=BD=E4=BB=A4=E6=9E=84=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 插件服务改走内置 supervisor(#1254)后,`src/core/plugins/pm2.ts` 被删除, `buildPm2SpawnCommand` 随之在全仓零调用方——已无任何代码路径 spawn PM2。 一并移除它的专测,并清掉 `tsconfig.test-mocks.json` 里指向已删除 `test/plugin-pm2-env.test.ts` 的 include 条目(tsc 对匹配不到的条目静默 容忍,不报错但是陈旧配置)。 有意保留 `src/cli/pm2-env.ts`:它虽然同样没有生产调用方,却是 `test/child-env.test.ts` 的**参照基准**—— `expect(viaExternal).toEqual(viaPm2)` 用旧 PM2 边界证明新的 `scrubExternalMemberEnv` 剥离的东西一模一样,删掉它等于让迁移 静默降低防护。 影响面:仅 PM2 spawn 命令行构造(Windows 引号/绝对 Node 路径那套)。 `legacy-pm2-reaper`(升级机清残留 God)、`pm2-graceful-exit`(退出码哨兵, 5 处在用)、`pm2-readonly-client`(桌面端按磁盘路径起子进程)均不受影响。 验证:tsc ✅ / typecheck:test-mocks ✅ / build ✅ / 6 文件 115 用例全通过;已确认参照基准用例 「strips exactly what the whole pm2 plugin boundary stripped」仍在执行。 --- src/cli/pm2-command.ts | 35 ----------------------- test/pm2-command.test.ts | 60 ---------------------------------------- tsconfig.test-mocks.json | 1 - 3 files changed, 96 deletions(-) delete mode 100644 src/cli/pm2-command.ts delete mode 100644 test/pm2-command.test.ts diff --git a/src/cli/pm2-command.ts b/src/cli/pm2-command.ts deleted file mode 100644 index 8a3212fca2..0000000000 --- a/src/cli/pm2-command.ts +++ /dev/null @@ -1,35 +0,0 @@ -export interface SpawnCommand { - command: string; - args: string[]; - shell?: boolean; -} - -export function buildPm2SpawnCommand( - pm2Script: string, - args: string[], - platform: NodeJS.Platform = process.platform, - nodePath: string = process.execPath, -): SpawnCommand { - if (platform === 'win32' && pm2Script !== 'pm2') { - if (pm2Script.toLowerCase().endsWith('.cmd')) { - // Node's spawn with `{ shell: true }` does NOT quote the command or args — - // it joins them verbatim into the cmd.exe command line. Without quoting, a - // space anywhere (the pm2.cmd path under "C:\Program Files\…", or the - // ecosystem config path under "C:\Users\First Last\.botmux\…") gets - // word-split by cmd.exe and pm2 receives a truncated path. Wrap each token - // in double quotes; cmd.exe (/s) strips them when it re-parses, and the - // npm .cmd shim forwards the quoted args through %* intact. Windows paths - // can't contain `"`, so simple wrapping is sufficient here. - const quote = (s: string): string => `"${s}"`; - return { command: quote(pm2Script), args: args.map(quote), shell: true }; - } - return { command: nodePath, args: [pm2Script, ...args] }; - } - if (pm2Script !== 'pm2') { - // PM2's package script uses `#!/usr/bin/env node`. GUI apps, launchd and - // systemd commonly have a deliberately small PATH, so always use the same - // absolute Node interpreter that is already running botmux. - return { command: nodePath, args: [pm2Script, ...args] }; - } - return { command: pm2Script, args }; -} diff --git a/test/pm2-command.test.ts b/test/pm2-command.test.ts deleted file mode 100644 index e126f28e4f..0000000000 --- a/test/pm2-command.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { buildPm2SpawnCommand } from '../src/cli/pm2-command.js'; - -describe('buildPm2SpawnCommand', () => { - it('runs bundled pm2 script through node.exe on Windows', () => { - expect(buildPm2SpawnCommand( - 'D:\\Application\\npm-global\\node_modules\\botmux\\node_modules\\pm2\\bin\\pm2', - ['logs', '/^botmux/', '--lines', '50'], - 'win32', - 'D:\\Application\\nodejs\\node.exe', - )).toEqual({ - command: 'D:\\Application\\nodejs\\node.exe', - args: [ - 'D:\\Application\\npm-global\\node_modules\\botmux\\node_modules\\pm2\\bin\\pm2', - 'logs', - '/^botmux/', - '--lines', - '50', - ], - }); - }); - - it('runs package-local pm2.cmd directly through a Windows shell (quoted)', () => { - const pm2Cmd = String.raw`D:\Application\npm-global\node_modules\botmux\node_modules\.bin\pm2.cmd`; - expect(buildPm2SpawnCommand( - pm2Cmd, - ['status'], - 'win32', - String.raw`D:\Application\nodejs\node.exe`, - )).toEqual({ - command: `"${pm2Cmd}"`, - args: ['"status"'], - shell: true, - }); - }); - - it('quotes pm2.cmd and args so spaces in the config path survive shell:true', () => { - const pm2Cmd = String.raw`C:\Users\First Last\AppData\Roaming\npm\node_modules\botmux\node_modules\.bin\pm2.cmd`; - const cfg = String.raw`C:\Users\First Last\.botmux\ecosystem.config.json`; - expect(buildPm2SpawnCommand(pm2Cmd, ['start', cfg], 'win32')).toEqual({ - command: `"${pm2Cmd}"`, - args: ['"start"', `"${cfg}"`], - shell: true, - }); - }); - - it('keeps direct pm2 command unchanged on Windows', () => { - expect(buildPm2SpawnCommand('pm2', ['status'], 'win32', 'node.exe')).toEqual({ - command: 'pm2', - args: ['status'], - }); - }); - - it('runs package-local PM2 through the current Node on Unix platforms', () => { - expect(buildPm2SpawnCommand('/app/node_modules/pm2/bin/pm2', ['status'], 'linux', '/usr/bin/node')).toEqual({ - command: '/usr/bin/node', - args: ['/app/node_modules/pm2/bin/pm2', 'status'], - }); - }); -}); diff --git a/tsconfig.test-mocks.json b/tsconfig.test-mocks.json index 7889ae3784..2a305622a0 100644 --- a/tsconfig.test-mocks.json +++ b/tsconfig.test-mocks.json @@ -10,7 +10,6 @@ "test/ensure-herdr-integrations.test.ts", "test/hermes-transcript.test.ts", "test/mtr-transcript.test.ts", - "test/plugin-pm2-env.test.ts", "test/tmux-pipe-backend.test.ts", "test/transient-snapshot.test.ts", "test/vc-agent-polling-source.test.ts"