@agentdeck/bridge 0.2.2 → 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 +1021 -93
- 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,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared spawn helper for the daemon-managed Python BLE sync clients
|
|
3
|
+
* (iDotMatrix `sync.py`, Timebox `sync_ble.py`).
|
|
4
|
+
*
|
|
5
|
+
* Both clients were previously spawned with `stdio: 'ignore'`, which keeps the
|
|
6
|
+
* child's verbose stdout from flooding the daemon log — but also discards the
|
|
7
|
+
* crash traceback on stderr. A missing `bleak`/`idotmatrix` dependency, a stale
|
|
8
|
+
* venv, or a bad BLE address then looks identical to a clean exit: the panel
|
|
9
|
+
* goes dark and the daemon logs only "respawning in Ns" with no cause. This
|
|
10
|
+
* helper pipes stdout/stderr into small ring buffers so the manager can log
|
|
11
|
+
* *why* the sync died without flooding the daemon log while it is running.
|
|
12
|
+
*/
|
|
13
|
+
import { spawn } from 'child_process';
|
|
14
|
+
/**
|
|
15
|
+
* Spawn a Python sync child with stdout/stderr captured into bounded tail
|
|
16
|
+
* buffers. Output is not streamed live; callers read it only if the child exits.
|
|
17
|
+
*/
|
|
18
|
+
export function spawnPythonSync(venvPython, args, maxTailLines = 8) {
|
|
19
|
+
// [stdin ignored, stdout/stderr piped into small rings for diagnostics]
|
|
20
|
+
const proc = spawn(venvPython, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
21
|
+
const stderrTail = [];
|
|
22
|
+
const outputTail = [];
|
|
23
|
+
let stderrPartial = '';
|
|
24
|
+
let stdoutPartial = '';
|
|
25
|
+
const capture = (label, chunk) => {
|
|
26
|
+
let partial = label === 'stderr' ? stderrPartial : stdoutPartial;
|
|
27
|
+
partial += chunk.toString();
|
|
28
|
+
const lines = partial.split('\n');
|
|
29
|
+
partial = lines.pop() ?? '';
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmed = line.trimEnd();
|
|
32
|
+
if (!trimmed)
|
|
33
|
+
continue;
|
|
34
|
+
const tagged = `${label}: ${trimmed}`;
|
|
35
|
+
outputTail.push(tagged);
|
|
36
|
+
if (outputTail.length > maxTailLines)
|
|
37
|
+
outputTail.shift();
|
|
38
|
+
if (label === 'stderr') {
|
|
39
|
+
stderrTail.push(trimmed);
|
|
40
|
+
if (stderrTail.length > maxTailLines)
|
|
41
|
+
stderrTail.shift();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (label === 'stderr')
|
|
45
|
+
stderrPartial = partial;
|
|
46
|
+
else
|
|
47
|
+
stdoutPartial = partial;
|
|
48
|
+
};
|
|
49
|
+
proc.stdout?.on('data', (chunk) => capture('stdout', chunk));
|
|
50
|
+
proc.stderr?.on('data', (chunk) => capture('stderr', chunk));
|
|
51
|
+
return {
|
|
52
|
+
proc,
|
|
53
|
+
stderrTail: () => {
|
|
54
|
+
const lines = stderrPartial.trim() ? [...stderrTail, stderrPartial.trim()] : stderrTail;
|
|
55
|
+
return lines.slice(-maxTailLines).join(' | ');
|
|
56
|
+
},
|
|
57
|
+
outputTail: () => {
|
|
58
|
+
const lines = [...outputTail];
|
|
59
|
+
if (stdoutPartial.trim())
|
|
60
|
+
lines.push(`stdout: ${stdoutPartial.trim()}`);
|
|
61
|
+
if (stderrPartial.trim())
|
|
62
|
+
lines.push(`stderr: ${stderrPartial.trim()}`);
|
|
63
|
+
return lines.slice(-maxTailLines).join(' | ');
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Emit a repeated-cycle summary at most this often while suppressing. */
|
|
68
|
+
const SQUELCH_SUMMARY_INTERVAL_MS = 60 * 60_000;
|
|
69
|
+
/** A run this long means the previous respawn loop ended; the next exit logs fresh. */
|
|
70
|
+
const SQUELCH_RESET_UPTIME_MS = 5 * 60_000;
|
|
71
|
+
/**
|
|
72
|
+
* Collapse variable parts so consecutive exits from the same failure loop
|
|
73
|
+
* compare equal: timestamps/addresses/hashes are masked, and the ` | `-joined
|
|
74
|
+
* output tail is reduced to its set of distinct lines — the ring buffer
|
|
75
|
+
* captures 1 or 2 copies of the same retry error depending on timing, which
|
|
76
|
+
* must not read as a different cycle.
|
|
77
|
+
*/
|
|
78
|
+
function cycleSignature(code, signal, tail) {
|
|
79
|
+
const masked = tail.replace(/[0-9A-Fa-f]{4,}/g, '#').replace(/\d+/g, '#');
|
|
80
|
+
const segments = [...new Set(masked.split(' | ').map((s) => s.trim()))].sort();
|
|
81
|
+
return `${code}|${signal}|${segments.join(' | ')}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Log gate for the respawn loop of a managed BLE sync child.
|
|
85
|
+
*
|
|
86
|
+
* A powered-off or out-of-range panel makes the sync child exit the same way
|
|
87
|
+
* every backoff period, all night — thousands of identical start/exit pairs in
|
|
88
|
+
* the daemon log. The gate logs the first two occurrences of an exit cycle in
|
|
89
|
+
* full, then suppresses identical repeats (including their respawn start
|
|
90
|
+
* lines), emitting an hourly count summary instead. Any *different* exit —
|
|
91
|
+
* new error text, non-zero code, a crash after a long healthy run — flushes
|
|
92
|
+
* the summary and logs immediately, so novel failures are never hidden.
|
|
93
|
+
*/
|
|
94
|
+
export function createSyncCycleSquelch(log) {
|
|
95
|
+
let lastSignature = null;
|
|
96
|
+
let repeats = 0; // suppressed repeats of lastSignature (beyond the logged first occurrence)
|
|
97
|
+
let suppressedSinceSummary = 0;
|
|
98
|
+
let suppressedSince = 0;
|
|
99
|
+
let lastSummaryAt = 0;
|
|
100
|
+
let lastLine = '';
|
|
101
|
+
const flush = () => {
|
|
102
|
+
if (suppressedSinceSummary > 0) {
|
|
103
|
+
log(`suppressed ${suppressedSinceSummary} repeats of the same sync cycle since ` +
|
|
104
|
+
`${new Date(suppressedSince).toISOString()}; latest: ${lastLine}`);
|
|
105
|
+
}
|
|
106
|
+
lastSignature = null;
|
|
107
|
+
repeats = 0;
|
|
108
|
+
suppressedSinceSummary = 0;
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
logStart(line) {
|
|
112
|
+
if (repeats > 0)
|
|
113
|
+
return; // inside a suppressed repeating cycle
|
|
114
|
+
log(line);
|
|
115
|
+
},
|
|
116
|
+
logExit(code, signal, uptimeMs, tail, line) {
|
|
117
|
+
// A long healthy run means the old loop ended; whatever exits next is a
|
|
118
|
+
// fresh incident even if the text matches the old one.
|
|
119
|
+
if (uptimeMs >= SQUELCH_RESET_UPTIME_MS)
|
|
120
|
+
flush();
|
|
121
|
+
const signature = cycleSignature(code, signal, tail);
|
|
122
|
+
if (signature !== lastSignature) {
|
|
123
|
+
flush();
|
|
124
|
+
lastSignature = signature;
|
|
125
|
+
log(line);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
repeats += 1;
|
|
129
|
+
suppressedSinceSummary += 1;
|
|
130
|
+
lastLine = line;
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
if (repeats === 1) {
|
|
133
|
+
// Second identical cycle: log it once more, then go quiet.
|
|
134
|
+
suppressedSinceSummary = 0;
|
|
135
|
+
suppressedSince = now;
|
|
136
|
+
lastSummaryAt = now;
|
|
137
|
+
log(`${line} — repeating cycle; suppressing identical start/exit logs (hourly summary, a different exit logs immediately)`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (now - lastSummaryAt >= SQUELCH_SUMMARY_INTERVAL_MS) {
|
|
141
|
+
log(`suppressed ${suppressedSinceSummary} repeats of the same sync cycle in the last ` +
|
|
142
|
+
`${Math.round((now - lastSummaryAt) / 60_000)}m; latest: ${lastLine}`);
|
|
143
|
+
lastSummaryAt = now;
|
|
144
|
+
suppressedSinceSummary = 0;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* SIGTERM a managed sync child and wait (bounded) for it to exit, so the child's
|
|
151
|
+
* OFFLINE / blank farewell frame finishes painting before the daemon process
|
|
152
|
+
* exits.
|
|
153
|
+
*
|
|
154
|
+
* The Python sync clients trap SIGTERM, break their loop, push a final OFFLINE
|
|
155
|
+
* (iDotMatrix) or black (Timebox) frame over BLE, then disconnect. That push
|
|
156
|
+
* takes ~1s. If we only fire SIGTERM and return immediately (the old behaviour),
|
|
157
|
+
* the daemon exits right away; when the daemon is a launchd job, launchd then
|
|
158
|
+
* tears the job down and SIGKILLs the orphaned child mid-farewell — leaving the
|
|
159
|
+
* stateful BLE panel frozen on its last dashboard frame instead of OFFLINE.
|
|
160
|
+
* Awaiting the child's exit here keeps the daemon alive just long enough for the
|
|
161
|
+
* farewell to land. Escalates to SIGKILL if the child overruns `timeoutMs`.
|
|
162
|
+
*/
|
|
163
|
+
export function terminateSyncChild(proc, timeoutMs = 3_000) {
|
|
164
|
+
return new Promise((resolve) => {
|
|
165
|
+
if (proc.exitCode !== null || proc.signalCode !== null) {
|
|
166
|
+
resolve();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
let settled = false;
|
|
170
|
+
const finish = () => {
|
|
171
|
+
if (settled)
|
|
172
|
+
return;
|
|
173
|
+
settled = true;
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
resolve();
|
|
176
|
+
};
|
|
177
|
+
const timer = setTimeout(() => {
|
|
178
|
+
try {
|
|
179
|
+
proc.kill('SIGKILL');
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
/* already gone */
|
|
183
|
+
}
|
|
184
|
+
finish();
|
|
185
|
+
}, timeoutMs);
|
|
186
|
+
timer.unref?.();
|
|
187
|
+
proc.once('exit', finish);
|
|
188
|
+
try {
|
|
189
|
+
proc.kill('SIGTERM');
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
finish();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=ble-sync-spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ble-sync-spawn.js","sourceRoot":"","sources":["../src/ble-sync-spawn.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge-core.d.ts
CHANGED
|
@@ -9,12 +9,28 @@ import { BridgeTimelineStore } from './timeline-store.js';
|
|
|
9
9
|
import type { BridgeLogStream } from './log-stream.js';
|
|
10
10
|
import { type ApiUsageData } from './usage-api.js';
|
|
11
11
|
import type { ApmeModule } from './apme/index.js';
|
|
12
|
-
import { type BridgeEvent, type StateSnapshot, type AgentType, type AgentCapabilities, type ModelCatalogEntry, type VoiceAssistantState } from './types.js';
|
|
12
|
+
import { type BridgeEvent, type StateSnapshot, type AgentType, type AgentCapabilities, type ModelCatalogEntry, type VoiceAssistantState, type TimelineEntry } from './types.js';
|
|
13
|
+
export declare const INITIAL_TIMELINE_HISTORY_MAX_BYTES: number;
|
|
14
|
+
/** Attach host-local "HH:MM" to a timeline entry. Devices have no timezone
|
|
15
|
+
* (their NTP runs UTC), so rendering `ts` directly shows a 9-hour-off clock
|
|
16
|
+
* in KST. Stamped at the SOURCE (broadcast + history) so both the serial and
|
|
17
|
+
* WS transports carry it; native apps simply ignore the extra field. */
|
|
18
|
+
export declare function stampLocalHm<T extends {
|
|
19
|
+
ts?: number;
|
|
20
|
+
localHm?: string;
|
|
21
|
+
}>(entry: T): T;
|
|
22
|
+
export declare function buildCappedTimelineHistory(entries: TimelineEntry[], maxBytes?: number, extraFields?: Record<string, unknown>): BridgeEvent | null;
|
|
13
23
|
export interface BridgeCoreOptions {
|
|
14
24
|
port: number;
|
|
15
25
|
sessionId?: string;
|
|
16
26
|
projectName: string;
|
|
17
27
|
httpServer: Server;
|
|
28
|
+
/**
|
|
29
|
+
* True for the daemon hub. The hub aggregates many agents and has no single
|
|
30
|
+
* session model, so it must always expose the account-level Claude subscription
|
|
31
|
+
* quota in usage events rather than gating it on the (arbitrary) active model.
|
|
32
|
+
*/
|
|
33
|
+
isDaemon?: boolean;
|
|
18
34
|
}
|
|
19
35
|
/**
|
|
20
36
|
* Shared infrastructure extracted from index.ts and daemon-server.ts.
|
|
@@ -47,6 +63,7 @@ export declare class BridgeCore {
|
|
|
47
63
|
cachedAntigravityStatus: import("./antigravity-local.js").AntigravityStatusInfo | null;
|
|
48
64
|
cachedGatewayAvailable: boolean;
|
|
49
65
|
cachedGatewayConnected: boolean;
|
|
66
|
+
cachedGatewayAuthStatus: 'gateway_not_found' | 'gateway_reachable' | 'gateway_token_missing' | 'pairing_required' | 'approval_pending' | 'connected' | 'auth_failed' | 'token_mismatch' | 'device_auth_invalid' | 'unsupported_protocol';
|
|
50
67
|
cachedGatewayHasError: boolean;
|
|
51
68
|
cachedModelCatalog: ModelCatalogEntry[] | null;
|
|
52
69
|
cachedVoiceAssistantState: VoiceAssistantState;
|
|
@@ -55,6 +72,7 @@ export declare class BridgeCore {
|
|
|
55
72
|
private intervals;
|
|
56
73
|
private timeouts;
|
|
57
74
|
private lastSessionsListBroadcast;
|
|
75
|
+
private lastSessionsListEvent;
|
|
58
76
|
private shutdownInProgress;
|
|
59
77
|
private shutdownCallbacks;
|
|
60
78
|
private sseBroadcast?;
|
|
@@ -69,6 +87,8 @@ export declare class BridgeCore {
|
|
|
69
87
|
private apmeAgentType;
|
|
70
88
|
private apmeCwd;
|
|
71
89
|
private static readonly USAGE_STALE_TTL;
|
|
90
|
+
/** True when this core backs the daemon hub (see BridgeCoreOptions.isDaemon). */
|
|
91
|
+
readonly isDaemon: boolean;
|
|
72
92
|
constructor(opts: BridgeCoreOptions);
|
|
73
93
|
/** Set optional SSE broadcast callback (for HookServer-based sessions) */
|
|
74
94
|
setSseBroadcast(cb: (evt: BridgeEvent) => void): void;
|
|
@@ -132,6 +152,9 @@ export declare class BridgeCore {
|
|
|
132
152
|
private hasClients;
|
|
133
153
|
/** Broadcast enriched sessions list (debounced 2s from state_changed) */
|
|
134
154
|
broadcastSessionsList(): Promise<void>;
|
|
155
|
+
/** The most recent sessions_list event, for the ESP32 serial heartbeat
|
|
156
|
+
* re-sync. Null until the first broadcast. */
|
|
157
|
+
getLastSessionsListEvent(): BridgeEvent | null;
|
|
135
158
|
/** Debounced sessions list broadcast (for state_changed handler) */
|
|
136
159
|
maybeBroadcastSessionsList(): void;
|
|
137
160
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-core.d.ts","sourceRoot":"","sources":["../src/bridge-core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge-core.d.ts","sourceRoot":"","sources":["../src/bridge-core.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge-core.js
CHANGED
|
@@ -8,16 +8,65 @@ import { BridgeTimelineStore } from './timeline-store.js';
|
|
|
8
8
|
import { readAntigravityLocalStatus } from './antigravity-local.js';
|
|
9
9
|
import { buildSubscriptions, buildUsageEvent } from './usage-event.js';
|
|
10
10
|
import { readCodexAuthStatus } from './codex-auth.js';
|
|
11
|
+
import { readCodexRateLimits } from './codex-rate-limits.js';
|
|
11
12
|
import { fetchMlxModels } from './mlx-probe.js';
|
|
13
|
+
import { buildDisplayStateEvent } from './display-dim.js';
|
|
12
14
|
import { loadMlxSettings } from '@agentdeck/shared';
|
|
13
15
|
import { probeGateway, checkGatewayHealth } from './gateway-probe.js';
|
|
14
16
|
import { fetchUsageFromApi, hasOAuthToken, getTokenStatus } from './usage-api.js';
|
|
15
17
|
import { buildEnrichedSessionsList } from './session-aggregator.js';
|
|
18
|
+
import { activityFor } from './session-activity.js';
|
|
16
19
|
import { register as registerSession, deregister as deregisterSession, } from './session-registry.js';
|
|
17
20
|
import { getOrCreateToken, getWsUrl } from './auth.js';
|
|
18
21
|
import { log, logError, debug } from './logger.js';
|
|
19
|
-
import { invalidateMdnsInstance } from './mdns.js';
|
|
22
|
+
import { invalidateMdnsInstance, isNonFatalMdnsError } from './mdns.js';
|
|
20
23
|
import { State, } from './types.js';
|
|
24
|
+
// log(), logError(), debug() imported from logger.ts
|
|
25
|
+
// - log(): suppressed in PTY mode (after setPtyMode(true))
|
|
26
|
+
// - logError(): always shown (critical errors requiring user action)
|
|
27
|
+
// - debug(): file-only (when --debug enabled)
|
|
28
|
+
// links2004/WebSockets, used by the ESP32-C3 e-ink firmware, closes inbound
|
|
29
|
+
// frames above 15KB with 1009 before the firmware parser can see them. Keep the
|
|
30
|
+
// initial replay comfortably below that library cap; Detail views can request a
|
|
31
|
+
// scoped session replay later via query_session_timeline.
|
|
32
|
+
export const INITIAL_TIMELINE_HISTORY_MAX_BYTES = 12 * 1024;
|
|
33
|
+
/** Attach host-local "HH:MM" to a timeline entry. Devices have no timezone
|
|
34
|
+
* (their NTP runs UTC), so rendering `ts` directly shows a 9-hour-off clock
|
|
35
|
+
* in KST. Stamped at the SOURCE (broadcast + history) so both the serial and
|
|
36
|
+
* WS transports carry it; native apps simply ignore the extra field. */
|
|
37
|
+
export function stampLocalHm(entry) {
|
|
38
|
+
if (!entry || !Number.isFinite(entry.ts) || entry.localHm)
|
|
39
|
+
return entry;
|
|
40
|
+
const d = new Date(entry.ts);
|
|
41
|
+
return {
|
|
42
|
+
...entry,
|
|
43
|
+
localHm: `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function buildCappedTimelineHistory(entries, maxBytes = INITIAL_TIMELINE_HISTORY_MAX_BYTES, extraFields = {}) {
|
|
47
|
+
const stamped = entries.map((e) => stampLocalHm(e));
|
|
48
|
+
const kept = [];
|
|
49
|
+
for (let i = stamped.length - 1; i >= 0; i--) {
|
|
50
|
+
const candidate = [stamped[i], ...kept];
|
|
51
|
+
const event = { type: 'timeline_history', ...extraFields, entries: candidate };
|
|
52
|
+
if (Buffer.byteLength(JSON.stringify(event), 'utf8') <= maxBytes) {
|
|
53
|
+
kept.unshift(stamped[i]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return kept.length > 0 ? { type: 'timeline_history', ...extraFields, entries: kept } : null;
|
|
57
|
+
}
|
|
58
|
+
function exitProcessNow(code = 0) {
|
|
59
|
+
if (code === 0) {
|
|
60
|
+
try {
|
|
61
|
+
process.kill(process.pid, 'SIGKILL');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// fall through
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
process.exit(code);
|
|
69
|
+
}
|
|
21
70
|
// ===== BridgeCore =====
|
|
22
71
|
/**
|
|
23
72
|
* Shared infrastructure extracted from index.ts and daemon-server.ts.
|
|
@@ -53,6 +102,7 @@ export class BridgeCore {
|
|
|
53
102
|
cachedAntigravityStatus = readAntigravityLocalStatus() ?? null;
|
|
54
103
|
cachedGatewayAvailable = false;
|
|
55
104
|
cachedGatewayConnected = false;
|
|
105
|
+
cachedGatewayAuthStatus = 'gateway_not_found';
|
|
56
106
|
cachedGatewayHasError = false;
|
|
57
107
|
cachedModelCatalog = null;
|
|
58
108
|
// Voice assistant state (for piggybacking on state_update)
|
|
@@ -63,6 +113,7 @@ export class BridgeCore {
|
|
|
63
113
|
intervals = [];
|
|
64
114
|
timeouts = [];
|
|
65
115
|
lastSessionsListBroadcast = 0;
|
|
116
|
+
lastSessionsListEvent = null;
|
|
66
117
|
shutdownInProgress = false;
|
|
67
118
|
shutdownCallbacks = [];
|
|
68
119
|
sseBroadcast;
|
|
@@ -77,10 +128,13 @@ export class BridgeCore {
|
|
|
77
128
|
apmeAgentType = null;
|
|
78
129
|
apmeCwd;
|
|
79
130
|
static USAGE_STALE_TTL = 10 * 60 * 1000; // 10 minutes
|
|
131
|
+
/** True when this core backs the daemon hub (see BridgeCoreOptions.isDaemon). */
|
|
132
|
+
isDaemon;
|
|
80
133
|
constructor(opts) {
|
|
81
134
|
this.port = opts.port;
|
|
82
135
|
this.sessionId = opts.sessionId ?? randomUUID();
|
|
83
136
|
this.projectName = opts.projectName;
|
|
137
|
+
this.isDaemon = opts.isDaemon ?? false;
|
|
84
138
|
// Core components
|
|
85
139
|
this.usageTracker = new UsageTracker();
|
|
86
140
|
this.stateMachine = new StateMachine(this.usageTracker);
|
|
@@ -105,13 +159,50 @@ export class BridgeCore {
|
|
|
105
159
|
}
|
|
106
160
|
// ===== Timeline wiring =====
|
|
107
161
|
wireTimeline(logStream) {
|
|
162
|
+
// Attribute every entry to this session at *storage time* so history
|
|
163
|
+
// replay (`timeline_history`) carries the same task/run/session metadata
|
|
164
|
+
// as live `timeline_event` broadcasts. taskId/runId pull from live APME
|
|
165
|
+
// state — turn rows inherit the active task header without each emitter
|
|
166
|
+
// having to thread the task id through.
|
|
167
|
+
//
|
|
168
|
+
// Idempotent: caller-set fields take precedence. This means relayed
|
|
169
|
+
// entries (e.g. timeline_event from session bridges into the daemon)
|
|
170
|
+
// keep their original sessionId/taskId; only entries emitted on this
|
|
171
|
+
// BridgeCore's own session get filled in.
|
|
172
|
+
this.bridgeTimeline.setAttributor((entry) => {
|
|
173
|
+
const apme = this.apme;
|
|
174
|
+
const sessionId = entry.sessionId ?? this.sessionId;
|
|
175
|
+
const taskId = entry.taskId ?? (apme ? apme.collector.getActiveTaskId(this.sessionId) ?? undefined : undefined);
|
|
176
|
+
const runId = entry.runId ?? (apme ? apme.collector.getRunId(this.sessionId) ?? undefined : undefined);
|
|
177
|
+
// agentType backfill — the single place every timeline row acquires its
|
|
178
|
+
// brand. Priority: caller-set → per-session collector run (authoritative
|
|
179
|
+
// for observed hook sessions, where this BridgeCore's own agentType is
|
|
180
|
+
// 'daemon') → this bridge's own session agent (managed session bridges
|
|
181
|
+
// whose collector run may not exist yet). 'daemon' is never stamped: it
|
|
182
|
+
// would mislabel every observed session's rows with the hub's identity.
|
|
183
|
+
const ownAgent = this.apmeAgentType && this.apmeAgentType !== 'daemon'
|
|
184
|
+
? this.apmeAgentType : undefined;
|
|
185
|
+
const runAgent = apme ? apme.collector.getRunAgentType(sessionId) : null;
|
|
186
|
+
const agentType = entry.agentType ?? runAgent ?? ownAgent ?? undefined;
|
|
187
|
+
return {
|
|
188
|
+
...entry,
|
|
189
|
+
projectName: entry.projectName ?? this.projectName,
|
|
190
|
+
sessionId,
|
|
191
|
+
...(agentType ? { agentType } : {}),
|
|
192
|
+
...(taskId ? { taskId } : {}),
|
|
193
|
+
...(runId ? { runId } : {}),
|
|
194
|
+
};
|
|
195
|
+
});
|
|
108
196
|
if (logStream) {
|
|
109
197
|
logStream.on('entry', (entry) => {
|
|
110
198
|
this.bridgeTimeline.addEntry(entry);
|
|
111
199
|
});
|
|
112
200
|
}
|
|
113
201
|
this.bridgeTimeline.onEntry((entry, upsert) => {
|
|
114
|
-
|
|
202
|
+
// Entry is already attributed by the storage-time attributor above;
|
|
203
|
+
// forward as-is (plus the device-local HH:MM stamp). Doing the
|
|
204
|
+
// attribution here too would be redundant and risks divergence.
|
|
205
|
+
const evt = { type: 'timeline_event', entry: stampLocalHm(entry), ...(upsert ? { upsert: true } : {}) };
|
|
115
206
|
this.broadcast(evt);
|
|
116
207
|
});
|
|
117
208
|
}
|
|
@@ -119,16 +210,16 @@ export class BridgeCore {
|
|
|
119
210
|
_previousDisplayOn = true;
|
|
120
211
|
_wakeHandler = null;
|
|
121
212
|
wireDisplayMonitor() {
|
|
122
|
-
this.displayMonitor.start();
|
|
123
213
|
this.displayMonitor.on('display_state_changed', (displayOn) => {
|
|
124
214
|
// Detect wake transition (asleep → awake)
|
|
125
215
|
if (displayOn && !this._previousDisplayOn) {
|
|
126
216
|
this._wakeHandler?.();
|
|
127
217
|
}
|
|
128
218
|
this._previousDisplayOn = displayOn;
|
|
129
|
-
const evt =
|
|
219
|
+
const evt = buildDisplayStateEvent(displayOn);
|
|
130
220
|
this.broadcast(evt);
|
|
131
221
|
});
|
|
222
|
+
this.displayMonitor.start();
|
|
132
223
|
// Time-discontinuity safety net (python3 process may die during deep sleep)
|
|
133
224
|
let lastTick = Date.now();
|
|
134
225
|
setInterval(() => {
|
|
@@ -195,6 +286,7 @@ export class BridgeCore {
|
|
|
195
286
|
antigravityStatus: this.cachedAntigravityStatus ?? undefined,
|
|
196
287
|
gatewayAvailable: this.cachedGatewayAvailable,
|
|
197
288
|
gatewayConnected: this.cachedGatewayConnected,
|
|
289
|
+
gatewayAuthStatus: this.cachedGatewayAuthStatus,
|
|
198
290
|
gatewayHasError: this.cachedGatewayHasError,
|
|
199
291
|
moduleHealth: this.moduleHealthProvider?.(),
|
|
200
292
|
voiceAssistantState: this.cachedVoiceAssistantState !== 'disabled' ? this.cachedVoiceAssistantState : undefined,
|
|
@@ -213,7 +305,7 @@ export class BridgeCore {
|
|
|
213
305
|
/** Build and return a usage event */
|
|
214
306
|
buildUsage() {
|
|
215
307
|
const snapshot = this.stateMachine.getSnapshot();
|
|
216
|
-
return buildUsageEvent(snapshot, this.cachedApiUsage, this.oauthConnected, this.cachedOllamaStatus, this.cachedMlxModels, this.apiUsageStale, readCodexAuthStatus(), snapshot.billingType, this.cachedModelCatalog, this.cachedAntigravityStatus, this.apiUsagePreAdjusted);
|
|
308
|
+
return buildUsageEvent(snapshot, this.cachedApiUsage, this.oauthConnected, this.cachedOllamaStatus, this.cachedMlxModels, this.apiUsageStale, readCodexAuthStatus(), snapshot.billingType, this.cachedModelCatalog, this.cachedAntigravityStatus, this.apiUsagePreAdjusted, this.isDaemon, readCodexRateLimits());
|
|
217
309
|
}
|
|
218
310
|
/** Broadcast current usage to all clients */
|
|
219
311
|
broadcastUsage() {
|
|
@@ -331,6 +423,8 @@ export class BridgeCore {
|
|
|
331
423
|
}
|
|
332
424
|
else if (!status.available && wasAvailable) {
|
|
333
425
|
this.cachedGatewayConnected = false;
|
|
426
|
+
this.cachedGatewayAuthStatus = 'gateway_not_found';
|
|
427
|
+
this.cachedGatewayHasError = false;
|
|
334
428
|
onDisappeared?.();
|
|
335
429
|
}
|
|
336
430
|
if (status.available !== wasAvailable) {
|
|
@@ -424,7 +518,25 @@ export class BridgeCore {
|
|
|
424
518
|
let sessions = await buildEnrichedSessionsList(this.sessionId, snapshot.state, snapshot.modelName ?? undefined, snapshot.effortLevel ?? undefined);
|
|
425
519
|
if (this.sessionsEnricher)
|
|
426
520
|
sessions = this.sessionsEnricher(sessions);
|
|
427
|
-
|
|
521
|
+
// Attach the shared per-session activity one-liner to the FINAL list (covers
|
|
522
|
+
// managed + observed sessions). Heuristic is immediate; a Foundation Models
|
|
523
|
+
// summary, when available, lands on a later periodic broadcast via the cache.
|
|
524
|
+
for (const s of sessions) {
|
|
525
|
+
const a = activityFor(s);
|
|
526
|
+
if (a)
|
|
527
|
+
s.activity = a;
|
|
528
|
+
}
|
|
529
|
+
const event = { type: 'sessions_list', sessions };
|
|
530
|
+
// Cache for the serial heartbeat re-sync (like display_state): a board that
|
|
531
|
+
// reconnects across a daemon handoff during a quiet window otherwise sits
|
|
532
|
+
// on an empty roster until the next unrelated session change broadcasts.
|
|
533
|
+
this.lastSessionsListEvent = event;
|
|
534
|
+
this.wsServer.broadcast(event);
|
|
535
|
+
}
|
|
536
|
+
/** The most recent sessions_list event, for the ESP32 serial heartbeat
|
|
537
|
+
* re-sync. Null until the first broadcast. */
|
|
538
|
+
getLastSessionsListEvent() {
|
|
539
|
+
return this.lastSessionsListEvent;
|
|
428
540
|
}
|
|
429
541
|
/** Debounced sessions list broadcast (for state_changed handler) */
|
|
430
542
|
maybeBroadcastSessionsList() {
|
|
@@ -457,15 +569,25 @@ export class BridgeCore {
|
|
|
457
569
|
sessionId: this.sessionId,
|
|
458
570
|
});
|
|
459
571
|
// Display state
|
|
460
|
-
this.wsServer.sendTo(ws,
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
//
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
572
|
+
this.wsServer.sendTo(ws, buildDisplayStateEvent(this.displayMonitor.isDisplayOn()));
|
|
573
|
+
// Timeline history — entries were already attributed at storage time by
|
|
574
|
+
// the attributor installed in `wireTimeline`, so they ship with taskId /
|
|
575
|
+
// runId / sessionId / projectName intact. The `??` fallbacks below
|
|
576
|
+
// protect entries that landed before `wireTimeline` ran (e.g. very early
|
|
577
|
+
// boot logStream events) so we never broadcast bare entries.
|
|
578
|
+
const history = this.bridgeTimeline.getHistory().map((entry) => ({
|
|
579
|
+
...entry,
|
|
580
|
+
projectName: entry.projectName ?? this.projectName,
|
|
581
|
+
sessionId: entry.sessionId ?? this.sessionId,
|
|
582
|
+
}));
|
|
583
|
+
// Always send a timeline_history on connect — an EMPTY one when the store
|
|
584
|
+
// is empty. Clients replace (not merge) their timeline on this event, so a
|
|
585
|
+
// freshly-restarted daemon must send `entries: []` to wipe the client's
|
|
586
|
+
// stale rows; suppressing it (the old `if (historyEvent)`) left old logs
|
|
587
|
+
// frozen on Android/ESP32 across a daemon restart.
|
|
588
|
+
const historyEvent = buildCappedTimelineHistory(history)
|
|
589
|
+
?? { type: 'timeline_history', entries: [] };
|
|
590
|
+
this.wsServer.sendTo(ws, historyEvent);
|
|
469
591
|
// Sessions list
|
|
470
592
|
buildEnrichedSessionsList(this.sessionId, snapshot.state, snapshot.modelName ?? undefined, snapshot.effortLevel ?? undefined).then((sessions) => {
|
|
471
593
|
const enriched = this.sessionsEnricher ? this.sessionsEnricher(sessions) : sessions;
|
|
@@ -551,11 +673,12 @@ export class BridgeCore {
|
|
|
551
673
|
const msg = err?.message ?? '';
|
|
552
674
|
const code = err?.code ?? '';
|
|
553
675
|
// mDNS errors are non-critical — network interface changes (sleep/wake,
|
|
554
|
-
// WiFi reconnect, VPN toggle)
|
|
555
|
-
//
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
676
|
+
// WiFi reconnect, VPN toggle) and unroutable virtual interfaces (Windows
|
|
677
|
+
// WSL/Hyper-V — EHOSTUNREACH to 224.0.0.251:5353) cause transient
|
|
678
|
+
// multicast failures. Null out the mDNS instance so the recovery timer
|
|
679
|
+
// can re-publish.
|
|
680
|
+
if (isNonFatalMdnsError(msg, code)) {
|
|
681
|
+
debug('mDNS', `error (ignored): ${code || msg}`);
|
|
559
682
|
invalidateMdnsInstance();
|
|
560
683
|
return;
|
|
561
684
|
}
|
|
@@ -602,10 +725,16 @@ export class BridgeCore {
|
|
|
602
725
|
}
|
|
603
726
|
/** Graceful shutdown */
|
|
604
727
|
async shutdown() {
|
|
605
|
-
if (this.shutdownInProgress)
|
|
728
|
+
if (this.shutdownInProgress) {
|
|
729
|
+
exitProcessNow(0);
|
|
606
730
|
return;
|
|
731
|
+
}
|
|
607
732
|
this.shutdownInProgress = true;
|
|
608
733
|
log('Shutting down...');
|
|
734
|
+
const hardExitTimer = setTimeout(() => {
|
|
735
|
+
logError('Shutdown timeout — forcing exit.');
|
|
736
|
+
exitProcessNow(0);
|
|
737
|
+
}, 7000);
|
|
609
738
|
// Clear all timers
|
|
610
739
|
for (const iv of this.intervals)
|
|
611
740
|
clearInterval(iv);
|
|
@@ -615,13 +744,17 @@ export class BridgeCore {
|
|
|
615
744
|
this.deregisterSession();
|
|
616
745
|
// Stop display monitor
|
|
617
746
|
this.displayMonitor.stop();
|
|
618
|
-
// Run shutdown callbacks (best-effort,
|
|
747
|
+
// Run shutdown callbacks (best-effort, 5s total budget). Budget covers the
|
|
748
|
+
// BLE stateful-panel farewell: device modules (iDotMatrix/Timebox) await
|
|
749
|
+
// their Python sync child painting a final OFFLINE/blank frame over BLE
|
|
750
|
+
// (~1-3s) so the panel doesn't freeze on its last dashboard frame.
|
|
619
751
|
const callbacksDone = Promise.all(this.shutdownCallbacks.map(cb => Promise.resolve().then(cb).catch(() => { })));
|
|
620
|
-
await Promise.race([callbacksDone, new Promise(r => { const t = setTimeout(r,
|
|
752
|
+
await Promise.race([callbacksDone, new Promise(r => { const t = setTimeout(r, 5000); t.unref(); })]);
|
|
621
753
|
// Close WS
|
|
622
754
|
this.wsServer.close();
|
|
623
755
|
// Exit immediately — all cleanup is done or timed out
|
|
624
|
-
|
|
756
|
+
clearTimeout(hardExitTimer);
|
|
757
|
+
exitProcessNow(0);
|
|
625
758
|
}
|
|
626
759
|
}
|
|
627
760
|
//# sourceMappingURL=bridge-core.js.map
|