@swttch/extend-kit 0.6.1 → 0.7.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/dist/cli/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { getCredentials, getAccessToken } from '../battery/auth/index.js';
3
3
  import { ClaudeCodeClient } from '../battery/api/index.js';
4
4
  import { oauthCommand } from './oauth.js';
5
+ import { sttCommand } from './stt.js';
5
6
  import { getAccountCredentials } from './account-credentials.js';
6
7
  import { CcbError } from '../battery/errors.js';
7
8
  import { readFileSync } from 'node:fs';
@@ -23,7 +24,10 @@ async function createClient() {
23
24
  async function run() {
24
25
  const [module, ...subcommand] = command;
25
26
  if (flags.has('--capabilities')) {
26
- console.log(JSON.stringify({ capabilities: ['oauth.usage.account-file'] }));
27
+ // A caller checks this before using a command: the plugin has to know
28
+ // whether the kit on this machine is new enough to stream dictation, since
29
+ // the only alternative it once had (importing the module) is gone.
30
+ console.log(JSON.stringify({ capabilities: ['oauth.usage.account-file', 'stt.stream'] }));
27
31
  return;
28
32
  }
29
33
  if (args.some(arg => arg.startsWith('--account-file')) && (!accountFile || module !== 'oauth' || subcommand[0] !== 'usage')) {
@@ -41,13 +45,32 @@ Usage: ccb <command> [options]
41
45
  Commands:
42
46
  oauth usage Show usage limits
43
47
  oauth profile Show account profile
48
+ stt Transcribe raw PCM audio from stdin (see below)
49
+ stt --check Report whether this machine can dictate
44
50
 
45
51
  Options:
46
52
  --account-file=<path> Read usage for a CCG saved-account snapshot (no account switch)
53
+ --language=<code> BCP-47 language for stt (default: en)
54
+ --keyterms=<a,b,c> Extra vocabulary to bias stt toward
55
+ --interims Also emit interim stt guesses
47
56
  --capabilities Output supported CLI capabilities as JSON
48
57
  --json Output as JSON
49
58
  -h, --help Show help
50
- -v, --version Show version`);
59
+ -v, --version Show version
60
+
61
+ stt reads raw PCM on stdin (16-bit LE, 16 kHz, mono) and writes one JSON
62
+ object per line to stdout. Any other audio format transcribes to silence.
63
+
64
+ ffmpeg -f avfoundation -i ':0' -ar 16000 -ac 1 -f s16le - | ccb stt`);
65
+ return;
66
+ }
67
+ // Handled before createClient(): stt reads the login inside the stream, and
68
+ // building a client up front would turn "not signed in" into a crash before
69
+ // `stt --check` could answer it calmly.
70
+ if (module === 'stt') {
71
+ const code = await sttCommand(subcommand, args, flags, jsonOutput);
72
+ if (code !== 0)
73
+ process.exit(code);
51
74
  return;
52
75
  }
53
76
  const client = await createClient();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `ccb stt` — dictation as a process, not as a library.
3
+ *
4
+ * The plugin cannot call {@link openSpeechToTextStream} directly. Transcribing
5
+ * reads the machine's Claude Code login, and importing this package into the
6
+ * backend would put that read inside the plugin's own process — the exact thing
7
+ * that moved these tools out to a separate package. Spawning instead keeps the
8
+ * token on this side of the process boundary: the caller writes audio and reads
9
+ * text, and never holds a credential.
10
+ *
11
+ * So the wire format here is the public contract, the same way `oauth usage
12
+ * --json` is. A terminal user can drive it by hand:
13
+ *
14
+ * ffmpeg -f avfoundation -i ':0' -ar 16000 -ac 1 -f s16le - | ccb stt
15
+ *
16
+ * ── stdin ── raw PCM: 16-bit little-endian, 16 kHz, mono. Nothing else is
17
+ * accepted, and a wrong format transcribes to silence rather than
18
+ * failing (see CLAUDE.md), so the caller must convert first.
19
+ * ── stdout ── one JSON object per line, flushed as it happens.
20
+ * ── exit ─── 0 when the speaker finished, 1 when the stream failed to open.
21
+ */
22
+ /** Line-oriented events written to stdout. */
23
+ export declare enum SttEventType {
24
+ /** The socket is open and audio is being transcribed. */
25
+ Open = "open",
26
+ /** Text arrived. `isFinal` false will be replaced by a later line. */
27
+ Transcript = "transcript",
28
+ /** The stream failed. `fatal` means retrying will not help. */
29
+ Error = "error"
30
+ }
31
+ /**
32
+ * Entry point for the `stt` module of the CLI.
33
+ *
34
+ * Takes no {@link ClaudeCodeClient}: unlike `oauth`, this command reads the
35
+ * login itself inside the stream, and building a client up front would turn
36
+ * "not signed in" into a crash before `--check` could answer it.
37
+ */
38
+ export declare function sttCommand(subcommand: string[], args: string[], flags: Set<string>, jsonOutput: boolean): Promise<number>;
@@ -0,0 +1,154 @@
1
+ import { openSpeechToTextStream, isSpeechToTextAvailable } from '../stt/index.js';
2
+ import { CcbError } from '../battery/errors.js';
3
+ /**
4
+ * `ccb stt` — dictation as a process, not as a library.
5
+ *
6
+ * The plugin cannot call {@link openSpeechToTextStream} directly. Transcribing
7
+ * reads the machine's Claude Code login, and importing this package into the
8
+ * backend would put that read inside the plugin's own process — the exact thing
9
+ * that moved these tools out to a separate package. Spawning instead keeps the
10
+ * token on this side of the process boundary: the caller writes audio and reads
11
+ * text, and never holds a credential.
12
+ *
13
+ * So the wire format here is the public contract, the same way `oauth usage
14
+ * --json` is. A terminal user can drive it by hand:
15
+ *
16
+ * ffmpeg -f avfoundation -i ':0' -ar 16000 -ac 1 -f s16le - | ccb stt
17
+ *
18
+ * ── stdin ── raw PCM: 16-bit little-endian, 16 kHz, mono. Nothing else is
19
+ * accepted, and a wrong format transcribes to silence rather than
20
+ * failing (see CLAUDE.md), so the caller must convert first.
21
+ * ── stdout ── one JSON object per line, flushed as it happens.
22
+ * ── exit ─── 0 when the speaker finished, 1 when the stream failed to open.
23
+ */
24
+ /** Line-oriented events written to stdout. */
25
+ export var SttEventType;
26
+ (function (SttEventType) {
27
+ /** The socket is open and audio is being transcribed. */
28
+ SttEventType["Open"] = "open";
29
+ /** Text arrived. `isFinal` false will be replaced by a later line. */
30
+ SttEventType["Transcript"] = "transcript";
31
+ /** The stream failed. `fatal` means retrying will not help. */
32
+ SttEventType["Error"] = "error";
33
+ })(SttEventType || (SttEventType = {}));
34
+ /**
35
+ * Write one event as a single line.
36
+ *
37
+ * `process.stdout.write` rather than `console.log` because the caller parses
38
+ * this by newline: console.log is the same thing here, but going through the
39
+ * stream directly makes it explicit that one call must produce exactly one line
40
+ * and that nothing else may ever be printed to stdout by this command.
41
+ */
42
+ function emit(event) {
43
+ process.stdout.write(`${JSON.stringify(event)}\n`);
44
+ }
45
+ /** Split a `--keyterms=a,b,c` value, dropping empties and surrounding spaces. */
46
+ function parseKeyterms(raw) {
47
+ if (!raw)
48
+ return [];
49
+ return raw
50
+ .split(',')
51
+ .map((term) => term.trim())
52
+ .filter(Boolean);
53
+ }
54
+ /**
55
+ * `ccb stt --check --json` — can this machine dictate at all?
56
+ *
57
+ * Separate from opening a stream because the answer drives different UI: a
58
+ * machine with no Claude Code login should be told to sign in, not shown a
59
+ * socket error. Always exits 0; "no" is an answer rather than a failure.
60
+ */
61
+ async function checkAvailability(jsonOutput) {
62
+ const available = await isSpeechToTextAvailable().catch(() => false);
63
+ if (jsonOutput) {
64
+ console.log(JSON.stringify({ available }));
65
+ return;
66
+ }
67
+ console.log(available ? 'available' : 'unavailable (no Claude Code login on this machine)');
68
+ }
69
+ /**
70
+ * Open the stream and pump stdin into it until the caller stops writing.
71
+ *
72
+ * Audio written before the socket finishes opening is not lost: the stream
73
+ * buffers pre-open chunks itself, which matters more here than in-process
74
+ * because a pipe delivers the caller's first chunk almost immediately.
75
+ */
76
+ async function runStream(options) {
77
+ if (process.stdin.isTTY) {
78
+ console.error('ccb stt reads raw PCM audio (16-bit LE, 16 kHz, mono) on stdin; pipe audio into it.');
79
+ return 1;
80
+ }
81
+ let stream;
82
+ try {
83
+ stream = await openSpeechToTextStream({
84
+ onTranscript: (text, isFinal) => emit({ type: SttEventType.Transcript, text, isFinal }),
85
+ onError: (message, info) => emit({ type: SttEventType.Error, message, fatal: info?.fatal ?? false }),
86
+ onOpen: () => emit({ type: SttEventType.Open }),
87
+ }, {
88
+ language: options.language,
89
+ extraKeyterms: options.extraKeyterms,
90
+ typedInterims: options.typedInterims,
91
+ });
92
+ }
93
+ catch (err) {
94
+ // Failing to open is reported on the same channel as everything else, so a
95
+ // caller parsing stdout never has to also watch stderr to learn why
96
+ // dictation did not start. A rejected login lands here.
97
+ emit({
98
+ type: SttEventType.Error,
99
+ message: err instanceof Error ? err.message : String(err),
100
+ fatal: true,
101
+ });
102
+ return 1;
103
+ }
104
+ return new Promise((resolve) => {
105
+ let finished = false;
106
+ /**
107
+ * Close once, then exit.
108
+ *
109
+ * `close()` waits for the service to flush the last words, so the trailing
110
+ * transcript is emitted before this resolves. Called from both stdin end
111
+ * and a termination signal, hence the guard.
112
+ */
113
+ const finish = () => {
114
+ if (finished)
115
+ return;
116
+ finished = true;
117
+ stream
118
+ .close()
119
+ .then(() => resolve(0))
120
+ .catch(() => resolve(0));
121
+ };
122
+ process.stdin.on('data', (chunk) => {
123
+ stream.sendAudio(new Uint8Array(chunk));
124
+ });
125
+ process.stdin.on('end', finish);
126
+ // A parent that kills us instead of closing stdin still gets the last words.
127
+ process.on('SIGTERM', finish);
128
+ process.on('SIGINT', finish);
129
+ });
130
+ }
131
+ /**
132
+ * Entry point for the `stt` module of the CLI.
133
+ *
134
+ * Takes no {@link ClaudeCodeClient}: unlike `oauth`, this command reads the
135
+ * login itself inside the stream, and building a client up front would turn
136
+ * "not signed in" into a crash before `--check` could answer it.
137
+ */
138
+ export async function sttCommand(subcommand, args, flags, jsonOutput) {
139
+ if (flags.has('--check') || subcommand[0] === 'check') {
140
+ await checkAvailability(jsonOutput);
141
+ return 0;
142
+ }
143
+ if (subcommand.length > 0) {
144
+ throw new CcbError(`Unknown stt subcommand: ${subcommand[0]}`, 'invalid_argument');
145
+ }
146
+ const language = args.find((arg) => arg.startsWith('--language='))?.slice('--language='.length);
147
+ const keyterms = args.find((arg) => arg.startsWith('--keyterms='))?.slice('--keyterms='.length);
148
+ return runStream({
149
+ language,
150
+ extraKeyterms: parseKeyterms(keyterms),
151
+ typedInterims: flags.has('--interims'),
152
+ });
153
+ }
154
+ //# sourceMappingURL=stt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stt.js","sourceRoot":"","sources":["../../src/cli/stt.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swttch/extend-kit",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Companion tools for Swttch that live outside the plugin bundle: Claude Code account/usage SDK, the ccb CLI, and speech-to-text.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",