@swttch/extend-kit 0.9.0 → 0.10.1

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/README.ko.md CHANGED
@@ -8,7 +8,7 @@
8
8
  | --- | --- |
9
9
  | **battery** | *사용자 본인의* 머신에 저장된 Claude Code 로그인 정보(keychain 또는 자격증명 파일)를 읽어야 합니다 |
10
10
  | **stt** | 받아쓰기는 OAuth 토큰을 전송하는데, JetBrains 플러그인은 자격증명을 다룰 수 없습니다 |
11
- | **swttch-ext** | 저장된 로그인으로 내 계정에 대해 호출합니다(아티팩트: 목록, 이름 변경, 고정, 삭제) |
11
+ | **swttch-ext** | 저장된 로그인으로 내 계정에 대해 호출합니다(아티팩트: 목록, 이름 변경, 고정, 삭제. `tts`: 글을 소리로 읽기) |
12
12
 
13
13
  ## battery
14
14
 
@@ -56,6 +56,16 @@ swttch-ext api frame delete --slug 3627498c-4893-4ce8-bfaf-524f7b041c1d --yes
56
56
  - **`<title>`이 없는 페이지는 이름을 바꿀 수 없습니다**(`rejected`). 페이지에 `<title>`을 넣고 다시 게시한 뒤에 바꾸세요.
57
57
  - 이름 변경·고정·삭제는 내가 소유한 아티팩트에만 됩니다. 남의 것은 `not_found`로 답합니다.
58
58
 
59
+ ### 글을 소리로 읽기: `swttch-ext tts`
60
+
61
+ 표준 입력으로 받은 글을 Claude 데스크탑 앱의 "소리 내어 읽기"와 같은 음성 서비스로 읽고, 소리를 원시 PCM(16비트 리틀엔디언, 16kHz, 모노)으로 표준 출력에 내보냅니다. API 키가 아니라 Claude 계정 로그인이 필요합니다. 엔드포인트는 공식 문서에 없습니다.
62
+
63
+ ```bash
64
+ echo "터미널에서 읽어 줍니다." | swttch-ext tts --language ko | play -t raw -r 16000 -e signed -b 16 -c 1 -
65
+ ```
66
+
67
+ 옵션: `--language <코드>`(`ko` 같은 힌트), `--speed <0.7~1.5>`, `--voice <이름>`. 따로 정하지 않은 것은 Claude 계정의 음성 설정(데스크탑 앱에서 고른 목소리·속도·언어. `/api/oauth/account/settings`에서 읽고, 아무것도 다시 쓰지 않습니다)을 따르므로, 데스크탑에서 고른 목소리가 여기서도 들립니다. `ko-balgeun`처럼 한 언어용 계정 목소리는 다른 언어의 글에서는 쓰지 않습니다. 다 읽으면 0으로, 실패하면 1로 끝나며 표준 오류에 한 줄 JSON(`{"ok":false,"error":{"code","message","fatal"}}`)을 씁니다. 프로세스를 끄면 읽기가 멈춥니다.
68
+
59
69
  ### 이 버전이 할 수 있는 일 알아보기
60
70
 
61
71
  ```bash
package/README.md CHANGED
@@ -8,7 +8,7 @@ The companion tools [Swttch](https://github.com/Swttch/swttch) needs but cannot
8
8
  | --- | --- |
9
9
  | **battery** | Reads the Claude Code login stored on *your* machine (keychain, or a credentials file) |
10
10
  | **stt** | Dictation sends your OAuth token, and a JetBrains plugin may not handle credentials |
11
- | **swttch-ext** | Acts on your account with the saved login (artifacts: list, rename, pin, delete) |
11
+ | **swttch-ext** | Acts on your account with the saved login (artifacts: list, rename, pin, delete; `tts`: read text aloud) |
12
12
 
13
13
  ## battery
14
14
 
@@ -56,6 +56,16 @@ swttch-ext api frame delete --slug 3627498c-4893-4ce8-bfaf-524f7b041c1d --yes
56
56
  - **A page with no `<title>` cannot be renamed** (`rejected`): add a `<title>` to the page and publish it again first.
57
57
  - Only artifacts you own can be renamed, pinned or deleted; for anyone else's the answer is `not_found`.
58
58
 
59
+ ### Reading text aloud: `swttch-ext tts`
60
+
61
+ Reads the text on stdin aloud with the same voice service the Claude desktop app's "Read aloud" uses, and writes the audio to stdout as raw PCM (16-bit little-endian, 16 kHz, mono). It needs a Claude account login, not an API key. The endpoint is undocumented.
62
+
63
+ ```bash
64
+ echo "Hello from the terminal." | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
65
+ ```
66
+
67
+ Options: `--language <code>` (a hint such as `ko`), `--speed <0.7-1.5>`, `--voice <name>`. What you do not name comes from your Claude account's own voice settings (the voice, speed and language chosen in the Claude desktop app, read from `/api/oauth/account/settings`; nothing is written back), so a voice picked there is the voice heard here. An account voice made for one language (such as `ko-balgeun`) is left out for text in another. It exits 0 when everything was read and 1 on failure, with one JSON line on stderr: `{"ok":false,"error":{"code","message","fatal"}}`. Killing the process stops the reading.
68
+
59
69
  ### Finding out what this version can do
60
70
 
61
71
  ```bash
package/dist/ext/index.js CHANGED
@@ -5,6 +5,7 @@ import { dirname, resolve } from 'node:path';
5
5
  import { applySettingsEnv } from '../settings-env.js';
6
6
  import { OPERATIONS, operationCapabilities } from './operations.js';
7
7
  import { describeError, runOperation } from './run.js';
8
+ import { runTextToSpeech, TTS_CAPABILITY } from './tts.js';
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
10
  const VERSION = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'utf-8')).version;
10
11
  const args = process.argv.slice(2);
@@ -65,6 +66,8 @@ Usage: swttch-ext api <service> <call> [--param value ...] [--yes] [--json]
65
66
 
66
67
  swttch-ext api --list List the calls this version can make
67
68
  swttch-ext --capabilities List what this version can do, as JSON
69
+ swttch-ext tts [--language ko] [--speed 1.0] [--voice <name>]
70
+ Read the text on stdin aloud: raw audio on stdout
68
71
  swttch-ext api frame retitle --slug <id> --title "New name"
69
72
  swttch-ext api frame pin --slug <id>
70
73
  swttch-ext api frame delete --slug <id> --yes
@@ -78,7 +81,7 @@ async function main() {
78
81
  return 0;
79
82
  }
80
83
  if (flags.has('capabilities')) {
81
- console.log(JSON.stringify({ capabilities: operationCapabilities() }));
84
+ console.log(JSON.stringify({ capabilities: [...operationCapabilities(), TTS_CAPABILITY] }));
82
85
  return 0;
83
86
  }
84
87
  if (flags.has('help') || positionals.length === 0) {
@@ -86,6 +89,12 @@ async function main() {
86
89
  return 0;
87
90
  }
88
91
  const [command, service, call] = positionals;
92
+ if (command === 'tts') {
93
+ for (const [name, value] of env)
94
+ process.env[name] = value;
95
+ await applySettingsEnv(process.cwd(), new Set(env.map(([name]) => name)));
96
+ return runTextToSpeech({ language: values.language, speed: values.speed, voice: values.voice });
97
+ }
89
98
  if (command !== 'api') {
90
99
  console.error(`Unknown command: ${command}\n\n${HELP}`);
91
100
  return 1;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ext/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ext/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** The name `--capabilities` lists for this command, so the plugin can tell a kit that has it from one that does not. */
2
+ export declare const TTS_CAPABILITY = "tts";
3
+ /**
4
+ * `swttch-ext tts` — reading text aloud as a process, not as a library.
5
+ *
6
+ * ── stdin ── the text to read, UTF-8, until it closes.
7
+ * ── stdout ── raw audio and nothing else: 16-bit little-endian PCM, 16 kHz, mono, as it arrives.
8
+ * ── stderr ── on failure, one JSON line: {"ok":false,"error":{"code","message","fatal"}}.
9
+ * ── exit ─── 0 when the service said everything, 1 when it failed. Killing the process stops the reading.
10
+ *
11
+ * What the caller does not name comes from the Claude account's own voice settings (the voice, speed and language
12
+ * chosen in the desktop app), so a voice picked there is the voice heard here.
13
+ *
14
+ * A terminal user can drive it by hand (macOS):
15
+ *
16
+ * echo "Hello" | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
17
+ *
18
+ * The plugin cannot ask the service itself: that sends the account's OAuth token, and a JetBrains plugin may not
19
+ * handle credentials. It runs this command and relays the audio.
20
+ */
21
+ export declare function runTextToSpeech(options: {
22
+ language?: string;
23
+ speed?: string;
24
+ voice?: string;
25
+ }): Promise<number>;
@@ -0,0 +1,52 @@
1
+ import { openTextToSpeechStream, readAccountVoiceSettings, chooseVoiceOptions } from '../tts/index.js';
2
+ /** The name `--capabilities` lists for this command, so the plugin can tell a kit that has it from one that does not. */
3
+ export const TTS_CAPABILITY = 'tts';
4
+ /**
5
+ * `swttch-ext tts` — reading text aloud as a process, not as a library.
6
+ *
7
+ * ── stdin ── the text to read, UTF-8, until it closes.
8
+ * ── stdout ── raw audio and nothing else: 16-bit little-endian PCM, 16 kHz, mono, as it arrives.
9
+ * ── stderr ── on failure, one JSON line: {"ok":false,"error":{"code","message","fatal"}}.
10
+ * ── exit ─── 0 when the service said everything, 1 when it failed. Killing the process stops the reading.
11
+ *
12
+ * What the caller does not name comes from the Claude account's own voice settings (the voice, speed and language
13
+ * chosen in the desktop app), so a voice picked there is the voice heard here.
14
+ *
15
+ * A terminal user can drive it by hand (macOS):
16
+ *
17
+ * echo "Hello" | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
18
+ *
19
+ * The plugin cannot ask the service itself: that sends the account's OAuth token, and a JetBrains plugin may not
20
+ * handle credentials. It runs this command and relays the audio.
21
+ */
22
+ export async function runTextToSpeech(options) {
23
+ const chunks = [];
24
+ for await (const chunk of process.stdin)
25
+ chunks.push(Buffer.from(chunk));
26
+ const text = Buffer.concat(chunks).toString('utf8');
27
+ const askedSpeed = options.speed === undefined || options.speed === '' ? undefined : Number(options.speed);
28
+ const chosen = chooseVoiceOptions({ voice: options.voice || undefined, speed: askedSpeed, language: options.language || undefined }, await readAccountVoiceSettings());
29
+ return new Promise((resolve) => {
30
+ const stopOnPipeClosed = () => {
31
+ // The reader went away (the plugin stopped listening): there is nobody to read to.
32
+ stream?.then((s) => s.cancel()).finally(() => resolve(0));
33
+ };
34
+ process.stdout.on('error', stopOnPipeClosed);
35
+ const stream = openTextToSpeechStream(text, {
36
+ onAudio: (audio) => {
37
+ process.stdout.write(audio);
38
+ },
39
+ onDone: () => resolve(0),
40
+ onError: (message, info) => {
41
+ process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'tts_failed', message, fatal: info?.fatal ?? false } })}\n`);
42
+ resolve(1);
43
+ },
44
+ }, chosen).catch((err) => {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'not_logged_in', message, fatal: true } })}\n`);
47
+ resolve(1);
48
+ return undefined;
49
+ });
50
+ });
51
+ }
52
+ //# sourceMappingURL=tts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/ext/tts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,yHAAyH;AACzH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAA8D;IAClG,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEpD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3G,MAAM,MAAM,GAAG,kBAAkB,CAC/B,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS,EAAE,EACjG,MAAM,wBAAwB,EAAE,CACjC,CAAC;IAEF,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,gBAAgB,GAAG,GAAS,EAAE;YAClC,mFAAmF;YACnF,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QAE7C,MAAM,MAAM,GAAG,sBAAsB,CACnC,IAAI,EACJ;YACE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;YACD,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YACxB,OAAO,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;gBACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;gBAChI,OAAO,CAAC,CAAC,CAAC,CAAC;YACb,CAAC;SACF,EACD,MAAM,CACP,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACvB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YACnH,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,OAAO,SAAkB,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,39 @@
1
+ /** What the Claude account says about how replies are read aloud (the voice settings of the desktop app and claude.ai). */
2
+ export interface AccountVoiceSettings {
3
+ /** A voice id such as "buttery" or "ko-balgeun". */
4
+ voice?: string;
5
+ /** Playback speed. */
6
+ speed?: number;
7
+ /** The language the account reads in, such as "ko". */
8
+ language?: string;
9
+ }
10
+ /**
11
+ * The voice, speed and language chosen in the Claude desktop app, as the account stores them.
12
+ *
13
+ * Those three are account settings, not per-device ones: choosing a "Bright" Korean voice in the desktop app writes
14
+ * `voice_preference` to the account, so reading the account gives the same voice here without a second setting to
15
+ * keep in step. Nothing is written back.
16
+ *
17
+ * Answers an empty object when the account cannot be read for any reason: reading aloud then uses the service's own
18
+ * defaults rather than failing because a preference was out of reach.
19
+ */
20
+ export declare function readAccountVoiceSettings(token?: string): Promise<AccountVoiceSettings>;
21
+ /** The three voice fields of an account settings document, each only when it is of the right kind. */
22
+ export declare function pickVoiceSettings(settings: Record<string, unknown>): AccountVoiceSettings;
23
+ /**
24
+ * What to ask the service for, given what the caller said and what the account chose.
25
+ *
26
+ * What the caller named wins. Otherwise the account's speed applies. The account's voice applies only when it can
27
+ * read the text: a voice made for one language (`ko-balgeun`) is left out for text in another, since the service
28
+ * would read that text in a voice that cannot pronounce it. The language hint is the caller's detection of the
29
+ * text first, and the account's language only when the caller could not tell.
30
+ */
31
+ export declare function chooseVoiceOptions(asked: {
32
+ voice?: string;
33
+ speed?: number;
34
+ language?: string;
35
+ }, account: AccountVoiceSettings): {
36
+ voice?: string;
37
+ speed?: number;
38
+ language?: string;
39
+ };
@@ -0,0 +1,60 @@
1
+ import { getCredentials, getAccessToken } from '../battery/auth/index.js';
2
+ import { getJson } from '../battery/api/http.js';
3
+ import { SPEECH_SPEED_RANGE } from './stream.js';
4
+ const ACCOUNT_SETTINGS_URL = 'https://api.anthropic.com/api/oauth/account/settings';
5
+ /**
6
+ * The voice, speed and language chosen in the Claude desktop app, as the account stores them.
7
+ *
8
+ * Those three are account settings, not per-device ones: choosing a "Bright" Korean voice in the desktop app writes
9
+ * `voice_preference` to the account, so reading the account gives the same voice here without a second setting to
10
+ * keep in step. Nothing is written back.
11
+ *
12
+ * Answers an empty object when the account cannot be read for any reason: reading aloud then uses the service's own
13
+ * defaults rather than failing because a preference was out of reach.
14
+ */
15
+ export async function readAccountVoiceSettings(token) {
16
+ try {
17
+ const accessToken = token ?? getAccessToken(await getCredentials());
18
+ const response = await getJson(ACCOUNT_SETTINGS_URL, { Authorization: `Bearer ${accessToken}`, 'anthropic-beta': 'oauth-2025-04-20', 'x-app': 'swttch' }, { deadlineMs: 8000 });
19
+ if (!response.ok || typeof response.body !== 'object' || response.body === null)
20
+ return {};
21
+ return pickVoiceSettings(response.body);
22
+ }
23
+ catch {
24
+ return {};
25
+ }
26
+ }
27
+ /** The three voice fields of an account settings document, each only when it is of the right kind. */
28
+ export function pickVoiceSettings(settings) {
29
+ const voice = typeof settings.voice_preference === 'string' && settings.voice_preference.trim() !== '' ? settings.voice_preference.trim() : undefined;
30
+ const speed = typeof settings.voice_speed === 'number' && settings.voice_speed >= SPEECH_SPEED_RANGE.min && settings.voice_speed <= SPEECH_SPEED_RANGE.max
31
+ ? settings.voice_speed
32
+ : undefined;
33
+ const language = typeof settings.voice_language_code === 'string' && settings.voice_language_code.trim() !== '' ? settings.voice_language_code.trim() : undefined;
34
+ return { voice, speed, language };
35
+ }
36
+ /** "ko-balgeun" is read for Korean text; "buttery", which names no language, for any. */
37
+ function voiceLanguage(voice) {
38
+ const at = voice.indexOf('-');
39
+ return at > 0 ? voice.slice(0, at).toLowerCase() : undefined;
40
+ }
41
+ /**
42
+ * What to ask the service for, given what the caller said and what the account chose.
43
+ *
44
+ * What the caller named wins. Otherwise the account's speed applies. The account's voice applies only when it can
45
+ * read the text: a voice made for one language (`ko-balgeun`) is left out for text in another, since the service
46
+ * would read that text in a voice that cannot pronounce it. The language hint is the caller's detection of the
47
+ * text first, and the account's language only when the caller could not tell.
48
+ */
49
+ export function chooseVoiceOptions(asked, account) {
50
+ const language = asked.language ?? account.language;
51
+ const textLanguage = (asked.language ?? account.language)?.split('-')[0].toLowerCase();
52
+ let voice = asked.voice;
53
+ if (voice === undefined && account.voice !== undefined) {
54
+ const needs = voiceLanguage(account.voice);
55
+ if (needs === undefined || textLanguage === undefined || needs === textLanguage)
56
+ voice = account.voice;
57
+ }
58
+ return { voice, speed: asked.speed ?? account.speed, language };
59
+ }
60
+ //# sourceMappingURL=accountVoice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accountVoice.js","sourceRoot":"","sources":["../../src/tts/accountVoice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Text-to-speech: reading a reply aloud with the service the Claude desktop app uses.
3
+ *
4
+ * Outside the plugin bundle for the same reason as dictation: it sends the account's OAuth token.
5
+ */
6
+ export { openTextToSpeechStream, isTextToSpeechAvailable, splitForSpeech, SPEECH_AUDIO_FORMAT, SPEECH_SPEED_RANGE } from './stream.js';
7
+ export type { TextToSpeechOptions, TextToSpeechHandlers, TextToSpeechStream } from './stream.js';
8
+ export { readAccountVoiceSettings, chooseVoiceOptions, pickVoiceSettings } from './accountVoice.js';
9
+ export type { AccountVoiceSettings } from './accountVoice.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Text-to-speech: reading a reply aloud with the service the Claude desktop app uses.
3
+ *
4
+ * Outside the plugin bundle for the same reason as dictation: it sends the account's OAuth token.
5
+ */
6
+ export { openTextToSpeechStream, isTextToSpeechAvailable, splitForSpeech, SPEECH_AUDIO_FORMAT, SPEECH_SPEED_RANGE } from './stream.js';
7
+ export { readAccountVoiceSettings, chooseVoiceOptions, pickVoiceSettings } from './accountVoice.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tts/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvI,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /** What the service sends back: 16-bit little-endian PCM, 16 kHz, mono. */
2
+ export declare const SPEECH_AUDIO_FORMAT: {
3
+ readonly encoding: "pcm_16000";
4
+ readonly sampleRate: 16000;
5
+ readonly channels: 1;
6
+ };
7
+ /** The speed range the service accepts, as the official clients clamp it. */
8
+ export declare const SPEECH_SPEED_RANGE: {
9
+ readonly min: 0.7;
10
+ readonly max: 1.5;
11
+ };
12
+ export interface TextToSpeechOptions {
13
+ /** A voice name the service knows; the service's own default when omitted. */
14
+ voice?: string;
15
+ /** Playback speed, kept within {@link SPEECH_SPEED_RANGE}. */
16
+ speed?: number;
17
+ /** A BCP-47 language hint such as "ko". */
18
+ language?: string;
19
+ }
20
+ export interface TextToSpeechHandlers {
21
+ /** A piece of audio, in {@link SPEECH_AUDIO_FORMAT}. Arrives in order. */
22
+ onAudio: (chunk: Buffer) => void;
23
+ /** The service said everything it was given. Fired once, after the last audio. */
24
+ onDone: () => void;
25
+ /** The reading failed. `fatal` means retrying will not help (the account is refused). */
26
+ onError: (message: string, info?: {
27
+ fatal?: boolean;
28
+ }) => void;
29
+ }
30
+ export interface TextToSpeechStream {
31
+ /** Stop now. No handler fires after this. */
32
+ cancel: () => void;
33
+ }
34
+ /**
35
+ * Cut `text` into pieces of at most {@link MAX_CHUNK_CHARS}, at sentence ends and blank lines where it can.
36
+ * The service reads each piece as it arrives, so the first audio comes before the last piece is sent.
37
+ */
38
+ export declare function splitForSpeech(text: string): string[];
39
+ /**
40
+ * Read `text` aloud by streaming it to the same service the Claude desktop app's "Read aloud" uses.
41
+ *
42
+ * Lives in this package, outside the plugin bundle, for the reason dictation does: asking for speech sends the
43
+ * account's OAuth token, and a JetBrains plugin may not handle credentials. The plugin runs `swttch-ext tts` and
44
+ * only ever sees audio coming back.
45
+ *
46
+ * The endpoint is undocumented. It is called the way the desktop app calls it, and every message is treated as
47
+ * untrusted: an unknown message type is ignored rather than raised, so a type added server-side breaks nothing.
48
+ */
49
+ export declare function openTextToSpeechStream(text: string, handlers: TextToSpeechHandlers, options?: TextToSpeechOptions, token?: string): Promise<TextToSpeechStream>;
50
+ /** Whether this machine can ask for speech: a Claude Code login that has not expired. */
51
+ export declare function isTextToSpeechAvailable(): Promise<boolean>;
@@ -0,0 +1,211 @@
1
+ import WebSocket from 'ws';
2
+ import { getCredentials, getAccessToken, isTokenExpired } from '../battery/auth/index.js';
3
+ import { CcbError } from '../battery/errors.js';
4
+ import { proxyAgentFor } from '../proxy.js';
5
+ const TEXT_TO_SPEECH_URL = 'wss://api.anthropic.com/api/ws/text_to_speech/text_stream';
6
+ /** What the service sends back: 16-bit little-endian PCM, 16 kHz, mono. */
7
+ export const SPEECH_AUDIO_FORMAT = { encoding: 'pcm_16000', sampleRate: 16000, channels: 1 };
8
+ /** The speed range the service accepts, as the official clients clamp it. */
9
+ export const SPEECH_SPEED_RANGE = { min: 0.7, max: 1.5 };
10
+ /** Longest text one `text_chunk` carries; longer text is cut at sentence ends. */
11
+ const MAX_CHUNK_CHARS = 800;
12
+ /** The service drops idle sockets; this is well inside its window. */
13
+ const KEEPALIVE_INTERVAL_MS = 4000;
14
+ /** How long to wait for the first audio before giving up on a socket that opened but says nothing. */
15
+ const FIRST_AUDIO_TIMEOUT_MS = 25000;
16
+ /**
17
+ * Cut `text` into pieces of at most {@link MAX_CHUNK_CHARS}, at sentence ends and blank lines where it can.
18
+ * The service reads each piece as it arrives, so the first audio comes before the last piece is sent.
19
+ */
20
+ export function splitForSpeech(text) {
21
+ const trimmed = text.trim();
22
+ if (trimmed === '')
23
+ return [];
24
+ if (trimmed.length <= MAX_CHUNK_CHARS)
25
+ return [trimmed];
26
+ const sentences = trimmed.split(/(?<=[.!?。!?])\s+|\n{2,}/);
27
+ const chunks = [];
28
+ let current = '';
29
+ for (const sentence of sentences) {
30
+ if (current && current.length + sentence.length + 1 > MAX_CHUNK_CHARS) {
31
+ chunks.push(current);
32
+ current = sentence;
33
+ }
34
+ else {
35
+ current = current ? `${current} ${sentence}` : sentence;
36
+ }
37
+ }
38
+ if (current)
39
+ chunks.push(current);
40
+ // A single sentence longer than the limit is cut where it stands: the service takes no more than this at once.
41
+ return chunks.flatMap((chunk) => {
42
+ if (chunk.length <= MAX_CHUNK_CHARS)
43
+ return [chunk];
44
+ const parts = [];
45
+ for (let at = 0; at < chunk.length; at += MAX_CHUNK_CHARS)
46
+ parts.push(chunk.slice(at, at + MAX_CHUNK_CHARS));
47
+ return parts;
48
+ });
49
+ }
50
+ function clampSpeed(speed) {
51
+ if (speed === undefined || !Number.isFinite(speed))
52
+ return undefined;
53
+ return Math.min(SPEECH_SPEED_RANGE.max, Math.max(SPEECH_SPEED_RANGE.min, speed));
54
+ }
55
+ /**
56
+ * Read `text` aloud by streaming it to the same service the Claude desktop app's "Read aloud" uses.
57
+ *
58
+ * Lives in this package, outside the plugin bundle, for the reason dictation does: asking for speech sends the
59
+ * account's OAuth token, and a JetBrains plugin may not handle credentials. The plugin runs `swttch-ext tts` and
60
+ * only ever sees audio coming back.
61
+ *
62
+ * The endpoint is undocumented. It is called the way the desktop app calls it, and every message is treated as
63
+ * untrusted: an unknown message type is ignored rather than raised, so a type added server-side breaks nothing.
64
+ */
65
+ export async function openTextToSpeechStream(text, handlers, options = {}, token) {
66
+ const accessToken = token ?? getAccessToken(await getCredentials());
67
+ const chunks = splitForSpeech(text);
68
+ const speed = clampSpeed(options.speed);
69
+ const query = new URLSearchParams({
70
+ output_format: SPEECH_AUDIO_FORMAT.encoding,
71
+ ...(options.voice ? { voice: options.voice } : {}),
72
+ ...(speed === undefined ? {} : { tts_speed: speed.toFixed(2) }),
73
+ client_platform: 'claude_code_swttch',
74
+ ...(options.language ? { language: options.language } : {}),
75
+ });
76
+ const url = `${TEXT_TO_SPEECH_URL}?${query.toString()}`;
77
+ const socket = new WebSocket(url, {
78
+ headers: {
79
+ Authorization: `Bearer ${accessToken}`,
80
+ 'x-app': 'swttch',
81
+ 'anthropic-client-platform': 'claude_code_swttch',
82
+ },
83
+ // The upgrade request needs the same proxy the other calls use.
84
+ agent: proxyAgentFor(url),
85
+ });
86
+ let finished = false;
87
+ let audioSeen = false;
88
+ let keepalive = null;
89
+ let firstAudioTimer = null;
90
+ const stopTimers = () => {
91
+ if (keepalive)
92
+ clearInterval(keepalive);
93
+ if (firstAudioTimer)
94
+ clearTimeout(firstAudioTimer);
95
+ keepalive = null;
96
+ firstAudioTimer = null;
97
+ };
98
+ const finish = () => {
99
+ if (finished)
100
+ return false;
101
+ finished = true;
102
+ stopTimers();
103
+ return true;
104
+ };
105
+ const fail = (message, info) => {
106
+ if (!finish())
107
+ return;
108
+ try {
109
+ socket.close();
110
+ }
111
+ catch { /* already gone */ }
112
+ handlers.onError(message, info);
113
+ };
114
+ if (chunks.length === 0) {
115
+ finished = true;
116
+ handlers.onDone();
117
+ return { cancel: () => undefined };
118
+ }
119
+ socket.on('open', () => {
120
+ for (const piece of chunks)
121
+ socket.send(JSON.stringify({ type: 'text_chunk', text: piece }));
122
+ socket.send(JSON.stringify({ type: 'close_stream' }));
123
+ keepalive = setInterval(() => {
124
+ if (socket.readyState === WebSocket.OPEN)
125
+ socket.send(JSON.stringify({ type: 'keep_alive' }));
126
+ }, KEEPALIVE_INTERVAL_MS);
127
+ firstAudioTimer = setTimeout(() => {
128
+ if (!audioSeen)
129
+ fail('The speech service did not answer in time.');
130
+ }, FIRST_AUDIO_TIMEOUT_MS);
131
+ });
132
+ socket.on('message', (data, isBinary) => {
133
+ if (finished)
134
+ return;
135
+ if (isBinary) {
136
+ const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
137
+ audioSeen = true;
138
+ if (firstAudioTimer) {
139
+ clearTimeout(firstAudioTimer);
140
+ firstAudioTimer = null;
141
+ }
142
+ handlers.onAudio(chunk);
143
+ return;
144
+ }
145
+ let message;
146
+ try {
147
+ message = JSON.parse(data.toString());
148
+ }
149
+ catch {
150
+ return;
151
+ }
152
+ if (message.type === 'SpeechComplete') {
153
+ if (!finish())
154
+ return;
155
+ try {
156
+ socket.close();
157
+ }
158
+ catch { /* already gone */ }
159
+ handlers.onDone();
160
+ }
161
+ else if (message.type === 'SpeechError' || message.type === 'error') {
162
+ fail(message.message ?? message.description ?? 'The speech service reported an error.');
163
+ }
164
+ // Any other type (SpeechStalled and whatever comes next) says nothing we have to act on.
165
+ });
166
+ socket.on('unexpected-response', (_request, response) => {
167
+ // A handshake the service refused: 401 and 403 are the account's problem, and retrying will not help.
168
+ const status = response.statusCode ?? 0;
169
+ fail(`The speech service refused the connection (${status}).`, { fatal: status >= 400 && status < 500 });
170
+ });
171
+ socket.on('error', (err) => {
172
+ if (finished)
173
+ return;
174
+ fail(`WebSocket error: ${err.message}`);
175
+ });
176
+ socket.on('close', () => {
177
+ // Closed with neither a completion nor an error: whatever audio came is all there will be.
178
+ if (finish()) {
179
+ if (audioSeen)
180
+ handlers.onDone();
181
+ else
182
+ handlers.onError('The speech service closed without audio.');
183
+ }
184
+ });
185
+ return {
186
+ cancel() {
187
+ if (finished)
188
+ return;
189
+ finished = true;
190
+ stopTimers();
191
+ try {
192
+ socket.close();
193
+ }
194
+ catch { /* already gone */ }
195
+ },
196
+ };
197
+ }
198
+ /** Whether this machine can ask for speech: a Claude Code login that has not expired. */
199
+ export async function isTextToSpeechAvailable() {
200
+ try {
201
+ const credentials = await getCredentials();
202
+ getAccessToken(credentials);
203
+ return !isTokenExpired(credentials);
204
+ }
205
+ catch (err) {
206
+ if (err instanceof CcbError)
207
+ return false;
208
+ throw err;
209
+ }
210
+ }
211
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/tts/stream.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@swttch/extend-kit",
3
- "version": "0.9.0",
4
- "description": "Companion tools for Swttch that live outside the plugin bundle: Claude Code account/usage SDK, the ccb CLI, and speech-to-text.",
3
+ "version": "0.10.1",
4
+ "description": "Companion tools for Swttch that live outside the plugin bundle: Claude Code account/usage SDK, the ccb CLI, speech-to-text and text-to-speech.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",