handy-remote-server 1.1.0 β†’ 1.2.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.
Files changed (3) hide show
  1. package/README.md +55 -14
  2. package/dist/index.js +36 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,30 +10,71 @@ The easiest way to run the external inference server is using `npx`:
10
10
  npx handy-remote-server
11
11
  ```
12
12
 
13
- _(You must have Node.js and npm installed)_
13
+ _(You must have Node.js and Rust/Cargo installed)_
14
14
 
15
15
  ## Usage
16
16
 
17
- When you run the server for the first time, it will automatically download the **GigaAM v3** model (Russian-only fast architecture model) if it's not present.
17
+ When you run the server for the first time, it will:
18
18
 
19
- It will also generate a unique Bearer API Token for your active session:
19
+ 1. **Download the GigaAM v3 model** (~100 MB) with a progress bar:
20
20
 
21
- ```bash
22
- Your API KEY is: xxxxx-xxxxx-xxxxx-xxxxx
21
+ ```
22
+ πŸ“₯ Downloading model...
23
+ URL: https://blob.handy.computer/giga-am-v3.int8.onnx
24
+ Dest: /path/to/models/gigaam.onnx
25
+
26
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 52.3% 52.10 MB / 99.60 MB 12.5 MB/s
27
+
28
+ βœ… Download complete in 8.2s
29
+ ```
30
+
31
+ 2. **Generate a persistent API key** saved to `~/.handy/api_key`:
32
+
33
+ ```
34
+ ======================================================
35
+ Generated a new API KEY (saved to /Users/you/.handy/api_key)
36
+ Your API KEY is: xxxxx...xxxxx
37
+ ======================================================
38
+ ```
39
+
40
+ The key persists across restarts. On the next launch, it will be loaded automatically.
41
+
42
+ 3. **Start the server** and log every request in detail:
43
+
44
+ ```
23
45
  Handy Remote Server is running on port 3000
46
+
47
+ [2026-03-07T12:00:00.000Z] ── REQUEST #1 ──────────────────────
48
+ Method: POST /transcribe
49
+ From: 192.168.1.5
50
+ Auth: OK
51
+ [#1] Audio received: 156.3 KB
52
+ [#1] Queued for inference (queue length: 0)
53
+ [2026-03-07T12:00:01.234Z] ── RESPONSE #1 ─────────────────────
54
+ Status: 200
55
+ Duration: 1.23s
56
+ Result: "ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΊΠ°ΠΊ Π΄Π΅Π»Π°?"
24
57
  ```
25
58
 
59
+ ### Connecting from Handy
60
+
26
61
  1. Open **Handy** on your client machine.
27
- 2. Go to **Settings > General**, select the `Remote` engine.
28
- 3. Provide the Server URL: `http://<your-server-ip>:3000`
29
- 4. Provide the generated Token.
30
- 5. All audio chunks will now be transcribed by the server!
62
+ 2. Go to **Settings > Models**, select **Remote Server**.
63
+ 3. Go to **Settings > General**, fill in:
64
+ - **Remote Server URL**: `http://<your-server-ip>:3000`
65
+ - **API Token**: the generated token
66
+ 4. All transcriptions will now be processed by the server!
31
67
 
32
- ## How it works
68
+ ## Environment Variables
33
69
 
34
- The `handy-remote-server` spins up a tiny Express server alongside a heavily optimized Rust CLI (`rust-infer`) powered by `transcribe-rs`. Audio files are dispatched sequentially from the Node server directly into the Rust engine.
70
+ | Variable | Default | Description |
71
+ | ---------------- | ------------------------------------------- | ------------------------------- |
72
+ | `PORT` | `3000` | Server port |
73
+ | `API_KEY` | auto-generated, saved to `~/.handy/api_key` | Bearer token for authentication |
74
+ | `INFER_CLI_PATH` | auto-detected | Path to the `rust-infer` binary |
35
75
 
36
- ### Environment variables
76
+ ## How It Works
77
+
78
+ The `handy-remote-server` spins up a tiny Express server alongside a heavily optimized Rust CLI (`rust-infer`) powered by `transcribe-rs`. Audio files are dispatched sequentially from the Node server directly into the Rust engine.
37
79
 
38
- - `PORT` - defaults to `3000`
39
- - `API_KEY` - defaults to an auto-generated token in development. Set this to a permanent token for production.
80
+ Currently the server uses the **GigaAM v3** model (Russian-language, fast inference, ~100 MB).
package/dist/index.js CHANGED
@@ -135,14 +135,46 @@ const gigaamModelPath = path_1.default.join(modelsDir, 'gigaam.onnx');
135
135
  async function downloadFile(url, dest) {
136
136
  if (fs_1.default.existsSync(dest))
137
137
  return;
138
- console.log(`Downloading ${url} to ${dest}...`);
138
+ console.log(`\nπŸ“₯ Downloading model...`);
139
+ console.log(` URL: ${url}`);
140
+ console.log(` Dest: ${dest}\n`);
139
141
  fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
140
142
  const response = await fetch(url);
141
143
  if (!response.ok)
142
144
  throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
143
- const arrBuffer = await response.arrayBuffer();
144
- fs_1.default.writeFileSync(dest, Buffer.from(arrBuffer));
145
- console.log(`Downloaded ${dest}`);
145
+ const totalBytes = parseInt(response.headers.get('content-length') || '0', 10);
146
+ let downloadedBytes = 0;
147
+ const startTime = Date.now();
148
+ const fileStream = fs_1.default.createWriteStream(dest);
149
+ const reader = response.body?.getReader();
150
+ if (!reader)
151
+ throw new Error('Response body is not readable');
152
+ const barWidth = 40;
153
+ while (true) {
154
+ const { done, value } = await reader.read();
155
+ if (done)
156
+ break;
157
+ fileStream.write(Buffer.from(value));
158
+ downloadedBytes += value.length;
159
+ // Draw progress bar
160
+ const percent = totalBytes > 0 ? downloadedBytes / totalBytes : 0;
161
+ const filled = Math.round(barWidth * percent);
162
+ const empty = barWidth - filled;
163
+ const bar = 'β–ˆ'.repeat(filled) + 'β–‘'.repeat(empty);
164
+ const pct = (percent * 100).toFixed(1).padStart(5);
165
+ const dl = formatBytes(downloadedBytes);
166
+ const tot = totalBytes > 0 ? formatBytes(totalBytes) : '?';
167
+ const elapsed = (Date.now() - startTime) / 1000;
168
+ const speed = elapsed > 0 ? formatBytes(downloadedBytes / elapsed) + '/s' : '';
169
+ process.stdout.write(`\r ${bar} ${pct}% ${dl} / ${tot} ${speed} `);
170
+ }
171
+ await new Promise((resolve, reject) => {
172
+ fileStream.end(() => resolve());
173
+ fileStream.on('error', reject);
174
+ });
175
+ const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
176
+ process.stdout.write('\n');
177
+ console.log(`\nβœ… Download complete in ${totalTime}s\n`);
146
178
  }
147
179
  async function ensureModels() {
148
180
  await downloadFile(GIGAAM_MODEL_URL, gigaamModelPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handy-remote-server",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Remote Transcription Server for Handy",
5
5
  "main": "dist/index.js",
6
6
  "bin": {