@fastagent-sh/voicenote 0.21.0 → 0.22.1

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
@@ -48,7 +48,7 @@ VOLCANO_TOS_SECRET_KEY="..." \
48
48
  bash <(curl -fsSL https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install.sh)
49
49
  ```
50
50
 
51
- The first install creates `~/.config/voicenote/config.json`. A legacy `speakers.json` is still read for compatibility and migrated into `config.json.speakers`. Once configured, run `vn doctor` to check the environment, and `vn install-launch-agent` if you want background monitoring.
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
52
 
53
53
  Manual install:
54
54
 
@@ -69,7 +69,7 @@ The CLI is cross-platform. Prerequisites: Bun, ffmpeg (provides `ffprobe.exe`),
69
69
  bun remove -g @kid7st/voicenote 2>$null # drop the pre-rebrand package if present (safe no-op otherwise)
70
70
  bun add -g @fastagent-sh/voicenote
71
71
  # Windows has no /Volumes mount points; set the recorder drive explicitly
72
- setx VOICENOTE_RECORD_DIR "E:\RECORD"
72
+ '{"env":{"VOICENOTE_RECORD_DIR":"E:\\RECORD"}}' | vn config set
73
73
  ```
74
74
 
75
75
  - Config: `%APPDATA%\voicenote\config.json`; logs/locks: `%LOCALAPPDATA%\voicenote\`
@@ -105,6 +105,8 @@ The install script only writes `vn` / Bun / Homebrew PATH entries to your shell
105
105
  }
106
106
  ```
107
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
+
108
110
  Optional settings:
109
111
 
110
112
  ```json
@@ -201,9 +203,7 @@ The install script writes an editable template:
201
203
  }
202
204
  ```
203
205
 
204
- Changes take effect on the next `vn run`. Config values and unquoted/double-quoted `.zshrc` exports support simple `$VAR` / `${VAR}` references to other settings and `$HOME`. Single-quoted shell values stay literal. Shell commands are never executed. Runtime references honor inherited environment values; scheduler comparisons resolve from files alone.
205
-
206
- A legacy `~/.config/voicenote/speakers.json` is still read as a compatibility fallback.
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
207
 
208
208
  ## Workflow
209
209
 
@@ -215,19 +215,19 @@ A legacy `~/.config/voicenote/speakers.json` is still read as a compatibility fa
215
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
216
  7. Write notes / metadata; the system makes no archiving decisions — files stay in the configured workspace
217
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 — `vn forget <name>` drops the record and re-queues it. Re-queuing is not the same as re-transcribing: if the transcript is already on disk it is reused, so `vn forget` never re-pays for ASR. (`vn forget` takes the run lock, so it refuses while a run is in progress — wait for that run to finish and repeat.)
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
219
 
220
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
221
 
222
222
  ## Output locations
223
223
 
224
- The installer defaults to `VOICENOTE_WORKSPACE=~/Documents/meetings`.
224
+ `VOICENOTE_WORKSPACE` defaults to `~/Documents/meetings`.
225
225
 
226
226
  - Notes entry point: `${VOICENOTE_WORKSPACE}/YYYY-MM/`
227
227
  - Original audio: `${VOICENOTE_WORKSPACE}/_audio/YYYY-MM/`
228
228
  - Full transcripts: `${VOICENOTE_WORKSPACE}/_transcripts/YYYY-MM/`
229
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` is the only writer; `vn jobs` and the GUI dashboard are pure reads of it, 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`.
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
231
  - Index: `${VOICENOTE_WORKSPACE}/_index/notes.jsonl`
232
232
 
233
233
  ## Automation
@@ -244,9 +244,7 @@ vn status
244
244
 
245
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
246
 
247
- > Config changes (`config.json` or `~/.zshrc`) are picked up automatically by the background agent on its next run — no reinstall needed. The plist only snapshots real environment variables and pi's absolute path: **after changing `VOICENOTE_PI_BIN`, re-run `vn install-launch-agent` and reload** (`vn upgrade` regenerates the plist automatically). If pi is not signed in or ASR is not configured, the agent skips processing instead of burning ASR spend.
248
- >
249
- > Proxy values that match the file configuration, including expanded variable references, are not embedded and produce no override warning. Values supplied only by the shell, or differing from the files, are embedded as explicit overrides. To clear an unwanted override, update or unset the shell variable, then run `vn install-launch-agent --load`. Prefer `LOCAL_PROXY_HOST`/`LOCAL_PROXY_PORT` in `config.json` for proxy configuration.
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.
250
248
 
251
249
  Logs:
252
250
 
@@ -265,18 +263,18 @@ bun run typecheck
265
263
  bun src/cli.ts doctor
266
264
  ```
267
265
 
268
- 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,envConfig,jobs,runLock}.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).
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).
269
267
 
270
268
  Routine release (tag triggers CI):
271
269
 
272
270
  ```bash
273
- npm version patch # then sync `VERSION` in src/cli.ts to match
271
+ npm version patch
274
272
  git push --follow-tags
275
273
  ```
276
274
 
277
- `src/cli.ts` hardcodes `VERSION` for `vn --version`, and `npm version` does not touch it — update both in the same commit or the CLI will report a version it isn't.
275
+ `package.json` is the CLI version source; `vn --version` reads it directly and CI rejects a mismatched `v*` tag.
278
276
 
279
- The workflow lives at `.github/workflows/release.yml`: CI explicitly runs typecheck/test/build + an artifact 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+test+build).
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).
280
278
 
281
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).
282
280
 
@@ -284,23 +282,23 @@ The workflow lives at `.github/workflows/release.yml`: CI explicitly runs typech
284
282
 
285
283
  A self-contained macOS `.app` (Tauri v2) for **non-terminal users**: the target machine needs no pre-installed bun / pi / ffprobe / global `vn`.
286
284
 
287
- **Positioning**: the GUI is only a "status dashboard + quick access to output" — it does **not** drive processing. The full pipeline runs autonomously every 60s via the background LaunchAgent using the bundled engine (it keeps running with the GUI closed).
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).
288
286
 
289
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).
290
- - After that: the main view shows agent activity + recent notes (open note / open folder)
288
+ - After that: the main view shows agent activity and recent notes, opens outputs, and can retry failed recordings.
291
289
 
292
290
  ### What's bundled
293
291
 
294
- `bun build --compile` compiles the `vn` engine (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`:
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`:
295
293
 
296
294
  | Component | Form | Purpose |
297
295
  |------|------|------|
298
296
  | `vn` (compiled) | externalBin | pipeline + ChatGPT sign-in |
299
297
  | `bun` | externalBin | runs pi |
300
- | `ffprobe` (native arm64 static) | externalBin | audio duration (pi only needs ffprobe, not all of ffmpeg) |
298
+ | `ffprobe` (native universal on macOS) | externalBin | audio duration (pi only needs ffprobe, not all of ffmpeg) |
301
299
  | `pi` + node_modules | resource | notes backend (ChatGPT, OpenAI API, or DeepSeek) |
302
300
 
303
- At runtime, Rust generates a wrapper (`exec <bundled bun> <bundled pi/cli.js> "$@"`) and injects `VOICENOTE_PI_BIN` / `VOICENOTE_FFPROBE_BIN` into `vn`. Release builds are **universal** (x86_64 + arm64; vn/bun/ffprobe each merged with `lipo`; pi is JS and needs none).
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).
304
302
 
305
303
  ### Build
306
304
 
@@ -344,7 +342,7 @@ irm https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/instal
344
342
 
345
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).
346
344
 
347
- **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 engine. Once credentials are configured, plug in the recorder for automatic transcription and notes.
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.
348
346
 
349
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.
350
348
 
package/README.zh-CN.md CHANGED
@@ -48,7 +48,7 @@ VOLCANO_TOS_SECRET_KEY="..." \
48
48
  bash <(curl -fsSL https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/install.sh)
49
49
  ```
50
50
 
51
- 首次安装会生成 `~/.config/voicenote/config.json`。旧版本的 `speakers.json` 会被自动兼容读取/迁移到 `config.json.speakers`。配置完成后再运行 `vn doctor` 检查,需要后台自动监控时再运行 `vn install-launch-agent`。
51
+ 首次安装会生成 `~/.config/voicenote/config.json`。配置完成后运行 `vn doctor` 检查,需要后台自动监控时再运行 `vn install-launch-agent`。
52
52
 
53
53
  手动安装:
54
54
 
@@ -69,7 +69,7 @@ CLI 已跨平台。前置:Bun、ffmpeg(提供 `ffprobe.exe`)、Node + pi。
69
69
  bun remove -g @kid7st/voicenote 2>$null # 若装过改名前的旧包则清掉(没装则安全跳过)
70
70
  bun add -g @fastagent-sh/voicenote
71
71
  # Windows 无 /Volumes 挂载点,录音盘按盘符设置
72
- setx VOICENOTE_RECORD_DIR "E:\RECORD"
72
+ '{"env":{"VOICENOTE_RECORD_DIR":"E:\\RECORD"}}' | vn config set
73
73
  ```
74
74
 
75
75
  - 配置:`%APPDATA%\voicenote\config.json`;日志/锁:`%LOCALAPPDATA%\voicenote\`
@@ -105,6 +105,8 @@ brew install ffmpeg
105
105
  }
106
106
  ```
107
107
 
108
+ 留空或不写的键 = 用内置默认值;这些默认值只定义在 `src/cli.ts`,所以安装脚本和 GUI 都把这类字段留空。
109
+
108
110
  可选配置:
109
111
 
110
112
  ```json
@@ -196,7 +198,7 @@ vn uninstall-launch-agent
196
198
  }
197
199
  ```
198
200
 
199
- 修改后下一次 `vn run` 即生效。旧版 `~/.config/voicenote/speakers.json` 仍会作为兼容 fallback 读取。
201
+ 修改后下一次 `vn run` 即生效。路径配置开头支持 `~`、`$HOME`、`${HOME}`。环境变量只覆盖当前 CLI 进程;后台运行读取 `config.json`,不读取 shell 启动文件。
200
202
 
201
203
  ## 工作流程
202
204
 
@@ -208,19 +210,19 @@ vn uninstall-launch-agent
208
210
  6. summary 模型(由 pi 自身配置决定)直接看原始 transcript,在纪要生成阶段内部完成必要清理、说话人还原、观点/争论/共识形成过程还原;如果 summary 失败,下一次 `vn run` / `vn run --latest` 会复用已保存 transcript,直接重试纪要生成,不需要 `vn forget`
209
211
  7. 写出 notes / metadata;系统不做任何归档决定,文件留在配置的 workspace 中
210
212
 
211
- 失败的录音会在后续运行中重试,但**最多 3 次**(转写失败、纪要失败、以及被中途 kill 的运行都算)。超过后标记为 `Gave up` 并不再自动重试,避免一个坏文件每个调度周期都烧一次 ASR/LLM 额度 —— `vn forget <name>` 会删掉该记录并重新入队。重新入队不等于重新转写:磁盘上已有 transcript 时会直接复用,所以 `vn forget` 不会让你再付一次 ASR。(`vn forget` 需要 run lock,因此在某次 run 进行中时会拒绝执行 —— 等该次 run 结束后重试即可。)
213
+ 失败的录音会在后续运行中重试,但**最多 3 次**(转写失败、纪要失败、以及被中途 kill 的运行都算)。超过后标记为 `Gave up` 并不再自动重试,避免一个坏文件每个调度周期都烧一次 ASR/LLM 额度。点击 GUI 记录上的**重试**会重置次数、保留已有产物并立即再跑;CLI 也可以用 `vn forget <name>` 删除记录后重新入队。两种方式都会复用磁盘上已有的 transcript,不会重复支付 ASR 费用。它们都需要 run lock;如果当前正在处理,请等本次 run 结束后再重试。
212
214
 
213
215
  源文件已不在录音笔上的记录,会在下一次扫描时被遗忘(并记入日志),**已经产出纪要或 transcript 的除外** —— 那部分历史会保留。所以换录音笔、或从设备上删文件,不再会留下永久的 “失败” 条目。
214
216
 
215
217
  ## 输出位置
216
218
 
217
- installer 默认设置:`VOICENOTE_WORKSPACE=~/Documents/meetings`。
219
+ `VOICENOTE_WORKSPACE` 默认为 `~/Documents/meetings`。
218
220
 
219
221
  - 笔记入口:`${VOICENOTE_WORKSPACE}/YYYY-MM/`
220
222
  - 原始音频:`${VOICENOTE_WORKSPACE}/_audio/YYYY-MM/`
221
223
  - 完整转写:`${VOICENOTE_WORKSPACE}/_transcripts/YYYY-MM/`
222
224
  - metadata:`${VOICENOTE_WORKSPACE}/_metadata/YYYY-MM/`
223
- - 状态:`${VOICENOTE_WORKSPACE}/_state/jobs.json` —— 每条录音一条记录,包含 `state`(生命周期位置:`queued`、`running`、`done`、`filtered`、`error`,以及重试耗尽后的 `gave_up`)、`code`(原因:`summary_failed`、`transcribe_failed`、`interrupted`、`too_small` 等)、重试次数和产物路径。`vn run` 是唯一的写入方,`vn jobs` 和 GUI 面板都只是它的纯读取 —— 你看到的队列就是会跑的队列。0.18 之前的 `processed.json` 会在首次运行时自动转换,旧文件保留为 `processed.json.v1.bak`。
225
+ - 状态:`${VOICENOTE_WORKSPACE}/_state/jobs.json` —— 每条录音一条记录,包含 `state`(生命周期位置:`queued`、`running`、`done`、`filtered`、`error`,以及重试耗尽后的 `gave_up`)、`code`(原因:`summary_failed`、`transcribe_failed`、`interrupted`、`too_small` 等)、重试次数和产物路径。`vn run` 写入正常生命周期变化,除此之外只有显式重试或 forget 操作会修改它;`vn jobs` 和 GUI 的被动刷新都是纯读取,因此看到的队列就是实际会跑的队列。0.18 之前的 `processed.json` 会在首次运行时自动转换,旧文件保留为 `processed.json.v1.bak`。
224
226
  - 索引:`${VOICENOTE_WORKSPACE}/_index/notes.jsonl`
225
227
 
226
228
  ## 自动化
@@ -237,9 +239,7 @@ vn status
237
239
 
238
240
  LaunchAgent 每 60 秒调用 `vn run`。没插录音笔时安全跳过;插上 VTR6500 后自动处理新录音。
239
241
 
240
- > 配置改动(`config.json` 或 `~/.zshrc`)会被后台 agent 在下一次运行时自动读取,无需重装。plist 只快照真实环境变量和 pi 的绝对路径:**改了 `VOICENOTE_PI_BIN` 后需重跑 `vn install-launch-agent` 并 reload**(`vn upgrade` 会自动重生成 plist)。未登录 pi / ASR 未配置时,agent 会跳过处理而不会白烧 ASR。
241
- >
242
- > 例外:若你在 shell 里直接 `export http_proxy=...`(而非用 `LOCAL_PROXY_HOST`)后跑 `vn install-launch-agent`,这个真实环境值会被快照进 plist 并持续覆盖后续对 `LOCAL_PROXY_HOST` 的修改;需重跑 `vn install-launch-agent` 才能清除。推荐统一用 `LOCAL_PROXY_HOST`/`LOCAL_PROXY_PORT` 配置代理。
242
+ > 后台 agent 会在下一次运行时读取 `config.json` 的改动。plist 只保存固定 PATH 和可执行文件路径:**改了 `VOICENOTE_PI_BIN` 后需重跑 `vn install-launch-agent --load`**(`vn upgrade` 会自动处理)。shell 中临时设置的值不会复制进 scheduler,请用 `vn config set` 持久化。未配置 pi / ASR 时,agent 会在支付 ASR 成本前跳过。
243
243
 
244
244
  日志:
245
245
 
@@ -258,18 +258,18 @@ bun run typecheck
258
258
  bun src/cli.ts doctor
259
259
  ```
260
260
 
261
- 分发:vn 以**源码**分发,没有构建步骤 —— 它只在 bun 上运行(shebang + `bun:ffi` + `engines.bun`),而 bun 原生跑 TypeScript,所以 `bin` 直接指向 `src/cli.ts`,npm tarball 只带 `src/{cli,envConfig,jobs,runLock}.ts`。安装脚本 / `vn upgrade` 从已发布的 npm 包安装(`bun add -g @fastagent-sh/voicenote`);`git+https` 安装也能直接用(git 树自带源码,无需 build 或安装脚本)。
261
+ 分发:vn 以**源码**分发,没有构建步骤 —— 它只在 bun 上运行(shebang + `bun:ffi` + `engines.bun`),而 bun 原生跑 TypeScript,所以 `bin` 直接指向 `src/cli.ts`,npm tarball 只带 `src/{cli,jobs,runLock,tos}.ts`。安装脚本 / `vn upgrade` 从已发布的 npm 包安装(`bun add -g @fastagent-sh/voicenote`);`git+https` 安装也能直接用(git 树自带源码,无需 build 或安装脚本)。
262
262
 
263
263
  日常发布(打 tag 触发 CI):
264
264
 
265
265
  ```bash
266
- npm version patch # 然后把 src/cli.ts 里的 `VERSION` 同步成一样
266
+ npm version patch
267
267
  git push --follow-tags
268
268
  ```
269
269
 
270
- `src/cli.ts` 里硬编码了 `VERSION`(供 `vn --version` 用),而 `npm version` 不会改它 —— 请在同一个 commit 里一起更新,否则 CLI 会报一个它并不是的版本号。
270
+ `package.json` 是 CLI 的版本来源;`vn --version` 直接读取它,CI 会拒绝版本不匹配的 `v*` tag。
271
271
 
272
- workflow 位于 `.github/workflows/release.yml`:CI 显式跑 typecheck/test/build + 产物冒烟,再 `npm publish --ignore-scripts`(确定发布,不依赖 lifecycle)。发布走 **npm trusted publishing(OIDC)**:免长期 token(`id-token: write` + npmjs.com 上配好 Trusted Publisher),自动带 provenance。本地裸 `npm publish` 则由 `prepublishOnly`(typecheck+test+build)兼底。
272
+ workflow 位于 `.github/workflows/release.yml`:CI 显式跑 typecheck、测试和入口冒烟,再 `npm publish --ignore-scripts`(确定发布,不依赖 lifecycle)。发布走 **npm trusted publishing(OIDC)**:免长期 token(`id-token: write` + npmjs.com 上配好 Trusted Publisher),自动带 provenance。本地裸 `npm publish` 则由 `prepublishOnly`(typecheck + 测试)兜底。
273
273
 
274
274
  > 本包这两步都已完成(Trusted Publisher 已配置,自 0.18.0 起由 CI 发布并带 provenance),常规发版只需打 tag。以下保留给 fork 者:npm 无 pending-publisher,trusted publishing 发不了包的**第一个**版本 —— 先本机 `npm login` 后手动 `npm publish --ignore-scripts` 发一次,再到 npmjs.com 包设置页加 Trusted Publisher(repo、workflow `release.yml`),之后 CI 自动接管(需 npm 账号开 2FA)。
275
275
 
@@ -277,23 +277,23 @@ workflow 位于 `.github/workflows/release.yml`:CI 显式跑 typecheck/test/buil
277
277
 
278
278
  面向**非终端用户**:一个自包含的 macOS `.app`(Tauri v2),目标机器无需预装 bun / pi / ffprobe / 全局 `vn`。
279
279
 
280
- **定位**:GUI 只是「工作状态 dashboard + 产出快捷入口」,**不驱动处理**。真正的全流程由后台 LaunchAgent 用包内引擎每 60s 自主运行(关掉 GUI 也跑)。
280
+ **定位**:GUI 是工作状态 dashboard,提供产出快捷入口以及手动同步/重试。全流程仍由后台 LaunchAgent 用包内 CLI 每 60s 自主运行(关掉 GUI 也跑)。
281
281
 
282
282
  - 首次:配置向导(身份 / Volcano keys / 代理)→ ChatGPT 登录(设备无终端,走 `vn login` 的浏览器回调流)
283
- - 之后:主界面显示 agent 活动 + 最近纪要(点开 / 打开文件夹)
283
+ - 之后:主界面显示 agent 活动和最近纪要,可打开产物或重试失败录音
284
284
 
285
285
  ### 打包内容
286
286
 
287
- `bun build --compile` 把 `vn` 引擎(含 bun 运行时 + pi-ai)编成单文件 sidecar;pi 不能 compile(运行时读磁盘数据文件),故整包随行,用一个随包的 `bun` 运行:
287
+ `bun build --compile` 把 `vn` CLI(含 bun 运行时 + pi-ai)编成单文件 sidecar;pi 不能 compile(运行时读磁盘数据文件),故整包随行,用一个随包的 `bun` 运行:
288
288
 
289
289
  | 组件 | 形式 | 用途 |
290
290
  |------|------|------|
291
291
  | `vn`(编译版) | externalBin | pipeline + ChatGPT 登录 |
292
292
  | `bun` | externalBin | 跑 pi |
293
- | `ffprobe`(原生 arm64 静态) | externalBin | 音频时长(pi 只用 ffprobe,不用整个 ffmpeg) |
293
+ | `ffprobe`(macOS 原生 universal) | externalBin | 音频时长(pi 只用 ffprobe,不用整个 ffmpeg) |
294
294
  | `pi` + node_modules | resource | 纪要后端(ChatGPT Codex agent) |
295
295
 
296
- 运行时 Rust 生成一个 wrapper(`exec <包内bun> <包内pi/cli.js> "$@"`)并给 `vn` 注入 `VOICENOTE_PI_BIN` / `VOICENOTE_FFPROBE_BIN`。发布构建为 **universal**(x86_64 + arm64,vn/bun/ffprobe 各自 `lipo` 合并;pi 是 JS 无需)。
296
+ 运行时 Rust 直接调用包内 `vn`,并注入 `VOICENOTE_PI_BIN`、`VOICENOTE_PI_CLI`、`VOICENOTE_FFPROBE_BIN`;`vn` 无 wrapper 地运行 `<包内bun> <包内pi/cli.js>`。发布构建为 **universal**(x86_64 + arm64,vn/bun/ffprobe 各自 `lipo` 合并;pi 是 JS 无需)。
297
297
 
298
298
  ### 构建
299
299
 
@@ -337,7 +337,7 @@ irm https://raw.githubusercontent.com/fastagent-sh/voicenote/main/scripts/instal
337
337
 
338
338
  `install-app.sh` 会:从 GitHub Releases 下载已打包的 `.app` → 装到 `/Applications` → **替用户去掉隔离标记**(未公证时绕过 Gatekeeper)→ 打开。目标机器无需 bun/pi/ffprobe/全局 vn(全内置)。
339
339
 
340
- **首次打开**:应用落在「设置」页 → 填身份 + 自己的火山 ASR/TOS 密钥 + 代理(BYOK)→ 保存 → 「状态」面板点「登录 ChatGPT」(浏览器授权一次)。完成后 GUI 自动安装并加载后台 LaunchAgent(指向包内引擎),插上录音笔即自动转写+生成纪要。
340
+ **首次打开**:应用落在「设置」页 → 填身份 + 自己的火山 ASR/TOS 密钥 + 代理(BYOK)→ 保存 → 「状态」面板点「登录 ChatGPT」(浏览器授权一次)。完成后 GUI 自动安装并加载后台 LaunchAgent(指向包内 CLI),插上录音笔即自动转写+生成纪要。
341
341
 
342
342
  > 后台 agent label 是 `sh.fastagent.voicenote`(与 CLI 版同名,机器上只保留一个)。`.app` 换位置后再打开一次即可重新校准 plist。
343
343
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastagent-sh/voicenote",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
4
4
  "description": "Voice recordings → diarized transcripts → integrated semantic Markdown notes. Currently optimized for the PHILIPS VTR6500 recorder, but the workflow is generic.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,9 +28,9 @@
28
28
  },
29
29
  "files": [
30
30
  "src/cli.ts",
31
- "src/envConfig.ts",
32
31
  "src/jobs.ts",
33
32
  "src/runLock.ts",
33
+ "src/tos.ts",
34
34
  "README.md",
35
35
  "LICENSE"
36
36
  ],
@@ -39,7 +39,7 @@
39
39
  "typecheck": "tsc --noEmit",
40
40
  "test": "bun test",
41
41
  "doctor": "bun src/cli.ts doctor",
42
- "prepublishOnly": "bun run typecheck && bun test src/"
42
+ "prepublishOnly": "bun run typecheck && bun test ./src"
43
43
  },
44
44
  "publishConfig": {
45
45
  "access": "public"
@@ -50,8 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/bun": "latest",
53
- "typescript": "latest",
54
- "unrun": "^0.3.0"
53
+ "typescript": "latest"
55
54
  },
56
55
  "engines": {
57
56
  "bun": ">=1.3.0"