@agentdeck/bridge 1.0.4 → 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 (202) 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 +269 -9
  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-discover.d.ts.map +1 -1
  82. package/dist/idotmatrix/idotmatrix-discover.js +7 -4
  83. package/dist/idotmatrix/idotmatrix-discover.js.map +1 -1
  84. package/dist/idotmatrix/idotmatrix-settings.d.ts +15 -0
  85. package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -1
  86. package/dist/idotmatrix/idotmatrix-settings.js +21 -0
  87. package/dist/idotmatrix/idotmatrix-settings.js.map +1 -1
  88. package/dist/index.d.ts +7 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +88 -7
  91. package/dist/index.js.map +1 -1
  92. package/dist/linux-service.d.ts +52 -0
  93. package/dist/linux-service.d.ts.map +1 -0
  94. package/dist/linux-service.js +206 -0
  95. package/dist/linux-service.js.map +1 -0
  96. package/dist/logger.d.ts +6 -0
  97. package/dist/logger.d.ts.map +1 -1
  98. package/dist/logger.js +15 -0
  99. package/dist/logger.js.map +1 -1
  100. package/dist/mdns-discover.d.ts +19 -0
  101. package/dist/mdns-discover.d.ts.map +1 -0
  102. package/dist/mdns-discover.js +109 -0
  103. package/dist/mdns-discover.js.map +1 -0
  104. package/dist/model-catalog.d.ts.map +1 -1
  105. package/dist/model-catalog.js +1 -0
  106. package/dist/model-catalog.js.map +1 -1
  107. package/dist/modules/adb-module.js +1 -1
  108. package/dist/modules/adb-module.js.map +1 -1
  109. package/dist/output-parser.d.ts +9 -0
  110. package/dist/output-parser.d.ts.map +1 -1
  111. package/dist/output-parser.js +67 -16
  112. package/dist/output-parser.js.map +1 -1
  113. package/dist/passive-observer.d.ts +57 -8
  114. package/dist/passive-observer.d.ts.map +1 -1
  115. package/dist/passive-observer.js +210 -74
  116. package/dist/passive-observer.js.map +1 -1
  117. package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
  118. package/dist/pixoo/pixoo-bridge.js +47 -6
  119. package/dist/pixoo/pixoo-bridge.js.map +1 -1
  120. package/dist/pixoo/pixoo-renderer.d.ts +2 -1
  121. package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
  122. package/dist/pixoo/pixoo-renderer.js +137 -8
  123. package/dist/pixoo/pixoo-renderer.js.map +1 -1
  124. package/dist/pocket-autonomy.d.ts +78 -0
  125. package/dist/pocket-autonomy.d.ts.map +1 -0
  126. package/dist/pocket-autonomy.js +529 -0
  127. package/dist/pocket-autonomy.js.map +1 -0
  128. package/dist/pty-manager.d.ts.map +1 -1
  129. package/dist/pty-manager.js +3 -2
  130. package/dist/pty-manager.js.map +1 -1
  131. package/dist/python-ble-runtime.d.ts +2 -0
  132. package/dist/python-ble-runtime.d.ts.map +1 -1
  133. package/dist/python-ble-runtime.js +2 -1
  134. package/dist/python-ble-runtime.js.map +1 -1
  135. package/dist/remote-sessions.d.ts +64 -0
  136. package/dist/remote-sessions.d.ts.map +1 -0
  137. package/dist/remote-sessions.js +81 -0
  138. package/dist/remote-sessions.js.map +1 -0
  139. package/dist/review-runner.js +2 -2
  140. package/dist/review-runner.js.map +1 -1
  141. package/dist/session-aggregator.d.ts +2 -0
  142. package/dist/session-aggregator.d.ts.map +1 -1
  143. package/dist/session-aggregator.js +6 -1
  144. package/dist/session-aggregator.js.map +1 -1
  145. package/dist/session-focus-relay.d.ts +47 -1
  146. package/dist/session-focus-relay.d.ts.map +1 -1
  147. package/dist/session-focus-relay.js +130 -15
  148. package/dist/session-focus-relay.js.map +1 -1
  149. package/dist/session-push-channel.d.ts +31 -0
  150. package/dist/session-push-channel.d.ts.map +1 -0
  151. package/dist/session-push-channel.js +94 -0
  152. package/dist/session-push-channel.js.map +1 -0
  153. package/dist/session-registry.d.ts +126 -0
  154. package/dist/session-registry.d.ts.map +1 -1
  155. package/dist/session-registry.js +180 -3
  156. package/dist/session-registry.js.map +1 -1
  157. package/dist/subagent-timeline.d.ts +34 -0
  158. package/dist/subagent-timeline.d.ts.map +1 -0
  159. package/dist/subagent-timeline.js +156 -0
  160. package/dist/subagent-timeline.js.map +1 -0
  161. package/dist/timebox/timebox-discover.js +1 -1
  162. package/dist/timebox/timebox-discover.js.map +1 -1
  163. package/dist/tui/gauge.d.ts +4 -2
  164. package/dist/tui/gauge.d.ts.map +1 -1
  165. package/dist/tui/gauge.js +8 -4
  166. package/dist/tui/gauge.js.map +1 -1
  167. package/dist/tui/renderer.d.ts.map +1 -1
  168. package/dist/tui/renderer.js +28 -0
  169. package/dist/tui/renderer.js.map +1 -1
  170. package/dist/types.d.ts +1 -1
  171. package/dist/types.d.ts.map +1 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/usage-api.d.ts +43 -0
  174. package/dist/usage-api.d.ts.map +1 -1
  175. package/dist/usage-api.js +114 -9
  176. package/dist/usage-api.js.map +1 -1
  177. package/dist/usage-event.d.ts +19 -0
  178. package/dist/usage-event.d.ts.map +1 -1
  179. package/dist/usage-event.js +35 -0
  180. package/dist/usage-event.js.map +1 -1
  181. package/dist/utils/project-name.d.ts.map +1 -1
  182. package/dist/utils/project-name.js +1 -0
  183. package/dist/utils/project-name.js.map +1 -1
  184. package/dist/version-check.d.ts.map +1 -1
  185. package/dist/version-check.js +3 -2
  186. package/dist/version-check.js.map +1 -1
  187. package/dist/voice-assistant.d.ts.map +1 -1
  188. package/dist/voice-assistant.js +2 -1
  189. package/dist/voice-assistant.js.map +1 -1
  190. package/dist/voice.d.ts.map +1 -1
  191. package/dist/voice.js +1 -0
  192. package/dist/voice.js.map +1 -1
  193. package/dist/weather.d.ts +72 -0
  194. package/dist/weather.d.ts.map +1 -0
  195. package/dist/weather.js +206 -0
  196. package/dist/weather.js.map +1 -0
  197. package/dist/windows-service.js +5 -5
  198. package/dist/windows-service.js.map +1 -1
  199. package/fm-helper/AgentDeckFMHelper.swift +200 -0
  200. package/package.json +15 -13
  201. package/src/idotmatrix/identity_generated.py +50 -0
  202. package/src/idotmatrix/scan.py +33 -13
@@ -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.4",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,16 +24,26 @@
24
24
  "bin": {
25
25
  "agentdeck": "./dist/cli.js"
26
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
+ },
27
36
  "dependencies": {
37
+ "@agentdeck/hooks": "workspace:*",
38
+ "@agentdeck/shared": "workspace:*",
28
39
  "@anthropic-ai/sdk": "^0.111.0",
29
40
  "bonjour-service": "^1.3.0",
30
41
  "commander": "^14.0.3",
31
42
  "express": "^5.2.1",
32
43
  "qrcode": "^1.5.4",
44
+ "sharp": "^0.34.5",
33
45
  "strip-ansi": "^7.2.0",
34
- "ws": "^8.20.0",
35
- "@agentdeck/hooks": "1.0.4",
36
- "@agentdeck/shared": "1.0.4"
46
+ "ws": "^8.20.0"
37
47
  },
38
48
  "optionalDependencies": {
39
49
  "@picovoice/porcupine-node": "^4.0.2",
@@ -49,13 +59,5 @@
49
59
  "@types/qrcode": "^1.5.6",
50
60
  "@types/ws": "^8.18.1",
51
61
  "typescript": "^6.0.3"
52
- },
53
- "scripts": {
54
- "build": "tsc",
55
- "build:fm-helper": "node scripts/build-fm-helper.mjs",
56
- "dev": "tsc --watch",
57
- "clean": "rm -rf dist",
58
- "typecheck": "tsc --noEmit",
59
- "start": "node dist/cli.js claude"
60
62
  }
61
- }
63
+ }
@@ -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)
@@ -1,35 +1,55 @@
1
+ """BLE scan for iDotMatrix-protocol panels (terminal-managed daemon + CLI).
2
+
3
+ Reports every peripheral it sees, flagging the ones that speak the iDotMatrix
4
+ protocol. Identification is brand-independent — advertised service UUID first,
5
+ known name families second — because the same hardware ships as 'IDM-...'
6
+ (iDotMatrix) and 'iPixel-...'. The rule lives in identity_generated.py, mirrored
7
+ from shared/src/idotmatrix-identity.ts; the Swift CoreBluetooth scanner in the
8
+ App Store daemon applies the identical predicate.
9
+
10
+ Extra name prefixes may be passed as argv (the daemon forwards
11
+ `idotmatrixNamePrefixes` from settings.json) so a further rebrand is a config
12
+ change rather than a release.
13
+ """
14
+
1
15
  import asyncio
2
16
  import json
3
17
  import sys
4
18
  from bleak import BleakScanner
5
19
 
6
- async def scan():
7
- # Scan for 5 seconds
8
- devices = await BleakScanner.discover(timeout=5.0)
9
-
20
+ from identity_generated import matches_advertisement
21
+
22
+
23
+ async def scan(extra_prefixes):
24
+ # Scan for 5 seconds. return_adv gives the advertisement payload, which is
25
+ # where the service UUIDs live — peripheral.name alone cannot identify a
26
+ # panel that advertises under an unknown brand name.
27
+ discovered = await BleakScanner.discover(timeout=5.0, return_adv=True)
28
+
10
29
  results = []
11
- for d in devices:
12
- name = d.name if d.name else "Unknown"
13
- # Gather all devices, but flag IDM devices
14
- is_idm = name.startswith("IDM-")
30
+ for device, adv in discovered.values():
31
+ name = adv.local_name or device.name or "Unknown"
32
+ service_uuids = list(adv.service_uuids or ())
15
33
  results.append({
16
34
  "name": name,
17
- "address": d.address,
18
- "is_idotmatrix": is_idm
35
+ "address": device.address,
36
+ "is_idotmatrix": matches_advertisement(name, service_uuids, extra_prefixes),
19
37
  })
20
-
38
+
21
39
  # Sort so that iDotMatrix devices are on top
22
40
  results.sort(key=lambda x: x["is_idotmatrix"], reverse=True)
23
-
41
+
24
42
  # Print as JSON so the calling Node.js CLI can easily parse it
25
43
  print(json.dumps(results))
26
44
 
45
+
27
46
  def main():
28
47
  try:
29
- asyncio.run(scan())
48
+ asyncio.run(scan(sys.argv[1:]))
30
49
  except Exception as e:
31
50
  print(json.dumps({"error": str(e)}))
32
51
  sys.exit(1)
33
52
 
53
+
34
54
  if __name__ == "__main__":
35
55
  main()