@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
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { join } from "node:path"
|
|
2
|
+
|
|
3
|
+
import type { ArgumentsCamelCase, CommandModule } from "yargs"
|
|
4
|
+
|
|
5
|
+
import { createLogger } from "@/util/logger"
|
|
6
|
+
import { BIN_NAME, PACKAGE_NAME, VERSION } from "@/version"
|
|
7
|
+
|
|
8
|
+
import { logsCommand } from "./commands/logs"
|
|
9
|
+
import { reload, type ReloadOptions } from "./commands/reload"
|
|
10
|
+
import { restart, type RestartOptions } from "./commands/restart"
|
|
11
|
+
import start, { type StartOptions } from "./commands/start"
|
|
12
|
+
import { status, type StatusOptions } from "./commands/status"
|
|
13
|
+
import { stop, type StopOptions } from "./commands/stop"
|
|
14
|
+
import { systemRequirementsCommand } from "./commands/system-requirements"
|
|
15
|
+
import { snapshotDaemonLog } from "@/util/log-reader"
|
|
16
|
+
import {
|
|
17
|
+
buildStartupBanner,
|
|
18
|
+
printAddonCheckResults,
|
|
19
|
+
printDaemonEvents,
|
|
20
|
+
printDaemonUrl,
|
|
21
|
+
printStartupComplete,
|
|
22
|
+
printStartupFailed,
|
|
23
|
+
waitForFullStart,
|
|
24
|
+
type StartOutcome,
|
|
25
|
+
} from "./startup-display"
|
|
26
|
+
import { runBuiltinAddonChecks } from "@/addon/check-runner"
|
|
27
|
+
import { installPackage, type InstallOptions } from "./commands/install"
|
|
28
|
+
import type { PackageManager } from "./package-manager"
|
|
29
|
+
import { openBrowser } from "@/outputClient/open-browser"
|
|
30
|
+
import { requestDaemonToken } from "@/util/daemon-control"
|
|
31
|
+
import {
|
|
32
|
+
isRunning,
|
|
33
|
+
readPid,
|
|
34
|
+
readRuntimeState,
|
|
35
|
+
resolveDaemonPaths,
|
|
36
|
+
} from "@/util/daemon"
|
|
37
|
+
import { cancel, intro, log } from "./prompt"
|
|
38
|
+
|
|
39
|
+
export interface GlobalOptions {
|
|
40
|
+
verbose?: boolean
|
|
41
|
+
logLevel?: string
|
|
42
|
+
quiet?: boolean
|
|
43
|
+
json?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface StartArgs extends GlobalOptions {
|
|
47
|
+
config?: string
|
|
48
|
+
port?: number
|
|
49
|
+
emulator?: boolean
|
|
50
|
+
remote?: boolean
|
|
51
|
+
deviceModel?: string
|
|
52
|
+
httpPort?: number
|
|
53
|
+
logs?: boolean
|
|
54
|
+
system?: boolean
|
|
55
|
+
autoopen?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface StatusArgs extends GlobalOptions {}
|
|
59
|
+
interface StopArgs extends GlobalOptions {}
|
|
60
|
+
interface LogsArgs extends GlobalOptions {
|
|
61
|
+
follow?: boolean
|
|
62
|
+
lines?: number
|
|
63
|
+
}
|
|
64
|
+
interface ReloadArgs extends GlobalOptions {
|
|
65
|
+
logs?: boolean
|
|
66
|
+
}
|
|
67
|
+
interface RestartArgs extends GlobalOptions {
|
|
68
|
+
logs?: boolean
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface InstallArgs extends GlobalOptions {
|
|
72
|
+
packageName: string
|
|
73
|
+
config?: string
|
|
74
|
+
global?: boolean
|
|
75
|
+
packageManager?: PackageManager
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const buildLogger = (
|
|
79
|
+
argv: ArgumentsCamelCase<GlobalOptions>,
|
|
80
|
+
): ReturnType<typeof createLogger> => {
|
|
81
|
+
const normalized = argv.logLevel === "none" ? "silent" : argv.logLevel
|
|
82
|
+
const level =
|
|
83
|
+
argv.quiet || normalized === "silent"
|
|
84
|
+
? "silent"
|
|
85
|
+
: (normalized ?? (argv.verbose ? "debug" : "info"))
|
|
86
|
+
return createLogger({
|
|
87
|
+
verbose: argv.verbose,
|
|
88
|
+
json: argv.json ?? false,
|
|
89
|
+
level,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const startCommand: CommandModule<object, StartArgs> = {
|
|
94
|
+
command: "start",
|
|
95
|
+
describe: "Start the sirenodeck daemon",
|
|
96
|
+
builder: (yargs) =>
|
|
97
|
+
yargs
|
|
98
|
+
.option("config", { type: "string", description: "Path to config.yml" })
|
|
99
|
+
.option("port", {
|
|
100
|
+
type: "number",
|
|
101
|
+
description: "Port for the local server (0 = random free port)",
|
|
102
|
+
})
|
|
103
|
+
.option("emulator", {
|
|
104
|
+
type: "boolean",
|
|
105
|
+
default: false,
|
|
106
|
+
description:
|
|
107
|
+
"Run the emulator (full UI with sidebar, toolbar, device selector) at 127.0.0.1. Browser auto-opens unless --no-autoopen.",
|
|
108
|
+
})
|
|
109
|
+
.option("autoopen", {
|
|
110
|
+
type: "boolean",
|
|
111
|
+
default: true,
|
|
112
|
+
description:
|
|
113
|
+
"Auto-open the emulator URL in a browser after start. Pass --no-autoopen for headless / capture runs.",
|
|
114
|
+
})
|
|
115
|
+
.option("remote", {
|
|
116
|
+
type: "boolean",
|
|
117
|
+
default: false,
|
|
118
|
+
description:
|
|
119
|
+
"Open the emulator server to LAN clients. Implies --emulator. Binds 0.0.0.0; prints a QR for the deck-only view with token-gated HTTP.",
|
|
120
|
+
})
|
|
121
|
+
.option("device-model", {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Emulator device model (mk2, plus, mini, xl)",
|
|
124
|
+
})
|
|
125
|
+
.option("http-port", {
|
|
126
|
+
type: "number",
|
|
127
|
+
default: 3939,
|
|
128
|
+
description:
|
|
129
|
+
"Port for the prod HTTP server that serves the bundled frontend with WS token injection",
|
|
130
|
+
})
|
|
131
|
+
.option("system", {
|
|
132
|
+
type: "boolean",
|
|
133
|
+
default: false,
|
|
134
|
+
description:
|
|
135
|
+
"Auto-install as system-wide service (requires root). Default: user-level.",
|
|
136
|
+
})
|
|
137
|
+
.option("logs", {
|
|
138
|
+
type: "boolean",
|
|
139
|
+
default: false,
|
|
140
|
+
description: "After start, follow the service log (Ctrl+C to exit)",
|
|
141
|
+
}),
|
|
142
|
+
handler: async (argv) => {
|
|
143
|
+
const logger = buildLogger(argv)
|
|
144
|
+
const options: StartOptions = {
|
|
145
|
+
logger,
|
|
146
|
+
...(argv.config !== undefined ? { config: argv.config } : {}),
|
|
147
|
+
...(argv.port !== undefined ? { port: argv.port } : {}),
|
|
148
|
+
...(argv.emulator !== undefined ? { emulator: argv.emulator } : {}),
|
|
149
|
+
...(argv.remote !== undefined ? { remote: argv.remote } : {}),
|
|
150
|
+
...(argv.deviceModel !== undefined
|
|
151
|
+
? { deviceModel: argv.deviceModel }
|
|
152
|
+
: {}),
|
|
153
|
+
...(argv.httpPort !== undefined
|
|
154
|
+
? { httpPort: argv.httpPort as number }
|
|
155
|
+
: {}),
|
|
156
|
+
...(argv.system === true ? { system: true } : {}),
|
|
157
|
+
...(argv.autoopen === false ? { noAutoOpen: true } : {}),
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
await buildStartupBanner(
|
|
161
|
+
{
|
|
162
|
+
emulator: argv.emulator === true,
|
|
163
|
+
...(argv.deviceModel !== undefined
|
|
164
|
+
? { deviceModel: argv.deviceModel }
|
|
165
|
+
: {}),
|
|
166
|
+
...(argv.port !== undefined ? { port: argv.port } : {}),
|
|
167
|
+
},
|
|
168
|
+
argv,
|
|
169
|
+
)
|
|
170
|
+
// ponytail: snapshot the daemon log size BEFORE the daemon's
|
|
171
|
+
// restart writes anything new. We use this to surface
|
|
172
|
+
// daemon-emitted warnings inline after the start completes,
|
|
173
|
+
// without reading the full log history.
|
|
174
|
+
const logSnapshot = snapshotDaemonLog()
|
|
175
|
+
const logPath = join(resolveDaemonPaths().runtimeDir, "service.log")
|
|
176
|
+
const startRequestedAt = Date.now()
|
|
177
|
+
const startPromise = start(options)
|
|
178
|
+
const outcome: StartOutcome = await waitForFullStart({
|
|
179
|
+
port: options.port ?? 52937,
|
|
180
|
+
tcpTimeoutMs: 30_000,
|
|
181
|
+
runtimeTimeoutMs: 30_000,
|
|
182
|
+
logPath,
|
|
183
|
+
logSnapshot,
|
|
184
|
+
notBefore: startRequestedAt,
|
|
185
|
+
})
|
|
186
|
+
// ponytail: print events the daemon emitted between the snapshot
|
|
187
|
+
// and now, BEFORE the success/failure line — operator's eye
|
|
188
|
+
// lands on each one even when stdout is a pipe. Silent on 0
|
|
189
|
+
// events.
|
|
190
|
+
printDaemonEvents(outcome.events)
|
|
191
|
+
|
|
192
|
+
if (outcome.runtimeReady && outcome.state !== null) {
|
|
193
|
+
await printDaemonUrl(outcome.state, outcome.token ?? "")
|
|
194
|
+
// ponytail: per-addon requirement checks (playerctl for media, etc.).
|
|
195
|
+
// Never blocks the daemon — failing checks are surfaced as warnings so
|
|
196
|
+
// the operator can act on them without digging into the log.
|
|
197
|
+
printAddonCheckResults(await runBuiltinAddonChecks())
|
|
198
|
+
printStartupComplete()
|
|
199
|
+
} else if (outcome.tcpReady) {
|
|
200
|
+
// ponytail: TCP is bound but the daemon didn't write runtime
|
|
201
|
+
// state in 30s. The daemon is alive but the runtime pipeline
|
|
202
|
+
// is still booting (or stuck). Fail loudly with whatever
|
|
203
|
+
// warnings the daemon emitted so the operator doesn't have to
|
|
204
|
+
// dig into the log file.
|
|
205
|
+
const message =
|
|
206
|
+
"daemon: TCP bound on 52937 but runtime state did not appear in 30s"
|
|
207
|
+
logger.warn(message)
|
|
208
|
+
process.exitCode = 1
|
|
209
|
+
printStartupFailed(message)
|
|
210
|
+
return
|
|
211
|
+
} else {
|
|
212
|
+
// ponytail: TCP never bound. Either the daemon crashed during
|
|
213
|
+
// boot, or port 52937 is held by an unrelated process. Either
|
|
214
|
+
// way, the start failed — the operator needs to know.
|
|
215
|
+
const message = `daemon: port ${options.port ?? 52937} did not accept connections in 30s`
|
|
216
|
+
logger.warn(message)
|
|
217
|
+
process.exitCode = 1
|
|
218
|
+
printStartupFailed(message)
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
await startPromise
|
|
222
|
+
} catch (err) {
|
|
223
|
+
printStartupFailed(err)
|
|
224
|
+
const e = err as { issues?: unknown; message?: unknown }
|
|
225
|
+
const message =
|
|
226
|
+
e &&
|
|
227
|
+
typeof e === "object" &&
|
|
228
|
+
"message" in e &&
|
|
229
|
+
typeof e.message === "string"
|
|
230
|
+
? e.message
|
|
231
|
+
: "command failed"
|
|
232
|
+
logger.error({ err }, message)
|
|
233
|
+
process.exitCode = 1
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const stopCommand: CommandModule<object, StopArgs> = {
|
|
239
|
+
command: "stop",
|
|
240
|
+
describe: "Stop the running sirenodeck daemon",
|
|
241
|
+
handler: async (argv) => {
|
|
242
|
+
const logger = buildLogger(argv)
|
|
243
|
+
const options: StopOptions = { logger }
|
|
244
|
+
await stop(options)
|
|
245
|
+
},
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const statusCommand: CommandModule<object, StatusArgs> = {
|
|
249
|
+
command: "status",
|
|
250
|
+
describe: "Check sirenodeck daemon status",
|
|
251
|
+
handler: async (argv) => {
|
|
252
|
+
const logger = buildLogger(argv)
|
|
253
|
+
const options: StatusOptions = { logger }
|
|
254
|
+
await status(options)
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const configUiCommand: CommandModule<object, GlobalOptions> = {
|
|
259
|
+
command: "config-ui",
|
|
260
|
+
describe: "Open the visual configuration editor",
|
|
261
|
+
handler: async (argv) => {
|
|
262
|
+
const logger = buildLogger(argv)
|
|
263
|
+
const paths = resolveDaemonPaths()
|
|
264
|
+
const pid = readPid(paths)
|
|
265
|
+
const state =
|
|
266
|
+
pid !== null && isRunning(pid) ? readRuntimeState(paths) : null
|
|
267
|
+
if (state === null) {
|
|
268
|
+
intro("sirenodeck config-ui")
|
|
269
|
+
log.error("Daemon is not running")
|
|
270
|
+
cancel("✗ Run `sirenodeck start` first")
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
const token = await requestDaemonToken(paths)
|
|
274
|
+
if (token === null) {
|
|
275
|
+
throw new Error("Could not retrieve the daemon token")
|
|
276
|
+
}
|
|
277
|
+
const base = state.configUiUrl
|
|
278
|
+
const url = `${base}${base.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}#/config`
|
|
279
|
+
log.info(`Config UI: ${url}`)
|
|
280
|
+
openBrowser(url, logger)
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const reloadCommand: CommandModule<object, ReloadArgs> = {
|
|
285
|
+
command: "reload",
|
|
286
|
+
describe: "Send SIGUSR1 to the daemon (in-place reload)",
|
|
287
|
+
handler: async (argv) => {
|
|
288
|
+
const logger = buildLogger(argv)
|
|
289
|
+
const options: ReloadOptions = { logger }
|
|
290
|
+
await reload(options)
|
|
291
|
+
},
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const restartCommand: CommandModule<object, RestartArgs> = {
|
|
295
|
+
command: "restart",
|
|
296
|
+
describe: "Restart the sirenodeck daemon",
|
|
297
|
+
handler: async (argv) => {
|
|
298
|
+
const logger = buildLogger(argv)
|
|
299
|
+
const options: RestartOptions = { logger }
|
|
300
|
+
await restart(options)
|
|
301
|
+
},
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const installCommand: CommandModule<object, InstallArgs> = {
|
|
305
|
+
command: "install <packageName>",
|
|
306
|
+
describe: "Install and configure an addon or theme",
|
|
307
|
+
builder: (yargs) =>
|
|
308
|
+
yargs
|
|
309
|
+
.positional("packageName", {
|
|
310
|
+
type: "string",
|
|
311
|
+
demandOption: true,
|
|
312
|
+
description: "npm package name",
|
|
313
|
+
})
|
|
314
|
+
.option("config", { type: "string", description: "Path to config.yml" })
|
|
315
|
+
.option("global", {
|
|
316
|
+
type: "boolean",
|
|
317
|
+
default: false,
|
|
318
|
+
description: "Install globally",
|
|
319
|
+
})
|
|
320
|
+
.option("package-manager", { choices: ["pnpm", "npm", "yarn"] as const }),
|
|
321
|
+
handler: async (argv) => {
|
|
322
|
+
await installPackage({
|
|
323
|
+
packageName: argv.packageName,
|
|
324
|
+
...(argv.config !== undefined ? { config: argv.config } : {}),
|
|
325
|
+
...(argv.global === true ? { global: true } : {}),
|
|
326
|
+
...(argv.packageManager !== undefined
|
|
327
|
+
? { packageManager: argv.packageManager }
|
|
328
|
+
: {}),
|
|
329
|
+
} satisfies InstallOptions)
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export const buildCli = async (): Promise<{
|
|
334
|
+
scriptName: string
|
|
335
|
+
commands: CommandModule<object, GlobalOptions>[]
|
|
336
|
+
packageName: string
|
|
337
|
+
version: string
|
|
338
|
+
}> => {
|
|
339
|
+
return {
|
|
340
|
+
scriptName: BIN_NAME,
|
|
341
|
+
commands: [
|
|
342
|
+
configUiCommand,
|
|
343
|
+
startCommand,
|
|
344
|
+
stopCommand,
|
|
345
|
+
statusCommand,
|
|
346
|
+
restartCommand,
|
|
347
|
+
reloadCommand,
|
|
348
|
+
logsCommand as CommandModule<object, GlobalOptions>,
|
|
349
|
+
systemRequirementsCommand as CommandModule<object, GlobalOptions>,
|
|
350
|
+
installCommand as CommandModule<object, GlobalOptions>,
|
|
351
|
+
],
|
|
352
|
+
packageName: PACKAGE_NAME,
|
|
353
|
+
version: VERSION,
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { dirname, join } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the install root of the distributed bundle:
|
|
7
|
+
* <root>/sirenodeck, <root>/lib/cli/main.mjs, <root>/frontend/dist, <root>/etc/install.json
|
|
8
|
+
*
|
|
9
|
+
* Priority: SIRENO_INSTALL_ROOT (set by the launcher/installer) → walk up from
|
|
10
|
+
* this module looking for the install markers (`etc/install.json`,
|
|
11
|
+
* `frontend/dist/index.html`). Returns null in the dev checkout so callers
|
|
12
|
+
* fall back to the monorepo layout.
|
|
13
|
+
*/
|
|
14
|
+
export const resolveInstallRoot = (): string | null => {
|
|
15
|
+
const env = process.env["SIRENO_INSTALL_ROOT"]
|
|
16
|
+
if (env !== undefined && env.length > 0) return env
|
|
17
|
+
let dir = dirname(fileURLToPath(import.meta.url))
|
|
18
|
+
for (let i = 0; i < 8; i += 1) {
|
|
19
|
+
if (existsSync(join(dir, "etc", "install.json"))) return dir
|
|
20
|
+
if (existsSync(join(dir, "frontend", "dist", "index.html"))) return dir
|
|
21
|
+
const parent = dirname(dir)
|
|
22
|
+
if (parent === dir) break
|
|
23
|
+
dir = parent
|
|
24
|
+
}
|
|
25
|
+
return null
|
|
26
|
+
}
|
package/src/cli/main.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url"
|
|
3
|
+
|
|
4
|
+
import yargs from "yargs"
|
|
5
|
+
import { hideBin } from "yargs/helpers"
|
|
6
|
+
|
|
7
|
+
import { buildCli } from "./index"
|
|
8
|
+
import { createLogger } from "@/util/logger"
|
|
9
|
+
import { terminateChildren } from "@/util/daemon"
|
|
10
|
+
import { holdsInstanceLock } from "@/util/single-instance"
|
|
11
|
+
import {
|
|
12
|
+
DAEMON_TITLE,
|
|
13
|
+
FOREGROUND_TITLE,
|
|
14
|
+
setProcessTitle,
|
|
15
|
+
startParentDeathWatchdog,
|
|
16
|
+
} from "@/util/process-title"
|
|
17
|
+
|
|
18
|
+
// ponytail: process name shows up in `ps`/`top`/`pstree` so operators can
|
|
19
|
+
// tell which role is holding a port. The detached daemon is `sirenodeck:dm`
|
|
20
|
+
// (12 chars, fits Linux 15-char `comm`); foreground CLI invocations are
|
|
21
|
+
// `sirenodeck:cli`. Naming is diagnostic only — the kill identity gate
|
|
22
|
+
// still relies on cmdline + port + tracked PIDs.
|
|
23
|
+
const isDaemon = process.env["SIRENO_DAEMON_CHILD"] === "1"
|
|
24
|
+
setProcessTitle(isDaemon ? DAEMON_TITLE : FOREGROUND_TITLE)
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Install global handlers for uncaught exceptions and unhandled promise
|
|
28
|
+
* rejections BEFORE any command runs. Without these, an error in an
|
|
29
|
+
* async addon poller or a stray promise rejection in the deck runtime
|
|
30
|
+
* would kill the process with no log output and no exit code.
|
|
31
|
+
*
|
|
32
|
+
* Handlers log at `fatal` level, then exit 1 so supervisors (systemd,
|
|
33
|
+
* launchd, the bin/sirenodeck.js wrapper) can detect the failure.
|
|
34
|
+
*
|
|
35
|
+
* `processExitInProgress` guards against double-exit when both `exit`
|
|
36
|
+
* events fire in sequence (common when child processes are still alive).
|
|
37
|
+
*/
|
|
38
|
+
let processExitInProgress = false
|
|
39
|
+
|
|
40
|
+
const killChildrenAndExit = (
|
|
41
|
+
logger: ReturnType<typeof createLogger>,
|
|
42
|
+
code: number,
|
|
43
|
+
): void => {
|
|
44
|
+
if (processExitInProgress) return
|
|
45
|
+
processExitInProgress = true
|
|
46
|
+
// Only the daemon that holds the instance lock owns the tracked children.
|
|
47
|
+
// Without this, a CLI whose start was refused because a daemon was already
|
|
48
|
+
// running would still run this cleanup on its way out — reading the shared
|
|
49
|
+
// children file and killing the RUNNING daemon's frontend. The start had
|
|
50
|
+
// changed nothing, but the deck went dark for as long as the daemon took to
|
|
51
|
+
// notice and respawn it.
|
|
52
|
+
if (!holdsInstanceLock()) {
|
|
53
|
+
process.exit(code)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
// ponytail: 5s grace matches the run-pipeline's pushBlackFrame + drain delay
|
|
57
|
+
// so the SIGINT handler doesn't cut off the in-flight device clear writes.
|
|
58
|
+
void terminateChildren({ logger, timeoutMs: 5_000 })
|
|
59
|
+
.catch((err: unknown) => {
|
|
60
|
+
logger.warn({ err }, "main: terminate children failed")
|
|
61
|
+
})
|
|
62
|
+
.finally(() => {
|
|
63
|
+
process.exit(code)
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const installProcessGuards = (
|
|
68
|
+
logger: ReturnType<typeof createLogger>,
|
|
69
|
+
): void => {
|
|
70
|
+
process.on("uncaughtException", (err, origin) => {
|
|
71
|
+
logger.fatal(
|
|
72
|
+
{ err, origin },
|
|
73
|
+
"uncaughtException — exiting to surface the failure",
|
|
74
|
+
)
|
|
75
|
+
killChildrenAndExit(logger, 1)
|
|
76
|
+
})
|
|
77
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
78
|
+
// ponytail: pino's `{ reason }` field serializes empty objects as `{}`,
|
|
79
|
+
// which hides the actual rejection value during operator triage. The
|
|
80
|
+
// `errSerializer` only fires on the literal key `err`, so dump the
|
|
81
|
+
// reason as a string fallback (so a thrown Error, a string, or a
|
|
82
|
+
// bare object all show up) and log the promise's stack when we can.
|
|
83
|
+
const reasonDump =
|
|
84
|
+
reason instanceof Error
|
|
85
|
+
? { type: reason.name, message: reason.message, stack: reason.stack }
|
|
86
|
+
: { value: String(reason) }
|
|
87
|
+
const stackDump =
|
|
88
|
+
promise &&
|
|
89
|
+
typeof (promise as unknown as { stack?: unknown }).stack === "string"
|
|
90
|
+
? (promise as unknown as { stack: string }).stack
|
|
91
|
+
: undefined
|
|
92
|
+
logger.fatal(
|
|
93
|
+
{
|
|
94
|
+
reason: reasonDump,
|
|
95
|
+
...(stackDump !== undefined ? { stack: stackDump } : {}),
|
|
96
|
+
},
|
|
97
|
+
"unhandledRejection — exiting to surface the failure",
|
|
98
|
+
)
|
|
99
|
+
killChildrenAndExit(logger, 1)
|
|
100
|
+
})
|
|
101
|
+
// ponytail: SIGINT/SIGTERM also reap children — supervisors (systemd,
|
|
102
|
+
// launchd, our `stop`) send SIGTERM and we don't want orphaned vite/playwright.
|
|
103
|
+
process.on("SIGTERM", () => {
|
|
104
|
+
logger.info("main: SIGTERM received")
|
|
105
|
+
killChildrenAndExit(logger, 0)
|
|
106
|
+
})
|
|
107
|
+
process.on("SIGINT", () => {
|
|
108
|
+
logger.info("main: SIGINT received")
|
|
109
|
+
killChildrenAndExit(logger, 0)
|
|
110
|
+
})
|
|
111
|
+
// ponytail: parent-death watchdog — when the dev wrapper dies (tsx watcher
|
|
112
|
+
// killed, `bin/dev.js` SIGKILL'd, ...) the daemon is reparented to init and
|
|
113
|
+
// its vite children keep their ports. The 1s ppid poll catches that and
|
|
114
|
+
// routes through the same teardown as a graceful shutdown. Linux-only and
|
|
115
|
+
// inactive when the daemon already runs under systemd (ppid=1 from boot).
|
|
116
|
+
if (isDaemon) {
|
|
117
|
+
startParentDeathWatchdog({
|
|
118
|
+
logger,
|
|
119
|
+
onOrphan: () => killChildrenAndExit(logger, 0),
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const main = async (): Promise<void> => {
|
|
125
|
+
const args = process.argv
|
|
126
|
+
const isJson = args.indexOf("--json") !== -1
|
|
127
|
+
const isVerbose =
|
|
128
|
+
args.indexOf("--verbose") !== -1 || args.indexOf("-v") !== -1
|
|
129
|
+
const logger = createLogger({ verbose: isVerbose, json: isJson })
|
|
130
|
+
installProcessGuards(logger)
|
|
131
|
+
const { scriptName, commands, packageName, version } = await buildCli()
|
|
132
|
+
|
|
133
|
+
const parser = yargs(hideBin(process.argv))
|
|
134
|
+
.scriptName(scriptName)
|
|
135
|
+
.usage("$0 <command> [options]")
|
|
136
|
+
.option("verbose", {
|
|
137
|
+
alias: "v",
|
|
138
|
+
type: "boolean",
|
|
139
|
+
description: "Enable verbose debug logging",
|
|
140
|
+
default: false,
|
|
141
|
+
})
|
|
142
|
+
.option("json", {
|
|
143
|
+
type: "boolean",
|
|
144
|
+
description: "Emit logs as JSON (default: human-friendly)",
|
|
145
|
+
default: false,
|
|
146
|
+
})
|
|
147
|
+
.option("log-level", {
|
|
148
|
+
type: "string",
|
|
149
|
+
description:
|
|
150
|
+
"Override log level (trace, debug, info, warn, error, fatal, silent, none)",
|
|
151
|
+
})
|
|
152
|
+
.option("quiet", {
|
|
153
|
+
alias: "q",
|
|
154
|
+
type: "boolean",
|
|
155
|
+
default: false,
|
|
156
|
+
description: "Suppress all logs and the startup banner (silent level)",
|
|
157
|
+
})
|
|
158
|
+
// ponytail: yargs only expands $0 in usage/command strings, not in the
|
|
159
|
+
// demandCommand message — it printed the literal "Run 0 --help". Build the
|
|
160
|
+
// sentence from scriptName instead.
|
|
161
|
+
.demandCommand(1, `Run ${scriptName} --help to see available commands.`)
|
|
162
|
+
.strict()
|
|
163
|
+
.help()
|
|
164
|
+
.alias("help", "h")
|
|
165
|
+
.version(`${packageName} ${version}`)
|
|
166
|
+
.alias("version", "V")
|
|
167
|
+
|
|
168
|
+
for (const cmd of commands) {
|
|
169
|
+
parser.command(cmd)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
await parser.parseAsync()
|
|
174
|
+
} catch (error) {
|
|
175
|
+
const e = error as { issues?: unknown; message?: string }
|
|
176
|
+
const message =
|
|
177
|
+
e &&
|
|
178
|
+
typeof e === "object" &&
|
|
179
|
+
"message" in e &&
|
|
180
|
+
typeof e.message === "string"
|
|
181
|
+
? e.message
|
|
182
|
+
: "command failed"
|
|
183
|
+
if (e && Array.isArray(e.issues)) {
|
|
184
|
+
logger.error({ err: error }, message)
|
|
185
|
+
} else {
|
|
186
|
+
logger.error({ err: error }, "command failed")
|
|
187
|
+
}
|
|
188
|
+
process.exitCode = 1
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
193
|
+
void main()
|
|
194
|
+
}
|