@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,206 @@
1
+ /**
2
+ * Glance weather provider (Open-Meteo) — daemon-side module for the card-feed
3
+ * sleep dashboard. The device never fetches weather itself; the daemon fetches,
4
+ * caches, and pre-renders it into `CardFeedResponse.glance.weather`
5
+ * (shared/src/protocol.ts § Glance).
6
+ *
7
+ * Open-Meteo is key-less and free for non-commercial local use; one bounded
8
+ * request per cache window. Config comes from settings.json:
9
+ *
10
+ * "weather": { "lat": 37.57, "lon": 126.98, "place": "Seoul" }
11
+ *
12
+ * No config → no weather in the glance (never a guess, never IP geolocation).
13
+ */
14
+ import { GLANCE_RAIN_PROBABILITY_MIN } from '@agentdeck/shared';
15
+ /** Serve from cache inside this window — weather cadence is slower than the
16
+ * fastest pull cadence (900s), so a fresh fetch per pull would be waste. */
17
+ export const WEATHER_CACHE_MS = 30 * 60 * 1000;
18
+ /** After a fetch failure, keep serving the last good report up to this age —
19
+ * a flaky WAN must degrade to slightly-old weather, not a blank panel. */
20
+ export const WEATHER_STALE_SERVE_MS = 3 * 60 * 60 * 1000;
21
+ /** External peer await — timeout is first-line, not optional. */
22
+ export const WEATHER_FETCH_TIMEOUT_MS = 5000;
23
+ export function parseWeatherSettings(settings) {
24
+ const w = settings?.weather;
25
+ if (!w || typeof w !== 'object')
26
+ return null;
27
+ const lat = Number(w.lat);
28
+ const lon = Number(w.lon);
29
+ if (!Number.isFinite(lat) || !Number.isFinite(lon))
30
+ return null;
31
+ if (Math.abs(lat) > 90 || Math.abs(lon) > 180)
32
+ return null;
33
+ return {
34
+ lat,
35
+ lon,
36
+ ...(typeof w.place === 'string' && w.place ? { place: w.place } : {}),
37
+ };
38
+ }
39
+ /** WMO weather interpretation code → one short summary word. Kept ASCII so
40
+ * every panel font can draw it; codes ride alongside for icon-capable
41
+ * clients. */
42
+ export function wmoSummary(code) {
43
+ if (code === undefined || !Number.isFinite(code))
44
+ return '';
45
+ if (code === 0)
46
+ return 'Clear';
47
+ if (code <= 2)
48
+ return 'Fair';
49
+ if (code === 3)
50
+ return 'Cloudy';
51
+ if (code === 45 || code === 48)
52
+ return 'Fog';
53
+ if (code >= 51 && code <= 57)
54
+ return 'Drizzle';
55
+ if (code >= 61 && code <= 67)
56
+ return 'Rain';
57
+ if (code >= 71 && code <= 77)
58
+ return 'Snow';
59
+ if (code >= 80 && code <= 82)
60
+ return 'Showers';
61
+ if (code === 85 || code === 86)
62
+ return 'Snow';
63
+ if (code >= 95)
64
+ return 'Storm';
65
+ return 'Cloudy';
66
+ }
67
+ export function buildForecastUrl(cfg) {
68
+ const p = new URLSearchParams({
69
+ latitude: String(cfg.lat),
70
+ longitude: String(cfg.lon),
71
+ current: 'temperature_2m,weather_code',
72
+ hourly: 'precipitation_probability',
73
+ daily: 'weather_code,temperature_2m_min,temperature_2m_max,precipitation_probability_max',
74
+ timezone: 'auto',
75
+ forecast_days: '2',
76
+ });
77
+ return `https://api.open-meteo.com/v1/forecast?${p.toString()}`;
78
+ }
79
+ const hmOf = (iso) => iso.slice(11, 16);
80
+ const dayOf = (iso) => iso.slice(0, 10);
81
+ /** First remaining rain window today: contiguous hours ≥ the probability
82
+ * floor, starting from the current hour. */
83
+ export function findTodayRainWindow(hourly, now) {
84
+ const times = hourly?.time ?? [];
85
+ const probs = hourly?.precipitation_probability ?? [];
86
+ const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
87
+ const nowHm = `${String(now.getHours()).padStart(2, '0')}:00`;
88
+ let start;
89
+ let end;
90
+ let peak = 0;
91
+ for (let i = 0; i < times.length && i < probs.length; i++) {
92
+ const t = times[i];
93
+ if (dayOf(t) !== today) {
94
+ if (start)
95
+ break; // window ran to midnight
96
+ continue;
97
+ }
98
+ const hm = hmOf(t);
99
+ if (hm < nowHm)
100
+ continue;
101
+ const p = probs[i];
102
+ if (typeof p === 'number' && p >= GLANCE_RAIN_PROBABILITY_MIN) {
103
+ if (!start)
104
+ start = hm;
105
+ end = hm;
106
+ if (p > peak)
107
+ peak = p;
108
+ }
109
+ else if (start) {
110
+ break; // window closed
111
+ }
112
+ }
113
+ if (!start)
114
+ return undefined;
115
+ const win = { startHm: start, probability: Math.round(peak) };
116
+ if (end && end !== start)
117
+ win.endHm = end;
118
+ return win;
119
+ }
120
+ export function toGlanceWeather(raw, cfg, now) {
121
+ const out = {};
122
+ if (cfg.place)
123
+ out.place = cfg.place;
124
+ const cur = raw.current;
125
+ if (cur && typeof cur.temperature_2m === 'number')
126
+ out.tempC = Math.round(cur.temperature_2m);
127
+ if (cur && typeof cur.weather_code === 'number') {
128
+ out.code = cur.weather_code;
129
+ out.summary = wmoSummary(cur.weather_code);
130
+ }
131
+ const d = raw.daily;
132
+ if (d?.time?.length) {
133
+ const todayMin = d.temperature_2m_min?.[0];
134
+ const todayMax = d.temperature_2m_max?.[0];
135
+ if (typeof todayMin === 'number')
136
+ out.todayMinC = Math.round(todayMin);
137
+ if (typeof todayMax === 'number')
138
+ out.todayMaxC = Math.round(todayMax);
139
+ if (d.time.length > 1) {
140
+ const t = { summary: wmoSummary(d.weather_code?.[1]) };
141
+ if (typeof d.weather_code?.[1] === 'number')
142
+ t.code = d.weather_code[1];
143
+ if (typeof d.temperature_2m_min?.[1] === 'number')
144
+ t.minC = Math.round(d.temperature_2m_min[1]);
145
+ if (typeof d.temperature_2m_max?.[1] === 'number')
146
+ t.maxC = Math.round(d.temperature_2m_max[1]);
147
+ if (typeof d.precipitation_probability_max?.[1] === 'number') {
148
+ t.rainProbability = Math.round(d.precipitation_probability_max[1]);
149
+ }
150
+ out.tomorrow = t;
151
+ }
152
+ }
153
+ const rain = findTodayRainWindow(raw.hourly, now);
154
+ if (rain)
155
+ out.rain = rain;
156
+ return out;
157
+ }
158
+ export class WeatherProvider {
159
+ fetchImpl;
160
+ log;
161
+ cache = null;
162
+ inflight = null;
163
+ constructor(fetchImpl = fetch, log = () => { }) {
164
+ this.fetchImpl = fetchImpl;
165
+ this.log = log;
166
+ }
167
+ /** Current weather for `cfg`, from cache when fresh. Resolves `undefined`
168
+ * when unconfigured or when no report (fresh or stale-servable) exists —
169
+ * the glance simply omits weather. Never throws. */
170
+ async get(cfg, now = Date.now()) {
171
+ if (!cfg)
172
+ return undefined;
173
+ const key = `${cfg.lat},${cfg.lon}`;
174
+ const cached = this.cache;
175
+ if (cached && cached.key === key && now - cached.at < WEATHER_CACHE_MS)
176
+ return cached.data;
177
+ if (this.inflight)
178
+ return this.inflight;
179
+ this.inflight = this.fetchFresh(cfg, key, now).finally(() => { this.inflight = null; });
180
+ return this.inflight;
181
+ }
182
+ async fetchFresh(cfg, key, now) {
183
+ const ctl = new AbortController();
184
+ const timer = setTimeout(() => ctl.abort(), WEATHER_FETCH_TIMEOUT_MS);
185
+ try {
186
+ const res = await this.fetchImpl(buildForecastUrl(cfg), { signal: ctl.signal });
187
+ if (!res.ok)
188
+ throw new Error(`open-meteo HTTP ${res.status}`);
189
+ const raw = (await res.json());
190
+ const data = toGlanceWeather(raw, cfg, new Date(now));
191
+ this.cache = { key, at: now, data };
192
+ return data;
193
+ }
194
+ catch (err) {
195
+ this.log(`[weather] fetch failed: ${err instanceof Error ? err.message : String(err)}`);
196
+ const cached = this.cache;
197
+ if (cached && cached.key === key && now - cached.at < WEATHER_STALE_SERVE_MS)
198
+ return cached.data;
199
+ return undefined;
200
+ }
201
+ finally {
202
+ clearTimeout(timer);
203
+ }
204
+ }
205
+ }
206
+ //# sourceMappingURL=weather.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weather.js","sourceRoot":"","sources":["../src/weather.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAQhE;6EAC6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/C;2EAC2E;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACzD,iEAAiE;AACjE,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C,MAAM,UAAU,oBAAoB,CAAC,QAAiC;IACpE,MAAM,CAAC,GAAG,QAAQ,EAAE,OAA8C,CAAC;IACnE,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG;QAAE,OAAO,IAAI,CAAC;IAC3D,OAAO;QACL,GAAG;QACH,GAAG;QACH,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtE,CAAC;AACJ,CAAC;AAED;;eAEe;AACf,MAAM,UAAU,UAAU,CAAC,IAAwB;IACjD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5D,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC/B,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7B,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAChC,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC7C,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC5C,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC5C,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,IAAI,IAAI,EAAE;QAAE,OAAO,OAAO,CAAC;IAC/B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAeD,MAAM,UAAU,gBAAgB,CAAC,GAAoB;IACnD,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAC1B,OAAO,EAAE,6BAA6B;QACtC,MAAM,EAAE,2BAA2B;QACnC,KAAK,EAAE,kFAAkF;QACzF,QAAQ,EAAE,MAAM;QAChB,aAAa,EAAE,GAAG;KACnB,CAAC,CAAC;IACH,OAAO,0CAA0C,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;AAClE,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACxD,MAAM,KAAK,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAExD;6CAC6C;AAC7C,MAAM,UAAU,mBAAmB,CACjC,MAA6E,EAC7E,GAAS;IAET,MAAM,KAAK,GAAG,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,EAAE,yBAAyB,IAAI,EAAE,CAAC;IACtD,MAAM,KAAK,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC9H,MAAM,KAAK,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC;IAC9D,IAAI,KAAyB,CAAC;IAC9B,IAAI,GAAuB,CAAC;IAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1D,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;YACvB,IAAI,KAAK;gBAAE,MAAM,CAAC,yBAAyB;YAC3C,SAAS;QACX,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,EAAE,GAAG,KAAK;YAAE,SAAS;QACzB,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,2BAA2B,EAAE,CAAC;YAC9D,IAAI,CAAC,KAAK;gBAAE,KAAK,GAAG,EAAE,CAAC;YACvB,GAAG,GAAG,EAAE,CAAC;YACT,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YACjB,MAAM,CAAC,gBAAgB;QACzB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,GAAG,GAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;IAChF,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAsB,EAAE,GAAoB,EAAE,GAAS;IACrF,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,IAAI,GAAG,CAAC,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACrC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC;IACxB,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,QAAQ;QAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9F,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QAChD,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC;QAC5B,GAAG,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;IACpB,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvE,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,GAAqB,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,IAAI,OAAO,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK,QAAQ;gBAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACxE,IAAI,OAAO,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,KAAK,QAAQ;gBAAE,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;YAChG,IAAI,OAAO,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,KAAK,QAAQ;gBAAE,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;YAChG,IAAI,OAAO,CAAC,CAAC,6BAA6B,EAAE,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC7D,CAAC,CAAC,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC,CAAC;YACrE,CAAC;YACD,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC;AAQD,MAAM,OAAO,eAAe;IAKP;IACA;IALX,KAAK,GAAsB,IAAI,CAAC;IAChC,QAAQ,GAA8C,IAAI,CAAC;IAEnE,YACmB,YAA0B,KAAK,EAC/B,MAA6B,GAAG,EAAE,GAAE,CAAC;QADrC,cAAS,GAAT,SAAS,CAAsB;QAC/B,QAAG,GAAH,GAAG,CAAkC;IACrD,CAAC;IAEJ;;yDAEqD;IACrD,KAAK,CAAC,GAAG,CAAC,GAA2B,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;QAC7D,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1B,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,GAAG,MAAM,CAAC,EAAE,GAAG,gBAAgB;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC;QAC3F,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACxF,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,GAAoB,EAAE,GAAW,EAAE,GAAW;QACrE,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,wBAAwB,CAAC,CAAC;QACtE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAChF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9D,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;YACpD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACtD,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,2BAA2B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACxF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1B,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,GAAG,MAAM,CAAC,EAAE,GAAG,sBAAsB;gBAAE,OAAO,MAAM,CAAC,IAAI,CAAC;YACjG,OAAO,SAAS,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;CACF"}
@@ -111,7 +111,7 @@ export function installWindowsTask() {
111
111
  writeFileSync(tmpFile, '' + xml, 'utf16le');
112
112
  try {
113
113
  // /F overwrites an existing task (idempotent; mirrors macOS unload-before-load).
114
- execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, { stdio: 'pipe' });
114
+ execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, { stdio: 'pipe', windowsHide: true });
115
115
  }
116
116
  finally {
117
117
  try {
@@ -123,7 +123,7 @@ export function installWindowsTask() {
123
123
  /** True if the AgentDeckDaemon scheduled task is registered. */
124
124
  export function taskExists() {
125
125
  try {
126
- execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: 'pipe' });
126
+ execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: 'pipe', windowsHide: true });
127
127
  return true;
128
128
  }
129
129
  catch {
@@ -132,14 +132,14 @@ export function taskExists() {
132
132
  }
133
133
  /** Start the registered task immediately (so no logout is required). */
134
134
  export function runWindowsTask() {
135
- execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: 'pipe' });
135
+ execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: 'pipe', windowsHide: true });
136
136
  }
137
137
  /** Stop a running task instance (best-effort; ignores "not running"). */
138
138
  export function endWindowsTask() {
139
- execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: 'pipe' });
139
+ execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: 'pipe', windowsHide: true });
140
140
  }
141
141
  /** Delete the registered task (/F suppresses the confirm prompt). */
142
142
  export function deleteWindowsTask() {
143
- execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'pipe' });
143
+ execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'pipe', windowsHide: true });
144
144
  }
145
145
  //# sourceMappingURL=windows-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"windows-service.js","sourceRoot":"","sources":["../src/windows-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,CAAC,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAE3C,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,yDAAyD;AACzD,MAAM,UAAU,mBAAmB;IACjC,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC,QAAQ,CAAC;IACzD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAuD;IAC3F,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,mBAAmB,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,kBAAkB,EAAE,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,6EAA6E;IAC7E,6EAA6E;IAC7E,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,KAAK,6BAA6B,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,+EAA+E;IAC/E,2EAA2E;IAC3E,0DAA0D;IAC1D,OAAO;;;;aAII,SAAS;;;;;gBAKN,OAAO;;;;;gBAKP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BN,OAAO;mBACL,IAAI;0BACG,SAAS,CAAC,UAAU,CAAC;;;QAGvC,CAAC;AACT,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,GAAG,GAAG,qBAAqB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClF,yEAAyE;IACzE,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC;QACH,iFAAiF;QACjF,QAAQ,CAAC,yBAAyB,SAAS,WAAW,OAAO,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1F,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,8BAA8B,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,QAAQ,CAAC,wBAAwB,SAAS,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,cAAc;IAC5B,QAAQ,CAAC,sBAAsB,SAAS,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,cAAc;IAC5B,QAAQ,CAAC,sBAAsB,SAAS,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,iBAAiB;IAC/B,QAAQ,CAAC,yBAAyB,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AACxE,CAAC"}
1
+ {"version":3,"file":"windows-service.js","sourceRoot":"","sources":["../src/windows-service.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,8 @@ struct HelperRequest: Decodable {
19
19
  let text: String?
20
20
  let voice: String?
21
21
  let rate: Double?
22
+ /// `record`: hard cap on capture length; the reply arrives at stop or here.
23
+ let maxMs: Int?
22
24
  }
23
25
 
24
26
  @main
@@ -60,6 +62,20 @@ struct AgentDeckFMHelper {
60
62
  return
61
63
  }
62
64
 
65
+ if request.type == "record" {
66
+ // Recording spans an unknown PTT hold, and the stop arrives as a
67
+ // later stdin line — so this must not block the read loop.
68
+ Task.detached { await handleRecord(request) }
69
+ return
70
+ }
71
+
72
+ if request.type == "record_stop" || request.type == "record_cancel" {
73
+ let stopped = activeRecording.finish(
74
+ cancelled: request.type == "record_cancel", reason: "stopped")
75
+ write(["id": request.id, "stopped": stopped])
76
+ return
77
+ }
78
+
63
79
  guard let prompt = request.prompt, !prompt.isEmpty else {
64
80
  write(["id": request.id, "error": "bad_request", "reason": "missing prompt"])
65
81
  return
@@ -165,6 +181,111 @@ struct AgentDeckFMHelper {
165
181
  }
166
182
  }
167
183
 
184
+ // MARK: - Microphone capture
185
+
186
+ private static let activeRecording = RecordingBox()
187
+
188
+ /// Capture the host microphone into a 16 kHz mono PCM16 WAV. This is the
189
+ /// piece that retired the old Stream Deck Voice dial: capture used to mean
190
+ /// borrowing iTerm2's mic grant via AppleScript plus Homebrew `sox`. Here
191
+ /// the daemon's own bundled helper records, so the TCC grant belongs to
192
+ /// this process tree and there is nothing to install.
193
+ ///
194
+ /// The reply is deferred: it is written when `record_stop`/`record_cancel`
195
+ /// arrives on stdin or `maxMs` elapses, whichever comes first.
196
+ private static func handleRecord(_ request: HelperRequest) async {
197
+ guard let outPath = request.wav, !outPath.isEmpty else {
198
+ write(["id": request.id, "error": "bad_request", "reason": "missing wav output path"])
199
+ return
200
+ }
201
+ let granted = await ensureMicAuthorization()
202
+ guard granted else {
203
+ write([
204
+ "id": request.id,
205
+ "error": "unauthorized",
206
+ "reason": "microphone access not granted — grant it in System Settings › Privacy & Security › Microphone",
207
+ ])
208
+ return
209
+ }
210
+ let sampleRate = 16000.0
211
+ guard let outFormat = AVAudioFormat(commonFormat: .pcmFormatInt16,
212
+ sampleRate: sampleRate,
213
+ channels: 1,
214
+ interleaved: true) else {
215
+ write(["id": request.id, "error": "record_failed", "reason": "output format unavailable"])
216
+ return
217
+ }
218
+
219
+ let engine = AVAudioEngine()
220
+ let input = engine.inputNode
221
+ let inFormat = input.outputFormat(forBus: 0)
222
+ guard inFormat.sampleRate > 0, inFormat.channelCount > 0 else {
223
+ write(["id": request.id, "error": "record_failed", "reason": "no audio input device"])
224
+ return
225
+ }
226
+ let sink = PCMSink()
227
+ guard activeRecording.begin(requestId: request.id, engine: engine, sink: sink) else {
228
+ write(["id": request.id, "error": "busy", "reason": "a recording is already in progress"])
229
+ return
230
+ }
231
+ input.installTap(onBus: 0, bufferSize: 4096, format: inFormat) { buffer, _ in
232
+ sink.append(buffer, to: outFormat)
233
+ }
234
+ do {
235
+ try engine.start()
236
+ } catch {
237
+ activeRecording.clear()
238
+ input.removeTap(onBus: 0)
239
+ write(["id": request.id, "error": "record_failed", "reason": String(describing: error)])
240
+ return
241
+ }
242
+
243
+ let maxMs = max(1000, min(request.maxMs ?? 30_000, 120_000))
244
+ let outcome = await activeRecording.wait(maxMs: maxMs)
245
+ engine.stop()
246
+ input.removeTap(onBus: 0)
247
+
248
+ if outcome.cancelled {
249
+ write(["id": request.id, "cancelled": true])
250
+ return
251
+ }
252
+ if let failure = sink.failure {
253
+ write(["id": request.id, "error": "record_failed", "reason": failure])
254
+ return
255
+ }
256
+ let pcm = sink.pcm
257
+ guard pcm.count >= 2 else {
258
+ write(["id": request.id, "error": "record_failed", "reason": "no audio captured"])
259
+ return
260
+ }
261
+ do {
262
+ try wavData(pcm: pcm, sampleRate: Int(sampleRate)).write(to: URL(fileURLWithPath: outPath))
263
+ } catch {
264
+ write(["id": request.id, "error": "record_failed", "reason": String(describing: error)])
265
+ return
266
+ }
267
+ write([
268
+ "id": request.id,
269
+ "wav": outPath,
270
+ "sampleRate": Int(sampleRate),
271
+ "samples": pcm.count / 2,
272
+ "durationMs": Int(Double(pcm.count / 2) / sampleRate * 1000.0),
273
+ "stopReason": outcome.reason,
274
+ ])
275
+ }
276
+
277
+ private static func ensureMicAuthorization() async -> Bool {
278
+ switch AVCaptureDevice.authorizationStatus(for: .audio) {
279
+ case .authorized: return true
280
+ case .notDetermined:
281
+ return await withCheckedContinuation { continuation in
282
+ let box = ResumeOnce(continuation)
283
+ AVCaptureDevice.requestAccess(for: .audio) { box.resume($0) }
284
+ }
285
+ default: return false
286
+ }
287
+ }
288
+
168
289
  // MARK: - Text to speech
169
290
 
170
291
  /// Speak a reply through the host's audio output. Kept in the same helper
@@ -312,13 +433,92 @@ struct AgentDeckFMHelper {
312
433
  #endif
313
434
  }
314
435
 
436
+ /// stdout is shared by the serial request loop and the detached record
437
+ /// task; the lock keeps two replies from interleaving inside one line.
438
+ private static let writeLock = NSLock()
439
+
315
440
  private static func write(_ object: [String: Any]) {
316
441
  guard JSONSerialization.isValidJSONObject(object),
317
442
  let data = try? JSONSerialization.data(withJSONObject: object),
318
443
  let text = String(data: data, encoding: .utf8) else {
319
444
  return
320
445
  }
446
+ writeLock.lock()
321
447
  FileHandle.standardOutput.write(Data((text + "\n").utf8))
448
+ writeLock.unlock()
449
+ }
450
+ }
451
+
452
+ /// State for the single in-flight microphone capture. One at a time by design:
453
+ /// the daemon models push-to-talk, and two overlapping engine taps on the same
454
+ /// input device produce garbage anyway.
455
+ private final class RecordingBox: @unchecked Sendable {
456
+ struct Outcome { let cancelled: Bool; let reason: String }
457
+
458
+ private let lock = NSLock()
459
+ private var requestId: Int?
460
+ private var engine: AVAudioEngine?
461
+ private var sink: PCMSink?
462
+ private var continuation: ResumeOnce<Outcome>?
463
+ /// Bumped per recording so a stale max-duration timer from a finished
464
+ /// capture cannot terminate the one that started after it.
465
+ private var generation = 0
466
+
467
+ func begin(requestId: Int, engine: AVAudioEngine, sink: PCMSink) -> Bool {
468
+ lock.lock(); defer { lock.unlock() }
469
+ guard self.requestId == nil else { return false }
470
+ self.requestId = requestId
471
+ self.engine = engine
472
+ self.sink = sink
473
+ generation += 1
474
+ return true
475
+ }
476
+
477
+ func wait(maxMs: Int) async -> Outcome {
478
+ let gen = currentGeneration()
479
+ let outcome: Outcome = await withCheckedContinuation { cont in
480
+ arm(ResumeOnce(cont))
481
+ DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(maxMs)) { [weak self] in
482
+ guard let self, self.isLive(generation: gen) else { return }
483
+ _ = self.finish(cancelled: false, reason: "max_duration")
484
+ }
485
+ }
486
+ return outcome
487
+ }
488
+
489
+ private func currentGeneration() -> Int {
490
+ lock.lock(); defer { lock.unlock() }
491
+ return generation
492
+ }
493
+
494
+ private func arm(_ box: ResumeOnce<Outcome>) {
495
+ lock.lock(); defer { lock.unlock() }
496
+ continuation = box
497
+ }
498
+
499
+ private func isLive(generation gen: Int) -> Bool {
500
+ lock.lock(); defer { lock.unlock() }
501
+ return generation == gen && requestId != nil
502
+ }
503
+
504
+ /// Returns false when no recording is active — the stop raced a max-length
505
+ /// finish or arrived with nothing running.
506
+ func finish(cancelled: Bool, reason: String) -> Bool {
507
+ lock.lock()
508
+ let cont = continuation
509
+ let active = requestId != nil
510
+ continuation = nil
511
+ requestId = nil
512
+ engine = nil
513
+ sink = nil
514
+ lock.unlock()
515
+ guard active, let cont else { return false }
516
+ cont.resume(Outcome(cancelled: cancelled, reason: cancelled ? "cancelled" : reason))
517
+ return true
518
+ }
519
+
520
+ func clear() {
521
+ _ = finish(cancelled: true, reason: "cleared")
322
522
  }
323
523
  }
324
524
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentdeck/bridge",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,22 +14,36 @@
14
14
  "assets/fonts",
15
15
  "assets/fm-helper",
16
16
  "fm-helper",
17
- "scripts/build-fm-helper.mjs"
17
+ "scripts/build-fm-helper.mjs",
18
+ "python",
19
+ "src/idotmatrix/*.py",
20
+ "src/timebox/*.py",
21
+ "src/pysync/*.py"
18
22
  ],
19
23
  "main": "./dist/index.js",
20
24
  "bin": {
21
25
  "agentdeck": "./dist/cli.js"
22
26
  },
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "build:fm-helper": "node scripts/build-fm-helper.mjs",
30
+ "prepack": "node scripts/build-fm-helper.mjs",
31
+ "dev": "tsc --watch",
32
+ "clean": "rm -rf dist",
33
+ "typecheck": "tsc --noEmit",
34
+ "start": "node dist/cli.js claude"
35
+ },
23
36
  "dependencies": {
37
+ "@agentdeck/hooks": "workspace:*",
38
+ "@agentdeck/shared": "workspace:*",
24
39
  "@anthropic-ai/sdk": "^0.111.0",
25
40
  "bonjour-service": "^1.3.0",
26
41
  "commander": "^14.0.3",
27
42
  "express": "^5.2.1",
28
43
  "qrcode": "^1.5.4",
44
+ "sharp": "^0.34.5",
29
45
  "strip-ansi": "^7.2.0",
30
- "ws": "^8.20.0",
31
- "@agentdeck/shared": "1.0.3",
32
- "@agentdeck/hooks": "1.0.3"
46
+ "ws": "^8.20.0"
33
47
  },
34
48
  "optionalDependencies": {
35
49
  "@picovoice/porcupine-node": "^4.0.2",
@@ -45,13 +59,5 @@
45
59
  "@types/qrcode": "^1.5.6",
46
60
  "@types/ws": "^8.18.1",
47
61
  "typescript": "^6.0.3"
48
- },
49
- "scripts": {
50
- "build": "tsc",
51
- "build:fm-helper": "node scripts/build-fm-helper.mjs",
52
- "dev": "tsc --watch",
53
- "clean": "rm -rf dist",
54
- "typecheck": "tsc --noEmit",
55
- "start": "node dist/cli.js claude"
56
62
  }
57
- }
63
+ }
@@ -0,0 +1,5 @@
1
+ # Optional runtime for terminal-managed iDotMatrix and Timebox Mini BLE clients.
2
+ # Installed lazily by `agentdeck ble setup`; npm install itself stays PyPI-free.
3
+ bleak>=2.1,<3
4
+ Pillow>=12.0,<13
5
+ idotmatrix==0.0.9
@@ -0,0 +1,40 @@
1
+ import asyncio
2
+ import sys
3
+ import argparse
4
+ from idotmatrix import ConnectionManager
5
+ from idotmatrix.modules.common import Common as IdmCommon
6
+
7
+ async def set_brightness(address: str, brightness: int):
8
+ manager = ConnectionManager()
9
+ idm_common = IdmCommon()
10
+ idm_common.conn = manager
11
+
12
+ try:
13
+ print(f"Connecting to iDotMatrix ({address})...")
14
+ await manager.connectByAddress(address)
15
+ print(f"Setting brightness to {brightness}%...")
16
+ await idm_common.setBrightness(brightness)
17
+ print("Brightness set successfully.")
18
+ except Exception as e:
19
+ print(f"ERROR: Failed to set brightness: {e}")
20
+ sys.exit(1)
21
+ finally:
22
+ try:
23
+ await manager.disconnect()
24
+ except Exception:
25
+ pass
26
+
27
+ def main():
28
+ parser = argparse.ArgumentParser(description="Set iDotMatrix Brightness")
29
+ parser.add_argument("-a", "--address", required=True, help="BLE MAC/UUID Address")
30
+ parser.add_argument("-b", "--brightness", type=int, required=True, help="Brightness percent (5-100)")
31
+ args = parser.parse_args()
32
+
33
+ if args.brightness not in range(5, 101):
34
+ print("ERROR: Brightness must be between 5 and 100 percent.")
35
+ sys.exit(1)
36
+
37
+ asyncio.run(set_brightness(args.address, args.brightness))
38
+
39
+ if __name__ == "__main__":
40
+ main()
@@ -0,0 +1,50 @@
1
+ # GENERATED FILE — DO NOT EDIT.
2
+ # Source of truth: shared/src/idotmatrix-identity.ts
3
+ # Regenerate: pnpm generate-idotmatrix-identity (drift gated by shared/src/__tests__/idotmatrix-identity.test.ts)
4
+ """Brand-independent identity of an iDotMatrix-protocol BLE panel.
5
+
6
+ The same hardware advertises as 'IDM-...' (iDotMatrix) or 'iPixel-...', so a
7
+ scan filters on the advertised service first and only falls back to the name
8
+ families.
9
+ """
10
+
11
+ IDOTMATRIX_SERVICE_UUID = "000000fa-0000-1000-8000-00805f9b34fb"
12
+ IDOTMATRIX_WRITE_CHARACTERISTIC_UUID = "0000fa02-0000-1000-8000-00805f9b34fb"
13
+ IDOTMATRIX_NAME_PREFIXES = ["IDM-", "IPIXEL"]
14
+
15
+ _HEX = set("0123456789abcdef")
16
+
17
+
18
+ def normalize_uuid(value):
19
+ """Expand a 16-bit ('fa02') or 32-bit ('000000fa') UUID to Bluetooth-base form.
20
+
21
+ Short forms are left-padded first: 'fa' and '00fa' are the same service.
22
+ """
23
+ s = (value or "").strip().lower()
24
+ if not s or not set(s) <= _HEX:
25
+ return s
26
+ if len(s) <= 4:
27
+ return "0000" + s.rjust(4, "0") + "-0000-1000-8000-00805f9b34fb"
28
+ if len(s) <= 8:
29
+ return s.rjust(8, "0") + "-0000-1000-8000-00805f9b34fb"
30
+ return s
31
+
32
+
33
+ def matches_name(name, extra_prefixes=()):
34
+ """Whether an advertised local name belongs to a known panel family."""
35
+ n = (name or "").strip().upper()
36
+ if not n:
37
+ return False
38
+ for prefix in list(IDOTMATRIX_NAME_PREFIXES) + list(extra_prefixes or ()):
39
+ p = (prefix or "").strip().upper()
40
+ if p and n.startswith(p):
41
+ return True
42
+ return False
43
+
44
+
45
+ def matches_advertisement(name, service_uuids, extra_prefixes=()):
46
+ """The discovery predicate: service UUID first, name families second."""
47
+ for uuid in service_uuids or ():
48
+ if normalize_uuid(uuid) == IDOTMATRIX_SERVICE_UUID:
49
+ return True
50
+ return matches_name(name, extra_prefixes)