@swttch/extend-kit 0.9.0 → 0.10.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/README.ko.md +11 -1
- package/README.md +11 -1
- package/dist/ext/index.js +10 -1
- package/dist/ext/index.js.map +1 -1
- package/dist/ext/tts.d.ts +22 -0
- package/dist/ext/tts.js +48 -0
- package/dist/ext/tts.js.map +1 -0
- package/dist/tts/index.d.ts +7 -0
- package/dist/tts/index.js +7 -0
- package/dist/tts/index.js.map +1 -0
- package/dist/tts/stream.d.ts +51 -0
- package/dist/tts/stream.js +211 -0
- package/dist/tts/stream.js.map +1 -0
- package/package.json +2 -2
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 <이름>`. 다 읽으면 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>`. 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;
|
package/dist/ext/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ext/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAkB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ext/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
* A terminal user can drive it by hand (macOS):
|
|
12
|
+
*
|
|
13
|
+
* echo "Hello" | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
|
|
14
|
+
*
|
|
15
|
+
* The plugin cannot ask the service itself: that sends the account's OAuth token, and a JetBrains plugin may not
|
|
16
|
+
* handle credentials. It runs this command and relays the audio.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runTextToSpeech(options: {
|
|
19
|
+
language?: string;
|
|
20
|
+
speed?: string;
|
|
21
|
+
voice?: string;
|
|
22
|
+
}): Promise<number>;
|
package/dist/ext/tts.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { openTextToSpeechStream } 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
|
+
* A terminal user can drive it by hand (macOS):
|
|
13
|
+
*
|
|
14
|
+
* echo "Hello" | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
|
|
15
|
+
*
|
|
16
|
+
* The plugin cannot ask the service itself: that sends the account's OAuth token, and a JetBrains plugin may not
|
|
17
|
+
* handle credentials. It runs this command and relays the audio.
|
|
18
|
+
*/
|
|
19
|
+
export async function runTextToSpeech(options) {
|
|
20
|
+
const chunks = [];
|
|
21
|
+
for await (const chunk of process.stdin)
|
|
22
|
+
chunks.push(Buffer.from(chunk));
|
|
23
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
24
|
+
const speed = options.speed === undefined || options.speed === '' ? undefined : Number(options.speed);
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const stopOnPipeClosed = () => {
|
|
27
|
+
// The reader went away (the plugin stopped listening): there is nobody to read to.
|
|
28
|
+
stream?.then((s) => s.cancel()).finally(() => resolve(0));
|
|
29
|
+
};
|
|
30
|
+
process.stdout.on('error', stopOnPipeClosed);
|
|
31
|
+
const stream = openTextToSpeechStream(text, {
|
|
32
|
+
onAudio: (audio) => {
|
|
33
|
+
process.stdout.write(audio);
|
|
34
|
+
},
|
|
35
|
+
onDone: () => resolve(0),
|
|
36
|
+
onError: (message, info) => {
|
|
37
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'tts_failed', message, fatal: info?.fatal ?? false } })}\n`);
|
|
38
|
+
resolve(1);
|
|
39
|
+
},
|
|
40
|
+
}, { language: options.language || undefined, voice: options.voice || undefined, speed }).catch((err) => {
|
|
41
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
42
|
+
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'not_logged_in', message, fatal: true } })}\n`);
|
|
43
|
+
resolve(1);
|
|
44
|
+
return undefined;
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# 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,MAAM,iBAAiB,CAAC;AAEzD,yHAAyH;AACzH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;GAcG;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,KAAK,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;IAEtG,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,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS,EAAE,KAAK,EAAE,CACtF,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,7 @@
|
|
|
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';
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
//# 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"}
|
|
@@ -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.
|
|
4
|
-
"description": "Companion tools for Swttch that live outside the plugin bundle: Claude Code account/usage SDK, the ccb CLI, and
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|