botmux 2.96.1 → 2.97.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.
Files changed (42) hide show
  1. package/dist/adapters/backend/session-backend-selector.d.ts +32 -0
  2. package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
  3. package/dist/adapters/backend/session-backend-selector.js +41 -0
  4. package/dist/adapters/backend/session-backend-selector.js.map +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +32 -6
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +11 -8
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/command-handler.d.ts.map +1 -1
  12. package/dist/core/command-handler.js +6 -1
  13. package/dist/core/command-handler.js.map +1 -1
  14. package/dist/core/persistent-backend.d.ts +18 -5
  15. package/dist/core/persistent-backend.d.ts.map +1 -1
  16. package/dist/core/persistent-backend.js +21 -10
  17. package/dist/core/persistent-backend.js.map +1 -1
  18. package/dist/core/worker-pool.d.ts.map +1 -1
  19. package/dist/core/worker-pool.js +11 -0
  20. package/dist/core/worker-pool.js.map +1 -1
  21. package/dist/platform/bind.d.ts.map +1 -1
  22. package/dist/platform/bind.js +6 -1
  23. package/dist/platform/bind.js.map +1 -1
  24. package/dist/platform/tunnel-client.d.ts.map +1 -1
  25. package/dist/platform/tunnel-client.js +7 -2
  26. package/dist/platform/tunnel-client.js.map +1 -1
  27. package/dist/setup/ensure-tmux.d.ts +9 -0
  28. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  29. package/dist/setup/ensure-tmux.js +7 -2
  30. package/dist/setup/ensure-tmux.js.map +1 -1
  31. package/dist/setup/ensure-zellij.d.ts.map +1 -1
  32. package/dist/setup/ensure-zellij.js +4 -3
  33. package/dist/setup/ensure-zellij.js.map +1 -1
  34. package/dist/setup/index.d.ts +25 -0
  35. package/dist/setup/index.d.ts.map +1 -1
  36. package/dist/setup/index.js +52 -8
  37. package/dist/setup/index.js.map +1 -1
  38. package/dist/types.d.ts +12 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/worker.js +83 -22
  41. package/dist/worker.js.map +1 -1
  42. package/package.json +1 -1
@@ -1,4 +1,36 @@
1
1
  import type { BackendType, SessionBackend } from './types.js';
2
+ export type BackendGateDecision = {
3
+ action: 'spawn';
4
+ } | {
5
+ action: 'gate';
6
+ reason: string;
7
+ };
8
+ /**
9
+ * Hard gate (PTY 退役): a requested *persistent* backend (tmux/herdr/zellij)
10
+ * that isn't functional on this host no longer silently degrades to raw PTY.
11
+ * That silent fallback was the root of the "secretly running on PTY, then
12
+ * hitting all of PTY's problems (no survival across daemon restart, etc.)"
13
+ * bug class. Instead the worker refuses to spawn and posts an actionable card.
14
+ *
15
+ * PTY stays reachable ONLY as an explicit opt-in — `BACKEND_TYPE=pty` or a
16
+ * per-bot `backendType: 'pty'` — which arrives here as `requested === 'pty'`
17
+ * and is always allowed straight through.
18
+ *
19
+ * `hasExistingSession` lets an already-running persistent session reattach
20
+ * regardless of a transient probe failure (a disposable "can we start a new
21
+ * server?" probe is far less authoritative than a live session — see PR#249):
22
+ * abandoning it would spawn a duplicate CLI and orphan the real conversation.
23
+ * The caller computes it only for backends whose probe is a disposable
24
+ * session (tmux, zellij); herdr's probe is a cheap non-destructive
25
+ * `herdr --version`, so it passes `hasExistingSession: false`.
26
+ */
27
+ export declare function decideBackendGate(opts: {
28
+ requested: BackendType;
29
+ available: boolean;
30
+ hasExistingSession: boolean;
31
+ }): BackendGateDecision;
32
+ /** User-facing card shown when {@link decideBackendGate} gates a session. */
33
+ export declare function backendGateUserMessage(backend: BackendType, reason: string): string;
2
34
  export interface SelectedSessionBackend {
3
35
  backend: SessionBackend;
4
36
  isTmuxMode: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"session-backend-selector.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/session-backend-selector.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB;;mFAE+E;IAC/E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,GAAG,sBAAsB,CAwDlH"}
1
+ {"version":3,"file":"session-backend-selector.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/session-backend-selector.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,MAAM,mBAAmB,GAC3B;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;CAC7B,GAAG,mBAAmB,CAKtB;AAED,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAYnF;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB;;mFAE+E;IAC/E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,GAAG,sBAAsB,CAwDlH"}
@@ -3,6 +3,47 @@ import { PtyBackend } from './pty-backend.js';
3
3
  import { TmuxBackend } from './tmux-backend.js';
4
4
  import { TmuxPipeBackend } from './tmux-pipe-backend.js';
5
5
  import { ZellijBackend } from './zellij-backend.js';
6
+ /**
7
+ * Hard gate (PTY 退役): a requested *persistent* backend (tmux/herdr/zellij)
8
+ * that isn't functional on this host no longer silently degrades to raw PTY.
9
+ * That silent fallback was the root of the "secretly running on PTY, then
10
+ * hitting all of PTY's problems (no survival across daemon restart, etc.)"
11
+ * bug class. Instead the worker refuses to spawn and posts an actionable card.
12
+ *
13
+ * PTY stays reachable ONLY as an explicit opt-in — `BACKEND_TYPE=pty` or a
14
+ * per-bot `backendType: 'pty'` — which arrives here as `requested === 'pty'`
15
+ * and is always allowed straight through.
16
+ *
17
+ * `hasExistingSession` lets an already-running persistent session reattach
18
+ * regardless of a transient probe failure (a disposable "can we start a new
19
+ * server?" probe is far less authoritative than a live session — see PR#249):
20
+ * abandoning it would spawn a duplicate CLI and orphan the real conversation.
21
+ * The caller computes it only for backends whose probe is a disposable
22
+ * session (tmux, zellij); herdr's probe is a cheap non-destructive
23
+ * `herdr --version`, so it passes `hasExistingSession: false`.
24
+ */
25
+ export function decideBackendGate(opts) {
26
+ if (opts.requested === 'pty')
27
+ return { action: 'spawn' };
28
+ if (opts.hasExistingSession)
29
+ return { action: 'spawn' };
30
+ if (opts.available)
31
+ return { action: 'spawn' };
32
+ return { action: 'gate', reason: `${opts.requested} 后端在本机不可用` };
33
+ }
34
+ /** User-facing card shown when {@link decideBackendGate} gates a session. */
35
+ export function backendGateUserMessage(backend, reason) {
36
+ const installHint = backend === 'tmux'
37
+ ? 'macOS: brew install tmux | Debian/Ubuntu: sudo apt-get install -y tmux | 其它发行版用对应包管理器安装 tmux'
38
+ : `请确认 ${backend} 已正确安装并可用`;
39
+ return [
40
+ `⚠️ 本机 ${backend} 不可用,无法启动会话。`,
41
+ `原因:${reason}`,
42
+ `请安装/修复后重试 —— ${installHint}`,
43
+ `(如确需在没有 ${backend} 的环境运行,可显式设置环境变量 BACKEND_TYPE=pty 用 PTY 后端兜底;` +
44
+ `但 PTY 会话不跨 daemon 重启存活,仅作应急。)`,
45
+ ].join('\n');
46
+ }
6
47
  export function selectSessionBackend(opts) {
7
48
  if (opts.backendType === 'zellij') {
8
49
  const sessionName = ZellijBackend.sessionName(opts.sessionId);
@@ -1 +1 @@
1
- {"version":3,"file":"session-backend-selector.js","sourceRoot":"","sources":["../../../src/adapters/backend/session-backend-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAapD,MAAM,UAAU,oBAAoB,CAAC,IAAqD;IACxF,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QACvD,OAAO;YACL,OAAO,EAAE,IAAI,aAAa,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;YACpF,UAAU,EAAE,KAAK;YACjB,UAAU,EAAE,KAAK;YACjB,YAAY,EAAE,IAAI;SACnB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO;YACL,OAAO,EAAE,IAAI,UAAU,EAAE;YACzB,UAAU,EAAE,KAAK;YACjB,UAAU,EAAE,KAAK;YACjB,YAAY,EAAE,KAAK;SACpB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7D,IAAI,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACzC,OAAO;gBACL,OAAO,EAAE,IAAI,YAAY,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;gBAC5D,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,YAAY,EAAE,KAAK;aACpB,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI,YAAY,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;YAC/D,UAAU,EAAE,KAAK;YACjB,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,KAAK;SACpB,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5D,IAAI,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QACxC,OAAO;YACL,OAAO,EAAE,IAAI,eAAe,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;YAClF,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,KAAK;SACpB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI,eAAe,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACrF,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,YAAY,EAAE,KAAK;KACpB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"session-backend-selector.js","sourceRoot":"","sources":["../../../src/adapters/backend/session-backend-selector.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAy3JA;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,qBAAqB,EAAE,SAAS,CAAC,EAC9F,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC,GACzF,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA8F7B"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAq5JA;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,qBAAqB,EAAE,SAAS,CAAC,EAC9F,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,2BAA2B,EAAE,UAAU,GAAG,IAAI,CAAC,GACzF,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA8F7B"}
package/dist/cli.js CHANGED
@@ -1376,18 +1376,44 @@ async function cmdRestart() {
1376
1376
  }
1377
1377
  await printDashboardHintWithRetry();
1378
1378
  }
1379
- /** Wraps `ensureDependencies()`. Neither tmux nor fonts are load-bearing
1380
- * ensureDependencies surfaces failures as warnings and the daemon continues
1381
- * on PTY backend. Only an unexpected exception (programmer error) propagates. */
1379
+ /** Wraps `ensureDependencies()`. Fonts are nice-to-have (warn only). tmux is
1380
+ * required since PTY 退役: if it's GENUINELY ABSENT and a bot wants the tmux
1381
+ * backend (and the operator hasn't opted into BACKEND_TYPE=pty), we hard-fail
1382
+ * here — non-zero exit, no pm2 spawn — so an unattended `start`/`restart`
1383
+ * surfaces the failure instead of bringing up a daemon whose every session
1384
+ * would gate at first message. A present-but-broken tmux (functional probe
1385
+ * flaked) is NOT fatal: the daemon still starts and degrades per-session, so a
1386
+ * transient probe failure can't block reattaching live sessions (PR#249). An
1387
+ * unexpected exception in the probe itself is non-fatal for the same reason. */
1382
1388
  async function ensureSystemDependencies() {
1383
- const { ensureDependencies } = await import('./setup/index.js');
1389
+ const { ensureDependencies, shouldHardFailStartupForMissingTmux } = await import('./setup/index.js');
1390
+ let report;
1384
1391
  try {
1385
- await ensureDependencies();
1392
+ report = await ensureDependencies();
1386
1393
  }
1387
1394
  catch (err) {
1388
1395
  console.error('');
1389
1396
  console.error(`依赖检测内部错误: ${err?.message ?? String(err)}`);
1390
- // Don't exit — let daemon start try anyway; worst case PTY backend works.
1397
+ // Don't exit — a probe-internal error is not a confirmed "tmux missing".
1398
+ return;
1399
+ }
1400
+ // loadBotsJson() returns [] when bots.json is absent (→ no bot wants tmux) and
1401
+ // hard-exits on a malformed file (existing fast-fail) — it never throws here.
1402
+ const anyBotWantsTmux = loadBotsJson().some(b => (b?.backendType ?? config.daemon.backendType) === 'tmux');
1403
+ const ptyOptIn = (process.env.BACKEND_TYPE ?? '').toLowerCase() === 'pty';
1404
+ if (shouldHardFailStartupForMissingTmux({
1405
+ tmuxInstalled: report.tmux.installed,
1406
+ tmuxBinaryPresent: report.tmux.binaryPresent === true,
1407
+ anyBotWantsTmux,
1408
+ ptyOptIn,
1409
+ })) {
1410
+ console.error('');
1411
+ console.error('❌ tmux 未安装,已中止 daemon 启动 —— 默认走 tmux 后端的会话将全部无法运行。');
1412
+ console.error(' 请按上方指引安装 tmux 后重试。');
1413
+ console.error(' 如确需在没有 tmux 的环境运行,可显式用 PTY 兜底:BACKEND_TYPE=pty botmux start');
1414
+ console.error(' (注意:PTY 会话不跨 daemon 重启存活,仅作应急。)');
1415
+ console.error('');
1416
+ process.exit(1);
1391
1417
  }
1392
1418
  }
1393
1419
  /**