@agentdeck/bridge 1.0.3 → 1.0.5

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 (215) hide show
  1. package/assets/fm-helper/agentdeck-fm-helper +0 -0
  2. package/dist/adapters/claude-code.d.ts.map +1 -1
  3. package/dist/adapters/claude-code.js +3 -0
  4. package/dist/adapters/claude-code.js.map +1 -1
  5. package/dist/adb-reverse.d.ts.map +1 -1
  6. package/dist/adb-reverse.js +4 -2
  7. package/dist/adb-reverse.js.map +1 -1
  8. package/dist/apme/collector.js +2 -2
  9. package/dist/apme/collector.js.map +1 -1
  10. package/dist/apme/outcome.js +2 -2
  11. package/dist/apme/outcome.js.map +1 -1
  12. package/dist/apme/runner.js +3 -3
  13. package/dist/apme/runner.js.map +1 -1
  14. package/dist/ble-sync-spawn.js +1 -1
  15. package/dist/ble-sync-spawn.js.map +1 -1
  16. package/dist/calendar.d.ts +82 -0
  17. package/dist/calendar.d.ts.map +1 -0
  18. package/dist/calendar.js +324 -0
  19. package/dist/calendar.js.map +1 -0
  20. package/dist/card-feed.d.ts +63 -5
  21. package/dist/card-feed.d.ts.map +1 -1
  22. package/dist/card-feed.js +218 -14
  23. package/dist/card-feed.js.map +1 -1
  24. package/dist/card-modules.d.ts +4 -0
  25. package/dist/card-modules.d.ts.map +1 -1
  26. package/dist/card-modules.js +5 -3
  27. package/dist/card-modules.js.map +1 -1
  28. package/dist/check-deps.js +4 -4
  29. package/dist/check-deps.js.map +1 -1
  30. package/dist/cli.d.ts +35 -0
  31. package/dist/cli.d.ts.map +1 -1
  32. package/dist/cli.js +406 -76
  33. package/dist/cli.js.map +1 -1
  34. package/dist/codex-otel.d.ts +89 -0
  35. package/dist/codex-otel.d.ts.map +1 -0
  36. package/dist/codex-otel.js +487 -0
  37. package/dist/codex-otel.js.map +1 -0
  38. package/dist/codex-rate-limits.d.ts.map +1 -1
  39. package/dist/codex-rate-limits.js +15 -3
  40. package/dist/codex-rate-limits.js.map +1 -1
  41. package/dist/daemon-server.d.ts +4 -0
  42. package/dist/daemon-server.d.ts.map +1 -1
  43. package/dist/daemon-server.js +797 -54
  44. package/dist/daemon-server.js.map +1 -1
  45. package/dist/daemon-ws-client.d.ts +108 -24
  46. package/dist/daemon-ws-client.d.ts.map +1 -1
  47. package/dist/daemon-ws-client.js +175 -39
  48. package/dist/daemon-ws-client.js.map +1 -1
  49. package/dist/daemon.js +1 -1
  50. package/dist/device-voice-reply.d.ts.map +1 -1
  51. package/dist/device-voice-reply.js +15 -3
  52. package/dist/device-voice-reply.js.map +1 -1
  53. package/dist/device-voice.d.ts +37 -0
  54. package/dist/device-voice.d.ts.map +1 -1
  55. package/dist/device-voice.js +130 -1
  56. package/dist/device-voice.js.map +1 -1
  57. package/dist/diag-analyzer.d.ts.map +1 -1
  58. package/dist/diag-analyzer.js +1 -0
  59. package/dist/diag-analyzer.js.map +1 -1
  60. package/dist/display-monitor.d.ts.map +1 -1
  61. package/dist/display-monitor.js +1 -0
  62. package/dist/display-monitor.js.map +1 -1
  63. package/dist/esp32-serial.d.ts +4 -0
  64. package/dist/esp32-serial.d.ts.map +1 -1
  65. package/dist/esp32-serial.js +6 -3
  66. package/dist/esp32-serial.js.map +1 -1
  67. package/dist/foundation-models-helper.d.ts +23 -0
  68. package/dist/foundation-models-helper.d.ts.map +1 -1
  69. package/dist/foundation-models-helper.js +32 -0
  70. package/dist/foundation-models-helper.js.map +1 -1
  71. package/dist/gateway-probe.js +1 -1
  72. package/dist/gateway-probe.js.map +1 -1
  73. package/dist/glance-frame.d.ts +64 -0
  74. package/dist/glance-frame.d.ts.map +1 -0
  75. package/dist/glance-frame.js +364 -0
  76. package/dist/glance-frame.js.map +1 -0
  77. package/dist/hook-codex-sessions.d.ts +40 -0
  78. package/dist/hook-codex-sessions.d.ts.map +1 -0
  79. package/dist/hook-codex-sessions.js +174 -0
  80. package/dist/hook-codex-sessions.js.map +1 -0
  81. package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
  82. package/dist/idotmatrix/idotmatrix-daemon-sync.js +5 -18
  83. package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
  84. package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -1
  85. package/dist/idotmatrix/idotmatrix-discover.js +12 -17
  86. package/dist/idotmatrix/idotmatrix-discover.js.map +1 -1
  87. package/dist/idotmatrix/idotmatrix-settings.d.ts +15 -0
  88. package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -1
  89. package/dist/idotmatrix/idotmatrix-settings.js +21 -0
  90. package/dist/idotmatrix/idotmatrix-settings.js.map +1 -1
  91. package/dist/index.d.ts +7 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +88 -7
  94. package/dist/index.js.map +1 -1
  95. package/dist/linux-service.d.ts +52 -0
  96. package/dist/linux-service.d.ts.map +1 -0
  97. package/dist/linux-service.js +206 -0
  98. package/dist/linux-service.js.map +1 -0
  99. package/dist/logger.d.ts +6 -0
  100. package/dist/logger.d.ts.map +1 -1
  101. package/dist/logger.js +15 -0
  102. package/dist/logger.js.map +1 -1
  103. package/dist/mdns-discover.d.ts +19 -0
  104. package/dist/mdns-discover.d.ts.map +1 -0
  105. package/dist/mdns-discover.js +109 -0
  106. package/dist/mdns-discover.js.map +1 -0
  107. package/dist/model-catalog.d.ts.map +1 -1
  108. package/dist/model-catalog.js +1 -0
  109. package/dist/model-catalog.js.map +1 -1
  110. package/dist/modules/adb-module.js +1 -1
  111. package/dist/modules/adb-module.js.map +1 -1
  112. package/dist/output-parser.d.ts +9 -0
  113. package/dist/output-parser.d.ts.map +1 -1
  114. package/dist/output-parser.js +67 -16
  115. package/dist/output-parser.js.map +1 -1
  116. package/dist/passive-observer.d.ts +57 -8
  117. package/dist/passive-observer.d.ts.map +1 -1
  118. package/dist/passive-observer.js +210 -74
  119. package/dist/passive-observer.js.map +1 -1
  120. package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
  121. package/dist/pixoo/pixoo-bridge.js +47 -6
  122. package/dist/pixoo/pixoo-bridge.js.map +1 -1
  123. package/dist/pixoo/pixoo-renderer.d.ts +2 -1
  124. package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
  125. package/dist/pixoo/pixoo-renderer.js +137 -8
  126. package/dist/pixoo/pixoo-renderer.js.map +1 -1
  127. package/dist/pocket-autonomy.d.ts +78 -0
  128. package/dist/pocket-autonomy.d.ts.map +1 -0
  129. package/dist/pocket-autonomy.js +529 -0
  130. package/dist/pocket-autonomy.js.map +1 -0
  131. package/dist/pty-manager.d.ts.map +1 -1
  132. package/dist/pty-manager.js +3 -2
  133. package/dist/pty-manager.js.map +1 -1
  134. package/dist/python-ble-runtime.d.ts +64 -0
  135. package/dist/python-ble-runtime.d.ts.map +1 -0
  136. package/dist/python-ble-runtime.js +216 -0
  137. package/dist/python-ble-runtime.js.map +1 -0
  138. package/dist/remote-sessions.d.ts +64 -0
  139. package/dist/remote-sessions.d.ts.map +1 -0
  140. package/dist/remote-sessions.js +81 -0
  141. package/dist/remote-sessions.js.map +1 -0
  142. package/dist/review-runner.js +2 -2
  143. package/dist/review-runner.js.map +1 -1
  144. package/dist/session-aggregator.d.ts +2 -0
  145. package/dist/session-aggregator.d.ts.map +1 -1
  146. package/dist/session-aggregator.js +6 -1
  147. package/dist/session-aggregator.js.map +1 -1
  148. package/dist/session-focus-relay.d.ts +47 -1
  149. package/dist/session-focus-relay.d.ts.map +1 -1
  150. package/dist/session-focus-relay.js +130 -15
  151. package/dist/session-focus-relay.js.map +1 -1
  152. package/dist/session-push-channel.d.ts +31 -0
  153. package/dist/session-push-channel.d.ts.map +1 -0
  154. package/dist/session-push-channel.js +94 -0
  155. package/dist/session-push-channel.js.map +1 -0
  156. package/dist/session-registry.d.ts +126 -0
  157. package/dist/session-registry.d.ts.map +1 -1
  158. package/dist/session-registry.js +180 -3
  159. package/dist/session-registry.js.map +1 -1
  160. package/dist/subagent-timeline.d.ts +34 -0
  161. package/dist/subagent-timeline.d.ts.map +1 -0
  162. package/dist/subagent-timeline.js +156 -0
  163. package/dist/subagent-timeline.js.map +1 -0
  164. package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
  165. package/dist/timebox/timebox-daemon-sync.js +5 -20
  166. package/dist/timebox/timebox-daemon-sync.js.map +1 -1
  167. package/dist/timebox/timebox-discover.d.ts.map +1 -1
  168. package/dist/timebox/timebox-discover.js +7 -16
  169. package/dist/timebox/timebox-discover.js.map +1 -1
  170. package/dist/tui/gauge.d.ts +4 -2
  171. package/dist/tui/gauge.d.ts.map +1 -1
  172. package/dist/tui/gauge.js +8 -4
  173. package/dist/tui/gauge.js.map +1 -1
  174. package/dist/tui/renderer.d.ts.map +1 -1
  175. package/dist/tui/renderer.js +28 -0
  176. package/dist/tui/renderer.js.map +1 -1
  177. package/dist/types.d.ts +1 -1
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/types.js.map +1 -1
  180. package/dist/usage-api.d.ts +43 -0
  181. package/dist/usage-api.d.ts.map +1 -1
  182. package/dist/usage-api.js +114 -9
  183. package/dist/usage-api.js.map +1 -1
  184. package/dist/usage-event.d.ts +19 -0
  185. package/dist/usage-event.d.ts.map +1 -1
  186. package/dist/usage-event.js +35 -0
  187. package/dist/usage-event.js.map +1 -1
  188. package/dist/utils/project-name.d.ts.map +1 -1
  189. package/dist/utils/project-name.js +1 -0
  190. package/dist/utils/project-name.js.map +1 -1
  191. package/dist/version-check.d.ts.map +1 -1
  192. package/dist/version-check.js +3 -2
  193. package/dist/version-check.js.map +1 -1
  194. package/dist/voice-assistant.d.ts.map +1 -1
  195. package/dist/voice-assistant.js +2 -1
  196. package/dist/voice-assistant.js.map +1 -1
  197. package/dist/voice.d.ts.map +1 -1
  198. package/dist/voice.js +1 -0
  199. package/dist/voice.js.map +1 -1
  200. package/dist/weather.d.ts +72 -0
  201. package/dist/weather.d.ts.map +1 -0
  202. package/dist/weather.js +206 -0
  203. package/dist/weather.js.map +1 -0
  204. package/dist/windows-service.js +5 -5
  205. package/dist/windows-service.js.map +1 -1
  206. package/fm-helper/AgentDeckFMHelper.swift +200 -0
  207. package/package.json +20 -14
  208. package/python/requirements-ble.txt +5 -0
  209. package/src/idotmatrix/brightness.py +40 -0
  210. package/src/idotmatrix/identity_generated.py +50 -0
  211. package/src/idotmatrix/scan.py +55 -0
  212. package/src/idotmatrix/sync.py +400 -0
  213. package/src/pysync/matrix_sync_common.py +89 -0
  214. package/src/timebox/scan_ble.py +52 -0
  215. package/src/timebox/sync_ble.py +394 -0
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Glance Frame — daemon-side pixel rendering of the sleep/ambient dashboard
3
+ * (M8). The device-side glance (fork `renderGlance`) is the offline fallback;
4
+ * this is the rich face: the daemon lays the glance out as SVG (real
5
+ * typography, vector weather icons, gauge bars), rasterizes it with sharp,
6
+ * ordered-dithers to 1-bit, and packs it in the e-ink framebuffer format
7
+ * (1bpp, MSB-first rows, 1 = white — matching `clearScreen(0xFF)` semantics).
8
+ *
9
+ * tesserae-inspired (concepts only — tesserae is AGPL): server renders pixels,
10
+ * the panel is a display driver, and the browser preview shows the *exact*
11
+ * frame the panel will hold (`?format=png` returns the dithered pixels, so
12
+ * "looked fine in the preview, broke on the panel" cannot happen).
13
+ *
14
+ * Ordered (Bayer) dithering, not error diffusion: deterministic output means
15
+ * the frame bytes — and therefore the frame sig — are stable for stable input.
16
+ */
17
+ import sharp from 'sharp';
18
+ export const GLANCE_FRAME_BOARDS = {
19
+ // X3 panel: physical 792×528 (stride 99); layout stays portrait 528×792.
20
+ xteink_x3: { width: 528, height: 792, landscape: false, rotateToPhysical: true },
21
+ xteink_x4: { width: 800, height: 480, landscape: true },
22
+ inkdeck: { width: 800, height: 480, landscape: true },
23
+ };
24
+ // ===== Weather icons (vector, 1-bit friendly) =====
25
+ /** WMO code → icon key. Kept alongside `wmoSummary` in weather.ts semantics. */
26
+ export function wmoIconKey(code) {
27
+ if (code === undefined || !Number.isFinite(code))
28
+ return 'cloud';
29
+ if (code === 0)
30
+ return 'sun';
31
+ if (code <= 2)
32
+ return 'partly';
33
+ if (code === 3)
34
+ return 'cloud';
35
+ if (code === 45 || code === 48)
36
+ return 'fog';
37
+ if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82))
38
+ return 'rain';
39
+ if ((code >= 71 && code <= 77) || code === 85 || code === 86)
40
+ return 'snow';
41
+ if (code >= 95)
42
+ return 'storm';
43
+ return 'cloud';
44
+ }
45
+ /** Icon drawn in a 100×100 viewbox, stroke-based so it reads at 1 bit. */
46
+ function iconSvg(key) {
47
+ const cloud = '<path d="M28 62 a16 16 0 0 1 4 -31 a22 22 0 0 1 42 -4 a15 15 0 0 1 2 30 z" fill="none" stroke="black" stroke-width="7" stroke-linejoin="round"/>';
48
+ switch (key) {
49
+ case 'sun': {
50
+ const rays = Array.from({ length: 8 }, (_, i) => {
51
+ const a = (i * Math.PI) / 4;
52
+ const x1 = 50 + Math.cos(a) * 34, y1 = 50 + Math.sin(a) * 34;
53
+ const x2 = 50 + Math.cos(a) * 46, y2 = 50 + Math.sin(a) * 46;
54
+ return `<line x1="${x1.toFixed(1)}" y1="${y1.toFixed(1)}" x2="${x2.toFixed(1)}" y2="${y2.toFixed(1)}" stroke="black" stroke-width="7" stroke-linecap="round"/>`;
55
+ }).join('');
56
+ return `<circle cx="50" cy="50" r="21" fill="none" stroke="black" stroke-width="7"/>${rays}`;
57
+ }
58
+ case 'partly':
59
+ return ('<circle cx="66" cy="34" r="16" fill="none" stroke="black" stroke-width="6"/>' +
60
+ '<path d="M20 74 a13 13 0 0 1 3 -25 a18 18 0 0 1 34 -3 a12 12 0 0 1 2 28 z" fill="white" stroke="black" stroke-width="6" stroke-linejoin="round"/>');
61
+ case 'fog':
62
+ return [30, 46, 62, 78]
63
+ .map((y) => `<line x1="18" y1="${y}" x2="82" y2="${y}" stroke="black" stroke-width="7" stroke-linecap="round"/>`)
64
+ .join('');
65
+ case 'rain':
66
+ return (cloud +
67
+ [34, 52, 70]
68
+ .map((x) => `<line x1="${x}" y1="72" x2="${x - 6}" y2="88" stroke="black" stroke-width="6" stroke-linecap="round"/>`)
69
+ .join(''));
70
+ case 'snow':
71
+ return (cloud +
72
+ [34, 52, 70]
73
+ .map((x) => `<circle cx="${x}" cy="80" r="4" fill="black"/>`)
74
+ .join(''));
75
+ case 'storm':
76
+ return cloud + '<path d="M52 64 L40 84 h10 L44 100 L62 76 h-10 L58 64 z" fill="black"/>';
77
+ case 'cloud':
78
+ default:
79
+ return cloud;
80
+ }
81
+ }
82
+ // ===== SVG layout =====
83
+ const SANS = 'IBM Plex Sans, Helvetica, Arial, sans-serif';
84
+ const MONO = 'JetBrains Mono, Menlo, monospace';
85
+ /** 50% gray — the Bayer pass turns it into a clean checker; used for
86
+ * secondary fills so the frame has depth without real grayscale. */
87
+ const GRAY = '#808080';
88
+ const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
89
+ /** Conservative SVG text-width estimate used only to add an honest ellipsis
90
+ * before the hard clip. The production renderer cannot ask librsvg for text
91
+ * metrics before composing the SVG, and emitting the full string lets e-ink
92
+ * crop a word mid-letter. Count CJK/full-width glyphs as one em and bucket the
93
+ * common Latin widths; the clipPath remains the final safety boundary. */
94
+ function fitText(s, maxWidth, fontSize) {
95
+ const glyphEm = (ch) => {
96
+ const cp = ch.codePointAt(0) ?? 0;
97
+ if (cp >= 0x2e80)
98
+ return 1;
99
+ if (/\s/.test(ch))
100
+ return 0.33;
101
+ if (/[ilI1.,'`|!:;]/.test(ch))
102
+ return 0.28;
103
+ if (/[mwMW@#%&]/.test(ch))
104
+ return 0.82;
105
+ return 0.56;
106
+ };
107
+ const width = (value) => [...value].reduce((sum, ch) => sum + glyphEm(ch) * fontSize, 0);
108
+ if (width(s) <= maxWidth)
109
+ return s;
110
+ const suffix = '…';
111
+ const suffixWidth = width(suffix);
112
+ let used = 0;
113
+ let out = '';
114
+ for (const ch of s) {
115
+ const next = glyphEm(ch) * fontSize;
116
+ if (used + next + suffixWidth > maxWidth)
117
+ break;
118
+ out += ch;
119
+ used += next;
120
+ }
121
+ return `${out.trimEnd()}${suffix}`;
122
+ }
123
+ function text(c, x, y, size, s, opts = {}) {
124
+ if (!s)
125
+ return;
126
+ c.parts.push(`<text x="${x}" y="${y}" font-family="${opts.mono ? MONO : SANS}" font-size="${size}"` +
127
+ (opts.weight ? ` font-weight="${opts.weight}"` : '') +
128
+ (opts.anchor ? ` text-anchor="${opts.anchor}"` : '') +
129
+ (opts.spacing ? ` letter-spacing="${opts.spacing}"` : '') +
130
+ ` fill="${opts.fill ?? 'black'}">${esc(s)}</text>`);
131
+ }
132
+ function rule(c, x1, y, x2, thick = 2) {
133
+ c.parts.push(`<line x1="${x1}" y1="${y}" x2="${x2}" y2="${y}" stroke="black" stroke-width="${thick}"/>`);
134
+ }
135
+ function sectionLabel(c, x, y, label) {
136
+ text(c, x, y, 17, label, { weight: 700, spacing: '3' });
137
+ rule(c, x, y + 10, c.w - 36, 2);
138
+ }
139
+ /** One provider quota row: name + 5H/7D bars with the reset time. Returns the
140
+ * y consumed. */
141
+ function quotaRow(c, x, y, width, row) {
142
+ text(c, x, y + 20, 22, row.label + (row.stale ? ' *' : ''), { weight: 600 });
143
+ const barX = x + 118;
144
+ const barW = width - 118;
145
+ const drawBar = (by, tag, pct, extra) => {
146
+ if (pct === undefined)
147
+ return;
148
+ const h = 18;
149
+ text(c, barX - 8, by + h - 3, 15, tag, { weight: 700, anchor: 'end', mono: true });
150
+ c.parts.push(`<rect x="${barX}" y="${by}" width="${barW}" height="${h}" fill="white" stroke="black" stroke-width="2"/>`);
151
+ const fw = Math.round(((barW - 4) * Math.min(100, Math.max(0, pct))) / 100);
152
+ if (fw > 0)
153
+ c.parts.push(`<rect x="${barX + 2}" y="${by + 2}" width="${fw}" height="${h - 4}" fill="black"/>`);
154
+ const label = `${Math.round(pct)}%${extra ? ` ${extra}` : ''}`;
155
+ // Percent label sits inside the empty end of the bar when it fits, else
156
+ // just past the fill — always readable at 1 bit.
157
+ text(c, barX + barW - 6, by + h - 4, 14, label, { anchor: 'end', mono: true, fill: fw > barW - 90 ? 'white' : 'black' });
158
+ };
159
+ drawBar(y + 4, '5H', row.primaryPercent, row.primaryResetHm ? `→${row.primaryResetHm}` : undefined);
160
+ drawBar(y + 30, '7D', row.secondaryPercent);
161
+ return 62;
162
+ }
163
+ function weatherBlock(c, x, y, width, w) {
164
+ const startY = y;
165
+ // Hero: icon + big temperature.
166
+ const iconSize = 96;
167
+ c.parts.push(`<g transform="translate(${x},${y}) scale(${iconSize / 100})">${iconSvg(wmoIconKey(w.code))}</g>`);
168
+ const tx = x + iconSize + 26;
169
+ if (w.tempC !== undefined)
170
+ text(c, tx, y + 78, 88, `${w.tempC}°`, { weight: 700 });
171
+ const rx = x + width;
172
+ if (w.summary)
173
+ text(c, rx, y + 34, 26, w.summary, { weight: 600, anchor: 'end' });
174
+ if (w.todayMinC !== undefined && w.todayMaxC !== undefined) {
175
+ text(c, rx, y + 66, 21, `${w.todayMinC}–${w.todayMaxC}°`, { anchor: 'end' });
176
+ }
177
+ y += 112;
178
+ if (w.rain) {
179
+ const range = w.rain.endHm ? `${w.rain.startHm}–${w.rain.endHm}` : `~${w.rain.startHm}`;
180
+ text(c, x, y + 8, 22, `☂ Rain ${range} · ${w.rain.probability}%`, { weight: 600 });
181
+ y += 34;
182
+ }
183
+ if (w.tomorrow && (w.tomorrow.summary || w.tomorrow.minC !== undefined)) {
184
+ const t = w.tomorrow;
185
+ const bits = ['Tomorrow'];
186
+ if (t.summary)
187
+ bits.push(t.summary);
188
+ if (t.minC !== undefined && t.maxC !== undefined)
189
+ bits.push(`${t.minC}–${t.maxC}°`);
190
+ if (t.rainProbability !== undefined && t.rainProbability > 0)
191
+ bits.push(`rain ${t.rainProbability}%`);
192
+ text(c, x, y + 6, 19, bits.join(' · '), { fill: 'black' });
193
+ // A quiet gray underline separates the outlook from the sections below.
194
+ c.parts.push(`<rect x="${x}" y="${y + 16}" width="${width}" height="4" fill="${GRAY}"/>`);
195
+ y += 34;
196
+ }
197
+ return y - startY;
198
+ }
199
+ export function renderGlanceFrameSvg(input) {
200
+ const { width: W, height: H, landscape } = input.geometry;
201
+ const g = input.glance ?? {};
202
+ const c = { parts: [], w: W };
203
+ const M = 36; // outer margin
204
+ c.parts.push(`<rect width="${W}" height="${H}" fill="white"/>`);
205
+ // ── Masthead ──
206
+ text(c, M, 46, 24, 'AgentDeck', { weight: 700, spacing: '1' });
207
+ const mastRight = [g.weather?.place, input.serverHm ? `Synced ${input.serverHm}` : ''].filter(Boolean).join(' · ');
208
+ text(c, W - M, 46, 19, mastRight, { anchor: 'end' });
209
+ rule(c, M, 62, W - M, 3);
210
+ const colX = M;
211
+ const colW = landscape ? Math.floor((W - M * 2 - 40) / 2) : W - M * 2;
212
+ const rightX = M + colW + 40;
213
+ let yL = 96;
214
+ let yR = 96;
215
+ // ── Weather (left column / top block) ──
216
+ if (g.weather && (g.weather.tempC !== undefined || g.weather.summary)) {
217
+ yL += weatherBlock(c, colX, yL, colW, g.weather) + 18;
218
+ }
219
+ // ── Today's schedule (left column, under weather) ──
220
+ const events = g.events ?? [];
221
+ if (events.length > 0) {
222
+ sectionLabel(c, colX, yL, 'TODAY');
223
+ yL += 34;
224
+ for (const e of events) {
225
+ const time = e.startHm ? (e.endHm ? `${e.startHm}–${e.endHm}` : e.startHm) : 'All day';
226
+ text(c, colX, yL, 20, fitText(`${time} · ${e.title}`, colW, 20));
227
+ yL += 32;
228
+ }
229
+ yL += 10;
230
+ }
231
+ // ── Quota ──
232
+ let qx = colX, qy = yL, qw = colW;
233
+ if (landscape) {
234
+ qx = rightX;
235
+ qy = yR;
236
+ qw = W - M - rightX;
237
+ }
238
+ if (g.usage && g.usage.length > 0) {
239
+ sectionLabel(c, qx, qy, 'AI BUDGET');
240
+ qy += 26;
241
+ for (const row of g.usage)
242
+ qy += quotaRow(c, qx, qy, qw, row) + 8;
243
+ qy += 10;
244
+ }
245
+ if (landscape)
246
+ yR = qy;
247
+ else
248
+ yL = qy;
249
+ // ── Work wrap-up ──
250
+ let wx = colX, wy = yL, ww = colW;
251
+ if (landscape) {
252
+ wx = rightX;
253
+ wy = yR;
254
+ ww = W - M - rightX;
255
+ }
256
+ const wrapup = g.wrapup ?? [];
257
+ sectionLabel(c, wx, wy, 'WORK');
258
+ wy += 34;
259
+ if (wrapup.length === 0) {
260
+ text(c, wx, wy, 20, 'No active sessions', { fill: GRAY });
261
+ wy += 30;
262
+ }
263
+ // Clip the work lines to their column — a long CJK line must never bleed
264
+ // into the margin or the other column.
265
+ c.parts.push(`<clipPath id="work"><rect x="${wx}" y="${wy - 30}" width="${ww}" height="${H - wy - 30}"/></clipPath>`);
266
+ c.parts.push(`<g clip-path="url(#work)">`);
267
+ for (const line of wrapup) {
268
+ if (wy > H - 60)
269
+ break;
270
+ c.parts.push(`<circle cx="${wx + 5}" cy="${wy - 6}" r="4" fill="black"/>`);
271
+ text(c, wx + 20, wy, 20, fitText(line, ww - 20, 20));
272
+ wy += 32;
273
+ }
274
+ c.parts.push('</g>');
275
+ // ── Footer status ──
276
+ rule(c, M, H - 44, W - M, 2);
277
+ text(c, M, H - 18, 17, 'AGENTDECK · GLANCE', { weight: 700, spacing: '2', fill: GRAY });
278
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">${c.parts.join('')}</svg>`;
279
+ }
280
+ // ===== Rasterize → dither → pack =====
281
+ /** 4×4 Bayer matrix, thresholds spread over 0..255. Ordered dithering is
282
+ * deterministic, so identical input yields identical bytes (stable sig). */
283
+ const BAYER4 = [
284
+ [0, 8, 2, 10],
285
+ [12, 4, 14, 6],
286
+ [3, 11, 1, 9],
287
+ [15, 7, 13, 5],
288
+ ].map((row) => row.map((v) => Math.round(((v + 0.5) * 255) / 16)));
289
+ /** Rotate a logical-space grayscale raster (w×h) into physical panel space
290
+ * (h×w), using the device GfxRenderer Portrait mapping: phyX = y,
291
+ * phyY = physH − 1 − x (with physH = logical width). Kept exactly in sync
292
+ * with `rotateCoordinates` in the XTeink fork's GfxRenderer.cpp. */
293
+ export function rotateGrayToPhysical(gray, width, height) {
294
+ const physW = height;
295
+ const physH = width;
296
+ const out = Buffer.alloc(physW * physH);
297
+ for (let y = 0; y < height; y++) {
298
+ for (let x = 0; x < width; x++) {
299
+ out[(physH - 1 - x) * physW + y] = gray[y * width + x];
300
+ }
301
+ }
302
+ return out;
303
+ }
304
+ /** Pack 8-bit grayscale into the e-ink framebuffer format: 1bpp, MSB-first
305
+ * within each byte, row-major, bit 1 = white (clearScreen(0xFF) = white). */
306
+ export function packMono(gray, width, height) {
307
+ const rowBytes = Math.ceil(width / 8);
308
+ const out = Buffer.alloc(rowBytes * height, 0x00);
309
+ for (let y = 0; y < height; y++) {
310
+ const bay = BAYER4[y & 3];
311
+ for (let x = 0; x < width; x++) {
312
+ if (gray[y * width + x] > bay[x & 3]) {
313
+ out[y * rowBytes + (x >> 3)] |= 0x80 >> (x & 7);
314
+ }
315
+ }
316
+ }
317
+ return out;
318
+ }
319
+ /** FNV-1a over a byte buffer (frame sig / content sig). */
320
+ export function frameSig(packed) {
321
+ let h = 0x811c9dc5;
322
+ for (let i = 0; i < packed.length; i++) {
323
+ h ^= packed[i];
324
+ h = Math.imul(h, 0x01000193) >>> 0;
325
+ }
326
+ return h.toString(16).padStart(8, '0');
327
+ }
328
+ export async function renderGlanceFrame(input) {
329
+ const svg = Buffer.from(renderGlanceFrameSvg(input));
330
+ let gray = await sharp(svg, { density: 72 }).flatten({ background: 'white' }).greyscale().raw().toBuffer();
331
+ let { width, height } = input.geometry;
332
+ if (input.geometry.rotateToPhysical) {
333
+ gray = rotateGrayToPhysical(gray, width, height);
334
+ [width, height] = [height, width];
335
+ }
336
+ const packed = packMono(gray, width, height);
337
+ // The sig hashes the frame's CONTENT, not its pixels: the masthead bakes the
338
+ // "Synced HH:MM" clock stamp into the raster, so a pixel hash would change
339
+ // every minute and silently void the ?sig= conditional (a device would
340
+ // re-download ~50KB on every poll past a minute boundary). Hashing the SVG
341
+ // with the clock stamp blanked keeps the sig stable across clock ticks while
342
+ // still covering everything else that moves pixels — glance data, geometry,
343
+ // and layout-code changes all flow through the SVG string.
344
+ const sig = frameSig(Buffer.from(renderGlanceFrameSvg({ ...input, serverHm: '' })));
345
+ return {
346
+ packed,
347
+ sig,
348
+ width,
349
+ height,
350
+ png: async () => {
351
+ // Re-expand the packed bits so the PNG shows the dithered result, not
352
+ // the smooth grayscale — what you preview is what the panel holds.
353
+ const rowBytes = Math.ceil(width / 8);
354
+ const px = Buffer.alloc(width * height);
355
+ for (let y = 0; y < height; y++) {
356
+ for (let x = 0; x < width; x++) {
357
+ px[y * width + x] = packed[y * rowBytes + (x >> 3)] & (0x80 >> (x & 7)) ? 255 : 0;
358
+ }
359
+ }
360
+ return sharp(px, { raw: { width, height, channels: 1 } }).png().toBuffer();
361
+ },
362
+ };
363
+ }
364
+ //# sourceMappingURL=glance-frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glance-frame.js","sourceRoot":"","sources":["../src/glance-frame.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { ObservedSession } from './passive-observer.js';
2
+ export interface HookCodexSession {
3
+ sessionId: string;
4
+ projectName: string;
5
+ cwd?: string;
6
+ state: 'idle' | 'processing';
7
+ currentTool?: string;
8
+ startedAt: number;
9
+ lastHookAt: number;
10
+ terminalAt?: number;
11
+ }
12
+ export interface HookCodexPayload {
13
+ sessionId?: string;
14
+ cwd?: string;
15
+ projectName?: string;
16
+ toolName?: string;
17
+ }
18
+ export declare class HookCodexSessions {
19
+ private readonly sessions;
20
+ /** sessionId → epoch ms of its last terminal hook, outliving the row. */
21
+ private readonly terminated;
22
+ /** Fired when a hook changed something worth broadcasting. */
23
+ onChanged: (() => void) | undefined;
24
+ /**
25
+ * Record a `codex_*` lifecycle hook. Returns true when the sessions list
26
+ * should be rebroadcast.
27
+ */
28
+ note(event: string, payload: HookCodexPayload, now?: number): boolean;
29
+ /** Drop a session immediately (explicit end, not a timeout). */
30
+ forget(sessionId: string): void;
31
+ snapshot(): HookCodexSession[];
32
+ private reap;
33
+ /**
34
+ * Append a row for every hook-known Codex the process scan didn't produce.
35
+ * Sessions the observer already found are left entirely alone — its row is
36
+ * strictly richer.
37
+ */
38
+ applyTo(observed: ObservedSession[], now?: number): ObservedSession[];
39
+ }
40
+ //# sourceMappingURL=hook-codex-sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-codex-sessions.d.ts","sourceRoot":"","sources":["../src/hook-codex-sessions.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAuB7D,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuC;IAChE,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IAExD,8DAA8D;IAC9D,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAEpC;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,SAAa,GAAG,OAAO;IA0DzE,gEAAgE;IAChE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/B,QAAQ,IAAI,gBAAgB,EAAE;IAI9B,OAAO,CAAC,IAAI;IAWZ;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,GAAG,SAAa,GAAG,eAAe,EAAE;CAgC1E"}
@@ -0,0 +1,174 @@
1
+ // hook-codex-sessions.ts — Codex sessions derived from lifecycle hooks alone.
2
+ //
3
+ // The Node daemon has always let the ps/lsof passive observer own observed
4
+ // Codex session identity and state, with `codex_*` hooks feeding only the
5
+ // timeline (see the dispatch comment in daemon-server.ts). That works right up
6
+ // until the process scan can't answer: `lsof` is a 2 s-timeout subprocess, and
7
+ // when it times out — or a Codex holds no rollout open — the row disappears
8
+ // from every deck even though hooks are still arriving. The Swift daemon never
9
+ // had this failure mode because it has no process visibility at all and
10
+ // synthesizes a session from `codex_session_start` directly
11
+ // (`DaemonServer.handleHookEvent`). This is that path, ported.
12
+ //
13
+ // Ownership rules, so the two sources can't fight:
14
+ // • The observer wins whenever it sees the session. A hook row is emitted only
15
+ // for a session id no observed row carries — never a second row for one
16
+ // conversation.
17
+ // • Hook rows are deliberately thin (id, project, state, tool). Model, token
18
+ // counts, context %, and goal come from the rollout and stay the observer's
19
+ // job; a hook row that guessed them would flicker against the real values
20
+ // the moment the scan recovered.
21
+ import { resolveProjectNameFromCwdCached } from './utils/project-name.js';
22
+ /** Reaped this long after the turn-ending hook — Swift's `codexPostTerminalTTL`. */
23
+ const POST_TERMINAL_TTL_MS = 60_000;
24
+ /**
25
+ * Reaped this long after ANY hook when no terminal event arrived. A session
26
+ * killed mid-turn (SIGKILL, closed laptop) fires no `codex_stop`, and without
27
+ * this its creature would sit "processing" forever.
28
+ */
29
+ const SILENT_TTL_MS = 30 * 60_000;
30
+ /** Hook events that may create a row. */
31
+ const OPENING_EVENTS = new Set(['codex_session_start', 'codex_user_prompt_submit']);
32
+ /** Hook events that end a turn. `codex_turn_complete` is the notify-only fallback. */
33
+ const TERMINAL_EVENTS = new Set(['codex_stop', 'codex_session_end', 'codex_turn_complete']);
34
+ /**
35
+ * A finished session stays un-resurrectable this long — longer than its row
36
+ * lives, so a trailing tool callback can't revive a creature 90 s after the turn
37
+ * ended. Only an explicit opening event clears it.
38
+ */
39
+ const TOMBSTONE_TTL_MS = 30 * 60_000;
40
+ export class HookCodexSessions {
41
+ sessions = new Map();
42
+ /** sessionId → epoch ms of its last terminal hook, outliving the row. */
43
+ terminated = new Map();
44
+ /** Fired when a hook changed something worth broadcasting. */
45
+ onChanged;
46
+ /**
47
+ * Record a `codex_*` lifecycle hook. Returns true when the sessions list
48
+ * should be rebroadcast.
49
+ */
50
+ note(event, payload, now = Date.now()) {
51
+ const sessionId = payload.sessionId?.trim();
52
+ if (!sessionId || !event.startsWith('codex_'))
53
+ return false;
54
+ if (OPENING_EVENTS.has(event))
55
+ this.terminated.delete(sessionId);
56
+ const existing = this.sessions.get(sessionId);
57
+ if (!existing) {
58
+ // A mid-turn event may open a row too — that's how a session whose start
59
+ // hook predates this daemon (restart, or hooks installed mid-session)
60
+ // becomes visible at all. But only when the session has no tombstone: a
61
+ // trailing tool callback from a finished companion task must not
62
+ // resurrect a creature. Swift layers the same tombstone-gated bypass over
63
+ // `shouldSynthesizeUnknownHookSession`.
64
+ if (!OPENING_EVENTS.has(event) && !isProgressEvent(event))
65
+ return false;
66
+ if (this.terminated.has(sessionId))
67
+ return false;
68
+ }
69
+ const session = existing ?? {
70
+ sessionId,
71
+ projectName: '',
72
+ state: 'idle',
73
+ startedAt: now,
74
+ lastHookAt: now,
75
+ };
76
+ const before = JSON.stringify(session);
77
+ session.lastHookAt = now;
78
+ // cwd/project upgrade once, empty→set: later hooks on the same session can
79
+ // carry a subcommand's directory, and overwriting would relabel the row.
80
+ if (!session.cwd && payload.cwd) {
81
+ session.cwd = payload.cwd;
82
+ session.projectName = resolveProjectNameFromCwdCached(payload.cwd);
83
+ }
84
+ if (!session.projectName && payload.projectName)
85
+ session.projectName = payload.projectName;
86
+ if (TERMINAL_EVENTS.has(event)) {
87
+ session.state = 'idle';
88
+ session.currentTool = undefined;
89
+ session.terminalAt = now;
90
+ this.terminated.set(sessionId, now);
91
+ }
92
+ else {
93
+ // Any non-terminal hook means the turn is live again — including a
94
+ // follow-up prompt on a session whose previous turn ended.
95
+ session.terminalAt = undefined;
96
+ session.state = 'processing';
97
+ if (event === 'codex_tool_start')
98
+ session.currentTool = payload.toolName || undefined;
99
+ else if (event === 'codex_tool_end')
100
+ session.currentTool = undefined;
101
+ else if (event === 'codex_user_prompt_submit')
102
+ session.currentTool = undefined;
103
+ }
104
+ this.sessions.set(sessionId, session);
105
+ this.reap(now);
106
+ const changed = JSON.stringify(session) !== before || !existing;
107
+ if (changed)
108
+ this.onChanged?.();
109
+ return changed;
110
+ }
111
+ /** Drop a session immediately (explicit end, not a timeout). */
112
+ forget(sessionId) {
113
+ if (this.sessions.delete(sessionId))
114
+ this.onChanged?.();
115
+ }
116
+ snapshot() {
117
+ return [...this.sessions.values()];
118
+ }
119
+ reap(now) {
120
+ for (const [sessionId, session] of this.sessions) {
121
+ const finished = session.terminalAt !== undefined
122
+ && now - session.terminalAt > POST_TERMINAL_TTL_MS;
123
+ if (finished || now - session.lastHookAt > SILENT_TTL_MS)
124
+ this.sessions.delete(sessionId);
125
+ }
126
+ for (const [sessionId, at] of this.terminated) {
127
+ if (now - at > TOMBSTONE_TTL_MS)
128
+ this.terminated.delete(sessionId);
129
+ }
130
+ }
131
+ /**
132
+ * Append a row for every hook-known Codex the process scan didn't produce.
133
+ * Sessions the observer already found are left entirely alone — its row is
134
+ * strictly richer.
135
+ */
136
+ applyTo(observed, now = Date.now()) {
137
+ this.reap(now);
138
+ if (this.sessions.size === 0)
139
+ return observed;
140
+ const seen = new Set();
141
+ for (const session of observed) {
142
+ // `observed:codex:<uuid>` / `observed:codex-app:<uuid>` → the bare uuid
143
+ // hooks use. Both id forms name the same conversation.
144
+ const match = /^observed:codex(?:-app)?:(.+)$/.exec(session.id);
145
+ if (match?.[1])
146
+ seen.add(match[1]);
147
+ }
148
+ const extra = [];
149
+ for (const session of this.sessions.values()) {
150
+ if (seen.has(session.sessionId))
151
+ continue;
152
+ extra.push({
153
+ id: `observed:codex:${session.sessionId}`,
154
+ port: 0,
155
+ pid: 0,
156
+ projectName: session.projectName || 'Codex',
157
+ agentType: 'codex-cli',
158
+ alive: true,
159
+ state: session.state,
160
+ controlMode: 'observed',
161
+ cwd: session.cwd,
162
+ currentTask: session.currentTool,
163
+ startedAt: new Date(session.startedAt).toISOString(),
164
+ lastActivityAt: session.lastHookAt,
165
+ });
166
+ }
167
+ return extra.length > 0 ? [...observed, ...extra] : observed;
168
+ }
169
+ }
170
+ /** Mid-turn hooks: progress on a session that must already be known. */
171
+ function isProgressEvent(event) {
172
+ return event === 'codex_tool_start' || event === 'codex_tool_end';
173
+ }
174
+ //# sourceMappingURL=hook-codex-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-codex-sessions.js","sourceRoot":"","sources":["../src/hook-codex-sessions.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"idotmatrix-daemon-sync.d.ts","sourceRoot":"","sources":["../../src/idotmatrix/idotmatrix-daemon-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA+CH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CA8B1D;AAoDD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB7E"}
1
+ {"version":3,"file":"idotmatrix-daemon-sync.d.ts","sourceRoot":"","sources":["../../src/idotmatrix/idotmatrix-daemon-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAkCH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CA2B1D;AAoDD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB7E"}