@agentdeck/bridge 0.2.1 → 0.2.3
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/assets/fonts/IBMPlexSans-Bold.ttf +0 -0
- package/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Bold.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/assets/fonts/LICENSES.md +22 -0
- package/dist/adapters/claude-code.d.ts +6 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +8 -0
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/openclaw.d.ts +24 -0
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +291 -35
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/adapters/opencode-adapter.d.ts +59 -0
- package/dist/adapters/opencode-adapter.d.ts.map +1 -1
- package/dist/adapters/opencode-adapter.js +297 -35
- package/dist/adapters/opencode-adapter.js.map +1 -1
- package/dist/adb-reverse.d.ts +1 -1
- package/dist/adb-reverse.js +1 -1
- package/dist/antigravity-local.d.ts +1 -0
- package/dist/antigravity-local.d.ts.map +1 -1
- package/dist/antigravity-local.js +33 -0
- package/dist/antigravity-local.js.map +1 -1
- package/dist/apme/adapters/claude-hook.d.ts.map +1 -1
- package/dist/apme/adapters/claude-hook.js +5 -0
- package/dist/apme/adapters/claude-hook.js.map +1 -1
- package/dist/apme/adapters/codex-hook.d.ts +17 -0
- package/dist/apme/adapters/codex-hook.d.ts.map +1 -0
- package/dist/apme/adapters/codex-hook.js +95 -0
- package/dist/apme/adapters/codex-hook.js.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts +86 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.js +496 -0
- package/dist/apme/adapters/codex-turn-manager.js.map +1 -0
- package/dist/apme/adapters/openclaw-hook.d.ts +99 -0
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -0
- package/dist/apme/adapters/openclaw-hook.js +234 -0
- package/dist/apme/adapters/openclaw-hook.js.map +1 -0
- package/dist/apme/adapters/opencode-hook.d.ts +56 -0
- package/dist/apme/adapters/opencode-hook.d.ts.map +1 -0
- package/dist/apme/adapters/opencode-hook.js +182 -0
- package/dist/apme/adapters/opencode-hook.js.map +1 -0
- package/dist/apme/adapters/timeline.d.ts.map +1 -1
- package/dist/apme/adapters/timeline.js +6 -1
- package/dist/apme/adapters/timeline.js.map +1 -1
- package/dist/apme/antigravity-transcript.d.ts +18 -0
- package/dist/apme/antigravity-transcript.d.ts.map +1 -0
- package/dist/apme/antigravity-transcript.js +94 -0
- package/dist/apme/antigravity-transcript.js.map +1 -0
- package/dist/apme/classify-turn.d.ts +13 -0
- package/dist/apme/classify-turn.d.ts.map +1 -0
- package/dist/apme/classify-turn.js +49 -0
- package/dist/apme/classify-turn.js.map +1 -0
- package/dist/apme/claude-transcript-reader.d.ts +10 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
- package/dist/apme/claude-transcript-reader.js +35 -0
- package/dist/apme/claude-transcript-reader.js.map +1 -1
- package/dist/apme/collector.d.ts +129 -6
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +470 -23
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +87 -11
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +43 -6
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/index.d.ts +26 -5
- package/dist/apme/index.d.ts.map +1 -1
- package/dist/apme/index.js +135 -9
- package/dist/apme/index.js.map +1 -1
- package/dist/apme/pareto.d.ts +39 -0
- package/dist/apme/pareto.d.ts.map +1 -0
- package/dist/apme/pareto.js +59 -0
- package/dist/apme/pareto.js.map +1 -0
- package/dist/apme/recommend.d.ts +3 -0
- package/dist/apme/recommend.d.ts.map +1 -1
- package/dist/apme/recommend.js +43 -1
- package/dist/apme/recommend.js.map +1 -1
- package/dist/apme/runner.d.ts +92 -4
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +586 -70
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/sample-to-timeline.d.ts +26 -0
- package/dist/apme/sample-to-timeline.d.ts.map +1 -0
- package/dist/apme/sample-to-timeline.js +95 -0
- package/dist/apme/sample-to-timeline.js.map +1 -0
- package/dist/apme/scorers/index.d.ts +47 -0
- package/dist/apme/scorers/index.d.ts.map +1 -0
- package/dist/apme/scorers/index.js +96 -0
- package/dist/apme/scorers/index.js.map +1 -0
- package/dist/apme/settings.d.ts +14 -10
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +34 -8
- package/dist/apme/settings.js.map +1 -1
- package/dist/apme/store.d.ts +33 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +334 -25
- package/dist/apme/store.js.map +1 -1
- package/dist/awaiting-overlay.d.ts +99 -0
- package/dist/awaiting-overlay.d.ts.map +1 -0
- package/dist/awaiting-overlay.js +148 -0
- package/dist/awaiting-overlay.js.map +1 -0
- package/dist/ble-sync-spawn.d.ts +63 -0
- package/dist/ble-sync-spawn.d.ts.map +1 -0
- package/dist/ble-sync-spawn.js +196 -0
- package/dist/ble-sync-spawn.js.map +1 -0
- package/dist/bridge-core.d.ts +24 -1
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +157 -24
- package/dist/bridge-core.js.map +1 -1
- package/dist/broadcast.d.ts +3 -0
- package/dist/broadcast.d.ts.map +1 -0
- package/dist/broadcast.js +122 -0
- package/dist/broadcast.js.map +1 -0
- package/dist/check-deps.d.ts.map +1 -1
- package/dist/check-deps.js +25 -9
- package/dist/check-deps.js.map +1 -1
- package/dist/cli.d.ts +15 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1025 -94
- package/dist/cli.js.map +1 -1
- package/dist/codex-auth.d.ts +21 -0
- package/dist/codex-auth.d.ts.map +1 -1
- package/dist/codex-auth.js +52 -1
- package/dist/codex-auth.js.map +1 -1
- package/dist/codex-output-parser.d.ts +8 -0
- package/dist/codex-output-parser.d.ts.map +1 -1
- package/dist/codex-output-parser.js +110 -25
- package/dist/codex-output-parser.js.map +1 -1
- package/dist/codex-rate-limits.d.ts +16 -0
- package/dist/codex-rate-limits.d.ts.map +1 -0
- package/dist/codex-rate-limits.js +197 -0
- package/dist/codex-rate-limits.js.map +1 -0
- package/dist/codex-rollout-response.d.ts +31 -0
- package/dist/codex-rollout-response.d.ts.map +1 -0
- package/dist/codex-rollout-response.js +135 -0
- package/dist/codex-rollout-response.js.map +1 -0
- package/dist/daemon-server.d.ts +85 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +1627 -159
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon.js +15 -6
- package/dist/daemon.js.map +1 -1
- package/dist/display-dim.d.ts +9 -0
- package/dist/display-dim.d.ts.map +1 -0
- package/dist/display-dim.js +53 -0
- package/dist/display-dim.js.map +1 -0
- package/dist/display-monitor.d.ts +15 -2
- package/dist/display-monitor.d.ts.map +1 -1
- package/dist/display-monitor.js +104 -26
- package/dist/display-monitor.js.map +1 -1
- package/dist/esp32-serial.d.ts +160 -1
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +950 -153
- package/dist/esp32-serial.js.map +1 -1
- package/dist/fallback-task-timeline.d.ts +42 -0
- package/dist/fallback-task-timeline.d.ts.map +1 -0
- package/dist/fallback-task-timeline.js +112 -0
- package/dist/fallback-task-timeline.js.map +1 -0
- package/dist/foundation-models-helper.d.ts +11 -0
- package/dist/foundation-models-helper.d.ts.map +1 -0
- package/dist/foundation-models-helper.js +241 -0
- package/dist/foundation-models-helper.js.map +1 -0
- package/dist/hook-migration.js +1 -1
- package/dist/hook-migration.js.map +1 -1
- package/dist/hook-server.d.ts +2 -3
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +22 -10
- package/dist/hook-server.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +33 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +164 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts +18 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.js +85 -0
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts +20 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.js +57 -0
- package/dist/idotmatrix/idotmatrix-settings.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +215 -179
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +15 -16
- package/dist/log-stream.d.ts.map +1 -1
- package/dist/log-stream.js +18 -96
- package/dist/log-stream.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +12 -3
- package/dist/logger.js.map +1 -1
- package/dist/mdns.d.ts +17 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +52 -18
- package/dist/mdns.js.map +1 -1
- package/dist/modules/adb-module.d.ts +1 -1
- package/dist/modules/adb-module.d.ts.map +1 -1
- package/dist/modules/adb-module.js +4 -3
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/modules/broadcast-module.d.ts +17 -0
- package/dist/modules/broadcast-module.d.ts.map +1 -0
- package/dist/modules/broadcast-module.js +26 -0
- package/dist/modules/broadcast-module.js.map +1 -0
- package/dist/modules/idotmatrix-module.d.ts +9 -0
- package/dist/modules/idotmatrix-module.d.ts.map +1 -0
- package/dist/modules/idotmatrix-module.js +46 -0
- package/dist/modules/idotmatrix-module.js.map +1 -0
- package/dist/modules/index.d.ts +3 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +9 -3
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/pixoo-module.d.ts.map +1 -1
- package/dist/modules/pixoo-module.js +14 -4
- package/dist/modules/pixoo-module.js.map +1 -1
- package/dist/modules/serial-module.d.ts +4 -0
- package/dist/modules/serial-module.d.ts.map +1 -1
- package/dist/modules/serial-module.js +14 -12
- package/dist/modules/serial-module.js.map +1 -1
- package/dist/modules/timebox-module.d.ts +9 -0
- package/dist/modules/timebox-module.d.ts.map +1 -0
- package/dist/modules/timebox-module.js +46 -0
- package/dist/modules/timebox-module.js.map +1 -0
- package/dist/modules/types.d.ts +4 -2
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/openclaw-session.d.ts +18 -0
- package/dist/openclaw-session.d.ts.map +1 -0
- package/dist/openclaw-session.js +34 -0
- package/dist/openclaw-session.js.map +1 -0
- package/dist/output-parser.d.ts +30 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +125 -9
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +26 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +258 -49
- package/dist/passive-observer.js.map +1 -1
- package/dist/permission-resolver.d.ts +47 -0
- package/dist/permission-resolver.d.ts.map +1 -0
- package/dist/permission-resolver.js +103 -0
- package/dist/permission-resolver.js.map +1 -0
- package/dist/pixoo/micro-glyphs.d.ts +49 -0
- package/dist/pixoo/micro-glyphs.d.ts.map +1 -0
- package/dist/pixoo/micro-glyphs.js +276 -0
- package/dist/pixoo/micro-glyphs.js.map +1 -0
- package/dist/pixoo/pixoo-bridge.d.ts +2 -2
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
- package/dist/pixoo/pixoo-bridge.js +93 -25
- package/dist/pixoo/pixoo-bridge.js.map +1 -1
- package/dist/pixoo/pixoo-camera.d.ts +19 -3
- package/dist/pixoo/pixoo-camera.d.ts.map +1 -1
- package/dist/pixoo/pixoo-camera.js +62 -27
- package/dist/pixoo/pixoo-camera.js.map +1 -1
- package/dist/pixoo/pixoo-client.d.ts +13 -3
- package/dist/pixoo/pixoo-client.d.ts.map +1 -1
- package/dist/pixoo/pixoo-client.js +51 -22
- package/dist/pixoo/pixoo-client.js.map +1 -1
- package/dist/pixoo/pixoo-discover.d.ts +37 -0
- package/dist/pixoo/pixoo-discover.d.ts.map +1 -0
- package/dist/pixoo/pixoo-discover.js +105 -0
- package/dist/pixoo/pixoo-discover.js.map +1 -0
- package/dist/pixoo/pixoo-renderer.d.ts +8 -3
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +189 -60
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pixoo/pixoo-settings.d.ts +6 -0
- package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
- package/dist/pixoo/pixoo-settings.js +9 -0
- package/dist/pixoo/pixoo-settings.js.map +1 -1
- package/dist/pixoo/pixoo-sprites.d.ts +60 -11
- package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
- package/dist/pixoo/pixoo-sprites.js +512 -209
- package/dist/pixoo/pixoo-sprites.js.map +1 -1
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +8 -2
- package/dist/pty-manager.js.map +1 -1
- package/dist/session-activity.d.ts +14 -0
- package/dist/session-activity.d.ts.map +1 -0
- package/dist/session-activity.js +160 -0
- package/dist/session-activity.js.map +1 -0
- package/dist/session-aggregator.d.ts +17 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +48 -9
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-registry.d.ts +71 -3
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +137 -8
- package/dist/session-registry.js.map +1 -1
- package/dist/session-timeline-relay.d.ts.map +1 -1
- package/dist/session-timeline-relay.js +13 -4
- package/dist/session-timeline-relay.js.map +1 -1
- package/dist/session-transcript-timeline.d.ts +49 -0
- package/dist/session-transcript-timeline.d.ts.map +1 -0
- package/dist/session-transcript-timeline.js +276 -0
- package/dist/session-transcript-timeline.js.map +1 -0
- package/dist/state-machine.d.ts +6 -1
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +86 -28
- package/dist/state-machine.js.map +1 -1
- package/dist/timebox/timebox-daemon-sync.d.ts +19 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -0
- package/dist/timebox/timebox-daemon-sync.js +135 -0
- package/dist/timebox/timebox-daemon-sync.js.map +1 -0
- package/dist/timebox/timebox-discover.d.ts +19 -0
- package/dist/timebox/timebox-discover.d.ts.map +1 -0
- package/dist/timebox/timebox-discover.js +87 -0
- package/dist/timebox/timebox-discover.js.map +1 -0
- package/dist/timebox/timebox-settings.d.ts +32 -0
- package/dist/timebox/timebox-settings.d.ts.map +1 -0
- package/dist/timebox/timebox-settings.js +80 -0
- package/dist/timebox/timebox-settings.js.map +1 -0
- package/dist/timeline-store.d.ts +63 -3
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +292 -14
- package/dist/timeline-store.js.map +1 -1
- package/dist/timeline-summarizer.d.ts.map +1 -1
- package/dist/timeline-summarizer.js +29 -5
- package/dist/timeline-summarizer.js.map +1 -1
- package/dist/tui/ansi.d.ts +0 -4
- package/dist/tui/ansi.d.ts.map +1 -1
- package/dist/tui/ansi.js +14 -7
- package/dist/tui/ansi.js.map +1 -1
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.d.ts.map +1 -1
- package/dist/tui/dashboard.js +24 -7
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/tui/renderer.d.ts +36 -0
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +303 -119
- package/dist/tui/renderer.js.map +1 -1
- package/dist/tui/terrarium.d.ts.map +1 -1
- package/dist/tui/terrarium.js +37 -49
- package/dist/tui/terrarium.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +7 -1
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +10 -3
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +74 -12
- package/dist/usage-event.js.map +1 -1
- package/dist/utility-proxy.d.ts.map +1 -1
- package/dist/utility-proxy.js +8 -0
- package/dist/utility-proxy.js.map +1 -1
- package/dist/utils/project-name.d.ts +3 -0
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +44 -2
- package/dist/utils/project-name.js.map +1 -1
- package/dist/windows-service.d.ts +26 -0
- package/dist/windows-service.d.ts.map +1 -0
- package/dist/windows-service.js +145 -0
- package/dist/windows-service.js.map +1 -0
- package/dist/ws-server.d.ts +22 -1
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +89 -4
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +112 -0
- package/package.json +9 -3
- package/scripts/build-fm-helper.mjs +26 -0
- package/LICENSE +0 -21
- package/dist/apme/tuner.d.ts +0 -64
- package/dist/apme/tuner.d.ts.map +0 -1
- package/dist/apme/tuner.js +0 -297
- package/dist/apme/tuner.js.map +0 -1
- package/dist/d200h/hid-protocol.d.ts +0 -62
- package/dist/d200h/hid-protocol.d.ts.map +0 -1
- package/dist/d200h/hid-protocol.js +0 -124
- package/dist/d200h/hid-protocol.js.map +0 -1
- package/dist/d200h/image-renderer.d.ts +0 -36
- package/dist/d200h/image-renderer.d.ts.map +0 -1
- package/dist/d200h/image-renderer.js +0 -487
- package/dist/d200h/image-renderer.js.map +0 -1
- package/dist/modules/d200h-module.d.ts +0 -43
- package/dist/modules/d200h-module.d.ts.map +0 -1
- package/dist/modules/d200h-module.js +0 -360
- package/dist/modules/d200h-module.js.map +0 -1
- package/dist/modules/d200h-renderer.d.ts +0 -7
- package/dist/modules/d200h-renderer.d.ts.map +0 -1
- package/dist/modules/d200h-renderer.js +0 -321
- package/dist/modules/d200h-renderer.js.map +0 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows daemon autostart via a per-user Scheduled Task (logon trigger).
|
|
3
|
+
*
|
|
4
|
+
* The macOS LaunchAgent analog on Windows is a per-user Scheduled Task with a
|
|
5
|
+
* logon trigger — NOT a real Windows Service. A service runs in session 0 with
|
|
6
|
+
* no desktop and restricted device access, which breaks the daemon's USB-HID
|
|
7
|
+
* (D200H), audio (wake-word), mDNS, and Stream Deck app integration. A logon
|
|
8
|
+
* task runs in the interactive user session, needs no admin elevation, and uses
|
|
9
|
+
* only the built-in schtasks.exe (no npm dependency).
|
|
10
|
+
*
|
|
11
|
+
* The pure XML/args builder is unit-tested (cross-platform); the schtasks
|
|
12
|
+
* /Create|/Run|/Query|/Delete calls are integration-only (real Windows host +
|
|
13
|
+
* side effects) and are exercised manually per docs/daemon.md.
|
|
14
|
+
*/
|
|
15
|
+
import { writeFileSync, unlinkSync } from 'fs';
|
|
16
|
+
import { homedir, tmpdir, userInfo } from 'os';
|
|
17
|
+
import { dirname, join } from 'path';
|
|
18
|
+
import { execSync } from 'child_process';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
export const TASK_NAME = 'AgentDeckDaemon';
|
|
21
|
+
export function xmlEscape(s) {
|
|
22
|
+
return s
|
|
23
|
+
.replace(/&/g, '&')
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"');
|
|
27
|
+
}
|
|
28
|
+
// Resolve node.exe + cli.js directly rather than the `agentdeck` npm shim: the
|
|
29
|
+
// global bin on Windows is an `agentdeck.cmd` wrapper whose quoting breaks the
|
|
30
|
+
// Task Scheduler <Command>/<Arguments> split. process.execPath is the running
|
|
31
|
+
// node; cli.js sits next to this module in the dist dir.
|
|
32
|
+
export function getDaemonNodeTarget() {
|
|
33
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
return { node: process.execPath, cliJs: join(distDir, 'cli.js') };
|
|
35
|
+
}
|
|
36
|
+
export function getCurrentTaskUser() {
|
|
37
|
+
const domain = process.env.USERDOMAIN;
|
|
38
|
+
const user = process.env.USERNAME || userInfo().username;
|
|
39
|
+
return domain ? `${domain}\\${user}` : user;
|
|
40
|
+
}
|
|
41
|
+
export function buildScheduledTaskXml(opts) {
|
|
42
|
+
const { node, cliJs } = { ...getDaemonNodeTarget(), ...opts };
|
|
43
|
+
const user = opts?.user ?? getCurrentTaskUser();
|
|
44
|
+
const workingDir = join(homedir(), '.agentdeck');
|
|
45
|
+
const command = xmlEscape(node);
|
|
46
|
+
// cli.js wrapped in quotes to survive paths with spaces; --foreground so the
|
|
47
|
+
// task process IS the daemon (lets RestartOnFailure track the real process).
|
|
48
|
+
const args = xmlEscape(`"${cliJs}" daemon start --foreground`);
|
|
49
|
+
const userEsc = xmlEscape(user);
|
|
50
|
+
// schtasks /XML requires UTF-16 with a BOM (see installWindowsTask); declaring
|
|
51
|
+
// UTF-8 triggers "unable to switch the encoding". The bytes are written as
|
|
52
|
+
// UTF-16LE+BOM at install time to match this declaration.
|
|
53
|
+
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
54
|
+
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
55
|
+
<RegistrationInfo>
|
|
56
|
+
<Description>AgentDeck monitoring daemon (auto-start on logon)</Description>
|
|
57
|
+
<URI>\\${TASK_NAME}</URI>
|
|
58
|
+
</RegistrationInfo>
|
|
59
|
+
<Triggers>
|
|
60
|
+
<LogonTrigger>
|
|
61
|
+
<Enabled>true</Enabled>
|
|
62
|
+
<UserId>${userEsc}</UserId>
|
|
63
|
+
</LogonTrigger>
|
|
64
|
+
</Triggers>
|
|
65
|
+
<Principals>
|
|
66
|
+
<Principal id="Author">
|
|
67
|
+
<UserId>${userEsc}</UserId>
|
|
68
|
+
<LogonType>InteractiveToken</LogonType>
|
|
69
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
70
|
+
</Principal>
|
|
71
|
+
</Principals>
|
|
72
|
+
<Settings>
|
|
73
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
74
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
75
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
76
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
77
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
78
|
+
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
79
|
+
<IdleSettings>
|
|
80
|
+
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
81
|
+
<RestartOnIdle>false</RestartOnIdle>
|
|
82
|
+
</IdleSettings>
|
|
83
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
84
|
+
<Enabled>true</Enabled>
|
|
85
|
+
<Hidden>false</Hidden>
|
|
86
|
+
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
87
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
88
|
+
<Priority>7</Priority>
|
|
89
|
+
<RestartOnFailure>
|
|
90
|
+
<Interval>PT1M</Interval>
|
|
91
|
+
<Count>3</Count>
|
|
92
|
+
</RestartOnFailure>
|
|
93
|
+
</Settings>
|
|
94
|
+
<Actions Context="Author">
|
|
95
|
+
<Exec>
|
|
96
|
+
<Command>${command}</Command>
|
|
97
|
+
<Arguments>${args}</Arguments>
|
|
98
|
+
<WorkingDirectory>${xmlEscape(workingDir)}</WorkingDirectory>
|
|
99
|
+
</Exec>
|
|
100
|
+
</Actions>
|
|
101
|
+
</Task>`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Register (or overwrite) the AgentDeckDaemon scheduled task.
|
|
105
|
+
* Throws if schtasks /Create fails (e.g. group policy disabling user task creation).
|
|
106
|
+
*/
|
|
107
|
+
export function installWindowsTask() {
|
|
108
|
+
const xml = buildScheduledTaskXml();
|
|
109
|
+
const tmpFile = join(tmpdir(), `agentdeck-task-${process.pid}-${Date.now()}.xml`);
|
|
110
|
+
// Write UTF-16LE with a BOM (U+FEFF) — schtasks /XML rejects UTF-8 here.
|
|
111
|
+
writeFileSync(tmpFile, '' + xml, 'utf16le');
|
|
112
|
+
try {
|
|
113
|
+
// /F overwrites an existing task (idempotent; mirrors macOS unload-before-load).
|
|
114
|
+
execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, { stdio: 'pipe' });
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
try {
|
|
118
|
+
unlinkSync(tmpFile);
|
|
119
|
+
}
|
|
120
|
+
catch { /* temp cleanup best-effort */ }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** True if the AgentDeckDaemon scheduled task is registered. */
|
|
124
|
+
export function taskExists() {
|
|
125
|
+
try {
|
|
126
|
+
execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: 'pipe' });
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Start the registered task immediately (so no logout is required). */
|
|
134
|
+
export function runWindowsTask() {
|
|
135
|
+
execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: 'pipe' });
|
|
136
|
+
}
|
|
137
|
+
/** Stop a running task instance (best-effort; ignores "not running"). */
|
|
138
|
+
export function endWindowsTask() {
|
|
139
|
+
execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: 'pipe' });
|
|
140
|
+
}
|
|
141
|
+
/** Delete the registered task (/F suppresses the confirm prompt). */
|
|
142
|
+
export function deleteWindowsTask() {
|
|
143
|
+
execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'pipe' });
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=windows-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-service.js","sourceRoot":"","sources":["../src/windows-service.ts"],"names":[],"mappings":"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"}
|
package/dist/ws-server.d.ts
CHANGED
|
@@ -8,13 +8,26 @@ export declare class WsServer {
|
|
|
8
8
|
private onConnectCallback;
|
|
9
9
|
private onDisconnectCallback;
|
|
10
10
|
private clientAlive;
|
|
11
|
+
private esp32Clients;
|
|
12
|
+
private eventTransformer;
|
|
13
|
+
private ulanziClients;
|
|
14
|
+
private ulanziPresenceCallback;
|
|
15
|
+
private tuiClients;
|
|
11
16
|
private pingTimer;
|
|
12
17
|
constructor(server: Server);
|
|
13
18
|
private broadcastHooks;
|
|
14
19
|
/** Register a hook that gets called on every broadcast (e.g., ESP32 serial relay). */
|
|
15
20
|
onBroadcast(hook: (event: BridgeEvent) => void): void;
|
|
21
|
+
setEventTransformer(transformer: ((event: BridgeEvent, client: WebSocket) => BridgeEvent | null) | null): void;
|
|
22
|
+
isEsp32Client(ws: WebSocket): boolean;
|
|
23
|
+
markEsp32Client(ws: WebSocket): void;
|
|
24
|
+
private payloadFor;
|
|
16
25
|
broadcast(event: BridgeEvent): void;
|
|
17
26
|
onCommand(callback: (cmd: PluginCommand) => void): void;
|
|
27
|
+
/** Register a callback invoked when the Ulanzi-plugin presence flips (true =
|
|
28
|
+
* at least one connected, false = none). Fires once immediately with the
|
|
29
|
+
* current state so the consumer can sync on startup. */
|
|
30
|
+
onUlanziPluginPresence(callback: (present: boolean) => void): void;
|
|
18
31
|
/** Inject a command from a non-WS source (e.g., D200H agent via stdout/stdin pipe). */
|
|
19
32
|
dispatchCommand(cmd: PluginCommand): void;
|
|
20
33
|
/** Register a callback for raw messages before PluginCommand dispatch. Return true to consume. */
|
|
@@ -22,9 +35,17 @@ export declare class WsServer {
|
|
|
22
35
|
/** Broadcast to all clients except the sender */
|
|
23
36
|
broadcastExcept(event: BridgeEvent, except: WebSocket): void;
|
|
24
37
|
onClientConnect(callback: (ws: WebSocket) => void): void;
|
|
25
|
-
onClientDisconnect(callback: () => void): void;
|
|
38
|
+
onClientDisconnect(callback: (ws: WebSocket) => void): void;
|
|
26
39
|
sendTo(ws: WebSocket, event: BridgeEvent): void;
|
|
27
40
|
getClientCount(): number;
|
|
41
|
+
getUlanziClientCount(): number;
|
|
42
|
+
/** Registered TUI dashboards (`client_register {clientType:"tui"}`), deduped
|
|
43
|
+
* by client id so a TUI that reconnects (new WS, same host+pid) yields one
|
|
44
|
+
* entry while both sockets briefly overlap. */
|
|
45
|
+
getTuiClients(): Array<{
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
}>;
|
|
28
49
|
close(): void;
|
|
29
50
|
}
|
|
30
51
|
//# sourceMappingURL=ws-server.d.ts.map
|
package/dist/ws-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-server.d.ts","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,MAAM,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK7D,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,kBAAkB,CAA+E;IACzG,OAAO,CAAC,iBAAiB,CAA0C;IACnE,OAAO,CAAC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ws-server.d.ts","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,MAAM,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK7D,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,kBAAkB,CAA+E;IACzG,OAAO,CAAC,iBAAiB,CAA0C;IACnE,OAAO,CAAC,oBAAoB,CAA0C;IACtE,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,gBAAgB,CAAgF;IAIxG,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,sBAAsB,CAA6C;IAK3E,OAAO,CAAC,UAAU,CAAsD;IACxE,OAAO,CAAC,SAAS,CAAiC;gBAEtC,MAAM,EAAE,MAAM;IAmH1B,OAAO,CAAC,cAAc,CAA2C;IAEjE,sFAAsF;IACtF,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIrD,mBAAmB,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,KAAK,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAI9G,aAAa,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO;IAIrC,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAIpC,OAAO,CAAC,UAAU;IAOlB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAiBnC,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAIvD;;6DAEyD;IACzD,sBAAsB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAKlE,uFAAuF;IACvF,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAIzC,kGAAkG;IAClG,YAAY,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,GAAG,IAAI;IAI1F,iDAAiD;IACjD,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAW5D,eAAe,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAIxD,kBAAkB,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAI3D,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAQ/C,cAAc,IAAI,MAAM;IAIxB,oBAAoB,IAAI,MAAM;IAI9B;;oDAEgD;IAChD,aAAa,IAAI,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAMpD,KAAK,IAAI,IAAI;CASd"}
|
package/dist/ws-server.js
CHANGED
|
@@ -9,6 +9,18 @@ export class WsServer {
|
|
|
9
9
|
onConnectCallback = null;
|
|
10
10
|
onDisconnectCallback = null;
|
|
11
11
|
clientAlive = new Map();
|
|
12
|
+
esp32Clients = new Set();
|
|
13
|
+
eventTransformer = null;
|
|
14
|
+
// Clients that registered as the Ulanzi Studio plugin. While any are present,
|
|
15
|
+
// the daemon's direct-HID D200H module stands down so the two don't fight over
|
|
16
|
+
// the device (Ulanzi Studio drives it through the official plugin instead).
|
|
17
|
+
ulanziClients = new Set();
|
|
18
|
+
ulanziPresenceCallback = null;
|
|
19
|
+
// TUI dashboards (`agentdeck dashboard`) that registered via
|
|
20
|
+
// `client_register {clientType:"tui"}`. Volunteer-roster model like the
|
|
21
|
+
// Stream Deck plugin — presence only lives as long as the WS does, so the
|
|
22
|
+
// topology row disappears the moment the TUI exits.
|
|
23
|
+
tuiClients = new Map();
|
|
12
24
|
pingTimer;
|
|
13
25
|
constructor(server) {
|
|
14
26
|
this.wss = new WebSocketServer({ server });
|
|
@@ -49,6 +61,11 @@ export class WsServer {
|
|
|
49
61
|
}
|
|
50
62
|
debug('WS', `Remote client authenticated from ${remoteIp}`);
|
|
51
63
|
}
|
|
64
|
+
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
65
|
+
if (url.searchParams.get('clientType') === 'esp32' || url.searchParams.get('esp32') === '1') {
|
|
66
|
+
this.esp32Clients.add(ws);
|
|
67
|
+
debug('WS', 'ESP32 WiFi client tagged from query');
|
|
68
|
+
}
|
|
52
69
|
debug('WS', 'Plugin connected');
|
|
53
70
|
this.clientAlive.set(ws, true);
|
|
54
71
|
// Send current state to newly connected client
|
|
@@ -62,6 +79,23 @@ export class WsServer {
|
|
|
62
79
|
try {
|
|
63
80
|
const msg = JSON.parse(data.toString());
|
|
64
81
|
debug('WS', `recv cmd: ${msg.type}`);
|
|
82
|
+
// Track Ulanzi plugin presence (device-ownership arbitration).
|
|
83
|
+
if (msg.type === 'client_register' && msg.clientType === 'ulanzi-plugin') {
|
|
84
|
+
const was = this.ulanziClients.size > 0;
|
|
85
|
+
this.ulanziClients.add(ws);
|
|
86
|
+
if (!was) {
|
|
87
|
+
debug('WS', 'Ulanzi plugin registered — direct-HID D200H stands down');
|
|
88
|
+
this.ulanziPresenceCallback?.(true);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Track TUI dashboard presence (topology row on all dashboards).
|
|
92
|
+
if (msg.type === 'client_register' && msg.clientType === 'tui') {
|
|
93
|
+
const dev = (Array.isArray(msg.devices) ? msg.devices[0] : null);
|
|
94
|
+
const name = typeof dev?.name === 'string' && dev.name ? dev.name : 'terminal';
|
|
95
|
+
const id = typeof dev?.id === 'string' && dev.id ? dev.id : name;
|
|
96
|
+
this.tuiClients.set(ws, { id, name });
|
|
97
|
+
debug('WS', `TUI dashboard registered: ${id}`);
|
|
98
|
+
}
|
|
65
99
|
// Allow raw message callback to intercept relay events (e.g. deck_slot_map)
|
|
66
100
|
if (this.rawMessageCallback && this.rawMessageCallback(msg, ws)) {
|
|
67
101
|
return; // handled
|
|
@@ -77,8 +111,14 @@ export class WsServer {
|
|
|
77
111
|
ws.on('close', () => {
|
|
78
112
|
debug('WS', 'Plugin disconnected');
|
|
79
113
|
this.clientAlive.delete(ws);
|
|
114
|
+
this.esp32Clients.delete(ws);
|
|
115
|
+
this.tuiClients.delete(ws);
|
|
116
|
+
if (this.ulanziClients.delete(ws) && this.ulanziClients.size === 0) {
|
|
117
|
+
debug('WS', 'Ulanzi plugin gone — direct-HID D200H may resume');
|
|
118
|
+
this.ulanziPresenceCallback?.(false);
|
|
119
|
+
}
|
|
80
120
|
if (this.onDisconnectCallback) {
|
|
81
|
-
this.onDisconnectCallback();
|
|
121
|
+
this.onDisconnectCallback(ws);
|
|
82
122
|
}
|
|
83
123
|
});
|
|
84
124
|
ws.on('error', (err) => {
|
|
@@ -91,14 +131,34 @@ export class WsServer {
|
|
|
91
131
|
onBroadcast(hook) {
|
|
92
132
|
this.broadcastHooks.push(hook);
|
|
93
133
|
}
|
|
134
|
+
setEventTransformer(transformer) {
|
|
135
|
+
this.eventTransformer = transformer;
|
|
136
|
+
}
|
|
137
|
+
isEsp32Client(ws) {
|
|
138
|
+
return this.esp32Clients.has(ws);
|
|
139
|
+
}
|
|
140
|
+
markEsp32Client(ws) {
|
|
141
|
+
this.esp32Clients.add(ws);
|
|
142
|
+
}
|
|
143
|
+
payloadFor(event, client, cachedPayload) {
|
|
144
|
+
if (!this.eventTransformer)
|
|
145
|
+
return cachedPayload ?? JSON.stringify(event);
|
|
146
|
+
const transformed = this.eventTransformer(event, client);
|
|
147
|
+
if (!transformed)
|
|
148
|
+
return null;
|
|
149
|
+
return transformed === event ? (cachedPayload ?? JSON.stringify(event)) : JSON.stringify(transformed);
|
|
150
|
+
}
|
|
94
151
|
broadcast(event) {
|
|
95
152
|
const payload = JSON.stringify(event);
|
|
96
153
|
const clientCount = this.wss.clients.size;
|
|
97
154
|
debug('WS', `broadcast(${event.type}) to ${clientCount} clients`);
|
|
98
155
|
for (const client of this.wss.clients) {
|
|
99
156
|
if (client.readyState === WebSocket.OPEN) {
|
|
157
|
+
const clientPayload = this.payloadFor(event, client, payload);
|
|
158
|
+
if (!clientPayload)
|
|
159
|
+
continue;
|
|
100
160
|
try {
|
|
101
|
-
client.send(
|
|
161
|
+
client.send(clientPayload);
|
|
102
162
|
}
|
|
103
163
|
catch { /* client disconnecting */ }
|
|
104
164
|
}
|
|
@@ -114,6 +174,13 @@ export class WsServer {
|
|
|
114
174
|
onCommand(callback) {
|
|
115
175
|
this.commandCallback = callback;
|
|
116
176
|
}
|
|
177
|
+
/** Register a callback invoked when the Ulanzi-plugin presence flips (true =
|
|
178
|
+
* at least one connected, false = none). Fires once immediately with the
|
|
179
|
+
* current state so the consumer can sync on startup. */
|
|
180
|
+
onUlanziPluginPresence(callback) {
|
|
181
|
+
this.ulanziPresenceCallback = callback;
|
|
182
|
+
callback(this.ulanziClients.size > 0);
|
|
183
|
+
}
|
|
117
184
|
/** Inject a command from a non-WS source (e.g., D200H agent via stdout/stdin pipe). */
|
|
118
185
|
dispatchCommand(cmd) {
|
|
119
186
|
this.commandCallback?.(cmd);
|
|
@@ -127,8 +194,11 @@ export class WsServer {
|
|
|
127
194
|
const payload = JSON.stringify(event);
|
|
128
195
|
for (const client of this.wss.clients) {
|
|
129
196
|
if (client !== except && client.readyState === WebSocket.OPEN) {
|
|
197
|
+
const clientPayload = this.payloadFor(event, client, payload);
|
|
198
|
+
if (!clientPayload)
|
|
199
|
+
continue;
|
|
130
200
|
try {
|
|
131
|
-
client.send(
|
|
201
|
+
client.send(clientPayload);
|
|
132
202
|
}
|
|
133
203
|
catch { /* client disconnecting */ }
|
|
134
204
|
}
|
|
@@ -142,8 +212,11 @@ export class WsServer {
|
|
|
142
212
|
}
|
|
143
213
|
sendTo(ws, event) {
|
|
144
214
|
if (ws.readyState === WebSocket.OPEN) {
|
|
215
|
+
const payload = this.payloadFor(event, ws);
|
|
216
|
+
if (!payload)
|
|
217
|
+
return;
|
|
145
218
|
try {
|
|
146
|
-
ws.send(
|
|
219
|
+
ws.send(payload);
|
|
147
220
|
}
|
|
148
221
|
catch { /* client disconnecting */ }
|
|
149
222
|
}
|
|
@@ -151,6 +224,18 @@ export class WsServer {
|
|
|
151
224
|
getClientCount() {
|
|
152
225
|
return this.wss.clients.size;
|
|
153
226
|
}
|
|
227
|
+
getUlanziClientCount() {
|
|
228
|
+
return this.ulanziClients.size;
|
|
229
|
+
}
|
|
230
|
+
/** Registered TUI dashboards (`client_register {clientType:"tui"}`), deduped
|
|
231
|
+
* by client id so a TUI that reconnects (new WS, same host+pid) yields one
|
|
232
|
+
* entry while both sockets briefly overlap. */
|
|
233
|
+
getTuiClients() {
|
|
234
|
+
const byId = new Map();
|
|
235
|
+
for (const info of this.tuiClients.values())
|
|
236
|
+
byId.set(info.id, info);
|
|
237
|
+
return [...byId.values()];
|
|
238
|
+
}
|
|
154
239
|
close() {
|
|
155
240
|
clearInterval(this.pingTimer);
|
|
156
241
|
this.clientAlive.clear();
|
package/dist/ws-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAGhD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,OAAO,QAAQ;IACX,GAAG,CAAkB;IACrB,eAAe,GAA0C,IAAI,CAAC;IAC9D,kBAAkB,GAA0E,IAAI,CAAC;IACjG,iBAAiB,GAAqC,IAAI,CAAC;IAC3D,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
#if canImport(FoundationModels)
|
|
3
|
+
import FoundationModels
|
|
4
|
+
#endif
|
|
5
|
+
|
|
6
|
+
struct HelperRequest: Decodable {
|
|
7
|
+
let id: Int
|
|
8
|
+
let type: String?
|
|
9
|
+
let prompt: String?
|
|
10
|
+
let instructions: String?
|
|
11
|
+
let temperature: Double?
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@main
|
|
15
|
+
struct AgentDeckFMHelper {
|
|
16
|
+
static func main() async {
|
|
17
|
+
do {
|
|
18
|
+
for try await line in FileHandle.standardInput.bytes.lines {
|
|
19
|
+
await handle(line)
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
write(["id": -1, "error": "stdin_error", "reason": String(describing: error)])
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private static func handle(_ line: String) async {
|
|
27
|
+
guard let data = line.data(using: .utf8),
|
|
28
|
+
let request = try? JSONDecoder().decode(HelperRequest.self, from: data) else {
|
|
29
|
+
write(["id": -1, "error": "bad_request", "reason": "invalid JSON line"])
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if request.type == "health" {
|
|
34
|
+
write(healthResponse(id: request.id))
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
guard let prompt = request.prompt, !prompt.isEmpty else {
|
|
39
|
+
write(["id": request.id, "error": "bad_request", "reason": "missing prompt"])
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#if canImport(FoundationModels)
|
|
44
|
+
if #available(macOS 26.0, *) {
|
|
45
|
+
guard case .available = SystemLanguageModel.default.availability else {
|
|
46
|
+
write([
|
|
47
|
+
"id": request.id,
|
|
48
|
+
"error": "unavailable",
|
|
49
|
+
"reason": unavailableReason(),
|
|
50
|
+
])
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
do {
|
|
55
|
+
let session = LanguageModelSession(
|
|
56
|
+
instructions: request.instructions ?? "You are an exacting code evaluator. Reply with strict JSON only."
|
|
57
|
+
)
|
|
58
|
+
let options = GenerationOptions(temperature: request.temperature ?? 0)
|
|
59
|
+
let response = try await session.respond(to: prompt, options: options)
|
|
60
|
+
write(["id": request.id, "text": response.content])
|
|
61
|
+
} catch {
|
|
62
|
+
write(["id": request.id, "error": "session_error", "reason": String(describing: error)])
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
write(["id": request.id, "error": "unavailable", "reason": "macOS 26 or later required"])
|
|
66
|
+
}
|
|
67
|
+
#else
|
|
68
|
+
write(["id": request.id, "error": "unavailable", "reason": "FoundationModels framework not present"])
|
|
69
|
+
#endif
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private static func healthResponse(id: Int) -> [String: Any] {
|
|
73
|
+
#if canImport(FoundationModels)
|
|
74
|
+
if #available(macOS 26.0, *) {
|
|
75
|
+
if case .available = SystemLanguageModel.default.availability {
|
|
76
|
+
return ["id": id, "status": "ready"]
|
|
77
|
+
}
|
|
78
|
+
return ["id": id, "status": "unavailable", "reason": unavailableReason()]
|
|
79
|
+
}
|
|
80
|
+
return ["id": id, "status": "unavailable", "reason": "macOS 26 or later required"]
|
|
81
|
+
#else
|
|
82
|
+
return ["id": id, "status": "unavailable", "reason": "FoundationModels framework not present"]
|
|
83
|
+
#endif
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private static func unavailableReason() -> String {
|
|
87
|
+
#if canImport(FoundationModels)
|
|
88
|
+
if #available(macOS 26.0, *) {
|
|
89
|
+
switch SystemLanguageModel.default.availability {
|
|
90
|
+
case .available:
|
|
91
|
+
return "available"
|
|
92
|
+
case .unavailable(let reason):
|
|
93
|
+
return "unavailable: \(reason)"
|
|
94
|
+
@unknown default:
|
|
95
|
+
return "unavailable: unknown state"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return "macOS 26 or later required"
|
|
99
|
+
#else
|
|
100
|
+
return "FoundationModels framework not present"
|
|
101
|
+
#endif
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private static func write(_ object: [String: Any]) {
|
|
105
|
+
guard JSONSerialization.isValidJSONObject(object),
|
|
106
|
+
let data = try? JSONSerialization.data(withJSONObject: object),
|
|
107
|
+
let text = String(data: data, encoding: .utf8) else {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
FileHandle.standardOutput.write(Data((text + "\n").utf8))
|
|
111
|
+
}
|
|
112
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentdeck/bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
-
"!dist/__tests__"
|
|
13
|
+
"!dist/__tests__",
|
|
14
|
+
"assets/fonts",
|
|
15
|
+
"assets/fm-helper",
|
|
16
|
+
"fm-helper",
|
|
17
|
+
"scripts/build-fm-helper.mjs"
|
|
14
18
|
],
|
|
15
19
|
"main": "./dist/index.js",
|
|
16
20
|
"bin": {
|
|
@@ -23,7 +27,8 @@
|
|
|
23
27
|
"qrcode": "^1.5.4",
|
|
24
28
|
"strip-ansi": "^7.2.0",
|
|
25
29
|
"ws": "^8.20.0",
|
|
26
|
-
"@agentdeck/
|
|
30
|
+
"@agentdeck/hooks": "0.2.3",
|
|
31
|
+
"@agentdeck/shared": "0.2.3"
|
|
27
32
|
},
|
|
28
33
|
"optionalDependencies": {
|
|
29
34
|
"@picovoice/porcupine-node": "^4.0.2",
|
|
@@ -43,6 +48,7 @@
|
|
|
43
48
|
},
|
|
44
49
|
"scripts": {
|
|
45
50
|
"build": "tsc",
|
|
51
|
+
"build:fm-helper": "node scripts/build-fm-helper.mjs",
|
|
46
52
|
"dev": "tsc --watch",
|
|
47
53
|
"clean": "rm -rf dist",
|
|
48
54
|
"typecheck": "tsc --noEmit",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { chmodSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const source = join(root, 'fm-helper', 'AgentDeckFMHelper.swift');
|
|
9
|
+
const out = join(root, 'assets', 'fm-helper', 'agentdeck-fm-helper');
|
|
10
|
+
const arch = process.arch === 'arm64' ? 'arm64' : 'x86_64';
|
|
11
|
+
const target = `${arch}-apple-macos26.0`;
|
|
12
|
+
|
|
13
|
+
if (process.platform !== 'darwin') {
|
|
14
|
+
console.log('[fm-helper] skipped: darwin only');
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
19
|
+
const swiftc = execFileSync('/usr/bin/xcrun', ['--find', 'swiftc'], { encoding: 'utf8' }).trim();
|
|
20
|
+
// swiftc is run directly (not via `xcrun swiftc`), so SDKROOT isn't set and the
|
|
21
|
+
// stdlib can't be found ("unable to load standard library for target ..."). Pass
|
|
22
|
+
// the SDK path explicitly so the build is reproducible from the npm lifecycle.
|
|
23
|
+
const sdk = execFileSync('/usr/bin/xcrun', ['--sdk', 'macosx', '--show-sdk-path'], { encoding: 'utf8' }).trim();
|
|
24
|
+
execFileSync(swiftc, ['-parse-as-library', '-sdk', sdk, '-target', target, source, '-o', out], { stdio: 'inherit' });
|
|
25
|
+
chmodSync(out, 0o755);
|
|
26
|
+
console.log(`[fm-helper] built ${out}`);
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 SerendipityBound
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|