@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.
- package/assets/fm-helper/agentdeck-fm-helper +0 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +3 -0
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adb-reverse.d.ts.map +1 -1
- package/dist/adb-reverse.js +4 -2
- package/dist/adb-reverse.js.map +1 -1
- package/dist/apme/collector.js +2 -2
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/outcome.js +2 -2
- package/dist/apme/outcome.js.map +1 -1
- package/dist/apme/runner.js +3 -3
- package/dist/apme/runner.js.map +1 -1
- package/dist/ble-sync-spawn.js +1 -1
- package/dist/ble-sync-spawn.js.map +1 -1
- package/dist/calendar.d.ts +82 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +324 -0
- package/dist/calendar.js.map +1 -0
- package/dist/card-feed.d.ts +63 -5
- package/dist/card-feed.d.ts.map +1 -1
- package/dist/card-feed.js +218 -14
- package/dist/card-feed.js.map +1 -1
- package/dist/card-modules.d.ts +4 -0
- package/dist/card-modules.d.ts.map +1 -1
- package/dist/card-modules.js +5 -3
- package/dist/card-modules.js.map +1 -1
- package/dist/check-deps.js +4 -4
- package/dist/check-deps.js.map +1 -1
- package/dist/cli.d.ts +35 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +269 -9
- package/dist/cli.js.map +1 -1
- package/dist/codex-otel.d.ts +89 -0
- package/dist/codex-otel.d.ts.map +1 -0
- package/dist/codex-otel.js +487 -0
- package/dist/codex-otel.js.map +1 -0
- package/dist/codex-rate-limits.d.ts.map +1 -1
- package/dist/codex-rate-limits.js +15 -3
- package/dist/codex-rate-limits.js.map +1 -1
- package/dist/daemon-server.d.ts +4 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +797 -54
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-ws-client.d.ts +108 -24
- package/dist/daemon-ws-client.d.ts.map +1 -1
- package/dist/daemon-ws-client.js +175 -39
- package/dist/daemon-ws-client.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/device-voice-reply.d.ts.map +1 -1
- package/dist/device-voice-reply.js +15 -3
- package/dist/device-voice-reply.js.map +1 -1
- package/dist/device-voice.d.ts +37 -0
- package/dist/device-voice.d.ts.map +1 -1
- package/dist/device-voice.js +130 -1
- package/dist/device-voice.js.map +1 -1
- package/dist/diag-analyzer.d.ts.map +1 -1
- package/dist/diag-analyzer.js +1 -0
- package/dist/diag-analyzer.js.map +1 -1
- package/dist/display-monitor.d.ts.map +1 -1
- package/dist/display-monitor.js +1 -0
- package/dist/display-monitor.js.map +1 -1
- package/dist/esp32-serial.d.ts +4 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +6 -3
- package/dist/esp32-serial.js.map +1 -1
- package/dist/foundation-models-helper.d.ts +23 -0
- package/dist/foundation-models-helper.d.ts.map +1 -1
- package/dist/foundation-models-helper.js +32 -0
- package/dist/foundation-models-helper.js.map +1 -1
- package/dist/gateway-probe.js +1 -1
- package/dist/gateway-probe.js.map +1 -1
- package/dist/glance-frame.d.ts +64 -0
- package/dist/glance-frame.d.ts.map +1 -0
- package/dist/glance-frame.js +364 -0
- package/dist/glance-frame.js.map +1 -0
- package/dist/hook-codex-sessions.d.ts +40 -0
- package/dist/hook-codex-sessions.d.ts.map +1 -0
- package/dist/hook-codex-sessions.js +174 -0
- package/dist/hook-codex-sessions.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-discover.js +7 -4
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-settings.d.ts +15 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-settings.js +21 -0
- package/dist/idotmatrix/idotmatrix-settings.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +88 -7
- package/dist/index.js.map +1 -1
- package/dist/linux-service.d.ts +52 -0
- package/dist/linux-service.d.ts.map +1 -0
- package/dist/linux-service.js +206 -0
- package/dist/linux-service.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -1
- package/dist/mdns-discover.d.ts +19 -0
- package/dist/mdns-discover.d.ts.map +1 -0
- package/dist/mdns-discover.js +109 -0
- package/dist/mdns-discover.js.map +1 -0
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +1 -0
- package/dist/model-catalog.js.map +1 -1
- package/dist/modules/adb-module.js +1 -1
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/output-parser.d.ts +9 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +67 -16
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +57 -8
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +210 -74
- package/dist/passive-observer.js.map +1 -1
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
- package/dist/pixoo/pixoo-bridge.js +47 -6
- package/dist/pixoo/pixoo-bridge.js.map +1 -1
- package/dist/pixoo/pixoo-renderer.d.ts +2 -1
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +137 -8
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pocket-autonomy.d.ts +78 -0
- package/dist/pocket-autonomy.d.ts.map +1 -0
- package/dist/pocket-autonomy.js +529 -0
- package/dist/pocket-autonomy.js.map +1 -0
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +3 -2
- package/dist/pty-manager.js.map +1 -1
- package/dist/python-ble-runtime.d.ts +2 -0
- package/dist/python-ble-runtime.d.ts.map +1 -1
- package/dist/python-ble-runtime.js +2 -1
- package/dist/python-ble-runtime.js.map +1 -1
- package/dist/remote-sessions.d.ts +64 -0
- package/dist/remote-sessions.d.ts.map +1 -0
- package/dist/remote-sessions.js +81 -0
- package/dist/remote-sessions.js.map +1 -0
- package/dist/review-runner.js +2 -2
- package/dist/review-runner.js.map +1 -1
- package/dist/session-aggregator.d.ts +2 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +6 -1
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-focus-relay.d.ts +47 -1
- package/dist/session-focus-relay.d.ts.map +1 -1
- package/dist/session-focus-relay.js +130 -15
- package/dist/session-focus-relay.js.map +1 -1
- package/dist/session-push-channel.d.ts +31 -0
- package/dist/session-push-channel.d.ts.map +1 -0
- package/dist/session-push-channel.js +94 -0
- package/dist/session-push-channel.js.map +1 -0
- package/dist/session-registry.d.ts +126 -0
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +180 -3
- package/dist/session-registry.js.map +1 -1
- package/dist/subagent-timeline.d.ts +34 -0
- package/dist/subagent-timeline.d.ts.map +1 -0
- package/dist/subagent-timeline.js +156 -0
- package/dist/subagent-timeline.js.map +1 -0
- package/dist/timebox/timebox-discover.js +1 -1
- package/dist/timebox/timebox-discover.js.map +1 -1
- package/dist/tui/gauge.d.ts +4 -2
- package/dist/tui/gauge.d.ts.map +1 -1
- package/dist/tui/gauge.js +8 -4
- package/dist/tui/gauge.js.map +1 -1
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +28 -0
- package/dist/tui/renderer.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts +43 -0
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +114 -9
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +19 -0
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +35 -0
- package/dist/usage-event.js.map +1 -1
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +1 -0
- package/dist/utils/project-name.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +3 -2
- package/dist/version-check.js.map +1 -1
- package/dist/voice-assistant.d.ts.map +1 -1
- package/dist/voice-assistant.js +2 -1
- package/dist/voice-assistant.js.map +1 -1
- package/dist/voice.d.ts.map +1 -1
- package/dist/voice.js +1 -0
- package/dist/voice.js.map +1 -1
- package/dist/weather.d.ts +72 -0
- package/dist/weather.d.ts.map +1 -0
- package/dist/weather.js +206 -0
- package/dist/weather.js.map +1 -0
- package/dist/windows-service.js +5 -5
- package/dist/windows-service.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +200 -0
- package/package.json +15 -13
- package/src/idotmatrix/identity_generated.py +50 -0
- package/src/idotmatrix/scan.py +33 -13
package/dist/windows-service.js
CHANGED
|
@@ -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;
|
|
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
|
+
"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)
|
package/src/idotmatrix/scan.py
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
12
|
-
name =
|
|
13
|
-
|
|
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":
|
|
18
|
-
"is_idotmatrix":
|
|
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()
|