@fastagent-sh/voicenote 0.22.1 → 1.0.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.md CHANGED
@@ -1,395 +1,19 @@
1
- # voicenote
1
+ # @fastagent-sh/voicenote
2
2
 
3
- Voice recordings → diarized transcripts → integrated semantic Markdown notes.
4
-
5
- CLI command: `vn`
6
-
7
- [中文文档 / Chinese documentation](README.zh-CN.md)
8
-
9
- Currently tuned for the PHILIPS VTR6500 voice recorder, but the workflow is generic: scan recordings under a mount point → transcribe with speaker diarization → the selected summary model performs cleanup and process reconstruction → produce smart notes.
10
-
11
- **Two ways to use it:**
12
-
13
- - 🖥️ **Desktop app (GUI)** — for non-terminal users, a self-contained `.app`, one-line install:
14
- ```bash
15
- curl -fsSL https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install-app.sh | bash
16
- ```
17
- See [Desktop app](#desktop-app-gui-app) below.
18
- - ⌨️ **CLI (`vn`)** — for terminal users / developers, see "Install (CLI)" below.
19
-
20
- ## Install (CLI)
21
-
22
- > The CLI installs from the npm package `@fastagent-sh/voicenote`. The install script / `bun add -g` below require the package to be **published to npm** (see "Development" at the end for the release flow).
23
-
24
- Recommended: the install script (macOS):
25
-
26
- ```bash
27
- curl -fsSL https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install.sh | bash
28
- ```
29
-
30
- The install script does **no interactive configuration** by default: it installs/checks `ffmpeg`, Bun, Node/npm, pi, and `vn`, then writes an editable `config.json` template. After installation, open the config file and fill in your keys and name:
31
-
32
- ```bash
33
- open ~/.config/voicenote/config.json
34
- # or open the directory
35
- vn open config
36
- ```
37
-
38
- Advanced users can preseed the template with environment variables:
39
-
40
- ```bash
41
- VOICENOTE_NAME="Jane Doe" \
42
- VOICENOTE_ALIAS="jane" \
43
- VOICENOTE_WORKSPACE="$HOME/Documents/meetings" \
44
- VOLCANO_ASR_KEY="..." \
45
- VOLCANO_TOS_BUCKET="..." \
46
- VOLCANO_TOS_ACCESS_KEY="..." \
47
- VOLCANO_TOS_SECRET_KEY="..." \
48
- bash <(curl -fsSL https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install.sh)
49
- ```
50
-
51
- The first install creates `~/.config/voicenote/config.json`. Once configured, run `vn doctor` to check it, and `vn install-launch-agent` if you want background monitoring.
52
-
53
- Manual install:
54
-
55
- ```bash
56
- bun remove -g @kid7st/voicenote 2>/dev/null || true # drop the pre-rebrand package if present (safe no-op otherwise)
57
- bun add -g @fastagent-sh/voicenote
58
- mkdir -p ~/.local/bin
59
- ln -sf ~/.bun/bin/vn ~/.local/bin/vn
60
- ```
61
-
62
- An older `git+…#main` install is replaced in place by `bun add -g @fastagent-sh/voicenote`. The one exception is the **pre-rebrand `@kid7st/voicenote`** package: it ships the same `vn` bin, so the `bun remove -g` line above clears it first (the one-line `install.sh` does this automatically).
63
-
64
- ### Windows (CLI)
65
-
66
- The CLI is cross-platform. Prerequisites: Bun, ffmpeg (provides `ffprobe.exe`), Node + pi.
67
-
68
- ```powershell
69
- bun remove -g @kid7st/voicenote 2>$null # drop the pre-rebrand package if present (safe no-op otherwise)
70
- bun add -g @fastagent-sh/voicenote
71
- # Windows has no /Volumes mount points; set the recorder drive explicitly
72
- '{"env":{"VOICENOTE_RECORD_DIR":"E:\\RECORD"}}' | vn config set
73
- ```
74
-
75
- - Config: `%APPDATA%\voicenote\config.json`; logs/locks: `%LOCALAPPDATA%\voicenote\`
76
- - Background automation uses the **Windows Task Scheduler**: `vn install-launch-agent` to register / `vn status` to inspect / `vn uninstall-launch-agent` to remove (same command names as macOS; dispatched per platform internally)
77
-
78
- ## Dependencies
79
-
80
- - **Bun >= 1.3 (required at runtime)** — the code uses `Bun.Glob` / `Bun.file`; plain Node cannot run it
81
- - Node / npm — only used to install the pi CLI (the notes backend)
82
- - ffmpeg / ffprobe (audio duration detection):
83
-
84
- ```bash
85
- brew install ffmpeg
86
- ```
87
-
88
- The install script only writes `vn` / Bun / Homebrew PATH entries to your shell config; app configuration lives in `~/.config/voicenote/config.json`. A manual setup needs at least:
89
-
90
- ```json
91
- {
92
- "VOICENOTE_WORKSPACE": "/Users/you/Documents/meetings",
93
- "VOLCANO_ASR_KEY": "...",
94
- "VOLCANO_ASR_RESOURCE_ID": "volc.seedasr.auc",
95
- "VOLCANO_TOS_REGION": "cn-guangzhou",
96
- "VOLCANO_TOS_ENDPOINT": "tos-s3-cn-guangzhou.volces.com",
97
- "VOLCANO_TOS_BUCKET": "...",
98
- "VOLCANO_TOS_ACCESS_KEY": "...",
99
- "VOLCANO_TOS_SECRET_KEY": "...",
100
- "VOLCANO_TOS_KEEP": "0",
101
- "speakers": {
102
- "self": { "name": "Your name", "aliases": ["nickname", "alias"] },
103
- "known": []
104
- }
105
- }
106
- ```
107
-
108
- An empty or missing key means "use the built-in default" — those defaults live in `src/cli.ts` and nowhere else, so the installer and the GUI leave such fields blank.
109
-
110
- Optional settings:
111
-
112
- ```json
113
- {
114
- "VOICENOTE_DEVICE_VOLUME": "VTR6500",
115
- "VOICENOTE_RECORD_DIR": "/Volumes/VTR6500/RECORD",
116
- "VOICENOTE_MAX_AGE_HOURS": "48",
117
- "VOICENOTE_PI_BIN": "pi",
118
- "VOICENOTE_PI_MODEL": "openai-codex/gpt-5.6-sol",
119
- "PI_CODING_AGENT_DIR": "$HOME/.config/voicenote/pi-agent",
120
- "VOICENOTE_PI_THINKING": "high",
121
- "VOICENOTE_PI_SUMMARY_TOOLS": "read,grep",
122
- "VOICENOTE_CONTEXT_DIR": "/Users/you/vault"
123
- }
124
- ```
125
-
126
- ### Which model writes the notes
127
-
128
- `VOICENOTE_PI_MODEL` is passed straight to pi as `--model`. pi accepts
129
- `provider/id`, so one value pins both (`openai-codex/gpt-5.6-sol`). Leave it unset
130
- and pi's own configured default model is used.
131
-
132
- Credentials always belong to pi (`pi` → `/login <provider>`, or a provider API key
133
- in the environment); voicenote never picks a provider and never falls back to a
134
- second one. If pi fails, the transcript is kept and the summary can be retried
135
- with `vn run --latest`.
136
-
137
- ### Credentials of their own
138
-
139
- `PI_CODING_AGENT_DIR` relocates pi's config directory, which is where it keeps
140
- `auth.json`. Point it at a voicenote-owned directory and `vn login` writes there,
141
- pi refreshes the tokens there, and an interactive pi session cannot clobber
142
- them — it rewrites its own `auth.json` wholesale on exit, which has silently
143
- dropped providers before:
144
-
145
- ```bash
146
- echo '{"env":{"PI_CODING_AGENT_DIR":"$HOME/.config/voicenote/pi-agent"}}' | vn config set
147
- vn login # signs in and stores credentials in that directory
148
- ```
149
-
150
- `vn doctor` prints the path it will read (`pi.auth=...`).
151
-
152
- `DEEPSEEK_API_KEY` and `OPENAI_API_KEY` in the config are only forwarded to pi's
153
- environment for providers that read them.
154
-
155
- Transient failures (dropped socket, 5xx, 429) are retried on the same provider up
156
- to `VOICENOTE_PI_RETRIES` times (default 3). Quota and auth errors are not
157
- retried.
158
-
159
- ## Usage
160
-
161
- ```bash
162
- vn doctor # check environment and config
163
- vn run # default: Volcano ASR + pi notes
164
- vn run --mode transcript # transcript only, skip semantic notes
165
- vn run --latest # process only the latest valid recording
166
- vn run --latest --force # re-run the latest one
167
- vn run --pdf # additionally render a PDF after notes
168
- vn run --dry-run # print the plan only
169
- vn run /path/to/audio.m4a # process one file by path (skips the scan, ignores age/size/duration filters)
170
- vn list # list this month's notes
171
- vn list --month 2026-05 # specific month
172
- vn last # print the latest processing summary
173
- vn open # open the notes directory in Finder
174
- vn open config # open ~/.config/voicenote/
175
- vn open logs # open the logs directory
176
- vn open <slug> # open a note by filename fragment
177
- vn forget <id|filename> # let a recording be processed again
178
- vn log # print today's log tail (--lines N / -f follow / --err include launchd.err / --date YYYY-MM-DD)
179
- vn errors # print recent ERROR logs
180
- vn login # sign in to ChatGPT for the notes backend (browser callback; `--device-code` for headless machines). No pi TUI needed
181
- vn upgrade # reinstall latest npm package
182
- vn install-launch-agent
183
- vn status
184
- vn uninstall-launch-agent
185
- ```
186
-
187
- ## Configuration file
188
-
189
- The install script writes an editable template:
190
-
191
- ```text
192
- ~/.config/voicenote/config.json # workspace, Volcano ASR/TOS, summary backend, your name/aliases, etc.
193
- ```
194
-
195
- `speakers` maps Speaker A/B/C back to real names; `known` lists known contacts:
196
-
197
- ```json
198
- {
199
- "speakers": {
200
- "self": { "name": "Your name", "aliases": ["nickname", "alias"] },
201
- "known": []
202
- }
203
- }
204
- ```
205
-
206
- Changes take effect on the next `vn run`. `~`, `$HOME`, and `${HOME}` are accepted at the start of path settings. Environment variables override the file for the current CLI process; background runs use `config.json`, not shell startup files.
207
-
208
- ## Workflow
209
-
210
- 1. Scan recordings under `/Volumes/VTR6500/RECORD/`
211
- 2. Filter: ignore `._*`, small files (<100KB), short recordings (<60s), and already-processed recordings; if a previous run failed only at the summary stage and the transcript is saved, it is not considered done — processing resumes from there
212
- 3. Copy the original audio into `${VOICENOTE_WORKSPACE}/_audio/YYYY-MM/`
213
- 4. Transcribe with the Volcano Doubao large-model audio-file recognition API: upload local audio to TOS, submit the job, poll for results, and delete the TOS object by default when done
214
- 5. Persist the raw transcript immediately after transcription (no lossy cleanup), so a later-stage failure never wastes the ASR spend
215
- 6. The summary model (default: pi codex via ChatGPT Plus) reads the raw transcript directly, performing necessary cleanup, speaker restoration, and reconstruction of views/debates/consensus inside the notes-generation stage; if the summary fails, the next `vn run` / `vn run --latest` reuses the saved transcript and retries only the notes generation — no `vn forget` needed
216
- 7. Write notes / metadata; the system makes no archiving decisions — files stay in the configured workspace
217
-
218
- A failing recording is retried on later runs, but at most **3 times** (whether it fails in transcription or in summarisation, and a run killed mid-job counts too). After that it is marked `Gave up` and left alone, so one broken file can't burn ASR/LLM budget on every scheduler tick. Use **Retry** on its GUI row to reset the budget, preserve saved outputs, and run it again; `vn forget <name>` is the CLI escape hatch that drops the record and re-queues it. Either path reuses a saved transcript instead of paying for ASR again. Both take the run lock, so retry after the active run finishes if the state file is busy.
219
-
220
- Records whose source file is no longer on the recorder are forgotten on the next scan (and the removal is logged), *unless* they already produced notes or a transcript — that history is kept. This is why swapping recorders, or deleting files from the device, no longer leaves permanent "failed" rows behind.
221
-
222
- ## Output locations
223
-
224
- `VOICENOTE_WORKSPACE` defaults to `~/Documents/meetings`.
225
-
226
- - Notes entry point: `${VOICENOTE_WORKSPACE}/YYYY-MM/`
227
- - Original audio: `${VOICENOTE_WORKSPACE}/_audio/YYYY-MM/`
228
- - Full transcripts: `${VOICENOTE_WORKSPACE}/_transcripts/YYYY-MM/`
229
- - Metadata: `${VOICENOTE_WORKSPACE}/_metadata/YYYY-MM/`
230
- - State: `${VOICENOTE_WORKSPACE}/_state/jobs.json` — one record per recording, holding its `state` — where it is in its lifecycle (`queued`, `running`, `done`, `filtered`, `error`, or `gave_up` once retries are spent) — plus a `code` saying why (`summary_failed`, `transcribe_failed`, `interrupted`, `too_small`, …), its attempt count and its output paths. `vn run` writes lifecycle updates; only explicit retry/forget actions mutate it otherwise. `vn jobs` and passive GUI refreshes are pure reads, so what you see is what will run. A pre-0.18 `processed.json` is converted automatically on the first run and kept as `processed.json.v1.bak`.
231
- - Index: `${VOICENOTE_WORKSPACE}/_index/notes.jsonl`
232
-
233
- ## Automation
234
-
235
- The install script can set this up automatically. Manual setup:
236
-
237
- ```bash
238
- vn install-launch-agent
239
- launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/sh.fastagent.voicenote.plist 2>/dev/null || true
240
- launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/sh.fastagent.voicenote.plist
241
- launchctl enable gui/$(id -u)/sh.fastagent.voicenote
242
- vn status
243
- ```
244
-
245
- The LaunchAgent invokes `vn run` every 60 seconds. It skips safely when no recorder is plugged in; once the VTR6500 is connected, new recordings are processed automatically.
246
-
247
- > `config.json` changes are picked up by the background agent on its next run. The plist stores only a fixed PATH and executable paths: **after changing `VOICENOTE_PI_BIN`, re-run `vn install-launch-agent --load`** (`vn upgrade` does this automatically). Shell-only settings are deliberately not copied into the scheduler; persist them with `vn config set`. If pi or ASR is not configured, the agent skips before spending ASR.
248
-
249
- Logs:
250
-
251
- ```text
252
- ~/.local/state/voicenote/logs/launchd.out.log
253
- ~/.local/state/voicenote/logs/launchd.err.log
254
- ```
255
-
256
- ## Development
257
-
258
- ```bash
259
- git clone https://github.com/fastagent-sh/voicenote.git
260
- cd voicenote
261
- bun install
262
- bun run typecheck
263
- bun src/cli.ts doctor
264
- ```
265
-
266
- Distribution: vn ships as **source** with no build step — it only runs on bun (shebang + `bun:ffi` + `engines.bun`), and bun runs TypeScript natively, so `bin` points straight at `src/cli.ts` and the npm tarball only contains `src/{cli,jobs,runLock,tos}.ts`. The install script / `vn upgrade` install from the published npm package (`bun add -g @fastagent-sh/voicenote`); a `git+https` install also works directly (the git tree carries the source; no build or install script needed).
267
-
268
- Routine release (tag triggers CI):
269
-
270
- ```bash
271
- npm version patch
272
- git push --follow-tags
273
- ```
274
-
275
- `package.json` is the CLI version source; `vn --version` reads it directly and CI rejects a mismatched `v*` tag.
276
-
277
- The workflow lives at `.github/workflows/release.yml`: CI explicitly runs typecheck, tests, and an entry-point smoke test, then `npm publish --ignore-scripts` (deterministic publishing, no lifecycle dependence). Publishing uses **npm trusted publishing (OIDC)**: no long-lived token (`id-token: write` + a Trusted Publisher configured on npmjs.com), with provenance attached automatically. A bare local `npm publish` is still guarded by `prepublishOnly` (typecheck + tests).
278
-
279
- > Both are already done for this package (Trusted Publisher configured, CI publishing since 0.18.0 with provenance), so a routine release needs nothing but the tag. Kept for forks: npm has no pending-publisher, so trusted publishing cannot publish a package's *very first* version — publish once manually with `npm login` + `npm publish --ignore-scripts`, then add a Trusted Publisher on the package settings page at npmjs.com (repo, workflow `release.yml`); CI takes over afterwards (the npm account needs 2FA).
280
-
281
- ## Desktop app (GUI, `app/`)
282
-
283
- A self-contained macOS `.app` (Tauri v2) for **non-terminal users**: the target machine needs no pre-installed bun / pi / ffprobe / global `vn`.
284
-
285
- **Positioning**: the GUI is a status dashboard with quick access to output and manual Sync/Retry controls. The full pipeline still runs autonomously every 60s via the background LaunchAgent using the bundled CLI (it keeps running with the GUI closed).
286
-
287
- - First run: settings (identity / Volcano keys / proxy). The notes model comes from pi; ChatGPT users can sign in from the Status panel (`vn login`'s browser-callback flow).
288
- - After that: the main view shows agent activity and recent notes, opens outputs, and can retry failed recordings.
289
-
290
- ### What's bundled
291
-
292
- `bun build --compile` compiles the `vn` CLI (bun runtime + pi-ai included) into a single-file sidecar; pi cannot be compiled (it reads data files from disk at runtime), so the whole package ships alongside and runs with a bundled `bun`:
293
-
294
- | Component | Form | Purpose |
295
- |------|------|------|
296
- | `vn` (compiled) | externalBin | pipeline + ChatGPT sign-in |
297
- | `bun` | externalBin | runs pi |
298
- | `ffprobe` (native universal on macOS) | externalBin | audio duration (pi only needs ffprobe, not all of ffmpeg) |
299
- | `pi` + node_modules | resource | notes backend (ChatGPT, OpenAI API, or DeepSeek) |
300
-
301
- At runtime, Rust invokes the bundled `vn` directly and injects `VOICENOTE_PI_BIN`, `VOICENOTE_PI_CLI`, and `VOICENOTE_FFPROBE_BIN`; `vn` then runs `<bundled bun> <bundled pi/cli.js>` without a wrapper script. Release builds are **universal** (x86_64 + arm64; vn/bun/ffprobe each merged with `lipo`; pi is JS and needs none).
302
-
303
- ### Build
304
-
305
- Prerequisites: Rust + cargo, bun, node/npm, Xcode CLT, and **pi installed globally on the build machine** (`npm i -g @earendil-works/pi-coding-agent`; the build script stages pi from there).
3
+ Installs the **VoiceNote desktop app** on macOS:
306
4
 
307
5
  ```bash
308
- cd app
309
- bun install
310
- bun run tauri build
311
- # Output: src-tauri/target/release/bundle/macos/VoiceNote.app
312
- ```
313
-
314
- **Windows** (build on Windows with Rust + MSVC C++ build tools; WebView2 is preinstalled on Win10/11, NSIS is downloaded by Tauri automatically):
315
-
316
- ```powershell
317
- cd app
318
- bun install
319
- bun run tauri build --config src-tauri/tauri.windows.conf.json
320
- # Output: app\src-tauri\target\release\bundle\nsis\VoiceNote_<version>_x64-setup.exe
6
+ npx @fastagent-sh/voicenote
321
7
  ```
322
8
 
323
- Windows uses `scripts/build-vn-sidecar.ps1` to stage `vn.exe` (`--windows-hide-console`, no console window) / `bun.exe` / `ffprobe.exe` + pi; `tauri.windows.conf.json` produces the NSIS installer (currentUser, no admin).
324
-
325
- `beforeBuildCommand` first runs `scripts/build-vn-sidecar.sh` to stage vn/bun/ffprobe/pi (`binaries/` and `resources/` are gitignored; pi/ffprobe copying is idempotent). For development use `bun run tauri dev` (dev mode runs `../src/cli.ts` directly, no bundling, no background agent install).
326
-
327
- ### How users install (one line, recommended)
328
-
329
- > This is separate from the CLI `install.sh` above: the CLI script targets developers (installs bun/pi/vn); this one targets **non-technical users** (download .app → /Applications).
330
-
331
- ```bash
332
- curl -fsSL https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install-app.sh | bash
333
- ```
334
-
335
- **Windows** (one line, no admin):
336
-
337
- ```powershell
338
- irm https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install-app.ps1 | iex
339
- ```
340
-
341
- `install-app.ps1` downloads the NSIS installer from the GitHub Release (self-contained vn/bun/ffprobe/pi) → silent install into `%LOCALAPPDATA%` (no admin) → launches it.
342
-
343
- `install-app.sh` downloads the packaged `.app` from GitHub Releases → installs to `/Applications` → **removes the quarantine flag for the user** (Gatekeeper bypass for un-notarized builds) → opens it. The target machine needs no bun/pi/ffprobe/global vn (all bundled).
344
-
345
- **First launch**: the app lands on Settings. Fill in identity, your Volcano ASR/TOS keys, and proxy as needed. Notes are written by pi with pi's own provider and model; for ChatGPT, click "Sign in to ChatGPT" in the Status panel. Saving installs and loads the background LaunchAgent using the bundled CLI. Once credentials are configured, plug in the recorder for automatic transcription and notes.
346
-
347
- > The background agent label is `sh.fastagent.voicenote` (same as the CLI version; only one exists per machine). If the `.app` is moved, open it once to recalibrate the plist.
348
-
349
- **Upgrades**: since 0.1.9 the app has a built-in updater — open the app → "Settings → Software update" → "Check for updates"; when a new version appears, click "Download & install"; the app restarts automatically with config/notes preserved. For first installs, or upgrades from 0.1.8 and earlier (which had no updater), re-run the one-line install script above.
350
-
351
- > **Windows, from 0.1.11 or earlier**: those builds point their updater at the pre-rebrand repo, which still exists and stops at 0.1.11 — "Check for updates" therefore always reports "up to date". The bundle identifier changed in the same rebrand, so re-running the installer does *not* replace them; both copies stay installed under the same name. Uninstall the old VoiceNote (Settings → Apps) first, then run the one-line install. Config and notes are untouched by the uninstall.
352
-
353
- ### Maintainers: packaging + release
354
-
355
- **Automatic (recommended)**: push an `app-v*` tag to trigger `.github/workflows/release-app.yml`:
356
-
357
- ```bash
358
- git tag app-v0.1.0 && git push --tags
359
- ```
360
-
361
- **One `app-v*` tag = one Release covering mac + Windows.** `release-app.yml` is a single workflow: mac (universal + ad-hoc deep signing) and Windows (NSIS) build in parallel, then the `release` job publishes. Each Release carries:
362
-
363
- - **First-install packages** `VoiceNote.zip` (mac) / `VoiceNote-setup.exe` (win) — fetched by `install-app.*` from `releases/latest/download/...`;
364
- - **Updater artifacts** `VoiceNote.app.tar.gz` + `latest.json` — used by the in-app Tauri updater (Settings → Software update).
365
-
366
- > The updater needs signing secrets `TAURI_SIGNING_PRIVATE_KEY` / `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` (generated with `tauri signer generate`; the public key goes in `tauri.conf.json`); the `preflight` job blocks the release while the pubkey is still a placeholder.
367
-
368
- > Use `app-v*` (distinct from the CLI's `v*` npm release tags). Artifacts are **universal** (x86_64 + arm64), working on both Intel and Apple Silicon.
369
-
370
- **Manual**:
371
-
372
- ```bash
373
- cd app
374
- bash scripts/package.sh # → app/release/VoiceNote-<version>.zip (~110MB)
375
- gh release create app-v0.1.0 app/release/VoiceNote-<version>.zip#VoiceNote.zip -t "VoiceNote 0.1.0" -n "Desktop app"
376
- ```
377
-
378
- The asset name must be **`VoiceNote.zip`** (`install-app.sh` fetches `releases/latest/download/VoiceNote.zip`). For local testing bypass the Release with: `VOICENOTE_APP_URL=file:///path/to/VoiceNote.zip bash scripts/install-app.sh`.
379
-
380
- ### Signing / notarization (no `xattr`, double-click to run)
381
-
382
- JIT entitlements are in place (`src-tauri/entitlements.plist`: `allow-jit` etc. for bun/vn; referenced from `tauri.conf.json`). `scripts/sign-macos.sh` performs inside-out deep signing (hardened runtime + entitlements):
383
-
384
- ```bash
385
- # Internal ad-hoc (JIT verified to survive under hardened runtime)
386
- bash scripts/sign-macos.sh /Applications/VoiceNote.app
387
-
388
- # Official distribution (requires a Developer ID certificate, Apple Developer Program $99/yr)
389
- bash scripts/sign-macos.sh VoiceNote.app "Developer ID Application: NAME (TEAMID)"
390
- xcrun notarytool submit ... && xcrun stapler staple VoiceNote.app
391
- ```
9
+ It downloads the latest release from
10
+ [GitHub](https://github.com/fastagent-sh/voicenote/releases), replaces any
11
+ existing copy, and puts `VoiceNote.app` in `/Applications`. The app updates
12
+ itself after that, so this command is only needed once (or to repair an
13
+ install).
392
14
 
393
- ## License
15
+ Everything the app needs ships inside the bundle — no Node, no Bun, no
16
+ separate transcription tooling.
394
17
 
395
- MIT
18
+ Looking for the command line tool? That is
19
+ [`@fastagent-sh/vn`](https://www.npmjs.com/package/@fastagent-sh/vn).
package/install.mjs ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ // Installs the VoiceNote desktop app from its GitHub release.
3
+ //
4
+ // The app itself is a signed .app bundle, not something npm can hold; this
5
+ // package exists so `npx @fastagent-sh/voicenote` is a one-line install (and
6
+ // re-install, and repair) without hunting for a download page. Everything the
7
+ // app needs is inside the bundle, so there is nothing else to set up.
8
+ //
9
+ // A file downloaded by this script is not quarantined the way a browser
10
+ // download is, so the app opens on the first double-click — no right-click →
11
+ // Open dance, even though the signature is self-signed.
12
+ import { execFileSync } from 'node:child_process'
13
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises'
14
+ import { existsSync } from 'node:fs'
15
+ import { tmpdir } from 'node:os'
16
+ import { join } from 'node:path'
17
+
18
+ const REPO = 'fastagent-sh/voicenote'
19
+ const APPS = '/Applications'
20
+
21
+ function fail(message) {
22
+ console.error(`\nVoiceNote 安装失败:${message}\n`)
23
+ process.exit(1)
24
+ }
25
+
26
+ if (process.platform !== 'darwin') {
27
+ fail(`目前只有 macOS 版本(检测到 ${process.platform})。`)
28
+ }
29
+
30
+ const release = await fetch(`https://api.github.com/repos/${REPO}/releases/latest`, {
31
+ headers: { accept: 'application/vnd.github+json' },
32
+ }).then(r => r.ok ? r.json() : fail(`读取发布信息失败(HTTP ${r.status})。`))
33
+
34
+ // The zip is what ships the .app; the dmg is for people who download by hand.
35
+ const wanted = process.arch === 'arm64' ? 'arm64' : 'x64'
36
+ const asset = (release.assets ?? []).find(a => a.name.endsWith('-mac.zip') && a.name.includes(wanted))
37
+ if (!asset) fail(`这个版本没有 ${wanted} 的 macOS 包(${release.tag_name})。`)
38
+
39
+ console.log(`下载 VoiceNote ${release.tag_name.replace(/^app-v/, '')}(${(asset.size / 1e6).toFixed(0)} MB)…`)
40
+ const work = await mkdtemp(join(tmpdir(), 'voicenote-install-'))
41
+ const zipPath = join(work, asset.name)
42
+ try {
43
+ const download = await fetch(asset.browser_download_url)
44
+ if (!download.ok) fail(`下载失败(HTTP ${download.status})。`)
45
+ await writeFile(zipPath, Buffer.from(await download.arrayBuffer()))
46
+
47
+ const target = join(APPS, 'VoiceNote.app')
48
+ if (existsSync(target)) {
49
+ console.log('替换已安装的版本…')
50
+ // Quit it first: replacing a running bundle leaves the old process alive
51
+ // with files that no longer exist.
52
+ try { execFileSync('osascript', ['-e', 'tell application "VoiceNote" to quit'], { stdio: 'ignore' }) } catch { /* not running */ }
53
+ await rm(target, { recursive: true, force: true })
54
+ }
55
+ execFileSync('ditto', ['-x', '-k', zipPath, APPS], { stdio: 'inherit' })
56
+ if (!existsSync(target)) fail('解压后没有找到 VoiceNote.app。')
57
+
58
+ console.log(`\n✓ 已安装到 ${target}`)
59
+ console.log(' 打开方式:访达 → 应用程序 → VoiceNote,或执行 open -a VoiceNote')
60
+ console.log(' 应用会自己检查更新,之后不需要再跑这个命令。\n')
61
+ } catch (error) {
62
+ if (error?.code === 'EACCES' || /Permission denied/i.test(String(error?.message))) {
63
+ fail(`没有写入 ${APPS} 的权限。用 sudo 重试:sudo npx @fastagent-sh/voicenote`)
64
+ }
65
+ fail(String(error?.message ?? error))
66
+ } finally {
67
+ await rm(work, { recursive: true, force: true })
68
+ }
package/package.json CHANGED
@@ -1,58 +1,25 @@
1
1
  {
2
2
  "name": "@fastagent-sh/voicenote",
3
- "version": "0.22.1",
4
- "description": "Voice recordings → diarized transcripts → integrated semantic Markdown notes. Currently optimized for the PHILIPS VTR6500 recorder, but the workflow is generic.",
3
+ "version": "1.0.0",
4
+ "description": "Installs the VoiceNote desktop app (macOS). The command line tool is @fastagent-sh/vn.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "fastagent-sh",
8
8
  "homepage": "https://github.com/fastagent-sh/voicenote",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/fastagent-sh/voicenote.git"
11
+ "url": "git+https://github.com/fastagent-sh/voicenote.git",
12
+ "directory": "installer"
12
13
  },
13
- "bugs": {
14
- "url": "https://github.com/fastagent-sh/voicenote/issues"
15
- },
16
- "keywords": [
17
- "voice",
18
- "voicenote",
19
- "transcription",
20
- "diarization",
21
- "meeting-notes",
22
- "philips",
23
- "cli",
24
- "bun"
25
- ],
14
+ "keywords": ["voicenote", "installer", "macos", "transcription", "meeting-notes"],
26
15
  "bin": {
27
- "vn": "src/cli.ts"
28
- },
29
- "files": [
30
- "src/cli.ts",
31
- "src/jobs.ts",
32
- "src/runLock.ts",
33
- "src/tos.ts",
34
- "README.md",
35
- "LICENSE"
36
- ],
37
- "scripts": {
38
- "dev": "bun src/cli.ts",
39
- "typecheck": "tsc --noEmit",
40
- "test": "bun test",
41
- "doctor": "bun src/cli.ts doctor",
42
- "prepublishOnly": "bun run typecheck && bun test ./src"
16
+ "voicenote": "install.mjs"
43
17
  },
18
+ "files": ["install.mjs", "README.md"],
44
19
  "publishConfig": {
45
20
  "access": "public"
46
21
  },
47
- "dependencies": {
48
- "@earendil-works/pi-ai": "^0.79.3",
49
- "cac": "latest"
50
- },
51
- "devDependencies": {
52
- "@types/bun": "latest",
53
- "typescript": "latest"
54
- },
55
22
  "engines": {
56
- "bun": ">=1.3.0"
23
+ "node": ">=20"
57
24
  }
58
25
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 fastagent-sh
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.