@sirenodeck/sirenodeck 0.1.1-alpha.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/LICENSE +21 -0
- package/README.md +69 -0
- package/bin/_wrapper-shared.js +42 -0
- package/bin/dev.js +95 -0
- package/bin/sirenodeck.js +42 -0
- package/config/default-config.yml +117 -0
- package/config/starter-config.yml +54 -0
- package/dist/main.mjs +64702 -0
- package/package.json +140 -0
- package/src/action/executor.ts +156 -0
- package/src/action/index.ts +7 -0
- package/src/addon/api-types.ts +15 -0
- package/src/addon/api.ts +461 -0
- package/src/addon/check-runner.ts +84 -0
- package/src/addon/index.ts +5 -0
- package/src/addon/loader.ts +441 -0
- package/src/addon/registry.ts +230 -0
- package/src/addon/spec.ts +69 -0
- package/src/api/addon.ts +33 -0
- package/src/api/asset-url.ts +24 -0
- package/src/api/index.ts +50 -0
- package/src/api/protocol-internal.ts +656 -0
- package/src/api/protocol.ts +2 -0
- package/src/api/react/index.ts +11 -0
- package/src/api/react/registry.ts +91 -0
- package/src/api/react/use-addon-channel.ts +26 -0
- package/src/api/react/use-button-state.tsx +23 -0
- package/src/api/react/use-deck.ts +24 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/logo72x72.png +0 -0
- package/src/assets/logoFull.png +0 -0
- package/src/builtin-addons/brightness/README.md +51 -0
- package/src/builtin-addons/brightness/buttons/brightness/backend.ts +13 -0
- package/src/builtin-addons/brightness/buttons/brightness/config.ts +10 -0
- package/src/builtin-addons/brightness/buttons/brightness/frontend.tsx +50 -0
- package/src/builtin-addons/brightness/domain/macos.ts +46 -0
- package/src/builtin-addons/brightness/index.d.ts +5 -0
- package/src/builtin-addons/brightness/index.ts +20 -0
- package/src/builtin-addons/brightness/poller.ts +27 -0
- package/src/builtin-addons/brightness/providers/darwin.ts +107 -0
- package/src/builtin-addons/brightness/providers/index.ts +81 -0
- package/src/builtin-addons/brightness/providers/linux.ts +108 -0
- package/src/builtin-addons/brightness/providers/windows.ts +77 -0
- package/src/builtin-addons/brightness/sirenodeck.json +6 -0
- package/src/builtin-addons/coding-agents/assets/claude-code.svg +1 -0
- package/src/builtin-addons/coding-agents/assets/opencode-dark-square.svg +1 -0
- package/src/builtin-addons/coding-agents/buttons/agent/backend.ts +82 -0
- package/src/builtin-addons/coding-agents/buttons/agent/config.ts +13 -0
- package/src/builtin-addons/coding-agents/buttons/agent/frontend.tsx +130 -0
- package/src/builtin-addons/coding-agents/buttons/agent/pages.tsx +150 -0
- package/src/builtin-addons/coding-agents/buttons/summary/backend.ts +48 -0
- package/src/builtin-addons/coding-agents/buttons/summary/config.ts +11 -0
- package/src/builtin-addons/coding-agents/buttons/summary/frontend.tsx +254 -0
- package/src/builtin-addons/coding-agents/decks/agents.ts +69 -0
- package/src/builtin-addons/coding-agents/global/backend.ts +402 -0
- package/src/builtin-addons/coding-agents/global-entry.ts +7 -0
- package/src/builtin-addons/coding-agents/hook/script.ts +123 -0
- package/src/builtin-addons/coding-agents/index.ts +15 -0
- package/src/builtin-addons/coding-agents/manifest.ts +115 -0
- package/src/builtin-addons/coding-agents/providers/claude-code.ts +470 -0
- package/src/builtin-addons/coding-agents/providers/claude-instances.ts +184 -0
- package/src/builtin-addons/coding-agents/providers/opencode-instances.ts +108 -0
- package/src/builtin-addons/coding-agents/providers/opencode.ts +636 -0
- package/src/builtin-addons/coding-agents/providers/registry.ts +120 -0
- package/src/builtin-addons/coding-agents/providers/spawn.ts +112 -0
- package/src/builtin-addons/coding-agents/shared/claude-hook-events.ts +55 -0
- package/src/builtin-addons/coding-agents/shared/claude-status.ts +236 -0
- package/src/builtin-addons/coding-agents/shared/live-count.ts +39 -0
- package/src/builtin-addons/coding-agents/shared/notifier.ts +58 -0
- package/src/builtin-addons/coding-agents/shared/opencode-status.ts +82 -0
- package/src/builtin-addons/coding-agents/shared/snapshot.ts +66 -0
- package/src/builtin-addons/coding-agents/shared/state.ts +72 -0
- package/src/builtin-addons/coding-agents/sirenodeck.json +6 -0
- package/src/builtin-addons/coding-agents/types/types.ts +33 -0
- package/src/builtin-addons/core/README.md +89 -0
- package/src/builtin-addons/core/buttons/action/backend.ts +7 -0
- package/src/builtin-addons/core/buttons/action/config.ts +13 -0
- package/src/builtin-addons/core/buttons/action/frontend.tsx +16 -0
- package/src/builtin-addons/core/buttons/change-deck/backend.ts +18 -0
- package/src/builtin-addons/core/buttons/change-deck/config.ts +15 -0
- package/src/builtin-addons/core/buttons/change-deck/frontend.tsx +18 -0
- package/src/builtin-addons/core/buttons/media-sample/backend.ts +8 -0
- package/src/builtin-addons/core/buttons/media-sample/config.ts +7 -0
- package/src/builtin-addons/core/buttons/media-sample/frontend.tsx +2 -0
- package/src/builtin-addons/core/buttons/page-nav/backend.ts +28 -0
- package/src/builtin-addons/core/buttons/page-nav/config.ts +9 -0
- package/src/builtin-addons/core/buttons/page-nav/frontend.tsx +68 -0
- package/src/builtin-addons/core/buttons/toggle/backend.ts +119 -0
- package/src/builtin-addons/core/buttons/toggle/config.ts +57 -0
- package/src/builtin-addons/core/buttons/toggle/frontend.tsx +57 -0
- package/src/builtin-addons/core/buttons/toggle/global-service.ts +174 -0
- package/src/builtin-addons/core/index.ts +120 -0
- package/src/builtin-addons/core/sirenodeck.json +6 -0
- package/src/builtin-addons/date-time/README.md +73 -0
- package/src/builtin-addons/date-time/buttons/analog-clock/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/analog-clock/config.ts +4 -0
- package/src/builtin-addons/date-time/buttons/analog-clock/frontend.tsx +88 -0
- package/src/builtin-addons/date-time/buttons/custom/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/custom/config.ts +9 -0
- package/src/builtin-addons/date-time/buttons/custom/frontend.tsx +26 -0
- package/src/builtin-addons/date-time/buttons/date/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/date/config.ts +10 -0
- package/src/builtin-addons/date-time/buttons/date/frontend.tsx +42 -0
- package/src/builtin-addons/date-time/buttons/time/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/time/config.ts +9 -0
- package/src/builtin-addons/date-time/buttons/time/frontend.tsx +29 -0
- package/src/builtin-addons/date-time/index.d.ts +5 -0
- package/src/builtin-addons/date-time/index.ts +30 -0
- package/src/builtin-addons/date-time/poller.ts +11 -0
- package/src/builtin-addons/date-time/shared/format-date.ts +36 -0
- package/src/builtin-addons/date-time/shared/format.ts +105 -0
- package/src/builtin-addons/date-time/shared/use-now.ts +10 -0
- package/src/builtin-addons/date-time/sirenodeck.json +6 -0
- package/src/builtin-addons/emoji-selector/README.md +45 -0
- package/src/builtin-addons/emoji-selector/assets/launcher.svg +10 -0
- package/src/builtin-addons/emoji-selector/assets/smileys.svg +8 -0
- package/src/builtin-addons/emoji-selector/buttons/category/backend.ts +14 -0
- package/src/builtin-addons/emoji-selector/buttons/category/config.ts +8 -0
- package/src/builtin-addons/emoji-selector/buttons/category/frontend.tsx +19 -0
- package/src/builtin-addons/emoji-selector/buttons/emoji/backend.ts +7 -0
- package/src/builtin-addons/emoji-selector/buttons/emoji/config.ts +9 -0
- package/src/builtin-addons/emoji-selector/buttons/emoji/frontend.tsx +11 -0
- package/src/builtin-addons/emoji-selector/buttons/launcher/backend.ts +13 -0
- package/src/builtin-addons/emoji-selector/buttons/launcher/config.ts +6 -0
- package/src/builtin-addons/emoji-selector/buttons/launcher/frontend.tsx +31 -0
- package/src/builtin-addons/emoji-selector/data/categories.ts +1084 -0
- package/src/builtin-addons/emoji-selector/decks/index.ts +137 -0
- package/src/builtin-addons/emoji-selector/index.d.ts +5 -0
- package/src/builtin-addons/emoji-selector/index.ts +29 -0
- package/src/builtin-addons/emoji-selector/sirenodeck.json +6 -0
- package/src/builtin-addons/emoji-selector/support.ts +49 -0
- package/src/builtin-addons/index.ts +13 -0
- package/src/builtin-addons/internal-settings/README.md +26 -0
- package/src/builtin-addons/internal-settings/backend.ts +58 -0
- package/src/builtin-addons/internal-settings/buttons/app-info/backend.ts +7 -0
- package/src/builtin-addons/internal-settings/buttons/app-info/config.ts +13 -0
- package/src/builtin-addons/internal-settings/buttons/app-info/frontend.tsx +18 -0
- package/src/builtin-addons/internal-settings/buttons/brightness/common/frontend.tsx +50 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-down/backend.ts +16 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-down/config.ts +6 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-down/frontend.tsx +8 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-up/backend.ts +16 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-up/config.ts +6 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-up/frontend.tsx +8 -0
- package/src/builtin-addons/internal-settings/decks/settings.ts +25 -0
- package/src/builtin-addons/internal-settings/index.d.ts +4 -0
- package/src/builtin-addons/internal-settings/index.ts +53 -0
- package/src/builtin-addons/internal-settings/logo72x72.png +0 -0
- package/src/builtin-addons/internal-settings/sirenodeck.json +6 -0
- package/src/builtin-addons/media/README.md +45 -0
- package/src/builtin-addons/media/backend.ts +143 -0
- package/src/builtin-addons/media/buttons/media-player/backend.ts +19 -0
- package/src/builtin-addons/media/buttons/media-player/components/MediaStatus.tsx +25 -0
- package/src/builtin-addons/media/buttons/media-player/components/MediaSurface.tsx +52 -0
- package/src/builtin-addons/media/buttons/media-player/components/ProgressBar.tsx +21 -0
- package/src/builtin-addons/media/buttons/media-player/components/status-meta.ts +36 -0
- package/src/builtin-addons/media/buttons/media-player/config.ts +4 -0
- package/src/builtin-addons/media/buttons/media-player/frontend.tsx +29 -0
- package/src/builtin-addons/media/buttons/volume/common/frontend.tsx +52 -0
- package/src/builtin-addons/media/buttons/volume/mute/backend.ts +13 -0
- package/src/builtin-addons/media/buttons/volume/mute/config.ts +4 -0
- package/src/builtin-addons/media/buttons/volume/mute/frontend.tsx +26 -0
- package/src/builtin-addons/media/buttons/volume/volume-down/backend.ts +14 -0
- package/src/builtin-addons/media/buttons/volume/volume-down/config.ts +6 -0
- package/src/builtin-addons/media/buttons/volume/volume-down/frontend.tsx +10 -0
- package/src/builtin-addons/media/buttons/volume/volume-up/backend.ts +14 -0
- package/src/builtin-addons/media/buttons/volume/volume-up/config.ts +6 -0
- package/src/builtin-addons/media/buttons/volume/volume-up/frontend.tsx +8 -0
- package/src/builtin-addons/media/checks.ts +104 -0
- package/src/builtin-addons/media/executor.ts +36 -0
- package/src/builtin-addons/media/index.d.ts +5 -0
- package/src/builtin-addons/media/index.ts +53 -0
- package/src/builtin-addons/media/progress.ts +16 -0
- package/src/builtin-addons/media/providers/darwin.ts +391 -0
- package/src/builtin-addons/media/providers/index.ts +27 -0
- package/src/builtin-addons/media/providers/linux.ts +196 -0
- package/src/builtin-addons/media/providers/types.ts +48 -0
- package/src/builtin-addons/media/providers/windows.ts +121 -0
- package/src/builtin-addons/media/sirenodeck.json +6 -0
- package/src/builtin-addons/media/state.ts +19 -0
- package/src/builtin-addons/register-builtins.ts +65 -0
- package/src/builtin-addons/session/README.md +14 -0
- package/src/builtin-addons/session/buttons/session-info/backend.ts +8 -0
- package/src/builtin-addons/session/buttons/session-info/config.ts +4 -0
- package/src/builtin-addons/session/buttons/session-info/frontend.tsx +2 -0
- package/src/builtin-addons/session/buttons/time/backend.ts +7 -0
- package/src/builtin-addons/session/buttons/time/config.ts +6 -0
- package/src/builtin-addons/session/buttons/time/frontend.tsx +2 -0
- package/src/builtin-addons/session/index.d.ts +5 -0
- package/src/builtin-addons/session/index.ts +18 -0
- package/src/builtin-addons/session/sirenodeck.json +6 -0
- package/src/builtin-addons/system-status/README.md +169 -0
- package/src/builtin-addons/system-status/buttons/_shared.ts +131 -0
- package/src/builtin-addons/system-status/buttons/system-status/frontend.tsx +224 -0
- package/src/builtin-addons/system-status/buttons/system-status/schemas.ts +67 -0
- package/src/builtin-addons/system-status/domain/catalog.ts +53 -0
- package/src/builtin-addons/system-status/domain/chart-sampler.ts +58 -0
- package/src/builtin-addons/system-status/domain/display-metrics.ts +128 -0
- package/src/builtin-addons/system-status/domain/index.ts +10 -0
- package/src/builtin-addons/system-status/domain/live-metrics.ts +1021 -0
- package/src/builtin-addons/system-status/domain/metric-ids.ts +0 -0
- package/src/builtin-addons/system-status/index.ts +16 -0
- package/src/builtin-addons/system-status/manifest.ts +105 -0
- package/src/builtin-addons/system-status/shared/metrics-catalog.ts +316 -0
- package/src/builtin-addons/system-status/sirenodeck.json +6 -0
- package/src/builtin-addons/test-buildin/decks/action-buttons-test.ts +56 -0
- package/src/builtin-addons/test-buildin/index.d.ts +5 -0
- package/src/builtin-addons/test-buildin/index.ts +16 -0
- package/src/builtin-addons/value-display/README.md +59 -0
- package/src/builtin-addons/value-display/backend.ts +86 -0
- package/src/builtin-addons/value-display/buttons/value-display/backend.ts +17 -0
- package/src/builtin-addons/value-display/buttons/value-display/config.ts +36 -0
- package/src/builtin-addons/value-display/buttons/value-display/frontend.tsx +54 -0
- package/src/builtin-addons/value-display/index.d.ts +5 -0
- package/src/builtin-addons/value-display/index.ts +23 -0
- package/src/builtin-addons/value-display/sirenodeck.json +6 -0
- package/src/builtin-addons/weather/README.md +46 -0
- package/src/builtin-addons/weather/backend.ts +121 -0
- package/src/builtin-addons/weather/buttons/weather/backend.ts +27 -0
- package/src/builtin-addons/weather/buttons/weather/config.ts +29 -0
- package/src/builtin-addons/weather/buttons/weather/frontend.tsx +63 -0
- package/src/builtin-addons/weather/buttons/weather/icons.tsx +88 -0
- package/src/builtin-addons/weather/buttons/weather/pages.tsx +109 -0
- package/src/builtin-addons/weather/domain/codes.ts +35 -0
- package/src/builtin-addons/weather/domain/fetch.ts +47 -0
- package/src/builtin-addons/weather/index.d.ts +5 -0
- package/src/builtin-addons/weather/index.ts +23 -0
- package/src/builtin-addons/weather/poller.ts +70 -0
- package/src/builtin-addons/weather/provider/city-key.ts +9 -0
- package/src/builtin-addons/weather/provider/codes.ts +35 -0
- package/src/builtin-addons/weather/provider/fetch.ts +97 -0
- package/src/builtin-addons/weather/provider/geocode.ts +67 -0
- package/src/builtin-addons/weather/provider/types.ts +42 -0
- package/src/builtin-addons/weather/sirenodeck.json +6 -0
- package/src/cli/coding-agents-onboarding.ts +434 -0
- package/src/cli/commands/addon-decks.ts +441 -0
- package/src/cli/commands/addon-registry.ts +639 -0
- package/src/cli/commands/emulator-mode.ts +289 -0
- package/src/cli/commands/install.ts +128 -0
- package/src/cli/commands/logs.ts +74 -0
- package/src/cli/commands/network-bind.ts +111 -0
- package/src/cli/commands/pipeline/helpers.ts +75 -0
- package/src/cli/commands/pipeline/preflight.ts +20 -0
- package/src/cli/commands/port-identity.ts +152 -0
- package/src/cli/commands/reload.ts +37 -0
- package/src/cli/commands/restart.ts +139 -0
- package/src/cli/commands/run.ts +2270 -0
- package/src/cli/commands/service/install.ts +121 -0
- package/src/cli/commands/service/render-template.ts +158 -0
- package/src/cli/commands/service-manager.ts +184 -0
- package/src/cli/commands/spawn-daemon.ts +251 -0
- package/src/cli/commands/start.ts +1061 -0
- package/src/cli/commands/status.ts +110 -0
- package/src/cli/commands/stop.ts +121 -0
- package/src/cli/commands/subprocess-supervisor.ts +203 -0
- package/src/cli/commands/system-requirements.ts +699 -0
- package/src/cli/cwd.ts +28 -0
- package/src/cli/first-run.ts +149 -0
- package/src/cli/http-server.ts +244 -0
- package/src/cli/index.ts +355 -0
- package/src/cli/install-root.ts +26 -0
- package/src/cli/main.ts +194 -0
- package/src/cli/package-manager.ts +175 -0
- package/src/cli/probe-deps.ts +45 -0
- package/src/cli/prompt.ts +22 -0
- package/src/cli/startup-display.ts +452 -0
- package/src/config/builtin-icons.ts +57 -0
- package/src/config/config-diff.ts +54 -0
- package/src/config/discovery.ts +64 -0
- package/src/config/icon-resolver.ts +90 -0
- package/src/config/include-resolver.ts +202 -0
- package/src/config/index.ts +9 -0
- package/src/config/loader.ts +187 -0
- package/src/config/mutation.ts +514 -0
- package/src/config/reference-expander.ts +84 -0
- package/src/config/schemas.ts +223 -0
- package/src/config/validation.ts +254 -0
- package/src/core/asset-id.ts +21 -0
- package/src/core/gesture-state.ts +344 -0
- package/src/core/icon-asset-registry.ts +127 -0
- package/src/core/icon-source.ts +86 -0
- package/src/core/index.ts +30 -0
- package/src/core/mime.ts +16 -0
- package/src/core/pagination.ts +90 -0
- package/src/core/pub-sub.ts +126 -0
- package/src/core/settings.ts +153 -0
- package/src/core/store.ts +88 -0
- package/src/core/watcher.ts +73 -0
- package/src/deck/addon-handler-bridge.ts +516 -0
- package/src/deck/deck-config.ts +271 -0
- package/src/deck/editor-surfaces.ts +87 -0
- package/src/deck/host-context.ts +18 -0
- package/src/deck/index.ts +102 -0
- package/src/deck/macro-parse.ts +182 -0
- package/src/deck/methods.ts +348 -0
- package/src/deck/paginate-deck.ts +98 -0
- package/src/deck/position-buttons.ts +57 -0
- package/src/deck/runtime/index.ts +5 -0
- package/src/deck/runtime/runtime.ts +1060 -0
- package/src/deck/runtime-subscriptions.ts +45 -0
- package/src/deck/system-back-injection.ts +62 -0
- package/src/deck/system-buttons/registry.tsx +97 -0
- package/src/deck/system-buttons/types.ts +14 -0
- package/src/device/black-frame.ts +73 -0
- package/src/device/index.ts +13 -0
- package/src/device/linux-udev.ts +36 -0
- package/src/device/models.ts +89 -0
- package/src/device/reconnecting.ts +193 -0
- package/src/device/registry.ts +38 -0
- package/src/device/stream-deck.ts +155 -0
- package/src/index.ts +74 -0
- package/src/outputClient/emulator.ts +357 -0
- package/src/outputClient/index.ts +26 -0
- package/src/outputClient/open-browser.ts +26 -0
- package/src/outputClient/real.ts +393 -0
- package/src/outputClient/types.ts +83 -0
- package/src/render/browser-renderer.ts +240 -0
- package/src/render/buffer-hash.ts +20 -0
- package/src/render/editor-handler.ts +197 -0
- package/src/render/emulator-server.ts +128 -0
- package/src/render/icon-source-resolver.ts +69 -0
- package/src/render/index.ts +16 -0
- package/src/render/protocol.ts +61 -0
- package/src/render/push-raw-image.ts +100 -0
- package/src/render/screenshot-cadence.ts +87 -0
- package/src/render/state-publisher.ts +110 -0
- package/src/render/vite-server.ts +138 -0
- package/src/render/ws-bridge.ts +315 -0
- package/src/system/device-selection.ts +80 -0
- package/src/system/glob-match.ts +74 -0
- package/src/system/providers/active-app/darwin.ts +117 -0
- package/src/system/providers/active-app/linux.ts +245 -0
- package/src/system/providers/active-app/wayland-gnome.ts +303 -0
- package/src/system/providers/active-app/windows.ts +94 -0
- package/src/system/providers/active-app.ts +97 -0
- package/src/system/providers/clipboard/darwin.ts +65 -0
- package/src/system/providers/clipboard/linux.ts +163 -0
- package/src/system/providers/clipboard/windows.ts +94 -0
- package/src/system/providers/clipboard.ts +84 -0
- package/src/system/providers/error.ts +16 -0
- package/src/system/providers/key-macro/darwin.ts +221 -0
- package/src/system/providers/key-macro/linux.ts +651 -0
- package/src/system/providers/key-macro/parser.ts +215 -0
- package/src/system/providers/key-macro/windows.ts +476 -0
- package/src/system/providers/key-macro.ts +79 -0
- package/src/system/providers/notification/darwin.ts +36 -0
- package/src/system/providers/notification/linux.ts +115 -0
- package/src/system/providers/notification/windows.ts +72 -0
- package/src/system/providers/notification.ts +75 -0
- package/src/system/providers/session/darwin.ts +82 -0
- package/src/system/providers/session/linux.ts +175 -0
- package/src/system/providers/session/windows.ts +73 -0
- package/src/system/providers/session.ts +89 -0
- package/src/system/providers/shared.ts +65 -0
- package/src/system/requirements.ts +235 -0
- package/src/system/setup-wizard/config-seed.ts +30 -0
- package/src/system/setup-wizard/format.ts +275 -0
- package/src/system/setup-wizard/index.ts +45 -0
- package/src/system/setup-wizard/open-settings.ts +26 -0
- package/src/system/setup-wizard/plan.ts +230 -0
- package/src/system/setup-wizard/probe-cache.ts +34 -0
- package/src/system/setup-wizard/probe.ts +486 -0
- package/src/system/setup-wizard/runtime-features.ts +85 -0
- package/src/system/setup-wizard/sudo.ts +178 -0
- package/src/system/setup-wizard/types.ts +133 -0
- package/src/system/virtual-stream-deck.ts +67 -0
- package/src/themes/css.ts +210 -0
- package/src/themes/default/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/default/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Italic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Light.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Thin.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/OFL.txt +93 -0
- package/src/themes/default/assets/OFL.txt +93 -0
- package/src/themes/default/assets/README.txt +106 -0
- package/src/themes/default/assets/static/IBMPlexSans-Bold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Italic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Light.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Medium.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Regular.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-SemiBold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Thin.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-ThinItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Bold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Italic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Light.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-LightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Medium.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Regular.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Thin.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-ThinItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Bold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Italic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Light.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-LightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Medium.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Regular.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Thin.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ThinItalic.ttf +0 -0
- package/src/themes/default/components.css +81 -0
- package/src/themes/default/sirenodeck.json +170 -0
- package/src/themes/index.ts +14 -0
- package/src/themes/light/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/light/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/OFL.txt +93 -0
- package/src/themes/light/assets/OFL.txt +93 -0
- package/src/themes/light/assets/README.txt +106 -0
- package/src/themes/light/assets/static/IBMPlexSans-Light.ttf +0 -0
- package/src/themes/light/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
- package/src/themes/light/assets/static/IBMPlexSans-Thin.ttf +0 -0
- package/src/themes/light/components.css +70 -0
- package/src/themes/light/sirenodeck.json +170 -0
- package/src/themes/loader.ts +343 -0
- package/src/themes/manifest.ts +164 -0
- package/src/themes/use-resolved-theme.tsx +32 -0
- package/src/ui/ButtonFrame.tsx +230 -0
- package/src/ui/contexts/AssetCacheContext.tsx +43 -0
- package/src/ui/index.ts +20 -0
- package/src/ui/primitives/Chip.tsx +53 -0
- package/src/ui/primitives/Icon.tsx +209 -0
- package/src/ui/primitives/Label.tsx +82 -0
- package/src/ui/primitives/ProgressBar.tsx +38 -0
- package/src/ui/primitives/TapIndicator.tsx +67 -0
- package/src/ui/primitives/Text.tsx +810 -0
- package/src/ui/primitives/index.ts +6 -0
- package/src/ui/surfaces/BarsSurface.tsx +211 -0
- package/src/ui/surfaces/IconLabelProgressSurface.tsx +133 -0
- package/src/ui/surfaces/IconLabelSurface.tsx +59 -0
- package/src/ui/surfaces/LabelValueListSurface.tsx +202 -0
- package/src/ui/surfaces/PaginatedSurface.tsx +164 -0
- package/src/ui/surfaces/SplitActionSurface.tsx +98 -0
- package/src/ui/surfaces/TemporaryErrorSurface.tsx +68 -0
- package/src/ui/surfaces/ValueChart.tsx +218 -0
- package/src/ui/surfaces/index.ts +32 -0
- package/src/ui/surfaces/paginated-schema.ts +22 -0
- package/src/ui/theme-presentation-builder.ts +184 -0
- package/src/ui/theme-presentation.tsx +146 -0
- package/src/ui/utils/cn.ts +12 -0
- package/src/util/cache-paths.ts +38 -0
- package/src/util/config-diff.ts +16 -0
- package/src/util/daemon-control.ts +85 -0
- package/src/util/daemon.ts +575 -0
- package/src/util/device-config.ts +56 -0
- package/src/util/errors.ts +13 -0
- package/src/util/log-reader.ts +114 -0
- package/src/util/log-tail.ts +103 -0
- package/src/util/logger.ts +417 -0
- package/src/util/process-title.ts +79 -0
- package/src/util/single-instance.ts +202 -0
- package/src/version.ts +10 -0
- package/src/vite/index.ts +6 -0
- package/src/vite/virtual-modules.ts +631 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
import { execFile } from "node:child_process"
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
3
|
+
import { promisify } from "node:util"
|
|
4
|
+
|
|
5
|
+
import type { CommandModule } from "yargs"
|
|
6
|
+
|
|
7
|
+
import type pino from "pino"
|
|
8
|
+
|
|
9
|
+
import { formatInstallInstructions, UDEV_RULES } from "@/device/linux-udev"
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
type InstallStep,
|
|
13
|
+
type InstallStepResult,
|
|
14
|
+
type SudoRunResult,
|
|
15
|
+
type SystemReport,
|
|
16
|
+
ACCESSIBILITY_SETTINGS_URL,
|
|
17
|
+
DARWIN_ACCESSIBILITY_STEP_ID,
|
|
18
|
+
buildInstallPlan,
|
|
19
|
+
hasDarwinAccessibility,
|
|
20
|
+
openSettingsUrl,
|
|
21
|
+
capturePassword,
|
|
22
|
+
isSudoNopasswd,
|
|
23
|
+
needsConfigSeed,
|
|
24
|
+
probeAll,
|
|
25
|
+
runWithSudo,
|
|
26
|
+
seedDefaultConfig,
|
|
27
|
+
summarizeReport,
|
|
28
|
+
} from "@/system/setup-wizard"
|
|
29
|
+
import {
|
|
30
|
+
formatCapabilityPanel,
|
|
31
|
+
formatResultLine,
|
|
32
|
+
formatStepInstructions,
|
|
33
|
+
color,
|
|
34
|
+
} from "@/system/setup-wizard/format"
|
|
35
|
+
import {
|
|
36
|
+
cancel,
|
|
37
|
+
confirm,
|
|
38
|
+
intro,
|
|
39
|
+
isCancel,
|
|
40
|
+
log,
|
|
41
|
+
note,
|
|
42
|
+
outro,
|
|
43
|
+
spinner,
|
|
44
|
+
} from "@/cli/prompt"
|
|
45
|
+
import { collectBuiltinAddonChecks, runAddonChecks } from "@/addon/check-runner"
|
|
46
|
+
import { onboardCodingAgents } from "@/cli/coding-agents-onboarding"
|
|
47
|
+
|
|
48
|
+
const execFileAsync = promisify(execFile)
|
|
49
|
+
|
|
50
|
+
// ponytail: failed sudo runs return stderr in the result. The spinners
|
|
51
|
+
// used to swallow it ("udev write failed" with no hint why). Surface the
|
|
52
|
+
// last line of stderr so the user sees the real reason
|
|
53
|
+
// (Permission denied, command not found, "udevadm: not found", etc.).
|
|
54
|
+
const formatFailure = (result: SudoRunResult): string => {
|
|
55
|
+
const tail = (s: string): string => {
|
|
56
|
+
const line = s
|
|
57
|
+
.split("\n")
|
|
58
|
+
.map((l) => l.trimEnd())
|
|
59
|
+
.filter((l) => l.length > 0)
|
|
60
|
+
.at(-1)
|
|
61
|
+
return line ?? ""
|
|
62
|
+
}
|
|
63
|
+
const pieces = [
|
|
64
|
+
`exit ${result.exitCode}`,
|
|
65
|
+
result.neededPassword ? "sudo rejected password" : null,
|
|
66
|
+
tail(result.stderr) || null,
|
|
67
|
+
tail(result.stdout) || null,
|
|
68
|
+
].filter((p): p is string => p !== null && p.length > 0)
|
|
69
|
+
return pieces.join(" — ")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const realExecutor = {
|
|
73
|
+
async run(command: string, args: ReadonlyArray<string>) {
|
|
74
|
+
try {
|
|
75
|
+
const result = await execFileAsync(command, [...args], {
|
|
76
|
+
timeout: 5_000,
|
|
77
|
+
})
|
|
78
|
+
return {
|
|
79
|
+
exitCode: 0,
|
|
80
|
+
stdout: result.stdout ?? "",
|
|
81
|
+
stderr: result.stderr ?? "",
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const e = err as {
|
|
85
|
+
code?: number | string
|
|
86
|
+
stdout?: string
|
|
87
|
+
stderr?: string
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
exitCode: typeof e.code === "number" ? e.code : 1,
|
|
91
|
+
stdout: e.stdout ?? "",
|
|
92
|
+
stderr: e.stderr ?? "",
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const resolveXdgConfigHome = (options: SystemRequirementsOptions): string => {
|
|
99
|
+
const home = options.homeDir ?? process.env["HOME"] ?? ""
|
|
100
|
+
return (
|
|
101
|
+
options.xdgConfigHome ?? process.env["XDG_CONFIG_HOME"] ?? `${home}/.config`
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const resolveHome = (options: SystemRequirementsOptions): string =>
|
|
106
|
+
options.homeDir ?? process.env["HOME"] ?? ""
|
|
107
|
+
|
|
108
|
+
export interface SystemRequirementsOptions {
|
|
109
|
+
readonly yes?: boolean
|
|
110
|
+
readonly nonInteractive?: boolean
|
|
111
|
+
readonly homeDir?: string
|
|
112
|
+
readonly xdgConfigHome?: string
|
|
113
|
+
readonly logger: pino.Logger
|
|
114
|
+
readonly configPath?: string
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const SESSION_LABEL: Readonly<Record<string, string>> = {
|
|
118
|
+
wayland: "Wayland",
|
|
119
|
+
x11: "X11",
|
|
120
|
+
unknown: "unknown",
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const pmLabel = (pm: string): string => {
|
|
124
|
+
switch (pm) {
|
|
125
|
+
case "apt":
|
|
126
|
+
return "apt"
|
|
127
|
+
case "dnf":
|
|
128
|
+
return "dnf"
|
|
129
|
+
case "pacman":
|
|
130
|
+
return "pacman (Arch)"
|
|
131
|
+
case "zypper":
|
|
132
|
+
return "zypper (openSUSE)"
|
|
133
|
+
case "brew":
|
|
134
|
+
return "Homebrew"
|
|
135
|
+
case "none":
|
|
136
|
+
return "none detected"
|
|
137
|
+
default:
|
|
138
|
+
return pm
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const printProbeSummary = (report: SystemReport): void => {
|
|
143
|
+
// ponytail: pad labels to the longest one ("Stream Deck" → 11) plus ":" so
|
|
144
|
+
// every row's value starts at the same column. The icon is green/red
|
|
145
|
+
// depending on whether the row represents a "ready" state.
|
|
146
|
+
const rows: Array<{ label: string; value: string; ok: boolean }> = []
|
|
147
|
+
rows.push({
|
|
148
|
+
label: "Platform",
|
|
149
|
+
value: `${report.platform} · ${SESSION_LABEL[report.session] ?? "unknown"} session`,
|
|
150
|
+
ok: true,
|
|
151
|
+
})
|
|
152
|
+
rows.push({
|
|
153
|
+
label: "Packages",
|
|
154
|
+
value: pmLabel(report.packageManager),
|
|
155
|
+
ok: true,
|
|
156
|
+
})
|
|
157
|
+
if (report.platform === "linux") {
|
|
158
|
+
rows.push({
|
|
159
|
+
label: "udev",
|
|
160
|
+
value: report.udev.rulesInstalled
|
|
161
|
+
? "installed"
|
|
162
|
+
: `missing at ${report.udev.rulesPath}`,
|
|
163
|
+
ok: report.udev.rulesInstalled,
|
|
164
|
+
})
|
|
165
|
+
rows.push({
|
|
166
|
+
label: "Stream Deck",
|
|
167
|
+
value: report.udev.streamDeckConnected
|
|
168
|
+
? `connected (${report.udev.matchedProductIds.join(", ")})`
|
|
169
|
+
: "not detected",
|
|
170
|
+
ok: report.udev.streamDeckConnected,
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
rows.push({
|
|
174
|
+
label: "Config",
|
|
175
|
+
value: report.config.exists ? "present" : `missing — ${report.config.path}`,
|
|
176
|
+
ok: report.config.exists,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const labelWidth = Math.max(...rows.map((r) => r.label.length))
|
|
180
|
+
const lines = rows.map((r) => {
|
|
181
|
+
const padded = `${r.label}:`.padEnd(labelWidth + 2)
|
|
182
|
+
const icon = r.ok ? color.green("✓") : color.red("✗")
|
|
183
|
+
return `${icon} ${color.dim(padded)} ${r.value}`
|
|
184
|
+
})
|
|
185
|
+
note(lines.join("\n"), "Detected")
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const reProbeCapability = async (
|
|
189
|
+
step: InstallStep,
|
|
190
|
+
deps: SystemRequirementsOptions,
|
|
191
|
+
): Promise<boolean> => {
|
|
192
|
+
if (step.capability === "udev" || step.capability === "config") {
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
const home = resolveHome(deps)
|
|
196
|
+
const xdg = resolveXdgConfigHome(deps)
|
|
197
|
+
const fresh = await probeAll({
|
|
198
|
+
platform: process.platform,
|
|
199
|
+
homeDir: home,
|
|
200
|
+
xdgConfigHome: xdg,
|
|
201
|
+
env: process.env,
|
|
202
|
+
executor: realExecutor,
|
|
203
|
+
fileExists: (p) => existsSync(p),
|
|
204
|
+
readFile: (p) => {
|
|
205
|
+
try {
|
|
206
|
+
return readFileSync(p, "utf8")
|
|
207
|
+
} catch {
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
return fresh.capabilities[step.capability].available
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ponytail: the Accessibility grant is the one step no package manager can
|
|
216
|
+
// perform, so the wizard used to hand the user a System Settings breadcrumb and
|
|
217
|
+
// give up. macOS can deep-link straight to the pane, so offer to open it, then
|
|
218
|
+
// re-probe in place — the grant takes effect immediately for processes started
|
|
219
|
+
// afterwards, and re-probing here saves a whole extra wizard run.
|
|
220
|
+
const runDarwinAccessibilityStep = async (
|
|
221
|
+
yesBatched: boolean,
|
|
222
|
+
): Promise<InstallStepResult> => {
|
|
223
|
+
if (await hasDarwinAccessibility(realExecutor)) return "installed"
|
|
224
|
+
|
|
225
|
+
if (!yesBatched) {
|
|
226
|
+
const shouldOpen = await confirm({
|
|
227
|
+
message: "Open System Settings at Privacy & Security → Accessibility?",
|
|
228
|
+
initialValue: true,
|
|
229
|
+
})
|
|
230
|
+
if (isCancel(shouldOpen) || !shouldOpen) return "manual"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const opened = await openSettingsUrl(realExecutor, ACCESSIBILITY_SETTINGS_URL)
|
|
234
|
+
if (!opened) {
|
|
235
|
+
log.warn("Could not open System Settings automatically.")
|
|
236
|
+
return "manual"
|
|
237
|
+
}
|
|
238
|
+
log.info(
|
|
239
|
+
`Enable ${color.green(appNeedingAccessibility())} in the list that just opened.`,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
// Non-interactive runs cannot wait for a human to flip the switch, so report
|
|
243
|
+
// the step as manual rather than re-probing a grant that cannot have changed.
|
|
244
|
+
if (yesBatched) return "manual"
|
|
245
|
+
|
|
246
|
+
const granted = await confirm({
|
|
247
|
+
message: "Granted? (re-checks the permission)",
|
|
248
|
+
initialValue: true,
|
|
249
|
+
})
|
|
250
|
+
if (isCancel(granted) || !granted) return "manual"
|
|
251
|
+
|
|
252
|
+
if (await hasDarwinAccessibility(realExecutor)) return "installed"
|
|
253
|
+
log.warn(
|
|
254
|
+
"Still not granted for this process — macOS applies the grant to the app that launched sirenodeck, so restart your terminal and re-run.",
|
|
255
|
+
)
|
|
256
|
+
return "manual"
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Best-effort name of the app the user must tick in the Accessibility list.
|
|
261
|
+
* macOS grants TCC to the bundle hosting the process, which is the terminal,
|
|
262
|
+
* not sirenodeck itself — naming it removes the most common misstep.
|
|
263
|
+
*/
|
|
264
|
+
const appNeedingAccessibility = (): string =>
|
|
265
|
+
process.env["TERM_PROGRAM"] ?? "your terminal"
|
|
266
|
+
|
|
267
|
+
const runInstallStep = async (
|
|
268
|
+
step: InstallStep,
|
|
269
|
+
yesBatched: boolean,
|
|
270
|
+
logger: pino.Logger,
|
|
271
|
+
deps: SystemRequirementsOptions,
|
|
272
|
+
): Promise<InstallStepResult> => {
|
|
273
|
+
if (step.id === DARWIN_ACCESSIBILITY_STEP_ID) {
|
|
274
|
+
return await runDarwinAccessibilityStep(yesBatched)
|
|
275
|
+
}
|
|
276
|
+
if (step.manualOnly) return "manual"
|
|
277
|
+
if (step.packages.length === 0 || step.packageManager === "none") {
|
|
278
|
+
return "manual"
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ponytail: re-probe before shell-out. If the binary is already on PATH
|
|
282
|
+
// (e.g. user installed manually between runs), skip the install step
|
|
283
|
+
// entirely — silently. The Capabilities panel already shows "Installed"
|
|
284
|
+
// for this capability, so any spinner or result line here would duplicate
|
|
285
|
+
// the same fact. The user explicitly asked for "only ask about the
|
|
286
|
+
// missing ones" — installed ones are silent.
|
|
287
|
+
const alreadyInstalled = await reProbeCapability(step, deps)
|
|
288
|
+
if (alreadyInstalled) {
|
|
289
|
+
return "installed"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const s = spinner()
|
|
293
|
+
s.start(`Checking ${step.packages.join(", ")}`)
|
|
294
|
+
s.stop(`${step.packages.join(", ")} not found — install needed`)
|
|
295
|
+
|
|
296
|
+
if (!yesBatched) {
|
|
297
|
+
const shouldRun = await confirm({
|
|
298
|
+
message: `Install ${step.title}?`,
|
|
299
|
+
initialValue: !step.sudo,
|
|
300
|
+
})
|
|
301
|
+
if (isCancel(shouldRun) || !shouldRun) return "skipped"
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let command: string
|
|
305
|
+
let args: ReadonlyArray<string>
|
|
306
|
+
switch (step.packageManager) {
|
|
307
|
+
case "apt":
|
|
308
|
+
command = "apt-get"
|
|
309
|
+
args = ["install", "-y", ...step.packages]
|
|
310
|
+
break
|
|
311
|
+
case "dnf":
|
|
312
|
+
command = "dnf"
|
|
313
|
+
args = ["install", "-y", ...step.packages]
|
|
314
|
+
break
|
|
315
|
+
case "pacman":
|
|
316
|
+
command = "pacman"
|
|
317
|
+
args = ["-S", "--noconfirm", ...step.packages]
|
|
318
|
+
break
|
|
319
|
+
case "zypper":
|
|
320
|
+
command = "zypper"
|
|
321
|
+
args = ["install", "-y", ...step.packages]
|
|
322
|
+
break
|
|
323
|
+
case "brew":
|
|
324
|
+
command = "brew"
|
|
325
|
+
args = ["install", ...step.packages]
|
|
326
|
+
break
|
|
327
|
+
default:
|
|
328
|
+
return "manual"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const install = spinner()
|
|
332
|
+
install.start(`Installing ${step.packages.join(", ")}`)
|
|
333
|
+
|
|
334
|
+
if (!step.sudo) {
|
|
335
|
+
try {
|
|
336
|
+
await execFileAsync(command, [...args], { timeout: 120_000 })
|
|
337
|
+
install.stop(`${step.packages.join(", ")} installed`)
|
|
338
|
+
return "installed"
|
|
339
|
+
} catch (err) {
|
|
340
|
+
install.stop(`Install failed: ${(err as Error).message ?? "unknown"}`)
|
|
341
|
+
logger.warn({ err, step: step.id }, "non-sudo install failed")
|
|
342
|
+
return "failed"
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const nopasswd = await isSudoNopasswd()
|
|
347
|
+
let password = ""
|
|
348
|
+
if (!nopasswd) {
|
|
349
|
+
if (!process.stdin.isTTY) {
|
|
350
|
+
install.stop("sudo password required but no TTY — skipping")
|
|
351
|
+
return "skipped"
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
password = await capturePassword("[sudo] password: ")
|
|
355
|
+
} catch {
|
|
356
|
+
install.stop("password prompt cancelled")
|
|
357
|
+
return "skipped"
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const result = await runWithSudo({
|
|
361
|
+
command,
|
|
362
|
+
args,
|
|
363
|
+
...(password.length > 0 ? { stdinInput: password + "\n" } : {}),
|
|
364
|
+
timeoutMs: 120_000,
|
|
365
|
+
logger,
|
|
366
|
+
})
|
|
367
|
+
if (result.succeeded) {
|
|
368
|
+
install.stop(`${step.packages.join(", ")} installed`)
|
|
369
|
+
return "installed"
|
|
370
|
+
}
|
|
371
|
+
install.stop(`Install failed: ${formatFailure(result)}`)
|
|
372
|
+
return "failed"
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const runUdevStep = async (
|
|
376
|
+
step: InstallStep,
|
|
377
|
+
yesBatched: boolean,
|
|
378
|
+
logger: pino.Logger,
|
|
379
|
+
): Promise<InstallStepResult> => {
|
|
380
|
+
if (!yesBatched) {
|
|
381
|
+
const shouldRun = await confirm({
|
|
382
|
+
message: `Install udev rules to ${process.platform === "linux" ? "/etc/udev/rules.d/70-sirenodeck.rules" : "system location"}?`,
|
|
383
|
+
initialValue: false,
|
|
384
|
+
})
|
|
385
|
+
if (isCancel(shouldRun) || !shouldRun) return "skipped"
|
|
386
|
+
}
|
|
387
|
+
const nopasswd = await isSudoNopasswd()
|
|
388
|
+
let password = ""
|
|
389
|
+
if (!nopasswd) {
|
|
390
|
+
if (!process.stdin.isTTY) {
|
|
391
|
+
log.warn("sudo requires a password but no TTY — skipping udev")
|
|
392
|
+
return "skipped"
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
password = await capturePassword("[sudo] password: ")
|
|
396
|
+
} catch {
|
|
397
|
+
return "skipped"
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const write = spinner()
|
|
401
|
+
write.start("Installing udev rules")
|
|
402
|
+
// ponytail: write the rules via `sudo sh -c "cat > /path <<'EOF'..."` instead
|
|
403
|
+
// of `sudo -- tee`. `execFile`'s `input` option writes stdin then closes the
|
|
404
|
+
// pipe, so `tee` inherits a closed pipe and reads EOF (writes nothing). The
|
|
405
|
+
// heredoc is embedded in the command string — the child doesn't need stdin.
|
|
406
|
+
// `SIRENO_UDEV_EOF` is unique enough to not collide with UDEV_RULES content.
|
|
407
|
+
const writeCmd = `cat > /etc/udev/rules.d/70-sirenodeck.rules <<'SIRENO_UDEV_EOF'\n${UDEV_RULES}SIRENO_UDEV_EOF`
|
|
408
|
+
const w = await runWithSudo({
|
|
409
|
+
command: "sh",
|
|
410
|
+
args: ["-c", writeCmd],
|
|
411
|
+
...(password.length > 0 ? { stdinInput: password + "\n" } : {}),
|
|
412
|
+
logger,
|
|
413
|
+
timeoutMs: 30_000,
|
|
414
|
+
})
|
|
415
|
+
if (!w.succeeded) {
|
|
416
|
+
write.stop(`udev write failed: ${formatFailure(w)}`)
|
|
417
|
+
note(formatInstallInstructions(), "You can install udev rules manually:")
|
|
418
|
+
return "failed"
|
|
419
|
+
}
|
|
420
|
+
// ponytail: still verify. A successful `cat` over `sudo` is the highest
|
|
421
|
+
// confidence signal short of running `udevadm test`, but the verify step
|
|
422
|
+
// below catches the rare case where sudo+cwd/permissions landed the file in
|
|
423
|
+
// the wrong place (e.g. EPERM on the parent dir).
|
|
424
|
+
const written = readFileSync("/etc/udev/rules.d/70-sirenodeck.rules", "utf8")
|
|
425
|
+
if (!written.includes("ATTRS{idVendor}")) {
|
|
426
|
+
write.stop("udev write failed: file is empty or incomplete")
|
|
427
|
+
logger.warn(
|
|
428
|
+
{ writtenLen: written.length },
|
|
429
|
+
"udev: write succeeded but rules file does not contain expected content",
|
|
430
|
+
)
|
|
431
|
+
note(formatInstallInstructions(), "You can install udev rules manually:")
|
|
432
|
+
return "failed"
|
|
433
|
+
}
|
|
434
|
+
const reload = spinner()
|
|
435
|
+
reload.start("Reloading udev rules")
|
|
436
|
+
const reloadResult = await runWithSudo({
|
|
437
|
+
command: "sh",
|
|
438
|
+
args: ["-c", "udevadm control --reload-rules && udevadm trigger"],
|
|
439
|
+
...(password.length > 0 ? { stdinInput: password + "\n" } : {}),
|
|
440
|
+
logger,
|
|
441
|
+
timeoutMs: 30_000,
|
|
442
|
+
})
|
|
443
|
+
if (!reloadResult.succeeded) {
|
|
444
|
+
reload.stop(`udev reload failed: ${formatFailure(reloadResult)}`)
|
|
445
|
+
note(formatInstallInstructions(), "You can install udev rules manually:")
|
|
446
|
+
return "failed"
|
|
447
|
+
}
|
|
448
|
+
reload.stop("udev rules installed")
|
|
449
|
+
return "installed"
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const runConfigSeed = async (
|
|
453
|
+
report: SystemReport,
|
|
454
|
+
options: SystemRequirementsOptions,
|
|
455
|
+
logger: pino.Logger,
|
|
456
|
+
): Promise<boolean> => {
|
|
457
|
+
if (report.config.exists) return false
|
|
458
|
+
if (options.nonInteractive) {
|
|
459
|
+
logger.warn(
|
|
460
|
+
{ path: report.config.path },
|
|
461
|
+
"config missing and non-interactive mode — skipping seed",
|
|
462
|
+
)
|
|
463
|
+
return false
|
|
464
|
+
}
|
|
465
|
+
const shouldSeed = await confirm({
|
|
466
|
+
message: `Seed default config to ${report.config.path}?`,
|
|
467
|
+
initialValue: true,
|
|
468
|
+
})
|
|
469
|
+
if (isCancel(shouldSeed) || !shouldSeed) return false
|
|
470
|
+
const s = spinner()
|
|
471
|
+
s.start(`Seeding ${report.config.path}`)
|
|
472
|
+
try {
|
|
473
|
+
const result = seedDefaultConfig(report.config.path)
|
|
474
|
+
s.stop(`Seeded default config to ${result.targetPath}`)
|
|
475
|
+
logger.info({ path: result.targetPath }, "config seeded")
|
|
476
|
+
return true
|
|
477
|
+
} catch (err) {
|
|
478
|
+
s.stop(`Seed failed: ${(err as Error).message ?? "unknown"}`)
|
|
479
|
+
logger.warn({ err, path: report.config.path }, "config seed failed")
|
|
480
|
+
return false
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export const systemRequirements = async (
|
|
485
|
+
options: SystemRequirementsOptions,
|
|
486
|
+
): Promise<void> => {
|
|
487
|
+
const { logger } = options
|
|
488
|
+
const tty = process.stdin.isTTY
|
|
489
|
+
const yes = options.yes === true
|
|
490
|
+
const nonInteractive = options.nonInteractive || yes || !tty
|
|
491
|
+
|
|
492
|
+
const homeDir = resolveHome(options)
|
|
493
|
+
const xdgConfigHome = resolveXdgConfigHome(options)
|
|
494
|
+
const platform = process.platform
|
|
495
|
+
|
|
496
|
+
const probe = spinner()
|
|
497
|
+
probe.start("Probing system")
|
|
498
|
+
|
|
499
|
+
const report = await probeAll({
|
|
500
|
+
platform,
|
|
501
|
+
homeDir,
|
|
502
|
+
xdgConfigHome,
|
|
503
|
+
env: process.env,
|
|
504
|
+
executor: realExecutor,
|
|
505
|
+
fileExists: (p) => existsSync(p),
|
|
506
|
+
readFile: (p) => {
|
|
507
|
+
try {
|
|
508
|
+
return readFileSync(p, "utf8")
|
|
509
|
+
} catch {
|
|
510
|
+
return null
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
probe.stop("Probe complete")
|
|
516
|
+
intro("sirenodeck — system requirements")
|
|
517
|
+
printProbeSummary(report)
|
|
518
|
+
|
|
519
|
+
const summary = summarizeReport(report)
|
|
520
|
+
|
|
521
|
+
// ponytail: per-addon requirement checks (playerctl for media on Linux,
|
|
522
|
+
// osascript on macOS, etc.) — each addon owns its own list of OS-specific
|
|
523
|
+
// probes and renders pass/fail here. Run before the early-return paths so
|
|
524
|
+
// operators see addon status even when core system requirements are clean.
|
|
525
|
+
// Probe failures don't block the wizard — they're informational, matching
|
|
526
|
+
// the system capability check.
|
|
527
|
+
const addonCheckInputs = collectBuiltinAddonChecks()
|
|
528
|
+
if (addonCheckInputs.length > 0) {
|
|
529
|
+
const addonOutcomes = await runAddonChecks(addonCheckInputs)
|
|
530
|
+
if (addonOutcomes.length > 0) {
|
|
531
|
+
const lines = addonOutcomes.map((o) => {
|
|
532
|
+
const mark = o.available ? color.green("●") : color.red("○")
|
|
533
|
+
const status = o.available
|
|
534
|
+
? color.green("available")
|
|
535
|
+
: color.red(
|
|
536
|
+
`missing${o.reason !== undefined ? ` — ${o.reason}` : ""}`,
|
|
537
|
+
)
|
|
538
|
+
return `${mark} ${o.addonName}/${o.checkName}: ${status}`
|
|
539
|
+
})
|
|
540
|
+
note(lines.join("\n"), "Addon checks")
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
await onboardCodingAgents(options.configPath ?? report.config.path, {
|
|
545
|
+
nonInteractive,
|
|
546
|
+
...(options.yes === true ? { yes: true } : {}),
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
if (nonInteractive) {
|
|
550
|
+
if (summary.ok) {
|
|
551
|
+
log.success("All requirements present.")
|
|
552
|
+
return
|
|
553
|
+
}
|
|
554
|
+
log.warn("Missing pieces detected (non-interactive mode — exiting).")
|
|
555
|
+
log.warn(`Run \`sirenodeck system-requirements\` interactively to fix.`)
|
|
556
|
+
for (const line of summary.lines) {
|
|
557
|
+
process.stdout.write(` ${line}\n`)
|
|
558
|
+
}
|
|
559
|
+
process.exitCode = 1
|
|
560
|
+
return
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const missingSteps = buildInstallPlan(report)
|
|
564
|
+
if (missingSteps.length === 0 && !needsConfigSeed(report)) {
|
|
565
|
+
outro("Everything looks good. Run `sirenodeck start` to begin.")
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// ponytail: no leading " " here — `note()` already pads the content to the
|
|
570
|
+
// title column. Adding more shifts the list right of "Capabilities" and the
|
|
571
|
+
// rows wrap with the wrong indent on long lines.
|
|
572
|
+
note(formatCapabilityPanel(report.capabilities), "Capabilities")
|
|
573
|
+
|
|
574
|
+
const results: Record<string, InstallStepResult> = {}
|
|
575
|
+
let anyInstalled = false
|
|
576
|
+
for (const step of missingSteps) {
|
|
577
|
+
const result =
|
|
578
|
+
step.capability === "udev"
|
|
579
|
+
? await runUdevStep(step, yes, logger)
|
|
580
|
+
: await runInstallStep(step, yes, logger, options)
|
|
581
|
+
results[step.id] = result
|
|
582
|
+
if (result === "installed") anyInstalled = true
|
|
583
|
+
// ponytail: the Capabilities panel already shows "Installed" once the
|
|
584
|
+
// install succeeds. Surface only failures/skipped/manual here — the
|
|
585
|
+
// user does not need a duplicate ✓ line per step.
|
|
586
|
+
if (result !== "installed") {
|
|
587
|
+
if (result === "manual" || result === "failed") {
|
|
588
|
+
log.warn(formatResultLine(step, result))
|
|
589
|
+
} else {
|
|
590
|
+
log.info(formatResultLine(step, result))
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// ponytail: re-print the Capabilities panel after installs so the user can
|
|
596
|
+
// see what changed without scrolling up. Skipped when nothing installed —
|
|
597
|
+
// the original panel still reflects reality.
|
|
598
|
+
if (anyInstalled) {
|
|
599
|
+
const freshReport = await probeAll({
|
|
600
|
+
platform,
|
|
601
|
+
homeDir,
|
|
602
|
+
xdgConfigHome,
|
|
603
|
+
env: process.env,
|
|
604
|
+
executor: realExecutor,
|
|
605
|
+
fileExists: (p) => existsSync(p),
|
|
606
|
+
readFile: (p) => {
|
|
607
|
+
try {
|
|
608
|
+
return readFileSync(p, "utf8")
|
|
609
|
+
} catch {
|
|
610
|
+
return null
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
})
|
|
614
|
+
note(formatCapabilityPanel(freshReport.capabilities), "Capabilities")
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (needsConfigSeed(report)) {
|
|
618
|
+
await runConfigSeed(report, options, logger)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const anyFailed = Object.values(results).some((r) => r === "failed")
|
|
622
|
+
const anyManual = Object.values(results).some((r) => r === "manual")
|
|
623
|
+
if (anyFailed || anyManual) {
|
|
624
|
+
const lines: string[] = []
|
|
625
|
+
for (const step of missingSteps) {
|
|
626
|
+
if (results[step.id] === "manual" || results[step.id] === "failed") {
|
|
627
|
+
lines.push(`[${step.id}] ${step.title}`)
|
|
628
|
+
lines.push(` ${formatStepInstructions(step)}`)
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (lines.length > 0) note(lines.join("\n"), "Manual steps remaining")
|
|
632
|
+
cancel(
|
|
633
|
+
"Setup incomplete. Run `sirenodeck system-requirements` again after manual steps.",
|
|
634
|
+
)
|
|
635
|
+
process.exitCode = 1
|
|
636
|
+
return
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
outro("Setup complete. Run `sirenodeck start` to begin.")
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
interface SystemRequirementsArgs {
|
|
643
|
+
yes?: boolean
|
|
644
|
+
nonInteractive?: boolean
|
|
645
|
+
config?: string
|
|
646
|
+
homeDir?: string
|
|
647
|
+
xdgConfigHome?: string
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export const systemRequirementsCommand: CommandModule<
|
|
651
|
+
object,
|
|
652
|
+
SystemRequirementsArgs
|
|
653
|
+
> = {
|
|
654
|
+
command: "system-requirements",
|
|
655
|
+
describe:
|
|
656
|
+
"Detect system capabilities (key macro, clipboard, notification, active-app) and optionally install missing pieces",
|
|
657
|
+
builder: (yargs) =>
|
|
658
|
+
yargs
|
|
659
|
+
.option("yes", {
|
|
660
|
+
alias: "y",
|
|
661
|
+
type: "boolean",
|
|
662
|
+
default: false,
|
|
663
|
+
description: "Accept all install prompts without confirmation",
|
|
664
|
+
})
|
|
665
|
+
.option("non-interactive", {
|
|
666
|
+
type: "boolean",
|
|
667
|
+
default: false,
|
|
668
|
+
description:
|
|
669
|
+
"Print a summary and exit non-zero if anything is missing (no prompts)",
|
|
670
|
+
})
|
|
671
|
+
.option("config", {
|
|
672
|
+
type: "string",
|
|
673
|
+
description: "Config file used to detect enabled builtin addons",
|
|
674
|
+
}),
|
|
675
|
+
handler: async (argv) => {
|
|
676
|
+
const { createLogger } = await import("@/util/logger")
|
|
677
|
+
const logger = createLogger({ verbose: false })
|
|
678
|
+
const options: SystemRequirementsOptions = {
|
|
679
|
+
logger,
|
|
680
|
+
...(argv.yes === true ? { yes: true } : {}),
|
|
681
|
+
...(argv.nonInteractive === true ? { nonInteractive: true } : {}),
|
|
682
|
+
...(argv.config !== undefined ? { configPath: argv.config } : {}),
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
await systemRequirements(options)
|
|
686
|
+
} catch (err) {
|
|
687
|
+
const e = err as { message?: string }
|
|
688
|
+
logger.error(
|
|
689
|
+
{ err },
|
|
690
|
+
e && typeof e.message === "string"
|
|
691
|
+
? e.message
|
|
692
|
+
: "system-requirements failed",
|
|
693
|
+
)
|
|
694
|
+
process.exitCode = 1
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export default systemRequirements
|