@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,110 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
isRunning,
|
|
5
|
+
readChildren,
|
|
6
|
+
readConfigPath,
|
|
7
|
+
readFlags,
|
|
8
|
+
readPid,
|
|
9
|
+
readRuntimeState,
|
|
10
|
+
resolveDaemonPaths,
|
|
11
|
+
} from "@/util/daemon"
|
|
12
|
+
import { listDevices } from "@/device"
|
|
13
|
+
|
|
14
|
+
import { cancel, intro, log, outro } from "@/cli/prompt"
|
|
15
|
+
|
|
16
|
+
export interface StatusOptions {
|
|
17
|
+
logger: pino.Logger
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const formatUptime = (ms: number): string => {
|
|
21
|
+
if (ms < 0) return "—"
|
|
22
|
+
const totalSeconds = Math.floor(ms / 1000)
|
|
23
|
+
const hours = Math.floor(totalSeconds / 3600)
|
|
24
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
25
|
+
const seconds = totalSeconds % 60
|
|
26
|
+
if (hours > 0) return `${hours}h ${minutes}m`
|
|
27
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`
|
|
28
|
+
return `${seconds}s`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const status = async ({ logger }: StatusOptions): Promise<void> => {
|
|
32
|
+
void logger // kept for callers that want pino logs out-of-band
|
|
33
|
+
|
|
34
|
+
const paths = resolveDaemonPaths()
|
|
35
|
+
const pid = readPid(paths)
|
|
36
|
+
|
|
37
|
+
if (pid === null) {
|
|
38
|
+
intro("sirenodeck status")
|
|
39
|
+
log.error("Daemon is not running")
|
|
40
|
+
cancel("✗ No daemon process found")
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const alive = isRunning(pid)
|
|
45
|
+
const flags = readFlags(paths)
|
|
46
|
+
const state = alive ? readRuntimeState(paths) : null
|
|
47
|
+
const childrenState = readChildren(paths)
|
|
48
|
+
const childPids = childrenState?.pids ?? []
|
|
49
|
+
const configPath = readConfigPath(paths)
|
|
50
|
+
|
|
51
|
+
intro("sirenodeck status")
|
|
52
|
+
|
|
53
|
+
if (alive) {
|
|
54
|
+
const startedAt = state?.startedAt ?? null
|
|
55
|
+
const uptime =
|
|
56
|
+
startedAt !== null ? formatUptime(Date.now() - startedAt) : "—"
|
|
57
|
+
log.info(`Status: running for ${uptime} (pid ${pid})`)
|
|
58
|
+
} else {
|
|
59
|
+
log.warn(`Status: stale pid file (pid ${pid} not alive)`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isEmulator = state?.emulatorMode === true || flags?.emulator === true
|
|
63
|
+
const deviceModel =
|
|
64
|
+
typeof flags?.deviceModel === "string" ? flags.deviceModel : "mk2"
|
|
65
|
+
|
|
66
|
+
if (isEmulator) {
|
|
67
|
+
log.info(`Device : Emulator (${deviceModel}) (emulator mode)`)
|
|
68
|
+
} else {
|
|
69
|
+
let hardwareLabel = "—"
|
|
70
|
+
try {
|
|
71
|
+
const devices = await listDevices()
|
|
72
|
+
if (devices.length > 0 && devices[0] !== undefined) {
|
|
73
|
+
hardwareLabel = devices[0].label
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// ponytail: HID enumeration can throw on missing kernel modules or
|
|
77
|
+
// permission errors. Fall through to the "—" placeholder — status
|
|
78
|
+
// must not blow up if device detection is unavailable.
|
|
79
|
+
}
|
|
80
|
+
log.info(`Device : ${hardwareLabel} (hardware mode)`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const remoteOn = state?.remote === true || flags?.remote === true
|
|
84
|
+
log.info(`Remote: ${remoteOn ? "on" : "off"}`)
|
|
85
|
+
|
|
86
|
+
log.info(`Config: ${configPath ?? "—"}`)
|
|
87
|
+
|
|
88
|
+
const theme = state?.theme ?? "default"
|
|
89
|
+
log.info(`Theme: ${theme}`)
|
|
90
|
+
|
|
91
|
+
if (childPids.length > 0) {
|
|
92
|
+
log.info(`Children: ${childPids.length} (${childPids.join(", ")})`)
|
|
93
|
+
} else {
|
|
94
|
+
log.info("Children: none tracked")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (alive && state !== null) {
|
|
98
|
+
const frontendUrl = state.frontendUrl
|
|
99
|
+
log.info(`Frontend URL : ${frontendUrl}`)
|
|
100
|
+
if (state.configUiUrl.length > 0) {
|
|
101
|
+
log.info(`Config UI URL : ${state.configUiUrl}`)
|
|
102
|
+
}
|
|
103
|
+
log.info(`Bridge URL : ${state.wsUrl}`)
|
|
104
|
+
outro("✓ Status snapshot")
|
|
105
|
+
} else {
|
|
106
|
+
cancel("✗ Stale pid file — run `p dev stop` to clean up")
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default status
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type pino from "pino"
|
|
2
|
+
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
isRunning,
|
|
7
|
+
readChildren,
|
|
8
|
+
readPid,
|
|
9
|
+
removeChildrenFile,
|
|
10
|
+
removePidFile,
|
|
11
|
+
removeRuntimeStateFile,
|
|
12
|
+
resolveDaemonPaths,
|
|
13
|
+
} from "@/util/daemon"
|
|
14
|
+
import { removeDaemonControl } from "@/util/daemon-control"
|
|
15
|
+
import {
|
|
16
|
+
readDaemonEventsFromSnapshot,
|
|
17
|
+
snapshotDaemonLog,
|
|
18
|
+
} from "@/util/log-reader"
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
printDaemonEvents,
|
|
22
|
+
printStopComplete,
|
|
23
|
+
waitForPortFree,
|
|
24
|
+
} from "../startup-display"
|
|
25
|
+
|
|
26
|
+
export interface StopOptions {
|
|
27
|
+
readonly logger: pino.Logger
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_PORT = 52937
|
|
31
|
+
|
|
32
|
+
const killGracefully = async (
|
|
33
|
+
pid: number,
|
|
34
|
+
logger: pino.Logger,
|
|
35
|
+
label: string,
|
|
36
|
+
): Promise<void> => {
|
|
37
|
+
if (!isRunning(pid)) {
|
|
38
|
+
logger.debug({ pid, label }, "stop: already exited")
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
logger.info({ pid, label }, "stop: SIGTERM")
|
|
42
|
+
try {
|
|
43
|
+
process.kill(pid, "SIGTERM")
|
|
44
|
+
} catch (err) {
|
|
45
|
+
logger.warn({ err, pid, label }, "stop: SIGTERM failed")
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
const deadline = Date.now() + 5_000
|
|
49
|
+
while (Date.now() < deadline && isRunning(pid)) {
|
|
50
|
+
await new Promise((r) => setTimeout(r, 100))
|
|
51
|
+
}
|
|
52
|
+
if (isRunning(pid)) {
|
|
53
|
+
logger.warn({ pid, label }, "stop: did not exit in 5s, sending SIGKILL")
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, "SIGKILL")
|
|
56
|
+
} catch (err) {
|
|
57
|
+
logger.warn({ err, pid, label }, "stop: SIGKILL failed")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const stop = async ({ logger }: StopOptions): Promise<void> => {
|
|
63
|
+
const pid = readPid()
|
|
64
|
+
if (pid === null) {
|
|
65
|
+
logger.info("stop: no running daemon found")
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ponytail: snapshot the log BEFORE we kill the daemon. After the
|
|
70
|
+
// kill, the daemon may write a final fatal/warn ("received SIGTERM"
|
|
71
|
+
// or vite-shutdown errors); we surface those inline so the
|
|
72
|
+
// operator sees the daemon's last words before the "stopped" line.
|
|
73
|
+
const snapshot = snapshotDaemonLog()
|
|
74
|
+
const logPath = join(resolveDaemonPaths().runtimeDir, "service.log")
|
|
75
|
+
|
|
76
|
+
const childrenState = readChildren()
|
|
77
|
+
const childPids = childrenState?.pids ?? []
|
|
78
|
+
|
|
79
|
+
for (const childPid of childPids) {
|
|
80
|
+
await killGracefully(childPid, logger, `child`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await killGracefully(pid, logger, "daemon")
|
|
84
|
+
|
|
85
|
+
// ponytail: confirm the daemon's WS port is genuinely free. After
|
|
86
|
+
// SIGTERM/SIGKILL the kernel usually releases the port within ~50ms,
|
|
87
|
+
// but TIME_WAIT or a stuck vite can hold it. A timeout here means
|
|
88
|
+
// the next `start` will see "port in use" — surface that to the
|
|
89
|
+
// operator so they don't have to wait until they re-run start to
|
|
90
|
+
// discover the leftover.
|
|
91
|
+
const portFree = await waitForPortFree(DEFAULT_PORT)
|
|
92
|
+
|
|
93
|
+
removePidFile()
|
|
94
|
+
removeDaemonControl(resolveDaemonPaths())
|
|
95
|
+
removeRuntimeStateFile()
|
|
96
|
+
removeChildrenFile()
|
|
97
|
+
|
|
98
|
+
// ponytail: print any warn / error / fatal the daemon wrote during
|
|
99
|
+
// its shutdown window BEFORE the success/failure line. On a clean
|
|
100
|
+
// stop this is empty; on a SIGKILL escalation it surfaces the
|
|
101
|
+
// daemon's last words ("vite didn't respond to SIGTERM within 5s")
|
|
102
|
+
// without the operator having to read service.log.
|
|
103
|
+
printDaemonEvents(readDaemonEventsFromSnapshot(logPath, snapshot))
|
|
104
|
+
|
|
105
|
+
printStopComplete(portFree)
|
|
106
|
+
|
|
107
|
+
if (!portFree) {
|
|
108
|
+
// ponytail: the daemon process exited (the pid file is gone) but
|
|
109
|
+
// the WS port is still bound. That's a TIME_WAIT or a stuck
|
|
110
|
+
// orphan vite — log it as a warning but don't exit 1, since the
|
|
111
|
+
// daemon IS stopped from a process-management perspective.
|
|
112
|
+
logger.warn(
|
|
113
|
+
{ port: DEFAULT_PORT },
|
|
114
|
+
"stop: daemon exited but port 52937 is still bound — check for orphans with `p dev status`",
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
logger.info("stop: cleanup complete")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default stop
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process"
|
|
2
|
+
|
|
3
|
+
import type pino from "pino"
|
|
4
|
+
|
|
5
|
+
// ponytail: incremental retry schedule for vite children + service-level
|
|
6
|
+
// supervision. 5 entries: 2s for transient blips, 60s tail for sticky failures.
|
|
7
|
+
// Total worst-case wallclock ~2 min before giving up. Replace the previous
|
|
8
|
+
// flat 60s × 2 retry which could take 3 min and never recovered from a brief
|
|
9
|
+
// fail without a 60s blank-period.
|
|
10
|
+
export const DEFAULT_VITE_RETRY_SCHEDULE_MS = [
|
|
11
|
+
2_000, 5_000, 15_000, 30_000, 60_000,
|
|
12
|
+
] as const
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_SERVICE_RETRY_SCHEDULE_MS = [
|
|
15
|
+
2_000, 5_000, 15_000, 30_000, 60_000,
|
|
16
|
+
] as const
|
|
17
|
+
|
|
18
|
+
// ponytail: minimal retry helper. Watches a single child; on unexpected exit
|
|
19
|
+
// schedules a respawn after `delayScheduleMs[retriesUsed - 1]` (or `delayMs`
|
|
20
|
+
// for the flat schedule). After the schedule is exhausted, gives up via
|
|
21
|
+
// `onGiveUp`. Graceful shutdown (via stop() or isShuttingDown()) suppresses
|
|
22
|
+
// both respawn and give-up — the parent is in control.
|
|
23
|
+
//
|
|
24
|
+
// Two schedule modes:
|
|
25
|
+
// - delayScheduleMs: incremental backoff. maxRetries defaults to the schedule's
|
|
26
|
+
// length. Use this for transient-restart-friendly children (vite).
|
|
27
|
+
// - delayMs + maxRetries: flat backoff. Kept for callers that don't need
|
|
28
|
+
// varying delays. If both are set, delayScheduleMs wins.
|
|
29
|
+
export interface SuperviseOptions {
|
|
30
|
+
readonly label: string
|
|
31
|
+
readonly spawn: () => Promise<ChildProcess>
|
|
32
|
+
readonly onGiveUp: () => void
|
|
33
|
+
readonly isShuttingDown: () => boolean
|
|
34
|
+
readonly logger: pino.Logger
|
|
35
|
+
readonly delayScheduleMs?: ReadonlyArray<number>
|
|
36
|
+
readonly delayMs?: number
|
|
37
|
+
readonly maxRetries?: number
|
|
38
|
+
readonly kill?: (child: ChildProcess) => Promise<void>
|
|
39
|
+
/**
|
|
40
|
+
* Called when the child exits unexpectedly — BEFORE the respawn timer is
|
|
41
|
+
* scheduled. Awaited so the next respawn waits for the side effect (e.g.
|
|
42
|
+
* black-frame push) to finish. Skipped when isShuttingDown() returns true.
|
|
43
|
+
*/
|
|
44
|
+
readonly onChildExit?: (
|
|
45
|
+
code: number | null,
|
|
46
|
+
signal: NodeJS.Signals | null,
|
|
47
|
+
) => void | Promise<void>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SuperviseHandle {
|
|
51
|
+
readonly process: ChildProcess
|
|
52
|
+
readonly stop: () => Promise<void>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const defaultKill = (child: ChildProcess): Promise<void> =>
|
|
56
|
+
new Promise<void>((resolve) => {
|
|
57
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
58
|
+
resolve()
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
let killTimer: ReturnType<typeof setTimeout> | null = null
|
|
62
|
+
child.once("exit", () => {
|
|
63
|
+
if (killTimer !== null) clearTimeout(killTimer)
|
|
64
|
+
resolve()
|
|
65
|
+
})
|
|
66
|
+
try {
|
|
67
|
+
child.kill("SIGTERM")
|
|
68
|
+
} catch {
|
|
69
|
+
if (killTimer !== null) clearTimeout(killTimer)
|
|
70
|
+
resolve()
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
killTimer = setTimeout(() => {
|
|
74
|
+
killTimer = null
|
|
75
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
76
|
+
try {
|
|
77
|
+
child.kill("SIGKILL")
|
|
78
|
+
} catch {
|
|
79
|
+
// already gone
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}, 2_000)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const resolveDelay = (
|
|
86
|
+
options: SuperviseOptions,
|
|
87
|
+
retriesUsed: number,
|
|
88
|
+
): number => {
|
|
89
|
+
if (options.delayScheduleMs !== undefined) {
|
|
90
|
+
const schedule = options.delayScheduleMs
|
|
91
|
+
if (schedule.length === 0) return options.delayMs ?? 60_000
|
|
92
|
+
const idx = Math.min(retriesUsed - 1, schedule.length - 1)
|
|
93
|
+
return schedule[idx] ?? schedule[schedule.length - 1] ?? 60_000
|
|
94
|
+
}
|
|
95
|
+
return options.delayMs ?? 60_000
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const resolveMaxRetries = (options: SuperviseOptions): number => {
|
|
99
|
+
if (options.delayScheduleMs !== undefined) {
|
|
100
|
+
return options.maxRetries ?? options.delayScheduleMs.length
|
|
101
|
+
}
|
|
102
|
+
return options.maxRetries ?? 2
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const supervise = async (
|
|
106
|
+
options: SuperviseOptions,
|
|
107
|
+
): Promise<SuperviseHandle> => {
|
|
108
|
+
const maxRetries = resolveMaxRetries(options)
|
|
109
|
+
const killChild = options.kill ?? defaultKill
|
|
110
|
+
let stopped = false
|
|
111
|
+
let respawnTimer: ReturnType<typeof setTimeout> | null = null
|
|
112
|
+
let retriesUsed = 0
|
|
113
|
+
let current: ChildProcess | null = null
|
|
114
|
+
let initial: ChildProcess | null = null
|
|
115
|
+
|
|
116
|
+
const clearTimer = (): void => {
|
|
117
|
+
if (respawnTimer !== null) {
|
|
118
|
+
clearTimeout(respawnTimer)
|
|
119
|
+
respawnTimer = null
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const giveUp = (): void => {
|
|
124
|
+
options.logger.fatal(
|
|
125
|
+
{
|
|
126
|
+
label: options.label,
|
|
127
|
+
retriesUsed,
|
|
128
|
+
maxRetries,
|
|
129
|
+
},
|
|
130
|
+
"subprocess exhausted retries — giving up",
|
|
131
|
+
)
|
|
132
|
+
stopped = true
|
|
133
|
+
clearTimer()
|
|
134
|
+
options.onGiveUp()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const scheduleRespawn = (): void => {
|
|
138
|
+
const delay = resolveDelay(options, retriesUsed)
|
|
139
|
+
respawnTimer = setTimeout(() => {
|
|
140
|
+
respawnTimer = null
|
|
141
|
+
if (stopped || options.isShuttingDown()) return
|
|
142
|
+
void spawnAndWire().catch((err) => {
|
|
143
|
+
options.logger.error(
|
|
144
|
+
{ err, label: options.label },
|
|
145
|
+
"subprocess respawn failed",
|
|
146
|
+
)
|
|
147
|
+
giveUp()
|
|
148
|
+
})
|
|
149
|
+
}, delay)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const onExit = async (
|
|
153
|
+
code: number | null,
|
|
154
|
+
signal: NodeJS.Signals | null,
|
|
155
|
+
): Promise<void> => {
|
|
156
|
+
if (stopped || options.isShuttingDown()) return
|
|
157
|
+
options.logger.warn(
|
|
158
|
+
{ label: options.label, code, signal, retriesUsed },
|
|
159
|
+
"subprocess exited unexpectedly",
|
|
160
|
+
)
|
|
161
|
+
if (options.onChildExit !== undefined) {
|
|
162
|
+
try {
|
|
163
|
+
await options.onChildExit(code, signal)
|
|
164
|
+
} catch (err) {
|
|
165
|
+
options.logger.error(
|
|
166
|
+
{ err, label: options.label },
|
|
167
|
+
"subprocess onChildExit failed",
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (retriesUsed >= maxRetries) {
|
|
172
|
+
giveUp()
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
retriesUsed += 1
|
|
176
|
+
scheduleRespawn()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const spawnAndWire = async (): Promise<ChildProcess> => {
|
|
180
|
+
const child = await options.spawn()
|
|
181
|
+
current = child
|
|
182
|
+
child.once("exit", onExit)
|
|
183
|
+
return child
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
initial = await spawnAndWire()
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
get process(): ChildProcess {
|
|
190
|
+
// Return the current mutable child so callers that capture the handle
|
|
191
|
+
// after a respawn still see the live process. Falls back to the initial
|
|
192
|
+
// child before the first spawn resolves (defensive — shouldn't happen).
|
|
193
|
+
return current ?? initial ?? (undefined as unknown as ChildProcess)
|
|
194
|
+
},
|
|
195
|
+
stop: async (): Promise<void> => {
|
|
196
|
+
stopped = true
|
|
197
|
+
clearTimer()
|
|
198
|
+
const target = current ?? initial
|
|
199
|
+
if (target === null) return
|
|
200
|
+
await killChild(target)
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
}
|