@ai-matrx/desktop-protocol 0.10.0 → 0.12.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.0
4
+
5
+ - `TOOL_CATALOG`: `desktop_transcribe` is built — one op, `models.transcribe`, reached through
6
+ aidream's generic `POST /api/local-proxy/{device}/op/{cap.op}`. matrx-local's `speech_locales`
7
+ needs no op of its own (Whisper detects the language).
8
+
9
+ Consumer action: none.
10
+
11
+ ## 0.11.0
12
+
13
+ - `createDesktopTranscriber(client, options)` (`./client`): speech to text on the person's own
14
+ computer from any connected surface — the app's screen, the web app or the phone through the
15
+ relay. It writes the audio into the device's temp folder (`fs.write`, at most 4 MiB:
16
+ `DESKTOP_TRANSCRIBE_MAX_BYTES`), calls `models.transcribe`, removes the file, and reports that
17
+ call's `models.progress` (install steps, then each segment). Its return is structurally a
18
+ `@ai-matrx/browser-audio` `TranscriptionTransport`, so any voice input can transcribe locally.
19
+
20
+ Consumer action: none.
21
+
3
22
  ## 0.10.0
4
23
 
5
24
  Protocol 1.2 adds the `apps` capability (SPEC §6): the desktop apps a person uses (Claude, Codex,
package/README.md CHANGED
@@ -63,6 +63,16 @@ term.write("ls\n");
63
63
  - Background problems (malformed messages, sequence gaps, heartbeat death, reconnect alarm) go to
64
64
  `onDiagnostic` — loud by default (`console.warn`).
65
65
 
66
+ ## Local transcription
67
+
68
+ ```ts
69
+ import { createDesktopTranscriber } from "@ai-matrx/desktop-protocol/client";
70
+
71
+ const transcriber = createDesktopTranscriber(client, { model: "base", onProgress: (p) => show(p) });
72
+ const { text, segments } = await transcriber.transcribe(recordingBlob); // on the person's computer
73
+ // Or as a voice input's transport: new BrowserAudioKernel({ transport: transcriber })
74
+ ```
75
+
66
76
  ## React
67
77
 
68
78
  ```tsx
package/dist/client.cjs CHANGED
@@ -20,10 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/client.ts
21
21
  var client_exports = {};
22
22
  __export(client_exports, {
23
+ DESKTOP_TRANSCRIBE_MAX_BYTES: () => DESKTOP_TRANSCRIBE_MAX_BYTES,
23
24
  DesktopProtocolError: () => DesktopProtocolError,
24
25
  RECONNECT_ALARM_ATTEMPTS: () => import_realtime.RECONNECT_ALARM_ATTEMPTS,
25
26
  bindDesktopWake: () => bindDesktopWake,
26
27
  createDesktopClient: () => createDesktopClient,
28
+ createDesktopTranscriber: () => createDesktopTranscriber,
27
29
  isDesktopProtocolError: () => isDesktopProtocolError
28
30
  });
29
31
  module.exports = __toCommonJS(client_exports);
@@ -1669,4 +1671,59 @@ function bindDesktopWake(client, options = {}) {
1669
1671
  targets.window.removeEventListener("focus", onWake);
1670
1672
  };
1671
1673
  }
1674
+
1675
+ // src/client/transcribe.ts
1676
+ var DESKTOP_TRANSCRIBE_MAX_BYTES = 4 * 1024 * 1024;
1677
+ var EXT = {
1678
+ "audio/webm": "webm",
1679
+ "audio/ogg": "ogg",
1680
+ "audio/mp4": "m4a",
1681
+ "audio/mpeg": "mp3",
1682
+ "audio/wav": "wav",
1683
+ "audio/x-wav": "wav",
1684
+ "video/webm": "webm",
1685
+ "video/mp4": "mp4"
1686
+ };
1687
+ function base64(bytes) {
1688
+ let binary = "";
1689
+ for (let i = 0; i < bytes.length; i += 32768) binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
1690
+ return btoa(binary);
1691
+ }
1692
+ function createDesktopTranscriber(client, options = {}) {
1693
+ let temp = null;
1694
+ const tempDir = () => temp ??= client.request("sysinfo.get", {}).then((s) => s.paths.temp.replace(/\/$/, ""));
1695
+ return {
1696
+ async transcribe(file, signal) {
1697
+ if (file.size > DESKTOP_TRANSCRIBE_MAX_BYTES) {
1698
+ throw new DesktopProtocolError("PAYLOAD_TOO_LARGE", "Recording is larger than 4 MB", { retryable: false, data: { limit_bytes: DESKTOP_TRANSCRIBE_MAX_BYTES } });
1699
+ }
1700
+ const type = file.type.split(";")[0]?.trim() ?? "";
1701
+ const name = `matrx-transcribe-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.${EXT[type] ?? "webm"}`;
1702
+ const path = `${await tempDir()}/${name}`;
1703
+ const bytes = new Uint8Array(await file.arrayBuffer());
1704
+ await client.request("fs.write", { path, content: base64(bytes), encoding: "base64", mode: 384 }, signal ? { signal } : {});
1705
+ let requestId = "";
1706
+ const off = options.onProgress ? client.on("models.progress", (payload) => {
1707
+ if (payload.request_id === requestId) options.onProgress?.(payload.progress);
1708
+ }) : () => void 0;
1709
+ try {
1710
+ const result = await client.request(
1711
+ "models.transcribe",
1712
+ { path, ...options.model ? { model: options.model } : {}, ...options.language ? { language: options.language } : {} },
1713
+ { timeoutMs: options.timeoutMs ?? 15 * 6e4, onRequestId: (id) => requestId = id, ...signal ? { signal } : {} }
1714
+ );
1715
+ return {
1716
+ text: result.text,
1717
+ language: result.language,
1718
+ duration: result.duration_s,
1719
+ segments: result.segments.map((s) => ({ start: s.start_s, end: s.end_s, text: s.text })),
1720
+ result
1721
+ };
1722
+ } finally {
1723
+ off();
1724
+ void client.request("fs.delete", { path, to_trash: false }).catch(() => void 0);
1725
+ }
1726
+ }
1727
+ };
1728
+ }
1672
1729
  //# sourceMappingURL=client.cjs.map