botmux 2.9.0 → 2.9.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.en.md +140 -76
- package/README.md +134 -75
- package/dist/adapters/backend/pty-backend.d.ts +6 -0
- package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +10 -0
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/session-backend-selector.d.ts +11 -0
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -0
- package/dist/adapters/backend/session-backend-selector.js +26 -0
- package/dist/adapters/backend/session-backend-selector.js.map +1 -0
- package/dist/adapters/backend/tmux-backend.d.ts +80 -3
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +301 -49
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +100 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -0
- package/dist/adapters/backend/tmux-pipe-backend.js +473 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -0
- package/dist/adapters/cli/aiden.d.ts.map +1 -1
- package/dist/adapters/cli/aiden.js +5 -0
- package/dist/adapters/cli/aiden.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts +40 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +470 -49
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/coco.d.ts.map +1 -1
- package/dist/adapters/cli/coco.js +191 -9
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +94 -17
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +2 -2
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +7 -5
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +38 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/autostart.d.ts +14 -0
- package/dist/autostart.d.ts.map +1 -0
- package/dist/autostart.js +357 -0
- package/dist/autostart.js.map +1 -0
- package/dist/bot-registry.d.ts +29 -3
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +91 -12
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/arg-utils.d.ts +11 -0
- package/dist/cli/arg-utils.d.ts.map +1 -0
- package/dist/cli/arg-utils.js +25 -0
- package/dist/cli/arg-utils.js.map +1 -0
- package/dist/cli/create-group-resolver.d.ts +32 -0
- package/dist/cli/create-group-resolver.d.ts.map +1 -0
- package/dist/cli/create-group-resolver.js +70 -0
- package/dist/cli/create-group-resolver.js.map +1 -0
- package/dist/cli/quoted-render.d.ts +30 -0
- package/dist/cli/quoted-render.d.ts.map +1 -0
- package/dist/cli/quoted-render.js +29 -0
- package/dist/cli/quoted-render.js.map +1 -0
- package/dist/cli.js +916 -272
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -8
- package/dist/config.js.map +1 -1
- package/dist/core/command-handler.d.ts +43 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +167 -64
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-events.d.ts +57 -0
- package/dist/core/dashboard-events.d.ts.map +1 -0
- package/dist/core/dashboard-events.js +23 -0
- package/dist/core/dashboard-events.js.map +1 -0
- package/dist/core/dashboard-ipc-server.d.ts +43 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -0
- package/dist/core/dashboard-ipc-server.js +481 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -0
- package/dist/core/dashboard-locate.d.ts +20 -0
- package/dist/core/dashboard-locate.d.ts.map +1 -0
- package/dist/core/dashboard-locate.js +26 -0
- package/dist/core/dashboard-locate.js.map +1 -0
- package/dist/core/dashboard-rows.d.ts +31 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -0
- package/dist/core/dashboard-rows.js +65 -0
- package/dist/core/dashboard-rows.js.map +1 -0
- package/dist/core/inherit-peer.d.ts +14 -0
- package/dist/core/inherit-peer.d.ts.map +1 -0
- package/dist/core/inherit-peer.js +32 -0
- package/dist/core/inherit-peer.js.map +1 -0
- package/dist/core/scheduler.d.ts +24 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +93 -2
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-activity.d.ts +3 -0
- package/dist/core/session-activity.d.ts.map +1 -0
- package/dist/core/session-activity.js +20 -0
- package/dist/core/session-activity.js.map +1 -0
- package/dist/core/session-discovery.d.ts +39 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +114 -21
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts +72 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +396 -106
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +27 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +14 -3
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +72 -3
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +459 -38
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +601 -309
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +41 -0
- package/dist/dashboard/aggregator.d.ts.map +1 -0
- package/dist/dashboard/aggregator.js +125 -0
- package/dist/dashboard/aggregator.js.map +1 -0
- package/dist/dashboard/auth.d.ts +23 -0
- package/dist/dashboard/auth.d.ts.map +1 -0
- package/dist/dashboard/auth.js +66 -0
- package/dist/dashboard/auth.js.map +1 -0
- package/dist/dashboard/operator-selector.d.ts +20 -0
- package/dist/dashboard/operator-selector.d.ts.map +1 -0
- package/dist/dashboard/operator-selector.js +39 -0
- package/dist/dashboard/operator-selector.js.map +1 -0
- package/dist/dashboard/registry.d.ts +35 -0
- package/dist/dashboard/registry.d.ts.map +1 -0
- package/dist/dashboard/registry.js +74 -0
- package/dist/dashboard/registry.js.map +1 -0
- package/dist/dashboard/web/app.d.ts +2 -0
- package/dist/dashboard/web/app.d.ts.map +1 -0
- package/dist/dashboard/web/app.js +45 -0
- package/dist/dashboard/web/app.js.map +1 -0
- package/dist/dashboard/web/bot-defaults.d.ts +2 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -0
- package/dist/dashboard/web/bot-defaults.js +201 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -0
- package/dist/dashboard/web/groups.d.ts +16 -0
- package/dist/dashboard/web/groups.d.ts.map +1 -0
- package/dist/dashboard/web/groups.js +584 -0
- package/dist/dashboard/web/groups.js.map +1 -0
- package/dist/dashboard/web/schedules.d.ts +2 -0
- package/dist/dashboard/web/schedules.d.ts.map +1 -0
- package/dist/dashboard/web/schedules.js +105 -0
- package/dist/dashboard/web/schedules.js.map +1 -0
- package/dist/dashboard/web/sessions.d.ts +2 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -0
- package/dist/dashboard/web/sessions.js +374 -0
- package/dist/dashboard/web/sessions.js.map +1 -0
- package/dist/dashboard/web/store.d.ts +23 -0
- package/dist/dashboard/web/store.d.ts.map +1 -0
- package/dist/dashboard/web/store.js +82 -0
- package/dist/dashboard/web/store.js.map +1 -0
- package/dist/dashboard-web/app.js +263 -0
- package/dist/dashboard-web/index.html +23 -0
- package/dist/dashboard-web/style.css +93 -0
- package/dist/dashboard.d.ts +2 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +639 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/im/lark/card-builder.d.ts +18 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +70 -9
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +123 -109
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +36 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +119 -13
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +92 -8
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +410 -89
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/forwarded-renderer.d.ts +23 -0
- package/dist/im/lark/forwarded-renderer.d.ts.map +1 -0
- package/dist/im/lark/forwarded-renderer.js +105 -0
- package/dist/im/lark/forwarded-renderer.js.map +1 -0
- package/dist/im/lark/md-card.d.ts +73 -0
- package/dist/im/lark/md-card.d.ts.map +1 -0
- package/dist/im/lark/md-card.js +332 -0
- package/dist/im/lark/md-card.js.map +1 -0
- package/dist/im/lark/merge-forward.d.ts +32 -0
- package/dist/im/lark/merge-forward.d.ts.map +1 -0
- package/dist/im/lark/merge-forward.js +110 -0
- package/dist/im/lark/merge-forward.js.map +1 -0
- package/dist/im/lark/message-parser.d.ts +9 -3
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +48 -13
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/quote-hint.d.ts +18 -0
- package/dist/im/lark/quote-hint.d.ts.map +1 -0
- package/dist/im/lark/quote-hint.js +23 -0
- package/dist/im/lark/quote-hint.js.map +1 -0
- package/dist/services/bridge-fallback-gate.d.ts +42 -0
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -0
- package/dist/services/bridge-fallback-gate.js +12 -0
- package/dist/services/bridge-fallback-gate.js.map +1 -0
- package/dist/services/bridge-rotation-policy.d.ts +139 -0
- package/dist/services/bridge-rotation-policy.d.ts.map +1 -0
- package/dist/services/bridge-rotation-policy.js +125 -0
- package/dist/services/bridge-rotation-policy.js.map +1 -0
- package/dist/services/bridge-turn-queue.d.ts +154 -0
- package/dist/services/bridge-turn-queue.d.ts.map +1 -0
- package/dist/services/bridge-turn-queue.js +316 -0
- package/dist/services/bridge-turn-queue.js.map +1 -0
- package/dist/services/chat-first-seen-store.d.ts +27 -0
- package/dist/services/chat-first-seen-store.d.ts.map +1 -0
- package/dist/services/chat-first-seen-store.js +114 -0
- package/dist/services/chat-first-seen-store.js.map +1 -0
- package/dist/services/claude-transcript.d.ts +268 -0
- package/dist/services/claude-transcript.d.ts.map +1 -0
- package/dist/services/claude-transcript.js +798 -0
- package/dist/services/claude-transcript.js.map +1 -0
- package/dist/services/coco-transcript.d.ts +35 -0
- package/dist/services/coco-transcript.d.ts.map +1 -0
- package/dist/services/coco-transcript.js +192 -0
- package/dist/services/coco-transcript.js.map +1 -0
- package/dist/services/codex-bridge-queue.d.ts +56 -0
- package/dist/services/codex-bridge-queue.d.ts.map +1 -0
- package/dist/services/codex-bridge-queue.js +150 -0
- package/dist/services/codex-bridge-queue.js.map +1 -0
- package/dist/services/codex-transcript.d.ts +84 -0
- package/dist/services/codex-transcript.d.ts.map +1 -0
- package/dist/services/codex-transcript.js +298 -0
- package/dist/services/codex-transcript.js.map +1 -0
- package/dist/services/group-creator.d.ts +23 -0
- package/dist/services/group-creator.d.ts.map +1 -0
- package/dist/services/group-creator.js +75 -0
- package/dist/services/group-creator.js.map +1 -0
- package/dist/services/groups-store.d.ts +98 -0
- package/dist/services/groups-store.d.ts.map +1 -0
- package/dist/services/groups-store.js +213 -0
- package/dist/services/groups-store.js.map +1 -0
- package/dist/services/oncall-store.d.ts +80 -8
- package/dist/services/oncall-store.d.ts.map +1 -1
- package/dist/services/oncall-store.js +265 -55
- package/dist/services/oncall-store.js.map +1 -1
- package/dist/services/project-scanner.d.ts +1 -2
- package/dist/services/project-scanner.d.ts.map +1 -1
- package/dist/services/project-scanner.js +118 -68
- package/dist/services/project-scanner.js.map +1 -1
- package/dist/services/schedule-store.d.ts +5 -0
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +77 -1
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/services/session-store.d.ts +22 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +62 -4
- package/dist/services/session-store.js.map +1 -1
- package/dist/setup/bots-store.d.ts +3 -0
- package/dist/setup/bots-store.d.ts.map +1 -0
- package/dist/setup/bots-store.js +24 -0
- package/dist/setup/bots-store.js.map +1 -0
- package/dist/setup/detect-platform.d.ts +14 -0
- package/dist/setup/detect-platform.d.ts.map +1 -0
- package/dist/setup/detect-platform.js +139 -0
- package/dist/setup/detect-platform.js.map +1 -0
- package/dist/setup/ensure-fonts.d.ts +13 -0
- package/dist/setup/ensure-fonts.d.ts.map +1 -0
- package/dist/setup/ensure-fonts.js +225 -0
- package/dist/setup/ensure-fonts.js.map +1 -0
- package/dist/setup/ensure-tmux.d.ts +60 -0
- package/dist/setup/ensure-tmux.d.ts.map +1 -0
- package/dist/setup/ensure-tmux.js +236 -0
- package/dist/setup/ensure-tmux.js.map +1 -0
- package/dist/setup/index.d.ts +9 -0
- package/dist/setup/index.d.ts.map +1 -0
- package/dist/setup/index.js +46 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/setup/lark-scopes.json +301 -0
- package/dist/setup/register-app.d.ts +52 -0
- package/dist/setup/register-app.d.ts.map +1 -0
- package/dist/setup/register-app.js +91 -0
- package/dist/setup/register-app.js.map +1 -0
- package/dist/setup/verify-permissions.d.ts +115 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -0
- package/dist/setup/verify-permissions.js +207 -0
- package/dist/setup/verify-permissions.js.map +1 -0
- package/dist/skills/definitions.d.ts +4 -0
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +133 -19
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/installer.d.ts +3 -1
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +18 -3
- package/dist/skills/installer.js.map +1 -1
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +6 -0
- package/dist/utils/bot-routing.d.ts.map +1 -0
- package/dist/utils/bot-routing.js +50 -0
- package/dist/utils/bot-routing.js.map +1 -0
- package/dist/utils/file-lock.d.ts +2 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +114 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/font-installer.js +1 -1
- package/dist/utils/font-installer.js.map +1 -1
- package/dist/utils/idle-detector.d.ts +6 -0
- package/dist/utils/idle-detector.d.ts.map +1 -1
- package/dist/utils/idle-detector.js +25 -4
- package/dist/utils/idle-detector.js.map +1 -1
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +60 -8
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/render-dimensions.d.ts +48 -0
- package/dist/utils/render-dimensions.d.ts.map +1 -0
- package/dist/utils/render-dimensions.js +55 -0
- package/dist/utils/render-dimensions.js.map +1 -0
- package/dist/utils/screen-analyzer.d.ts.map +1 -1
- package/dist/utils/screen-analyzer.js +24 -0
- package/dist/utils/screen-analyzer.js.map +1 -1
- package/dist/utils/screenshot-renderer.d.ts.map +1 -1
- package/dist/utils/screenshot-renderer.js +67 -23
- package/dist/utils/screenshot-renderer.js.map +1 -1
- package/dist/utils/terminal-renderer.d.ts +16 -0
- package/dist/utils/terminal-renderer.d.ts.map +1 -1
- package/dist/utils/terminal-renderer.js +40 -23
- package/dist/utils/terminal-renderer.js.map +1 -1
- package/dist/utils/transient-snapshot.d.ts +28 -0
- package/dist/utils/transient-snapshot.d.ts.map +1 -0
- package/dist/utils/transient-snapshot.js +96 -0
- package/dist/utils/transient-snapshot.js.map +1 -0
- package/dist/worker.js +2220 -83
- package/dist/worker.js.map +1 -1
- package/package.json +12 -5
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure the screenshot-renderer (`@napi-rs/canvas`) has CJK / Latin / emoji
|
|
3
|
+
* fonts available. Downloads missing categories from GitHub release
|
|
4
|
+
* artifacts to `~/.botmux/fonts/` so registration (`GlobalFonts.registerFromPath`)
|
|
5
|
+
* can find them at runtime.
|
|
6
|
+
*
|
|
7
|
+
* macOS is skipped entirely — PingFang + Menlo + Apple Color Emoji are
|
|
8
|
+
* preinstalled.
|
|
9
|
+
*
|
|
10
|
+
* Failures are NOT fatal: missing fonts only degrade the screenshot rendered
|
|
11
|
+
* for Lark (CJK becomes tofu, emoji becomes monochrome). The daemon should
|
|
12
|
+
* still come up. Tmux is the load-bearing dep, fonts are nice-to-have.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, mkdirSync, createWriteStream, statSync, unlinkSync, renameSync } from 'node:fs';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { get as httpsGet } from 'node:https';
|
|
18
|
+
import { detectPlatform } from './detect-platform.js';
|
|
19
|
+
const FONT_DIR = join(homedir(), '.botmux', 'fonts');
|
|
20
|
+
/** Download targets — pinned to specific release tags so the URLs don't rot.
|
|
21
|
+
*
|
|
22
|
+
* Sources (all permissive licenses, GitHub-hosted):
|
|
23
|
+
* - notofonts/noto-cjk @ Sans2.004 (Mono variant) — OFL
|
|
24
|
+
* - JetBrains/JetBrainsMono @ v2.304 — Apache 2.0 (replaces DejaVu since
|
|
25
|
+
* DejaVu only ships .tar.bz2 release artifacts, not individual TTFs)
|
|
26
|
+
* - googlefonts/noto-emoji @ v2.047 — OFL
|
|
27
|
+
*/
|
|
28
|
+
const FONT_SPECS = [
|
|
29
|
+
{
|
|
30
|
+
category: 'CJK',
|
|
31
|
+
files: [
|
|
32
|
+
{
|
|
33
|
+
name: 'NotoSansMonoCJKsc-Regular.otf',
|
|
34
|
+
url: 'https://github.com/notofonts/noto-cjk/raw/Sans2.004/Sans/Mono/NotoSansMonoCJKsc-Regular.otf',
|
|
35
|
+
minBytes: 1_000_000,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'NotoSansMonoCJKsc-Bold.otf',
|
|
39
|
+
url: 'https://github.com/notofonts/noto-cjk/raw/Sans2.004/Sans/Mono/NotoSansMonoCJKsc-Bold.otf',
|
|
40
|
+
minBytes: 1_000_000,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
systemPaths: [
|
|
44
|
+
'/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc',
|
|
45
|
+
'/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc',
|
|
46
|
+
'/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc',
|
|
47
|
+
'/usr/share/fonts/opentype/noto/NotoSansMonoCJK-Regular.ttc',
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
category: 'Latin',
|
|
52
|
+
files: [
|
|
53
|
+
{
|
|
54
|
+
name: 'JetBrainsMono-Regular.ttf',
|
|
55
|
+
url: 'https://github.com/JetBrains/JetBrainsMono/raw/v2.304/fonts/ttf/JetBrainsMono-Regular.ttf',
|
|
56
|
+
minBytes: 100_000,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'JetBrainsMono-Bold.ttf',
|
|
60
|
+
url: 'https://github.com/JetBrains/JetBrainsMono/raw/v2.304/fonts/ttf/JetBrainsMono-Bold.ttf',
|
|
61
|
+
minBytes: 100_000,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
systemPaths: [
|
|
65
|
+
'/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',
|
|
66
|
+
'/usr/share/fonts/dejavu/DejaVuSansMono.ttf',
|
|
67
|
+
'/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf',
|
|
68
|
+
'/usr/share/fonts/liberation/LiberationMono-Regular.ttf',
|
|
69
|
+
'/usr/share/fonts/truetype/jetbrains-mono/JetBrainsMono-Regular.ttf',
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
category: 'Emoji',
|
|
74
|
+
files: [
|
|
75
|
+
{
|
|
76
|
+
name: 'NotoColorEmoji.ttf',
|
|
77
|
+
url: 'https://github.com/googlefonts/noto-emoji/raw/v2.047/fonts/NotoColorEmoji.ttf',
|
|
78
|
+
minBytes: 5_000_000,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
systemPaths: [
|
|
82
|
+
'/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf',
|
|
83
|
+
'/usr/share/fonts/noto/NotoColorEmoji.ttf',
|
|
84
|
+
'/usr/share/fonts/google-noto-emoji/NotoColorEmoji.ttf',
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
/** Public: paths the screenshot-renderer should also probe. */
|
|
89
|
+
export function botmuxFontDir() {
|
|
90
|
+
return FONT_DIR;
|
|
91
|
+
}
|
|
92
|
+
function categoryAlreadyOk(spec) {
|
|
93
|
+
// 1. System path present?
|
|
94
|
+
if (spec.systemPaths.some(p => existsSync(p)))
|
|
95
|
+
return true;
|
|
96
|
+
// 2. We already downloaded it on a prior run?
|
|
97
|
+
return spec.files.every(f => {
|
|
98
|
+
const p = join(FONT_DIR, f.name);
|
|
99
|
+
if (!existsSync(p))
|
|
100
|
+
return false;
|
|
101
|
+
try {
|
|
102
|
+
return statSync(p).size >= f.minBytes;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** Download a URL to disk, following redirects (GitHub raw → CDN).
|
|
110
|
+
* All FONT_SPECS URLs are https, so we don't bother with an http fallback.
|
|
111
|
+
* Hard timeout (default 60s) covers connect + transfer; on expiry the
|
|
112
|
+
* request is destroyed and the partial file is unlinked. */
|
|
113
|
+
function downloadFile(url, destPath, timeoutMs = 60_000) {
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
const tmp = destPath + '.part';
|
|
116
|
+
let settled = false;
|
|
117
|
+
let activeReq = null;
|
|
118
|
+
const cleanup = () => {
|
|
119
|
+
try {
|
|
120
|
+
unlinkSync(tmp);
|
|
121
|
+
}
|
|
122
|
+
catch { /* may not exist */ }
|
|
123
|
+
};
|
|
124
|
+
const finish = (err) => {
|
|
125
|
+
if (settled)
|
|
126
|
+
return;
|
|
127
|
+
settled = true;
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
if (activeReq)
|
|
130
|
+
try {
|
|
131
|
+
activeReq.destroy();
|
|
132
|
+
}
|
|
133
|
+
catch { /* ignore */ }
|
|
134
|
+
if (err) {
|
|
135
|
+
cleanup();
|
|
136
|
+
reject(err);
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
resolve();
|
|
140
|
+
};
|
|
141
|
+
const timer = setTimeout(() => finish(new Error(`下载超时 (${timeoutMs}ms): ${url}`)), timeoutMs);
|
|
142
|
+
const followRedirect = (current, hops) => {
|
|
143
|
+
if (hops > 5)
|
|
144
|
+
return finish(new Error(`太多重定向: ${url}`));
|
|
145
|
+
const u = new URL(current);
|
|
146
|
+
if (u.protocol !== 'https:')
|
|
147
|
+
return finish(new Error(`仅支持 https://, got ${u.protocol} for ${current}`));
|
|
148
|
+
activeReq = httpsGet(u, { headers: { 'user-agent': 'botmux-font-installer' } }, (res) => {
|
|
149
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
150
|
+
res.resume();
|
|
151
|
+
followRedirect(new URL(res.headers.location, u).toString(), hops + 1);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (res.statusCode !== 200) {
|
|
155
|
+
res.resume();
|
|
156
|
+
return finish(new Error(`HTTP ${res.statusCode} for ${current}`));
|
|
157
|
+
}
|
|
158
|
+
const out = createWriteStream(tmp);
|
|
159
|
+
res.pipe(out);
|
|
160
|
+
out.on('error', (err) => finish(err));
|
|
161
|
+
out.on('finish', () => {
|
|
162
|
+
out.close(() => {
|
|
163
|
+
try {
|
|
164
|
+
renameSync(tmp, destPath);
|
|
165
|
+
finish();
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
finish(err);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
activeReq.on('error', (err) => finish(err));
|
|
174
|
+
};
|
|
175
|
+
followRedirect(url, 0);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
async function downloadCategory(spec) {
|
|
179
|
+
if (!existsSync(FONT_DIR))
|
|
180
|
+
mkdirSync(FONT_DIR, { recursive: true });
|
|
181
|
+
for (const f of spec.files) {
|
|
182
|
+
const dest = join(FONT_DIR, f.name);
|
|
183
|
+
// Skip if already present and big enough.
|
|
184
|
+
try {
|
|
185
|
+
if (existsSync(dest) && statSync(dest).size >= f.minBytes)
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
catch { /* fall through to redownload */ }
|
|
189
|
+
console.log(` 下载 ${spec.category}: ${f.name} ...`);
|
|
190
|
+
await downloadFile(f.url, dest);
|
|
191
|
+
const sz = statSync(dest).size;
|
|
192
|
+
if (sz < f.minBytes) {
|
|
193
|
+
try {
|
|
194
|
+
unlinkSync(dest);
|
|
195
|
+
}
|
|
196
|
+
catch { /* ignore */ }
|
|
197
|
+
throw new Error(`${f.name} 下载完毕但大小异常 (${sz} < ${f.minBytes})`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export async function ensureFonts(info) {
|
|
202
|
+
const platform = info ?? detectPlatform();
|
|
203
|
+
const result = { fontDir: FONT_DIR, ready: [], failed: [] };
|
|
204
|
+
// macOS: skip — system fonts cover everything we need.
|
|
205
|
+
if (platform.os === 'darwin') {
|
|
206
|
+
result.ready.push('CJK', 'Latin', 'Emoji');
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
for (const spec of FONT_SPECS) {
|
|
210
|
+
if (categoryAlreadyOk(spec)) {
|
|
211
|
+
result.ready.push(spec.category);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
await downloadCategory(spec);
|
|
216
|
+
result.ready.push(spec.category);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
console.warn(`⚠️ 字体下载失败 [${spec.category}]: ${err?.message ?? err}`);
|
|
220
|
+
result.failed.push(spec.category);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=ensure-fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-fonts.js","sourceRoot":"","sources":["../../src/setup/ensure-fonts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type PackageManager, type PlatformInfo } from './detect-platform.js';
|
|
2
|
+
export interface TmuxResult {
|
|
3
|
+
installed: boolean;
|
|
4
|
+
version?: string;
|
|
5
|
+
/** True iff we ran an installer (vs. tmux was already present). */
|
|
6
|
+
freshInstall: boolean;
|
|
7
|
+
/** Which strategy actually ran the install. */
|
|
8
|
+
strategy?: PackageManager;
|
|
9
|
+
/** When installed=false: human-readable reason for the caller's warning. */
|
|
10
|
+
reason?: string;
|
|
11
|
+
/** When installed=false: the manual command we'd have run, for the warning. */
|
|
12
|
+
manualCommand?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Strip tmux-injected env vars when spawning a tmux child process.
|
|
16
|
+
*
|
|
17
|
+
* If the parent (daemon / worker / cli wrapper) was launched from inside a
|
|
18
|
+
* tmux session, tmux exports `TMUX=<socket-path>,<pid>,<session-id>` and
|
|
19
|
+
* `TMUX_PANE=...` to the environment. Any `tmux` subcommand we run without
|
|
20
|
+
* explicit `-L <socket>` then targets *that* parent server — when the user's
|
|
21
|
+
* terminal tmux is gone (logged out, server killed, /tmp wiped) every
|
|
22
|
+
* subsequent call fails with `error connecting to <stale-socket>`.
|
|
23
|
+
*
|
|
24
|
+
* This affects botmux even when the user's *new* `tmux -V` works fine on the
|
|
25
|
+
* shell, because that test starts from a fresh shell with no stale `TMUX`.
|
|
26
|
+
* The daemon, autostarted via pm2/systemd at login, inherited the original
|
|
27
|
+
* shell's tmux env — `/tmp/tmux-1001/default` in the wild we see — and keeps
|
|
28
|
+
* hammering at it forever.
|
|
29
|
+
*
|
|
30
|
+
* Caller pattern: every execSync / execFileSync / spawnSync / pty.spawn that
|
|
31
|
+
* invokes the `tmux` binary must pass `env: tmuxEnv()` (or `tmuxEnv(opts.env)`
|
|
32
|
+
* when forwarding caller-provided env). TMUX_TMPDIR is intentionally left
|
|
33
|
+
* alone — it just changes the socket directory and is the user's deliberate
|
|
34
|
+
* override if set.
|
|
35
|
+
*/
|
|
36
|
+
export declare function tmuxEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
37
|
+
/**
|
|
38
|
+
* Functional tmux probe — actually starts a tmux server and tears it down.
|
|
39
|
+
*
|
|
40
|
+
* `tmux -V` only checks the binary, not whether tmux can create a socket and
|
|
41
|
+
* fork a session. Real-world failures we've seen: (1) /tmp owned by another
|
|
42
|
+
* user, (2) broken ~/.tmux.conf, (3) the binary is a libc-mismatched dynamic
|
|
43
|
+
* link, (4) tmux 1.x on minimal images missing libevent. All of those make
|
|
44
|
+
* `tmux -V` succeed but every `new-session` / `attach-session` fail, which
|
|
45
|
+
* floods daemon-error.log and leaves the worker with no working backend.
|
|
46
|
+
*
|
|
47
|
+
* Uses a unique `-L <socket-name>` so the probe never clobbers an existing
|
|
48
|
+
* user tmux server. Stderr is captured (not inherited) so the failure
|
|
49
|
+
* reason can surface in the bootstrap warning without spilling onto the
|
|
50
|
+
* user's terminal.
|
|
51
|
+
*/
|
|
52
|
+
export declare function probeTmuxFunctional(): {
|
|
53
|
+
ok: true;
|
|
54
|
+
version: string;
|
|
55
|
+
} | {
|
|
56
|
+
ok: false;
|
|
57
|
+
reason: string;
|
|
58
|
+
};
|
|
59
|
+
export declare function ensureTmux(info?: PlatformInfo): Promise<TmuxResult>;
|
|
60
|
+
//# sourceMappingURL=ensure-tmux.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-tmux.d.ts","sourceRoot":"","sources":["../../src/setup/ensure-tmux.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAkB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE9F,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,YAAY,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAgBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,OAAO,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,UAAU,CAG/E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAsBnG;AAiED,wBAAsB,UAAU,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAsEzE"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure tmux is installed before the daemon starts. Strategy (first one
|
|
3
|
+
* that fits wins):
|
|
4
|
+
*
|
|
5
|
+
* 1. Already installed AND functional → done.
|
|
6
|
+
* 2. brew available → `brew install tmux` (no sudo)
|
|
7
|
+
* 3. conda/mamba available → `conda install -y -c conda-forge tmux` (no sudo)
|
|
8
|
+
* 4. Linux + system pkg manager:
|
|
9
|
+
* a. NOPASSWD sudo or running as root → run non-interactively
|
|
10
|
+
* b. Has TTY → run interactively (sudo will prompt for password)
|
|
11
|
+
* c. No TTY (autostart / pm2 fork) → skip with manual command
|
|
12
|
+
* 5. Otherwise → return failure with manual command.
|
|
13
|
+
*
|
|
14
|
+
* Tmux is a NICE-TO-HAVE (enables /adopt + multi-pane Web terminal), not
|
|
15
|
+
* load-bearing: PTY backend works without it. So this function never throws —
|
|
16
|
+
* the caller inspects `installed` and routes accordingly. Earlier versions
|
|
17
|
+
* threw on failure; that broke users whose tmux binary was present but
|
|
18
|
+
* couldn't actually start a server (corrupt install / restricted /tmp /
|
|
19
|
+
* broken ~/.tmux.conf — see daemon-error logs full of "error connecting to
|
|
20
|
+
* /tmp/tmux-UID/default"), because `tmux -V` would pass but every subsequent
|
|
21
|
+
* tmux command would fail. Functional probe + soft fallback fixes both.
|
|
22
|
+
*/
|
|
23
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
24
|
+
import { detectPlatform } from './detect-platform.js';
|
|
25
|
+
function probeTmuxVersion() {
|
|
26
|
+
try {
|
|
27
|
+
const out = execSync('tmux -V', {
|
|
28
|
+
encoding: 'utf-8',
|
|
29
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
30
|
+
timeout: 3000,
|
|
31
|
+
env: tmuxEnv(),
|
|
32
|
+
});
|
|
33
|
+
return out.trim();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Strip tmux-injected env vars when spawning a tmux child process.
|
|
41
|
+
*
|
|
42
|
+
* If the parent (daemon / worker / cli wrapper) was launched from inside a
|
|
43
|
+
* tmux session, tmux exports `TMUX=<socket-path>,<pid>,<session-id>` and
|
|
44
|
+
* `TMUX_PANE=...` to the environment. Any `tmux` subcommand we run without
|
|
45
|
+
* explicit `-L <socket>` then targets *that* parent server — when the user's
|
|
46
|
+
* terminal tmux is gone (logged out, server killed, /tmp wiped) every
|
|
47
|
+
* subsequent call fails with `error connecting to <stale-socket>`.
|
|
48
|
+
*
|
|
49
|
+
* This affects botmux even when the user's *new* `tmux -V` works fine on the
|
|
50
|
+
* shell, because that test starts from a fresh shell with no stale `TMUX`.
|
|
51
|
+
* The daemon, autostarted via pm2/systemd at login, inherited the original
|
|
52
|
+
* shell's tmux env — `/tmp/tmux-1001/default` in the wild we see — and keeps
|
|
53
|
+
* hammering at it forever.
|
|
54
|
+
*
|
|
55
|
+
* Caller pattern: every execSync / execFileSync / spawnSync / pty.spawn that
|
|
56
|
+
* invokes the `tmux` binary must pass `env: tmuxEnv()` (or `tmuxEnv(opts.env)`
|
|
57
|
+
* when forwarding caller-provided env). TMUX_TMPDIR is intentionally left
|
|
58
|
+
* alone — it just changes the socket directory and is the user's deliberate
|
|
59
|
+
* override if set.
|
|
60
|
+
*/
|
|
61
|
+
export function tmuxEnv(env = process.env) {
|
|
62
|
+
const { TMUX: _tmux, TMUX_PANE: _pane, ...rest } = env;
|
|
63
|
+
return rest;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Functional tmux probe — actually starts a tmux server and tears it down.
|
|
67
|
+
*
|
|
68
|
+
* `tmux -V` only checks the binary, not whether tmux can create a socket and
|
|
69
|
+
* fork a session. Real-world failures we've seen: (1) /tmp owned by another
|
|
70
|
+
* user, (2) broken ~/.tmux.conf, (3) the binary is a libc-mismatched dynamic
|
|
71
|
+
* link, (4) tmux 1.x on minimal images missing libevent. All of those make
|
|
72
|
+
* `tmux -V` succeed but every `new-session` / `attach-session` fail, which
|
|
73
|
+
* floods daemon-error.log and leaves the worker with no working backend.
|
|
74
|
+
*
|
|
75
|
+
* Uses a unique `-L <socket-name>` so the probe never clobbers an existing
|
|
76
|
+
* user tmux server. Stderr is captured (not inherited) so the failure
|
|
77
|
+
* reason can surface in the bootstrap warning without spilling onto the
|
|
78
|
+
* user's terminal.
|
|
79
|
+
*/
|
|
80
|
+
export function probeTmuxFunctional() {
|
|
81
|
+
const version = probeTmuxVersion();
|
|
82
|
+
if (!version)
|
|
83
|
+
return { ok: false, reason: 'tmux 二进制不在 PATH 上' };
|
|
84
|
+
const sockName = `bmx-probe-${process.pid}-${Date.now()}`;
|
|
85
|
+
// env: tmuxEnv() — without this, if the daemon inherited TMUX from a tmux
|
|
86
|
+
// session that has since died, this probe would target the dead server
|
|
87
|
+
// (despite the `-L` flag tmux still walks $TMUX in some 2.x paths during
|
|
88
|
+
// startup) and report "ok: false" even on a perfectly healthy install.
|
|
89
|
+
const run = spawnSync('tmux', ['-L', sockName, 'new-session', '-d', '-s', 'probe', 'true'], {
|
|
90
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
91
|
+
timeout: 5000,
|
|
92
|
+
env: tmuxEnv(),
|
|
93
|
+
});
|
|
94
|
+
if (run.status !== 0) {
|
|
95
|
+
const stderr = (run.stderr?.toString() ?? '').trim();
|
|
96
|
+
return { ok: false, reason: stderr || `tmux new-session 失败 (exit ${run.status})` };
|
|
97
|
+
}
|
|
98
|
+
// Tear down the probe server. Best-effort — if this leaks, the kernel
|
|
99
|
+
// will GC the abstract socket when the (now-empty) tmux server exits on
|
|
100
|
+
// its own once the only client (which we didn't attach) goes away.
|
|
101
|
+
spawnSync('tmux', ['-L', sockName, 'kill-server'], { stdio: 'ignore', timeout: 3000, env: tmuxEnv() });
|
|
102
|
+
return { ok: true, version };
|
|
103
|
+
}
|
|
104
|
+
/** Wrap a system command with the appropriate sudo prefix for the current
|
|
105
|
+
* platform context, or return undefined if we cannot escalate (no
|
|
106
|
+
* passwordless sudo and no TTY to prompt on). */
|
|
107
|
+
function sudoPrefix(cmd, info) {
|
|
108
|
+
if (info.isRoot)
|
|
109
|
+
return cmd;
|
|
110
|
+
if (info.passwordlessSudo)
|
|
111
|
+
return ['sudo', '-n', ...cmd];
|
|
112
|
+
if (info.hasTty)
|
|
113
|
+
return ['sudo', ...cmd];
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
/** Build the install argv for a given package manager. Pure: returns argv[]
|
|
117
|
+
* ready for spawnSync, no side effects. Returns undefined if escalation
|
|
118
|
+
* isn't possible. */
|
|
119
|
+
function buildInstallArgv(pm, pkg, info) {
|
|
120
|
+
switch (pm) {
|
|
121
|
+
case 'brew': return ['brew', 'install', pkg];
|
|
122
|
+
case 'conda': return ['conda', 'install', '-y', '-c', 'conda-forge', pkg];
|
|
123
|
+
case 'apt': return sudoPrefix(['apt-get', 'install', '-y', pkg], info);
|
|
124
|
+
case 'dnf': return sudoPrefix(['dnf', 'install', '-y', pkg], info);
|
|
125
|
+
case 'yum': return sudoPrefix(['yum', 'install', '-y', pkg], info);
|
|
126
|
+
case 'pacman': return sudoPrefix(['pacman', '-S', '--noconfirm', pkg], info);
|
|
127
|
+
case 'apk': return sudoPrefix(['apk', 'add', pkg], info);
|
|
128
|
+
case 'zypper': return sudoPrefix(['zypper', 'install', '-y', pkg], info);
|
|
129
|
+
case 'unknown': return undefined;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** apt-get specifically needs an updated package list on minimal images
|
|
133
|
+
* before the install will find tmux. This is NOT part of buildInstallArgv
|
|
134
|
+
* (which is pure) — it runs once just before the apt install attempt.
|
|
135
|
+
* Failure here is non-fatal; the actual install will fail loudly if it
|
|
136
|
+
* can't find the package. */
|
|
137
|
+
function aptUpdateBeforeInstall(info) {
|
|
138
|
+
const argv = sudoPrefix(['apt-get', 'update'], info);
|
|
139
|
+
if (!argv)
|
|
140
|
+
return;
|
|
141
|
+
try {
|
|
142
|
+
spawnSync(argv[0], argv.slice(1), { stdio: 'inherit', timeout: 120_000 });
|
|
143
|
+
}
|
|
144
|
+
catch { /* best-effort */ }
|
|
145
|
+
}
|
|
146
|
+
/** Suggest the manual command we'd have run, for the failure message. */
|
|
147
|
+
function suggestManualCommand(pm, pkg) {
|
|
148
|
+
switch (pm) {
|
|
149
|
+
case 'brew': return `brew install ${pkg}`;
|
|
150
|
+
case 'conda': return `conda install -y -c conda-forge ${pkg}`;
|
|
151
|
+
case 'apt': return `sudo apt-get update && sudo apt-get install -y ${pkg}`;
|
|
152
|
+
case 'dnf': return `sudo dnf install -y ${pkg}`;
|
|
153
|
+
case 'yum': return `sudo yum install -y ${pkg}`;
|
|
154
|
+
case 'pacman': return `sudo pacman -S --noconfirm ${pkg}`;
|
|
155
|
+
case 'apk': return `sudo apk add ${pkg}`;
|
|
156
|
+
case 'zypper': return `sudo zypper install -y ${pkg}`;
|
|
157
|
+
default: return `(请手动安装 ${pkg})`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function runInstall(argv) {
|
|
161
|
+
const result = spawnSync(argv[0], argv.slice(1), {
|
|
162
|
+
stdio: 'inherit',
|
|
163
|
+
timeout: 10 * 60_000, // 10 min — apt-get on slow networks
|
|
164
|
+
});
|
|
165
|
+
return result.status === 0;
|
|
166
|
+
}
|
|
167
|
+
export async function ensureTmux(info) {
|
|
168
|
+
const platform = info ?? detectPlatform();
|
|
169
|
+
// Step 1: already installed AND functional?
|
|
170
|
+
// `tmux -V` alone is not enough — see probeTmuxFunctional jsdoc for the
|
|
171
|
+
// failure modes (broken /tmp perms / bad ~/.tmux.conf / mismatched libs)
|
|
172
|
+
// that pass -V but fail every actual tmux command.
|
|
173
|
+
const initialProbe = probeTmuxFunctional();
|
|
174
|
+
if (initialProbe.ok) {
|
|
175
|
+
return { installed: true, version: initialProbe.version, freshInstall: false };
|
|
176
|
+
}
|
|
177
|
+
// If the binary exists but the server can't start, no amount of
|
|
178
|
+
// `apt-get install tmux` will help — surface the underlying reason and
|
|
179
|
+
// let the caller fall back to PTY backend.
|
|
180
|
+
const versionPresent = probeTmuxVersion();
|
|
181
|
+
if (versionPresent) {
|
|
182
|
+
return {
|
|
183
|
+
installed: false,
|
|
184
|
+
freshInstall: false,
|
|
185
|
+
reason: `${versionPresent} 已安装但启动 server 失败:${initialProbe.reason}`,
|
|
186
|
+
manualCommand: '排查 ~/.tmux.conf / /tmp 权限 / libevent 依赖后再试',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
console.log('⚠️ tmux 未检测到,正在安装...');
|
|
190
|
+
// Step 2..4: walk the package-manager preference list.
|
|
191
|
+
const tried = [];
|
|
192
|
+
for (const pm of platform.packageManagers) {
|
|
193
|
+
if (pm === 'unknown')
|
|
194
|
+
continue;
|
|
195
|
+
const argv = buildInstallArgv(pm, 'tmux', platform);
|
|
196
|
+
if (!argv) {
|
|
197
|
+
tried.push(`${pm}(跳过:当前用户无 sudo 且无 TTY)`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (pm === 'apt')
|
|
201
|
+
aptUpdateBeforeInstall(platform);
|
|
202
|
+
console.log(` 尝试 ${pm}: ${argv.join(' ')}`);
|
|
203
|
+
if (runInstall(argv)) {
|
|
204
|
+
const postInstall = probeTmuxFunctional();
|
|
205
|
+
if (postInstall.ok) {
|
|
206
|
+
console.log(`✅ tmux ${postInstall.version} 安装完成 (via ${pm})`);
|
|
207
|
+
return { installed: true, version: postInstall.version, freshInstall: true, strategy: pm };
|
|
208
|
+
}
|
|
209
|
+
tried.push(`${pm}(装上了但 server 起不来:${postInstall.reason})`);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
tried.push(`${pm}(命令返回非零)`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Build a useful failure message with the most relevant manual command.
|
|
216
|
+
const preferred = platform.packageManagers.find(p => p !== 'unknown') ?? 'unknown';
|
|
217
|
+
const manual = suggestManualCommand(preferred, 'tmux');
|
|
218
|
+
const reasonLines = [
|
|
219
|
+
'自动安装 tmux 失败',
|
|
220
|
+
'已尝试:',
|
|
221
|
+
...tried.map(t => ` - ${t}`),
|
|
222
|
+
];
|
|
223
|
+
if (platform.os === 'darwin' && !platform.packageManagers.includes('brew')) {
|
|
224
|
+
reasonLines.push('macOS 推荐先安装 Homebrew:/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"');
|
|
225
|
+
}
|
|
226
|
+
if (!platform.hasTty && !platform.isRoot && !platform.passwordlessSudo && platform.os === 'linux') {
|
|
227
|
+
reasonLines.push('提示:当前不是交互式 TTY 且 sudo 需要密码,systemd/pm2 自启下无法弹密码 — 先在 shell 跑一次 `botmux start`,或配置 NOPASSWD sudoers。');
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
installed: false,
|
|
231
|
+
freshInstall: false,
|
|
232
|
+
reason: reasonLines.join('\n'),
|
|
233
|
+
manualCommand: manual,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=ensure-tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-tmux.js","sourceRoot":"","sources":["../../src/setup/ensure-tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type TmuxResult } from './ensure-tmux.js';
|
|
2
|
+
import { type FontResult } from './ensure-fonts.js';
|
|
3
|
+
export interface DependenciesReport {
|
|
4
|
+
tmux: TmuxResult;
|
|
5
|
+
fonts: FontResult;
|
|
6
|
+
}
|
|
7
|
+
export { botmuxFontDir } from './ensure-fonts.js';
|
|
8
|
+
export declare function ensureDependencies(): Promise<DependenciesReport>;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CA8BtE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency bootstrap. Called from `botmux start` and `botmux restart` so
|
|
3
|
+
* a fresh machine that just `npm i -g botmux`'d gets tmux + screenshot fonts
|
|
4
|
+
* provisioned without manual setup.
|
|
5
|
+
*
|
|
6
|
+
* - tmux is required: a failed install throws so cli.ts can exit non-zero.
|
|
7
|
+
* - fonts are nice-to-have: failures only print a warning.
|
|
8
|
+
*/
|
|
9
|
+
import { detectPlatform } from './detect-platform.js';
|
|
10
|
+
import { ensureTmux } from './ensure-tmux.js';
|
|
11
|
+
import { ensureFonts } from './ensure-fonts.js';
|
|
12
|
+
export { botmuxFontDir } from './ensure-fonts.js';
|
|
13
|
+
export async function ensureDependencies() {
|
|
14
|
+
const platform = detectPlatform();
|
|
15
|
+
// tmux: nice-to-have (enables /adopt + multi-pane Web terminal). Daemon
|
|
16
|
+
// still works on PTY backend without it, so failure is a warning, not fatal.
|
|
17
|
+
const tmux = await ensureTmux(platform);
|
|
18
|
+
if (tmux.installed) {
|
|
19
|
+
if (!tmux.freshInstall)
|
|
20
|
+
console.log(`✓ tmux ${tmux.version} (existing)`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.warn('');
|
|
24
|
+
console.warn('⚠️ tmux 不可用,已退回到 PTY backend');
|
|
25
|
+
console.warn(` 原因:${tmux.reason ?? '未知'}`);
|
|
26
|
+
if (tmux.manualCommand)
|
|
27
|
+
console.warn(` 手动尝试:${tmux.manualCommand}`);
|
|
28
|
+
console.warn(' 影响:/adopt(接管已有 CLI 会话)和多人 Web 终端不可用;常规对话不受影响。');
|
|
29
|
+
console.warn('');
|
|
30
|
+
}
|
|
31
|
+
// Fonts second — best-effort.
|
|
32
|
+
const fonts = await ensureFonts(platform);
|
|
33
|
+
if (fonts.failed.length === 0) {
|
|
34
|
+
if (platform.os === 'darwin') {
|
|
35
|
+
console.log('✓ 字体: 系统字体已就绪 (macOS)');
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log(`✓ 字体: ${fonts.ready.join(' / ')} 已就绪`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.warn(`⚠️ 字体部分缺失: ${fonts.failed.join(' / ')} —— 飞书截图中相关字符可能渲染为方块`);
|
|
43
|
+
}
|
|
44
|
+
return { tmux, fonts };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAOjE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAElC,wEAAwE;IACxE,6EAA6E;IAC7E,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;IAC3E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QACvE,OAAO,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;IAED,8BAA8B;IAC9B,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,IAAI,QAAQ,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
|