@agentdeck/bridge 1.0.7 → 1.0.8

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.
Files changed (66) hide show
  1. package/dist/check-deps.d.ts.map +1 -1
  2. package/dist/check-deps.js +4 -12
  3. package/dist/check-deps.js.map +1 -1
  4. package/dist/daemon-server.d.ts.map +1 -1
  5. package/dist/daemon-server.js +2 -3
  6. package/dist/daemon-server.js.map +1 -1
  7. package/dist/daemon.js +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +3 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/voice-assistant.d.ts +2 -2
  12. package/dist/voice-assistant.d.ts.map +1 -1
  13. package/dist/voice-assistant.js +1 -1
  14. package/dist/voice.d.ts +5 -5
  15. package/dist/voice.d.ts.map +1 -1
  16. package/dist/voice.js +8 -10
  17. package/dist/voice.js.map +1 -1
  18. package/package.json +3 -3
  19. package/dist/d200h/hid-protocol.d.ts +0 -62
  20. package/dist/d200h/hid-protocol.d.ts.map +0 -1
  21. package/dist/d200h/hid-protocol.js +0 -124
  22. package/dist/d200h/hid-protocol.js.map +0 -1
  23. package/dist/d200h/image-renderer.d.ts +0 -24
  24. package/dist/d200h/image-renderer.d.ts.map +0 -1
  25. package/dist/d200h/image-renderer.js +0 -321
  26. package/dist/d200h/image-renderer.js.map +0 -1
  27. package/dist/hook-migration.d.ts +0 -2
  28. package/dist/hook-migration.d.ts.map +0 -1
  29. package/dist/hook-migration.js +0 -116
  30. package/dist/hook-migration.js.map +0 -1
  31. package/dist/modules/d200h-module.d.ts +0 -65
  32. package/dist/modules/d200h-module.d.ts.map +0 -1
  33. package/dist/modules/d200h-module.js +0 -495
  34. package/dist/modules/d200h-module.js.map +0 -1
  35. package/dist/modules/d200h-renderer.d.ts +0 -7
  36. package/dist/modules/d200h-renderer.d.ts.map +0 -1
  37. package/dist/modules/d200h-renderer.js +0 -321
  38. package/dist/modules/d200h-renderer.js.map +0 -1
  39. package/dist/modules/trmnl-module.d.ts +0 -38
  40. package/dist/modules/trmnl-module.d.ts.map +0 -1
  41. package/dist/modules/trmnl-module.js +0 -111
  42. package/dist/modules/trmnl-module.js.map +0 -1
  43. package/dist/trmnl/byos-server.d.ts +0 -39
  44. package/dist/trmnl/byos-server.d.ts.map +0 -1
  45. package/dist/trmnl/byos-server.js +0 -301
  46. package/dist/trmnl/byos-server.js.map +0 -1
  47. package/dist/trmnl/frame-cache.d.ts +0 -37
  48. package/dist/trmnl/frame-cache.d.ts.map +0 -1
  49. package/dist/trmnl/frame-cache.js +0 -157
  50. package/dist/trmnl/frame-cache.js.map +0 -1
  51. package/dist/trmnl/image-renderer.d.ts +0 -27
  52. package/dist/trmnl/image-renderer.d.ts.map +0 -1
  53. package/dist/trmnl/image-renderer.js +0 -235
  54. package/dist/trmnl/image-renderer.js.map +0 -1
  55. package/dist/trmnl/trmnl-settings.d.ts +0 -96
  56. package/dist/trmnl/trmnl-settings.d.ts.map +0 -1
  57. package/dist/trmnl/trmnl-settings.js +0 -177
  58. package/dist/trmnl/trmnl-settings.js.map +0 -1
  59. package/dist/trmnl/trmnl-telemetry.d.ts +0 -47
  60. package/dist/trmnl/trmnl-telemetry.d.ts.map +0 -1
  61. package/dist/trmnl/trmnl-telemetry.js +0 -56
  62. package/dist/trmnl/trmnl-telemetry.js.map +0 -1
  63. package/dist/whisper-server-manager.d.ts +0 -19
  64. package/dist/whisper-server-manager.d.ts.map +0 -1
  65. package/dist/whisper-server-manager.js +0 -171
  66. package/dist/whisper-server-manager.js.map +0 -1
@@ -1,301 +0,0 @@
1
- import { TRMNL_WIDTH, TRMNL_HEIGHT } from '@agentdeck/shared';
2
- import { ensureDevice, findDeviceByMac, loadTrmnlConfig, normalizeMac, effectiveRefreshRate, effectiveImageTimeout, TRMNL_WEAK_RSSI_DBM, } from './trmnl-settings.js';
3
- import { getTrmnlFrame, getTrmnlFrameByKey, forceRenderTrmnlFrame, getTrmnlActivity } from './frame-cache.js';
4
- import { recordTelemetry } from './trmnl-telemetry.js';
5
- import { debug, logTagged } from '../logger.js';
6
- const TAG = 'trmnl-byos';
7
- /** MACs currently flagged weak-signal — tracked so we log a transition, not every poll. */
8
- const weakRssiMacs = new Set();
9
- /**
10
- * Log once when a panel's RSSI crosses into/out of the weak-link threshold. A
11
- * weak/lossy WiFi link is the dominant real-world cause of the firmware's
12
- * "WiFi connected / not responding" (WIFI_FAILED) screen, so this gives an
13
- * operator a correlatable record — always visible (not gated behind --debug),
14
- * but rate-limited to state transitions instead of spamming every poll.
15
- */
16
- function noteRssiHealth(mac, rssi) {
17
- const key = normalizeMac(mac);
18
- if (!key)
19
- return;
20
- const isWeak = rssi != null && Number.isFinite(rssi) && rssi <= TRMNL_WEAK_RSSI_DBM;
21
- const wasWeak = weakRssiMacs.has(key);
22
- if (isWeak && !wasWeak) {
23
- weakRssiMacs.add(key);
24
- logTagged(TAG, `weak WiFi signal on ${key}: rssi=${rssi}dBm (<= ${TRMNL_WEAK_RSSI_DBM}dBm threshold) — ` +
25
- `image_url_timeout widened; panel may show "not responding" if this persists`);
26
- }
27
- else if (!isWeak && wasWeak) {
28
- weakRssiMacs.delete(key);
29
- logTagged(TAG, `WiFi signal recovered on ${key}: rssi=${rssi ?? 'n/a'}dBm`);
30
- }
31
- }
32
- /** Test helper — clear weak-RSSI transition tracking. */
33
- export function _resetWeakRssiTracking() {
34
- weakRssiMacs.clear();
35
- }
36
- /**
37
- * Record a poll and log when the panel comes back after missing its window.
38
- * A battery e-ink panel polls on the cadence we hand it; a gap of more than
39
- * 2× the slow cadence (+ grace) means at least one poll round-trip failed at
40
- * the network layer — exactly the moment the firmware flashes its "TRMNL not
41
- * responding" screen. Logging the gap on recovery (always visible, one line
42
- * per incident) gives the operator a correlatable record of every dead window.
43
- */
44
- function notePollGap(mac, h, cfg) {
45
- const now = Date.now();
46
- const prevLastSeen = recordTelemetry(mac, h, now);
47
- if (prevLastSeen == null)
48
- return;
49
- const gapSec = Math.round((now - prevLastSeen) / 1000);
50
- const expected = Math.max(cfg.refreshRate, cfg.refreshActive);
51
- if (gapSec > expected * 2 + 30) {
52
- logTagged(TAG, `panel ${normalizeMac(mac)} back after ${gapSec}s silence (cadence ${expected}s) — ` +
53
- `it missed ≥1 poll window; likely showed "not responding" in between ` +
54
- `(rssi=${h.rssi ?? 'n/a'}dBm battery=${h.batteryVoltage ?? 'n/a'}V)`);
55
- }
56
- }
57
- // Sane device-reported resolution bounds; anything outside falls back to OG size.
58
- const MIN_DIM = 120;
59
- const MAX_DIM = 4000;
60
- function header(req, name) {
61
- const v = req.headers[name.toLowerCase()];
62
- return Array.isArray(v) ? (v[0] ?? '') : (v ?? '');
63
- }
64
- function toNum(v) {
65
- if (!v)
66
- return null;
67
- const n = Number(v.trim());
68
- return Number.isFinite(n) ? n : null;
69
- }
70
- /** Parse the BYOS telemetry headers a panel sends on /api/setup + /api/display. */
71
- function parseDeviceHeaders(req) {
72
- const sane = (n) => (n != null && n >= MIN_DIM && n <= MAX_DIM ? Math.round(n) : null);
73
- return {
74
- mac: header(req, 'ID'),
75
- accessToken: header(req, 'Access-Token'),
76
- fwVersion: header(req, 'FW-Version'),
77
- batteryVoltage: toNum(header(req, 'Battery-Voltage')),
78
- rssi: toNum(header(req, 'RSSI')),
79
- refreshRate: toNum(header(req, 'Refresh-Rate')),
80
- width: sane(toNum(header(req, 'Width'))),
81
- height: sane(toNum(header(req, 'Height'))),
82
- userAgent: header(req, 'User-Agent'),
83
- };
84
- }
85
- /** Render size for a device: its reported resolution, or the OG default. */
86
- function renderSize(h) {
87
- return { width: h.width ?? TRMNL_WIDTH, height: h.height ?? TRMNL_HEIGHT };
88
- }
89
- /** Build the image URL, qualified by resolution so the image route serves the
90
- * right-sized frame. The hash is the cache-buster the firmware re-downloads on. */
91
- function imageUrl(req, size, hash) {
92
- return `${imageBase(req)}/trmnl/image/${size.width}x${size.height}-${hash}.png`;
93
- }
94
- function imageBase(req) {
95
- // The device reached us at exactly this host:port — reuse it so the image_url
96
- // is always resolvable from the device's perspective (no IP guessing).
97
- return `http://${req.headers.host ?? 'localhost'}`;
98
- }
99
- function sendJson(res, status, body) {
100
- const payload = JSON.stringify(body);
101
- res.writeHead(status, {
102
- 'Content-Type': 'application/json',
103
- 'Cache-Control': 'no-store',
104
- 'Access-Control-Allow-Origin': '*',
105
- });
106
- res.end(payload);
107
- }
108
- /** GET /api/setup — enroll the device (by MAC) and hand back an api_key. */
109
- export function handleTrmnlSetup(req, res) {
110
- const h = parseDeviceHeaders(req);
111
- const mac = h.mac;
112
- if (!mac) {
113
- sendJson(res, 400, { status: 400, message: 'Missing ID (MAC) header' });
114
- return;
115
- }
116
- const { device, created } = ensureDevice(mac);
117
- if (!device) {
118
- // autoRegister is off and this MAC isn't enrolled.
119
- debug(TAG, `setup rejected for ${normalizeMac(mac)} (autoRegister off, not enrolled)`);
120
- sendJson(res, 404, { status: 404, message: 'Device not enrolled. Add it to settings.trmnl.devices.' });
121
- return;
122
- }
123
- notePollGap(mac, h, loadTrmnlConfig());
124
- noteRssiHealth(mac, h.rssi);
125
- const size = renderSize(h);
126
- if (created) {
127
- debug(TAG, `enrolled TRMNL ${device.mac} as ${device.friendlyId} (${size.width}x${size.height})`);
128
- forceRenderTrmnlFrame(size.width, size.height); // first served image reflects live state
129
- }
130
- const frame = getTrmnlFrame(size.width, size.height);
131
- sendJson(res, 200, {
132
- status: 200,
133
- api_key: device.apiKey,
134
- friendly_id: device.friendlyId,
135
- image_url: imageUrl(req, size, frame.contentHash),
136
- filename: frame.contentHash,
137
- message: 'Welcome to AgentDeck',
138
- });
139
- }
140
- /**
141
- * GET /api/display — return the next image + polling cadence.
142
- *
143
- * NOTE: we always reply with HTTP 200; the BYOS `status` field carries the real
144
- * signal (0 = show image, 202 = call /api/setup). Stock firmware treats a non-200
145
- * HTTP status as an error and goes back to sleep without rendering, so HTTP 200 is
146
- * required even in the "needs setup" case.
147
- */
148
- export function handleTrmnlDisplay(req, res) {
149
- const h = parseDeviceHeaders(req);
150
- const mac = h.mac;
151
- const cfg = loadTrmnlConfig();
152
- const size = renderSize(h);
153
- if (mac) {
154
- notePollGap(mac, h, cfg);
155
- noteRssiHealth(mac, h.rssi);
156
- }
157
- let device = mac ? findDeviceByMac(mac) : undefined;
158
- if (!device) {
159
- // Real devices often carry an api_key from a prior (cloud) setup and never
160
- // call /api/setup again — they poll /api/display straight away. If we answer
161
- // "not registered" (status 202) they just sleep/retry forever and never show
162
- // a screen. So auto-enroll on first poll (when autoRegister is on) and serve
163
- // a real dashboard (status 0). Only fall back to 202 when autoRegister is off.
164
- if (mac && cfg.autoRegister) {
165
- const r = ensureDevice(mac);
166
- device = r.device;
167
- if (r.created) {
168
- debug(TAG, `auto-enrolled ${normalizeMac(mac)} on display poll (${size.width}x${size.height})`);
169
- forceRenderTrmnlFrame(size.width, size.height);
170
- }
171
- }
172
- if (!device) {
173
- // autoRegister off and unenrolled: serve a real, correctly-sized frame (the
174
- // idle hero / dashboard) rather than a bogus `setup.png` the image route
175
- // can't match. status 202 still tells the firmware "not fully set up".
176
- const setupFrame = getTrmnlFrame(size.width, size.height);
177
- debug(TAG, `display from unenrolled ${normalizeMac(mac)} (autoRegister off) — requesting setup`);
178
- sendJson(res, 200, {
179
- status: 202,
180
- image_url: imageUrl(req, size, setupFrame.contentHash),
181
- filename: setupFrame.contentHash,
182
- refresh_rate: cfg.refreshRate,
183
- image_url_timeout: effectiveImageTimeout(cfg, { rssi: h.rssi }),
184
- special_function: 'sleep',
185
- reset_firmware: false,
186
- update_firmware: false,
187
- firmware_url: null,
188
- });
189
- return;
190
- }
191
- }
192
- // Soft auth only: the MAC (ID header) identifies the device. The api_key it
193
- // presents may have been issued by a previous/cloud server, so we don't
194
- // hard-reject on mismatch — this is same-LAN hardware.
195
- const frame = getTrmnlFrame(size.width, size.height);
196
- // Adaptive cadence: poll fast while an agent is AWAITING/WORKING, slow when idle.
197
- const activity = getTrmnlActivity();
198
- const refreshRate = effectiveRefreshRate(cfg, activity);
199
- // Widen the image-download window on a weak link so a lossy GET still finishes
200
- // before the firmware shows "not responding" (WIFI_FAILED).
201
- const imageTimeout = effectiveImageTimeout(cfg, { rssi: h.rssi });
202
- debug(TAG, `display ${normalizeMac(mac)} ${size.width}x${size.height} hash=${frame.contentHash} ` +
203
- `refresh=${refreshRate}s imageTimeout=${imageTimeout}s rssi=${h.rssi ?? 'n/a'} ` +
204
- `awaiting=${activity.awaiting} working=${activity.working}`);
205
- sendJson(res, 200, {
206
- status: 0,
207
- image_url: imageUrl(req, size, frame.contentHash),
208
- filename: frame.contentHash,
209
- // Number (matches the reference BYOS + firmware's uint parse), not a string.
210
- refresh_rate: refreshRate,
211
- // Generous, link-aware image-download window so a flaky WiFi link doesn't trip
212
- // the firmware's "not responding" screen.
213
- image_url_timeout: imageTimeout,
214
- special_function: 'sleep',
215
- reset_firmware: false,
216
- update_firmware: false,
217
- firmware_url: null,
218
- });
219
- }
220
- /** GET /trmnl/image/<W>x<H>-<hash>.png — serve the 1-bit PNG for that resolution. */
221
- export function handleTrmnlImage(req, res) {
222
- // The URL carries the resolution (and a cache-busting hash) so we serve the
223
- // right-sized frame. We ignore the hash itself — the device always wants the
224
- // freshest screen for its resolution. Unknown/legacy URLs fall back to the
225
- // default-size frame.
226
- const pathname = (req.url || '').split('?')[0];
227
- const m = /\/trmnl\/image\/(\d+)x(\d+)-[0-9a-f]+\.png$/.exec(pathname);
228
- let frame;
229
- if (m) {
230
- const key = `${m[1]}x${m[2]}`;
231
- frame = getTrmnlFrameByKey(key) ?? getTrmnlFrame(Number(m[1]), Number(m[2]));
232
- }
233
- if (!frame)
234
- frame = getTrmnlFrame();
235
- res.writeHead(200, {
236
- 'Content-Type': frame.contentType,
237
- 'Content-Length': frame.buffer.length,
238
- 'Cache-Control': 'no-store',
239
- 'Access-Control-Allow-Origin': '*',
240
- });
241
- res.end(frame.buffer);
242
- }
243
- // Device-log throttle: the firmware only POSTs /api/log when something went
244
- // wrong on its side (failed poll, image timeout, low battery), so these lines
245
- // ARE the root-cause record for "not responding" incidents. Always log them,
246
- // but cap the rate per panel so a wedged device can't flood stderr.
247
- const DEVICE_LOG_INTERVAL_MS = 10_000;
248
- const lastDeviceLogAt = new Map();
249
- /** Compress the firmware's log JSON into one greppable line (best effort). */
250
- function summarizeDeviceLog(body) {
251
- try {
252
- const parsed = JSON.parse(body);
253
- const arr = parsed?.log?.logs_array ?? parsed?.logs?.logs_array ?? parsed?.logs_array;
254
- if (Array.isArray(arr) && arr.length > 0) {
255
- return arr
256
- .map((e) => `[${e?.log_codeline ?? e?.log_sourcefile ?? '?'}] ${e?.log_message ?? JSON.stringify(e)}`)
257
- .join(' | ')
258
- .slice(0, 600);
259
- }
260
- }
261
- catch {
262
- /* not JSON — fall through to raw */
263
- }
264
- return body.replace(/\s+/g, ' ').slice(0, 600);
265
- }
266
- /** POST /api/log — the panel's own error report; always logged (rate-limited). */
267
- export function handleTrmnlLog(req, res) {
268
- let body = '';
269
- req.on('data', (c) => {
270
- body += c;
271
- if (body.length > 64_000)
272
- req.destroy();
273
- });
274
- req.on('end', () => {
275
- const mac = normalizeMac(header(req, 'ID'));
276
- const now = Date.now();
277
- if (now - (lastDeviceLogAt.get(mac) ?? 0) >= DEVICE_LOG_INTERVAL_MS) {
278
- lastDeviceLogAt.set(mac, now);
279
- logTagged(TAG, `device log from ${mac}: ${summarizeDeviceLog(body)}`);
280
- }
281
- else {
282
- debug(TAG, `device log from ${mac} (throttled): ${body.slice(0, 500)}`);
283
- }
284
- res.writeHead(204, { 'Access-Control-Allow-Origin': '*' });
285
- res.end();
286
- });
287
- req.on('error', () => {
288
- try {
289
- res.writeHead(204);
290
- res.end();
291
- }
292
- catch {
293
- /* ignore */
294
- }
295
- });
296
- }
297
- /** True for any path this module owns, so the daemon router can delegate. */
298
- export function isTrmnlImagePath(pathname) {
299
- return pathname.startsWith('/trmnl/image/');
300
- }
301
- //# sourceMappingURL=byos-server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"byos-server.js","sourceRoot":"","sources":["../../src/trmnl/byos-server.ts"],"names":[],"mappings":"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"}
@@ -1,37 +0,0 @@
1
- /**
2
- * TRMNL frame cache — the rendered 1-bit PNG dashboards the BYOS HTTP routes
3
- * serve. The device module keeps them fresh by re-rendering on state change; the
4
- * daemon's /api/display + /trmnl/image routes read them. Unlike push devices
5
- * (Pixoo/D200H), TRMNL pulls on its own schedule, so we only hold the latest
6
- * frame per resolution and let each device fetch it when it polls.
7
- *
8
- * Frames are keyed by `"<W>x<H>"` rather than a single global frame: different
9
- * BYOS panels report different resolutions, and each must get a correctly-sized
10
- * image. The map is bounded (typically one resolution is in use) to cap memory.
11
- */
12
- import { type TrmnlFrame } from './image-renderer.js';
13
- /** Visual-state fingerprint (excludes the wall clock so it doesn't churn). */
14
- export declare function trmnlStateHash(evt: any): string;
15
- /** AWAITING/WORKING counts from the last known state — drives adaptive cadence. */
16
- export declare function getTrmnlActivity(): {
17
- awaiting: number;
18
- working: number;
19
- };
20
- /** Store the latest broadcast state for lazy rendering, without rendering now. */
21
- export declare function setTrmnlState(evt: any): void;
22
- /**
23
- * Re-render every cached resolution whose visual state changed. Returns true if
24
- * any new frame was produced. Called by the device module on each broadcast. If
25
- * no device has polled yet, primes the default resolution so a first poll is
26
- * instant.
27
- */
28
- export declare function refreshTrmnlFrame(evt: any): boolean;
29
- /** Force a render from the last known state for a resolution (e.g. after setup). */
30
- export declare function forceRenderTrmnlFrame(width?: number, height?: number): TrmnlFrame;
31
- /** Frame for a resolution, lazily rendered from the last known state if absent. */
32
- export declare function getTrmnlFrame(width?: number, height?: number): TrmnlFrame;
33
- /** Frame for an exact `"<W>x<H>"` key, or undefined if not cached. */
34
- export declare function getTrmnlFrameByKey(key: string): TrmnlFrame | undefined;
35
- /** Active resolution keys currently held in the cache. */
36
- export declare function getTrmnlFrameKeys(): string[];
37
- //# sourceMappingURL=frame-cache.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"frame-cache.d.ts","sourceRoot":"","sources":["../../src/trmnl/frame-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAsCxE,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CA+B/C;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAUxE;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAE5C;AAuBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAcnD;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAEjF;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAGzE;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEtE;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C"}
@@ -1,157 +0,0 @@
1
- /**
2
- * TRMNL frame cache — the rendered 1-bit PNG dashboards the BYOS HTTP routes
3
- * serve. The device module keeps them fresh by re-rendering on state change; the
4
- * daemon's /api/display + /trmnl/image routes read them. Unlike push devices
5
- * (Pixoo/D200H), TRMNL pulls on its own schedule, so we only hold the latest
6
- * frame per resolution and let each device fetch it when it polls.
7
- *
8
- * Frames are keyed by `"<W>x<H>"` rather than a single global frame: different
9
- * BYOS panels report different resolutions, and each must get a correctly-sized
10
- * image. The map is bounded (typically one resolution is in use) to cap memory.
11
- */
12
- import { renderTrmnlFrame } from './image-renderer.js';
13
- import { TRMNL_WIDTH, TRMNL_HEIGHT } from '@agentdeck/shared';
14
- const DEFAULT_KEY = `${TRMNL_WIDTH}x${TRMNL_HEIGHT}`;
15
- /** Bound on distinct resolutions held at once (insertion-order LRU eviction). */
16
- const MAX_FRAMES = 8;
17
- /** Resolution-keyed cache. Insertion order doubles as LRU recency. */
18
- const frames = new Map();
19
- const lastHashByKey = new Map();
20
- let lastStateEvt = {
21
- state: 'IDLE',
22
- projectName: '',
23
- modelName: '',
24
- mode: 'default',
25
- agentType: 'daemon',
26
- fiveHourPercent: 0,
27
- sevenDayPercent: 0,
28
- totalTokens: 0,
29
- totalCost: 0,
30
- options: [],
31
- currentTool: '',
32
- allSessions: [],
33
- };
34
- function sizeKey(width, height) {
35
- const w = width && width > 0 ? Math.round(width) : TRMNL_WIDTH;
36
- const h = height && height > 0 ? Math.round(height) : TRMNL_HEIGHT;
37
- return `${w}x${h}`;
38
- }
39
- function parseKey(key) {
40
- const m = /^(\d+)x(\d+)$/.exec(key);
41
- if (!m)
42
- return { width: TRMNL_WIDTH, height: TRMNL_HEIGHT };
43
- return { width: Number(m[1]), height: Number(m[2]) };
44
- }
45
- /** Visual-state fingerprint (excludes the wall clock so it doesn't churn). */
46
- export function trmnlStateHash(evt) {
47
- const sessions = Array.isArray(evt?.allSessions) ? evt.allSessions : [];
48
- const sessKey = sessions
49
- .map((s) => `${s?.id}:${s?.agentType}:${s?.state}:${s?.projectName}:${s?.modelName}:${s?.goal ?? ''}`)
50
- .join('|');
51
- // No wall-clock / freshness component: a real TRMNL caches by `filename` and
52
- // skips the (battery + flaky-WiFi) re-download when it's unchanged. So the hash
53
- // must change ONLY on real visual change — never churn it for a ticking clock.
54
- return [
55
- evt?.state,
56
- evt?.projectName,
57
- evt?.modelName,
58
- evt?.usageKnown ? Math.round(evt?.fiveHourPercent ?? 0) : 'na',
59
- evt?.usageKnown ? Math.round(evt?.sevenDayPercent ?? 0) : 'na',
60
- // Reset windows roll over rarely — include them so a rollover re-renders the
61
- // countdown, but they don't churn minute-to-minute.
62
- evt?.fiveHourResetsAt ?? '',
63
- evt?.sevenDayResetsAt ?? '',
64
- // Codex (ChatGPT) rolling windows render a second footer row — fold them in
65
- // so a Codex-only usage change still re-renders the panel (otherwise the
66
- // Claude-only hash above would mask it and the TRMNL would skip the redraw).
67
- evt?.codexRateLimits?.primary?.usedPercent != null
68
- ? Math.round(evt.codexRateLimits.primary.usedPercent)
69
- : 'na',
70
- evt?.codexRateLimits?.secondary?.usedPercent != null
71
- ? Math.round(evt.codexRateLimits.secondary.usedPercent)
72
- : 'na',
73
- evt?.codexRateLimits?.primary?.resetsAt ?? '',
74
- evt?.codexRateLimits?.secondary?.resetsAt ?? '',
75
- sessKey,
76
- ].join('~');
77
- }
78
- /** AWAITING/WORKING counts from the last known state — drives adaptive cadence. */
79
- export function getTrmnlActivity() {
80
- const sessions = Array.isArray(lastStateEvt?.allSessions) ? lastStateEvt.allSessions : [];
81
- let awaiting = 0;
82
- let working = 0;
83
- for (const s of sessions) {
84
- const st = String(s?.state ?? '').toLowerCase();
85
- if (st.startsWith('awaiting'))
86
- awaiting++;
87
- else if (st === 'processing')
88
- working++;
89
- }
90
- return { awaiting, working };
91
- }
92
- /** Store the latest broadcast state for lazy rendering, without rendering now. */
93
- export function setTrmnlState(evt) {
94
- lastStateEvt = evt;
95
- }
96
- /** Render (and cache) the current state for one resolution key, with LRU eviction. */
97
- function renderForKey(key) {
98
- const { width, height } = parseKey(key);
99
- let frame = renderTrmnlFrame(lastStateEvt, undefined, { width, height });
100
- // A degraded frame is a blank fallback (render threw). Keep serving the last
101
- // good frame for this resolution — a stale dashboard beats an empty panel.
102
- // The state hash still advances so we retry on the next real state change.
103
- const prev = frames.get(key);
104
- if (frame.degraded && prev && !prev.degraded)
105
- frame = prev;
106
- frames.delete(key); // re-insert to mark most-recently-used
107
- frames.set(key, frame);
108
- lastHashByKey.set(key, trmnlStateHash(lastStateEvt));
109
- while (frames.size > MAX_FRAMES) {
110
- const oldest = frames.keys().next().value;
111
- if (oldest === undefined)
112
- break;
113
- frames.delete(oldest);
114
- lastHashByKey.delete(oldest);
115
- }
116
- return frame;
117
- }
118
- /**
119
- * Re-render every cached resolution whose visual state changed. Returns true if
120
- * any new frame was produced. Called by the device module on each broadcast. If
121
- * no device has polled yet, primes the default resolution so a first poll is
122
- * instant.
123
- */
124
- export function refreshTrmnlFrame(evt) {
125
- lastStateEvt = evt;
126
- const hash = trmnlStateHash(evt);
127
- if (frames.size === 0) {
128
- renderForKey(DEFAULT_KEY);
129
- return true;
130
- }
131
- let rendered = false;
132
- for (const key of [...frames.keys()]) {
133
- if (lastHashByKey.get(key) === hash)
134
- continue;
135
- renderForKey(key);
136
- rendered = true;
137
- }
138
- return rendered;
139
- }
140
- /** Force a render from the last known state for a resolution (e.g. after setup). */
141
- export function forceRenderTrmnlFrame(width, height) {
142
- return renderForKey(sizeKey(width, height));
143
- }
144
- /** Frame for a resolution, lazily rendered from the last known state if absent. */
145
- export function getTrmnlFrame(width, height) {
146
- const key = sizeKey(width, height);
147
- return frames.get(key) ?? renderForKey(key);
148
- }
149
- /** Frame for an exact `"<W>x<H>"` key, or undefined if not cached. */
150
- export function getTrmnlFrameByKey(key) {
151
- return frames.get(key);
152
- }
153
- /** Active resolution keys currently held in the cache. */
154
- export function getTrmnlFrameKeys() {
155
- return [...frames.keys()];
156
- }
157
- //# sourceMappingURL=frame-cache.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"frame-cache.js","sourceRoot":"","sources":["../../src/trmnl/frame-cache.ts"],"names":[],"mappings":"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"}
@@ -1,27 +0,0 @@
1
- /** Initialize the renderer (loads resvg-js if available). Call once at start. */
2
- export declare function initTrmnlRenderer(): Promise<void>;
3
- export declare function isTrmnlResvgLoaded(): boolean;
4
- export interface TrmnlFrame {
5
- buffer: Buffer;
6
- /** Short stable hash of the image bytes — used as the BYOS `filename`. */
7
- contentHash: string;
8
- width: number;
9
- height: number;
10
- contentType: 'image/png';
11
- /**
12
- * True when this frame is a blank fallback (render threw or resvg missing),
13
- * NOT a real dashboard. The frame cache uses this to keep serving the last
14
- * good frame instead of flashing the panel to an empty white screen.
15
- */
16
- degraded?: boolean;
17
- }
18
- /**
19
- * Render the AgentDeck dashboard for the given broadcast state into a 1-bit PNG.
20
- * Accepts a raw state event or a pre-parsed DashState (passed through to the
21
- * shared layout). `now` may be supplied for deterministic tests.
22
- */
23
- export declare function renderTrmnlFrame(stateEvt: any, now?: Date, size?: {
24
- width: number;
25
- height: number;
26
- }): TrmnlFrame;
27
- //# sourceMappingURL=image-renderer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"image-renderer.d.ts","sourceRoot":"","sources":["../../src/trmnl/image-renderer.ts"],"names":[],"mappings":"AA8EA,iFAAiF;AACjF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEvD;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AA4GD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,WAAW,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,GAAG,EACb,GAAG,CAAC,EAAE,IAAI,EACV,IAAI,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvC,UAAU,CAwCZ"}