@swttch/extend-kit 0.10.0 → 0.10.2
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 +3 -1
- package/README.md +3 -1
- package/dist/ext/index.js +4 -1
- package/dist/ext/index.js.map +1 -1
- package/dist/ext/tts.d.ts +9 -0
- package/dist/ext/tts.js +17 -3
- package/dist/ext/tts.js.map +1 -1
- package/dist/tts/accountVoice.d.ts +39 -0
- package/dist/tts/accountVoice.js +60 -0
- package/dist/tts/accountVoice.js.map +1 -0
- package/dist/tts/index.d.ts +2 -0
- package/dist/tts/index.js +1 -0
- package/dist/tts/index.js.map +1 -1
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -64,7 +64,9 @@ swttch-ext api frame delete --slug 3627498c-4893-4ce8-bfaf-524f7b041c1d --yes
|
|
|
64
64
|
echo "터미널에서 읽어 줍니다." | swttch-ext tts --language ko | play -t raw -r 16000 -e signed -b 16 -c 1 -
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
`swttch-ext tts --settings`는 계정에 저장된 값을 보여 줍니다(`{"ok":true,"data":{"voice","speed","language"}}`, 정해지지 않은 것은 null).
|
|
68
|
+
|
|
69
|
+
옵션: `--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
70
|
|
|
69
71
|
### 이 버전이 할 수 있는 일 알아보기
|
|
70
72
|
|
package/README.md
CHANGED
|
@@ -64,7 +64,9 @@ Reads the text on stdin aloud with the same voice service the Claude desktop app
|
|
|
64
64
|
echo "Hello from the terminal." | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
`swttch-ext tts --settings` prints what your account has chosen (`{"ok":true,"data":{"voice","speed","language"}}`, null for what is not set).
|
|
68
|
+
|
|
69
|
+
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
70
|
|
|
69
71
|
### Finding out what this version can do
|
|
70
72
|
|
package/dist/ext/index.js
CHANGED
|
@@ -5,7 +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
|
+
import { runTextToSpeech, printVoiceSettings, TTS_CAPABILITY } from './tts.js';
|
|
9
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const VERSION = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'utf-8')).version;
|
|
11
11
|
const args = process.argv.slice(2);
|
|
@@ -68,6 +68,7 @@ Usage: swttch-ext api <service> <call> [--param value ...] [--yes] [--json]
|
|
|
68
68
|
swttch-ext --capabilities List what this version can do, as JSON
|
|
69
69
|
swttch-ext tts [--language ko] [--speed 1.0] [--voice <name>]
|
|
70
70
|
Read the text on stdin aloud: raw audio on stdout
|
|
71
|
+
swttch-ext tts --settings The voice, speed and language your Claude account has chosen, as JSON
|
|
71
72
|
swttch-ext api frame retitle --slug <id> --title "New name"
|
|
72
73
|
swttch-ext api frame pin --slug <id>
|
|
73
74
|
swttch-ext api frame delete --slug <id> --yes
|
|
@@ -93,6 +94,8 @@ async function main() {
|
|
|
93
94
|
for (const [name, value] of env)
|
|
94
95
|
process.env[name] = value;
|
|
95
96
|
await applySettingsEnv(process.cwd(), new Set(env.map(([name]) => name)));
|
|
97
|
+
if (values.settings !== undefined)
|
|
98
|
+
return printVoiceSettings();
|
|
96
99
|
return runTextToSpeech({ language: values.language, speed: values.speed, voice: values.voice });
|
|
97
100
|
}
|
|
98
101
|
if (command !== 'api') {
|
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;AACvE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ext/index.ts"],"names":[],"mappings":"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"}
|
package/dist/ext/tts.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export declare const TTS_CAPABILITY = "tts";
|
|
|
8
8
|
* ── stderr ── on failure, one JSON line: {"ok":false,"error":{"code","message","fatal"}}.
|
|
9
9
|
* ── exit ─── 0 when the service said everything, 1 when it failed. Killing the process stops the reading.
|
|
10
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
|
+
*
|
|
11
14
|
* A terminal user can drive it by hand (macOS):
|
|
12
15
|
*
|
|
13
16
|
* echo "Hello" | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
|
|
@@ -20,3 +23,9 @@ export declare function runTextToSpeech(options: {
|
|
|
20
23
|
speed?: string;
|
|
21
24
|
voice?: string;
|
|
22
25
|
}): Promise<number>;
|
|
26
|
+
/**
|
|
27
|
+
* `swttch-ext tts --settings` — the voice, speed and language the Claude account has chosen, as one JSON object:
|
|
28
|
+
* `{"ok":true,"data":{"voice":"buttery","speed":1.2,"language":null}}`. A field the account has not set is null.
|
|
29
|
+
* Lets a caller show what reading aloud will use when it is not told otherwise.
|
|
30
|
+
*/
|
|
31
|
+
export declare function printVoiceSettings(): Promise<number>;
|
package/dist/ext/tts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { openTextToSpeechStream } from '../tts/index.js';
|
|
1
|
+
import { openTextToSpeechStream, readAccountVoiceSettings, chooseVoiceOptions } from '../tts/index.js';
|
|
2
2
|
/** The name `--capabilities` lists for this command, so the plugin can tell a kit that has it from one that does not. */
|
|
3
3
|
export const TTS_CAPABILITY = 'tts';
|
|
4
4
|
/**
|
|
@@ -9,6 +9,9 @@ export const TTS_CAPABILITY = 'tts';
|
|
|
9
9
|
* ── stderr ── on failure, one JSON line: {"ok":false,"error":{"code","message","fatal"}}.
|
|
10
10
|
* ── exit ─── 0 when the service said everything, 1 when it failed. Killing the process stops the reading.
|
|
11
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
|
+
*
|
|
12
15
|
* A terminal user can drive it by hand (macOS):
|
|
13
16
|
*
|
|
14
17
|
* echo "Hello" | swttch-ext tts | play -t raw -r 16000 -e signed -b 16 -c 1 -
|
|
@@ -21,7 +24,8 @@ export async function runTextToSpeech(options) {
|
|
|
21
24
|
for await (const chunk of process.stdin)
|
|
22
25
|
chunks.push(Buffer.from(chunk));
|
|
23
26
|
const text = Buffer.concat(chunks).toString('utf8');
|
|
24
|
-
const
|
|
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());
|
|
25
29
|
return new Promise((resolve) => {
|
|
26
30
|
const stopOnPipeClosed = () => {
|
|
27
31
|
// The reader went away (the plugin stopped listening): there is nobody to read to.
|
|
@@ -37,7 +41,7 @@ export async function runTextToSpeech(options) {
|
|
|
37
41
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'tts_failed', message, fatal: info?.fatal ?? false } })}\n`);
|
|
38
42
|
resolve(1);
|
|
39
43
|
},
|
|
40
|
-
},
|
|
44
|
+
}, chosen).catch((err) => {
|
|
41
45
|
const message = err instanceof Error ? err.message : String(err);
|
|
42
46
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'not_logged_in', message, fatal: true } })}\n`);
|
|
43
47
|
resolve(1);
|
|
@@ -45,4 +49,14 @@ export async function runTextToSpeech(options) {
|
|
|
45
49
|
});
|
|
46
50
|
});
|
|
47
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* `swttch-ext tts --settings` — the voice, speed and language the Claude account has chosen, as one JSON object:
|
|
54
|
+
* `{"ok":true,"data":{"voice":"buttery","speed":1.2,"language":null}}`. A field the account has not set is null.
|
|
55
|
+
* Lets a caller show what reading aloud will use when it is not told otherwise.
|
|
56
|
+
*/
|
|
57
|
+
export async function printVoiceSettings() {
|
|
58
|
+
const settings = await readAccountVoiceSettings();
|
|
59
|
+
process.stdout.write(`${JSON.stringify({ ok: true, data: { voice: settings.voice ?? null, speed: settings.speed ?? null, language: settings.language ?? null } })}\n`);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
48
62
|
//# sourceMappingURL=tts.js.map
|
package/dist/ext/tts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/ext/tts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/ext/tts.ts"],"names":[],"mappings":"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"}
|
|
@@ -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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"}
|
package/dist/tts/index.d.ts
CHANGED
|
@@ -5,3 +5,5 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { openTextToSpeechStream, isTextToSpeechAvailable, splitForSpeech, SPEECH_AUDIO_FORMAT, SPEECH_SPEED_RANGE } from './stream.js';
|
|
7
7
|
export type { TextToSpeechOptions, TextToSpeechHandlers, TextToSpeechStream } from './stream.js';
|
|
8
|
+
export { readAccountVoiceSettings, chooseVoiceOptions, pickVoiceSettings } from './accountVoice.js';
|
|
9
|
+
export type { AccountVoiceSettings } from './accountVoice.js';
|
package/dist/tts/index.js
CHANGED
|
@@ -4,4 +4,5 @@
|
|
|
4
4
|
* Outside the plugin bundle for the same reason as dictation: it sends the account's OAuth token.
|
|
5
5
|
*/
|
|
6
6
|
export { openTextToSpeechStream, isTextToSpeechAvailable, splitForSpeech, SPEECH_AUDIO_FORMAT, SPEECH_SPEED_RANGE } from './stream.js';
|
|
7
|
+
export { readAccountVoiceSettings, chooseVoiceOptions, pickVoiceSettings } from './accountVoice.js';
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
package/dist/tts/index.js.map
CHANGED
|
@@ -1 +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"}
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swttch/extend-kit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
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",
|