@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,79 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import { ProviderError } from "./error"
|
|
4
|
+
import { type CommandExecutor } from "./shared"
|
|
5
|
+
|
|
6
|
+
export interface KeyMacroProvider {
|
|
7
|
+
sendKey(comboOrText: string): Promise<void>
|
|
8
|
+
stop(): Promise<void>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { isValidKey, knownKeys, parseCombo } from "./key-macro/parser"
|
|
12
|
+
export type { ParsedCombo } from "./key-macro/parser"
|
|
13
|
+
|
|
14
|
+
export const createNullKeyMacroProvider = (
|
|
15
|
+
logger?: pino.Logger,
|
|
16
|
+
): KeyMacroProvider => {
|
|
17
|
+
if (logger) {
|
|
18
|
+
logger.warn(
|
|
19
|
+
{ provider: "key-macro" },
|
|
20
|
+
"OS key-macro provider unavailable, sendKey will throw ProviderError",
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
async sendKey(_comboOrText: string): Promise<void> {
|
|
25
|
+
throw new ProviderError(
|
|
26
|
+
"NOT_AVAILABLE",
|
|
27
|
+
"Key-macro provider not available on this platform",
|
|
28
|
+
)
|
|
29
|
+
},
|
|
30
|
+
async stop() {
|
|
31
|
+
return
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CreateKeyMacroProviderOptions {
|
|
37
|
+
readonly platform?: NodeJS.Platform
|
|
38
|
+
readonly executor: CommandExecutor
|
|
39
|
+
readonly env: Readonly<Record<string, string>>
|
|
40
|
+
readonly logger: pino.Logger
|
|
41
|
+
readonly extraFsProbe?: (tool: string) => boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const createKeyMacroProvider = async (
|
|
45
|
+
options: CreateKeyMacroProviderOptions,
|
|
46
|
+
): Promise<KeyMacroProvider> => {
|
|
47
|
+
const platform = options.platform ?? process.platform
|
|
48
|
+
if (platform === "linux") {
|
|
49
|
+
const { createLinuxKeyMacroProvider } = await import("./key-macro/linux")
|
|
50
|
+
return createLinuxKeyMacroProvider({
|
|
51
|
+
executor: options.executor,
|
|
52
|
+
env: options.env,
|
|
53
|
+
logger: options.logger,
|
|
54
|
+
...(options.extraFsProbe !== undefined
|
|
55
|
+
? { extraFsProbe: options.extraFsProbe }
|
|
56
|
+
: {}),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
if (platform === "darwin") {
|
|
60
|
+
const { createDarwinKeyMacroProvider } = await import("./key-macro/darwin")
|
|
61
|
+
return createDarwinKeyMacroProvider({
|
|
62
|
+
executor: options.executor,
|
|
63
|
+
logger: options.logger,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
if (platform === "win32") {
|
|
67
|
+
const { createWindowsKeyMacroProvider } =
|
|
68
|
+
await import("./key-macro/windows")
|
|
69
|
+
return createWindowsKeyMacroProvider({
|
|
70
|
+
executor: options.executor,
|
|
71
|
+
logger: options.logger,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
void createNullKeyMacroProvider(options.logger)
|
|
75
|
+
throw new ProviderError(
|
|
76
|
+
"UNSUPPORTED_PLATFORM",
|
|
77
|
+
`Key-macro provider not implemented for platform '${platform}' in this build`,
|
|
78
|
+
)
|
|
79
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CommandExecutor } from "../shared"
|
|
2
|
+
import type { NotificationProvider } from "../notification"
|
|
3
|
+
|
|
4
|
+
export interface DarwinNotificationDeps {
|
|
5
|
+
readonly executor: CommandExecutor
|
|
6
|
+
readonly soundPath?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const escapeForAppleScript = (value: string): string =>
|
|
10
|
+
value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
|
11
|
+
|
|
12
|
+
export const createDarwinNotificationProvider = async (
|
|
13
|
+
deps: DarwinNotificationDeps,
|
|
14
|
+
): Promise<NotificationProvider> => {
|
|
15
|
+
return {
|
|
16
|
+
async notify(args) {
|
|
17
|
+
const script = `display notification "${escapeForAppleScript(args.body)}" with title "${escapeForAppleScript(args.title)}"`
|
|
18
|
+
try {
|
|
19
|
+
const result = await deps.executor.run("osascript", ["-e", script])
|
|
20
|
+
if (result.exitCode !== 0) {
|
|
21
|
+
// swallow — osascript may fail in headless contexts; notification is best-effort
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// best-effort; never throw
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (args.sound === true && deps.soundPath !== undefined) {
|
|
28
|
+
try {
|
|
29
|
+
await deps.executor.run("afplay", [deps.soundPath])
|
|
30
|
+
} catch {
|
|
31
|
+
// best-effort
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import { type CommandExecutor, withTimeout } from "../shared"
|
|
4
|
+
import type { NotificationProvider } from "../notification"
|
|
5
|
+
|
|
6
|
+
export interface LinuxNotificationDeps {
|
|
7
|
+
readonly executor: CommandExecutor
|
|
8
|
+
readonly logger: pino.Logger
|
|
9
|
+
readonly timeoutMs?: number
|
|
10
|
+
readonly extraFsProbe?: (tool: string) => boolean
|
|
11
|
+
readonly soundPath?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const NOTIFY_SEND_TOOL = "notify-send"
|
|
15
|
+
const FFPLAY_TOOL = "ffplay"
|
|
16
|
+
const PAPLAY_TOOL = "paplay"
|
|
17
|
+
const DEFAULT_TIMEOUT_MS = 2_000
|
|
18
|
+
const SOUND_TIMEOUT_MS = 5_000
|
|
19
|
+
|
|
20
|
+
const probeTool = async (
|
|
21
|
+
executor: CommandExecutor,
|
|
22
|
+
tool: string,
|
|
23
|
+
extraFsProbe?: (tool: string) => boolean,
|
|
24
|
+
): Promise<boolean> => {
|
|
25
|
+
const result = await executor.run("which", [tool])
|
|
26
|
+
if (result.exitCode === 0 && result.stdout.trim().length > 0) return true
|
|
27
|
+
return extraFsProbe?.(tool) === true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const shellQuote = (value: string): string =>
|
|
31
|
+
`'${value.replace(/'/g, "'\\''")}'`
|
|
32
|
+
|
|
33
|
+
export const createLinuxNotificationProvider = async (
|
|
34
|
+
deps: LinuxNotificationDeps,
|
|
35
|
+
): Promise<NotificationProvider> => {
|
|
36
|
+
const notifySendOk = await probeTool(
|
|
37
|
+
deps.executor,
|
|
38
|
+
NOTIFY_SEND_TOOL,
|
|
39
|
+
deps.extraFsProbe,
|
|
40
|
+
)
|
|
41
|
+
if (!notifySendOk) {
|
|
42
|
+
deps.logger.warn(
|
|
43
|
+
{},
|
|
44
|
+
"linux notification: notify-send not found; notify() will be a no-op",
|
|
45
|
+
)
|
|
46
|
+
return {
|
|
47
|
+
async notify() {
|
|
48
|
+
return
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ffplayOk = await probeTool(
|
|
54
|
+
deps.executor,
|
|
55
|
+
FFPLAY_TOOL,
|
|
56
|
+
deps.extraFsProbe,
|
|
57
|
+
)
|
|
58
|
+
const paplayOk =
|
|
59
|
+
!ffplayOk &&
|
|
60
|
+
(await probeTool(deps.executor, PAPLAY_TOOL, deps.extraFsProbe))
|
|
61
|
+
const soundTool: string | null = ffplayOk
|
|
62
|
+
? FFPLAY_TOOL
|
|
63
|
+
: paplayOk
|
|
64
|
+
? PAPLAY_TOOL
|
|
65
|
+
: null
|
|
66
|
+
|
|
67
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
async notify(args) {
|
|
71
|
+
const sendArgs = [
|
|
72
|
+
"-t",
|
|
73
|
+
"5000",
|
|
74
|
+
shellQuote(args.title),
|
|
75
|
+
shellQuote(args.body),
|
|
76
|
+
]
|
|
77
|
+
try {
|
|
78
|
+
const result = await withTimeout(
|
|
79
|
+
deps.executor.run(NOTIFY_SEND_TOOL, sendArgs),
|
|
80
|
+
timeoutMs,
|
|
81
|
+
)
|
|
82
|
+
if (result.exitCode !== 0) {
|
|
83
|
+
deps.logger.warn(
|
|
84
|
+
{ stderr: result.stderr.trim() },
|
|
85
|
+
"linux notification: notify-send returned non-zero",
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
deps.logger.warn({ err }, "linux notification: notify-send failed")
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
args.sound === true &&
|
|
94
|
+
deps.soundPath !== undefined &&
|
|
95
|
+
soundTool !== null
|
|
96
|
+
) {
|
|
97
|
+
try {
|
|
98
|
+
await withTimeout(
|
|
99
|
+
deps.executor.run(soundTool, [
|
|
100
|
+
"-nodisp",
|
|
101
|
+
"-autoexit",
|
|
102
|
+
deps.soundPath,
|
|
103
|
+
]),
|
|
104
|
+
SOUND_TIMEOUT_MS,
|
|
105
|
+
)
|
|
106
|
+
} catch (err) {
|
|
107
|
+
deps.logger.warn(
|
|
108
|
+
{ err, tool: soundTool },
|
|
109
|
+
"linux notification: sound playback failed",
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import { type CommandExecutor, withTimeout } from "../shared"
|
|
4
|
+
import type { NotificationProvider } from "../notification"
|
|
5
|
+
|
|
6
|
+
export interface WindowsNotificationDeps {
|
|
7
|
+
readonly executor: CommandExecutor
|
|
8
|
+
readonly logger: pino.Logger
|
|
9
|
+
readonly timeoutMs?: number
|
|
10
|
+
readonly soundPath?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 5_000
|
|
14
|
+
|
|
15
|
+
const shellQuote = (value: string): string =>
|
|
16
|
+
`'${value.replace(/'/g, "''").replace(/'/g, "'\\''")}'`
|
|
17
|
+
|
|
18
|
+
// ponytail: raw toast template via PowerShell BurntToast-free path. Sufficient
|
|
19
|
+
// for our "Pomodoro done" use case; richer XML templates are out of scope.
|
|
20
|
+
const buildToastScript = (title: string, body: string): string => {
|
|
21
|
+
const safeTitle = title.replace(/[`$"\\]/g, "\\$&")
|
|
22
|
+
const safeBody = body.replace(/[`$"\\]/g, "\\$&")
|
|
23
|
+
return [
|
|
24
|
+
"Add-Type -AssemblyName System.Windows.Forms | Out-Null",
|
|
25
|
+
`$balloon = New-Object System.Windows.Forms.NotifyIcon`,
|
|
26
|
+
`$balloon.Icon = [System.Drawing.SystemIcons]::Information`,
|
|
27
|
+
`$balloon.BalloonTipTitle = "${safeTitle}"`,
|
|
28
|
+
`$balloon.BalloonTipText = "${safeBody}"`,
|
|
29
|
+
`$balloon.Visible = $true`,
|
|
30
|
+
`$balloon.ShowBalloonTip(5000)`,
|
|
31
|
+
"Start-Sleep -Milliseconds 5500",
|
|
32
|
+
"$balloon.Dispose()",
|
|
33
|
+
].join("; ")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const createWindowsNotificationProvider = async (
|
|
37
|
+
deps: WindowsNotificationDeps,
|
|
38
|
+
): Promise<NotificationProvider> => {
|
|
39
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
async notify(args) {
|
|
43
|
+
const script = buildToastScript(args.title, args.body)
|
|
44
|
+
try {
|
|
45
|
+
await withTimeout(
|
|
46
|
+
deps.executor.run("powershell", ["-NoProfile", "-Command", script]),
|
|
47
|
+
timeoutMs,
|
|
48
|
+
)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
deps.logger.warn({ err }, "windows notification: toast failed")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (args.sound === true && deps.soundPath !== undefined) {
|
|
54
|
+
try {
|
|
55
|
+
await withTimeout(
|
|
56
|
+
deps.executor.run("powershell", [
|
|
57
|
+
"-NoProfile",
|
|
58
|
+
"-Command",
|
|
59
|
+
`(New-Object Media.SoundPlayer '${shellQuote(deps.soundPath)}').PlaySync()`,
|
|
60
|
+
]),
|
|
61
|
+
timeoutMs,
|
|
62
|
+
)
|
|
63
|
+
} catch (err) {
|
|
64
|
+
deps.logger.warn(
|
|
65
|
+
{ err },
|
|
66
|
+
"windows notification: sound playback failed",
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import { logNull, type CommandExecutor } from "./shared"
|
|
4
|
+
|
|
5
|
+
export interface NotificationArgs {
|
|
6
|
+
readonly title: string
|
|
7
|
+
readonly body: string
|
|
8
|
+
readonly sound?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface NotificationProvider {
|
|
12
|
+
notify(args: NotificationArgs): Promise<void>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CreateNotificationProviderOptions {
|
|
16
|
+
readonly platform?: NodeJS.Platform
|
|
17
|
+
readonly executor: CommandExecutor
|
|
18
|
+
readonly env: Readonly<Record<string, string>>
|
|
19
|
+
readonly logger: pino.Logger
|
|
20
|
+
readonly extraFsProbe?: (tool: string) => boolean
|
|
21
|
+
readonly soundPath?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const createNullNotificationProvider = (
|
|
25
|
+
logger?: pino.Logger,
|
|
26
|
+
): NotificationProvider => {
|
|
27
|
+
logNull(logger, "notification", "platform unsupported, notify() is a no-op")
|
|
28
|
+
return {
|
|
29
|
+
async notify() {
|
|
30
|
+
return
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const createNotificationProvider = async (
|
|
36
|
+
options: CreateNotificationProviderOptions,
|
|
37
|
+
): Promise<NotificationProvider> => {
|
|
38
|
+
const platform = options.platform ?? process.platform
|
|
39
|
+
if (platform === "linux") {
|
|
40
|
+
const { createLinuxNotificationProvider } =
|
|
41
|
+
await import("./notification/linux")
|
|
42
|
+
return createLinuxNotificationProvider({
|
|
43
|
+
executor: options.executor,
|
|
44
|
+
logger: options.logger,
|
|
45
|
+
...(options.extraFsProbe !== undefined
|
|
46
|
+
? { extraFsProbe: options.extraFsProbe }
|
|
47
|
+
: {}),
|
|
48
|
+
...(options.soundPath !== undefined
|
|
49
|
+
? { soundPath: options.soundPath }
|
|
50
|
+
: {}),
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
if (platform === "darwin") {
|
|
54
|
+
const { createDarwinNotificationProvider } =
|
|
55
|
+
await import("./notification/darwin")
|
|
56
|
+
return createDarwinNotificationProvider({
|
|
57
|
+
executor: options.executor,
|
|
58
|
+
...(options.soundPath !== undefined
|
|
59
|
+
? { soundPath: options.soundPath }
|
|
60
|
+
: {}),
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
if (platform === "win32") {
|
|
64
|
+
const { createWindowsNotificationProvider } =
|
|
65
|
+
await import("./notification/windows")
|
|
66
|
+
return createWindowsNotificationProvider({
|
|
67
|
+
executor: options.executor,
|
|
68
|
+
logger: options.logger,
|
|
69
|
+
...(options.soundPath !== undefined
|
|
70
|
+
? { soundPath: options.soundPath }
|
|
71
|
+
: {}),
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
return createNullNotificationProvider(options.logger)
|
|
75
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import type { CommandExecutor } from "../shared"
|
|
4
|
+
import { type SessionProvider, type SessionState } from "../session"
|
|
5
|
+
|
|
6
|
+
export interface DarwinSessionDeps {
|
|
7
|
+
readonly executor: CommandExecutor
|
|
8
|
+
readonly logger: pino.Logger
|
|
9
|
+
readonly pollIntervalMs?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const toState = (locked: boolean): SessionState =>
|
|
13
|
+
locked ? "locked" : "unlocked"
|
|
14
|
+
|
|
15
|
+
// ponytail: this used to ask System Events whether the `loginwindow` process
|
|
16
|
+
// was running, which was wrong twice over. The script was a syntax error
|
|
17
|
+
// (`loginwindow process` instead of `process "loginwindow"`), so every tick
|
|
18
|
+
// exited non-zero and the state stayed "unknown" forever; and had it parsed,
|
|
19
|
+
// loginwindow runs for the whole login session whether or not the screen is
|
|
20
|
+
// locked, so it would have reported "locked" permanently.
|
|
21
|
+
//
|
|
22
|
+
// The real signal is CGSSessionScreenIsLocked in the console session
|
|
23
|
+
// dictionary, which `ioreg` exposes without any Accessibility grant:
|
|
24
|
+
// the key is present and true only while the screen is locked, and absent
|
|
25
|
+
// entirely when unlocked.
|
|
26
|
+
const IOREG_ARGS = ["-n", "Root", "-d1", "-a"] as const
|
|
27
|
+
|
|
28
|
+
const parseScreenLocked = (raw: string): boolean =>
|
|
29
|
+
/<key>CGSSessionScreenIsLocked<\/key>\s*<true\/>/.test(raw)
|
|
30
|
+
|
|
31
|
+
export const createDarwinSessionProvider = async (
|
|
32
|
+
deps: DarwinSessionDeps,
|
|
33
|
+
): Promise<SessionProvider> => {
|
|
34
|
+
const listeners = new Set<(s: SessionState) => void>()
|
|
35
|
+
let state: SessionState = "unknown"
|
|
36
|
+
let stopped = false
|
|
37
|
+
const pollMs = deps.pollIntervalMs ?? 5_000
|
|
38
|
+
|
|
39
|
+
const tick = async (): Promise<SessionState> => {
|
|
40
|
+
try {
|
|
41
|
+
const result = await deps.executor.run("ioreg", [...IOREG_ARGS], {
|
|
42
|
+
timeoutMs: 2_000,
|
|
43
|
+
})
|
|
44
|
+
if (result.exitCode !== 0) return state
|
|
45
|
+
return toState(parseScreenLocked(result.stdout))
|
|
46
|
+
} catch (err) {
|
|
47
|
+
deps.logger.debug({ err }, "session: ioreg failed")
|
|
48
|
+
return state
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
state = await tick()
|
|
53
|
+
let interval: ReturnType<typeof setInterval> | null = setInterval(() => {
|
|
54
|
+
if (stopped) return
|
|
55
|
+
void tick().then((s) => {
|
|
56
|
+
if (stopped) return
|
|
57
|
+
if (s !== state) {
|
|
58
|
+
state = s
|
|
59
|
+
for (const l of listeners) l(state)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}, pollMs)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
getState() {
|
|
66
|
+
return state
|
|
67
|
+
},
|
|
68
|
+
subscribe(handler) {
|
|
69
|
+
listeners.add(handler)
|
|
70
|
+
return () => {
|
|
71
|
+
listeners.delete(handler)
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
async stop() {
|
|
75
|
+
stopped = true
|
|
76
|
+
if (interval !== null) {
|
|
77
|
+
clearInterval(interval)
|
|
78
|
+
interval = null
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { sessionBus } from "dbus-next"
|
|
2
|
+
|
|
3
|
+
import type pino from "pino"
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
createNullSessionProvider,
|
|
7
|
+
type SessionProvider,
|
|
8
|
+
type SessionState,
|
|
9
|
+
} from "../session"
|
|
10
|
+
import type { LinuxDbusBus } from "../shared"
|
|
11
|
+
|
|
12
|
+
export interface LinuxSessionDeps {
|
|
13
|
+
readonly dbus?: LinuxDbusBus
|
|
14
|
+
readonly logger: pino.Logger
|
|
15
|
+
readonly idleMs?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SCREENSAVER_SERVICE = "org.gnome.ScreenSaver"
|
|
19
|
+
const SCREENSAVER_PATH = "/org/gnome/ScreenSaver"
|
|
20
|
+
const SCREENSAVER_IFACE = "org.gnome.ScreenSaver"
|
|
21
|
+
const IDLE_MONITOR_SERVICE = "org.gnome.Mutter.IdleMonitor"
|
|
22
|
+
const IDLE_MONITOR_PATH = "/org/gnome/Mutter/IdleMonitor/Core"
|
|
23
|
+
const IDLE_MONITOR_IFACE = "org.gnome.Mutter.IdleMonitor"
|
|
24
|
+
|
|
25
|
+
const toState = (locked: boolean): SessionState =>
|
|
26
|
+
locked ? "locked" : "unlocked"
|
|
27
|
+
|
|
28
|
+
export const createLinuxSessionProvider = async (
|
|
29
|
+
deps: LinuxSessionDeps,
|
|
30
|
+
): Promise<SessionProvider> => {
|
|
31
|
+
const idleMs = deps.idleMs ?? 5 * 60 * 1000
|
|
32
|
+
const listeners = new Set<(s: SessionState) => void>()
|
|
33
|
+
let state: SessionState = "unknown"
|
|
34
|
+
let stopped = false
|
|
35
|
+
let idleSupported = false
|
|
36
|
+
let bus: LinuxDbusBus | null = deps.dbus ?? null
|
|
37
|
+
if (bus === null) {
|
|
38
|
+
try {
|
|
39
|
+
bus = (await sessionBus()) as unknown as LinuxDbusBus
|
|
40
|
+
} catch (err) {
|
|
41
|
+
deps.logger.debug({ err }, "session: dbus sessionBus unavailable")
|
|
42
|
+
return createNullSessionProvider(deps.logger)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let screensaverOk = false
|
|
47
|
+
try {
|
|
48
|
+
const proxy = await bus.getProxyObject(
|
|
49
|
+
SCREENSAVER_SERVICE,
|
|
50
|
+
SCREENSAVER_PATH,
|
|
51
|
+
)
|
|
52
|
+
const saver = proxy.getInterface(SCREENSAVER_IFACE)
|
|
53
|
+
const initial = (await saver.GetActive?.()) ?? false
|
|
54
|
+
state = toState(initial)
|
|
55
|
+
saver.on?.("ActiveChanged", (locked: unknown) => {
|
|
56
|
+
if (typeof locked === "boolean") {
|
|
57
|
+
const next = toState(locked)
|
|
58
|
+
if (next !== state) {
|
|
59
|
+
state = next
|
|
60
|
+
for (const l of listeners) l(state)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
screensaverOk = true
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// ponytail: ScreenSaver can be unavailable on non-GNOME sessions; keep
|
|
67
|
+
// the provider alive so the idle-monitor fallback can still fire.
|
|
68
|
+
deps.logger.debug({ err }, "session: ScreenSaver init failed")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const idleProxy = await bus.getProxyObject(
|
|
73
|
+
IDLE_MONITOR_SERVICE,
|
|
74
|
+
IDLE_MONITOR_PATH,
|
|
75
|
+
)
|
|
76
|
+
const idleIface = idleProxy.getInterface(IDLE_MONITOR_IFACE)
|
|
77
|
+
await idleIface.GetIdletime?.()
|
|
78
|
+
idleSupported = true
|
|
79
|
+
} catch (err) {
|
|
80
|
+
deps.logger.debug(
|
|
81
|
+
{ err },
|
|
82
|
+
"session: IdleMonitor init failed; idle polling disabled",
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!screensaverOk && !idleSupported) {
|
|
87
|
+
deps.logger.debug("session: no lock source available, returning null")
|
|
88
|
+
return createNullSessionProvider(deps.logger)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
void screensaverOk
|
|
92
|
+
|
|
93
|
+
let interval: ReturnType<typeof setInterval> | null = null
|
|
94
|
+
if (idleSupported) {
|
|
95
|
+
let cachedIdleIface: {
|
|
96
|
+
GetIdletime?: () => Promise<unknown>
|
|
97
|
+
} | null = null
|
|
98
|
+
const getIdleIface = async (): Promise<{
|
|
99
|
+
GetIdletime?: () => Promise<unknown>
|
|
100
|
+
} | null> => {
|
|
101
|
+
if (cachedIdleIface !== null) return cachedIdleIface
|
|
102
|
+
const p = await bus!.getProxyObject(
|
|
103
|
+
IDLE_MONITOR_SERVICE,
|
|
104
|
+
IDLE_MONITOR_PATH,
|
|
105
|
+
)
|
|
106
|
+
cachedIdleIface = p.getInterface(IDLE_MONITOR_IFACE) as {
|
|
107
|
+
GetIdletime?: () => Promise<unknown>
|
|
108
|
+
}
|
|
109
|
+
return cachedIdleIface
|
|
110
|
+
}
|
|
111
|
+
interval = setInterval(() => {
|
|
112
|
+
if (stopped) return
|
|
113
|
+
void getIdleIface()
|
|
114
|
+
.then((iface) => iface?.GetIdletime?.())
|
|
115
|
+
.then((idleMsRaw) => {
|
|
116
|
+
deps.logger.debug(
|
|
117
|
+
{ idleMsRaw, idleMs, currentState: state },
|
|
118
|
+
"session: idle monitor tick",
|
|
119
|
+
)
|
|
120
|
+
if (typeof idleMsRaw === "number") {
|
|
121
|
+
if (idleMsRaw > idleMs && state === "unlocked") {
|
|
122
|
+
state = "locked"
|
|
123
|
+
deps.logger.info(
|
|
124
|
+
{ idleMsRaw, idleMs },
|
|
125
|
+
"session: locked via idle monitor",
|
|
126
|
+
)
|
|
127
|
+
for (const l of listeners) l(state)
|
|
128
|
+
} else if (idleMsRaw <= idleMs && state === "locked") {
|
|
129
|
+
state = "unlocked"
|
|
130
|
+
deps.logger.info(
|
|
131
|
+
{ idleMsRaw, idleMs },
|
|
132
|
+
"session: unlocked via idle monitor",
|
|
133
|
+
)
|
|
134
|
+
for (const l of listeners) l(state)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
.catch(() => undefined)
|
|
139
|
+
}, 5_000)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
getState() {
|
|
144
|
+
return state
|
|
145
|
+
},
|
|
146
|
+
subscribe(handler) {
|
|
147
|
+
let initialFired = false
|
|
148
|
+
const wrappedHandler = (s: SessionState): void => {
|
|
149
|
+
initialFired = true
|
|
150
|
+
handler(s)
|
|
151
|
+
}
|
|
152
|
+
listeners.add(wrappedHandler)
|
|
153
|
+
if (!initialFired && state !== "unknown") {
|
|
154
|
+
queueMicrotask(() => {
|
|
155
|
+
if (!initialFired) handler(state)
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
return () => {
|
|
159
|
+
listeners.delete(wrappedHandler)
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async stop() {
|
|
163
|
+
stopped = true
|
|
164
|
+
if (interval !== null) {
|
|
165
|
+
clearInterval(interval)
|
|
166
|
+
interval = null
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
bus?.disconnect?.()
|
|
170
|
+
} catch {
|
|
171
|
+
// ignore
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
}
|