vocalize-cli 0.10.2__tar.gz → 0.11.0__tar.gz
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.
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/CHANGELOG.md +96 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/PKG-INFO +39 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/README.md +38 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/installation.md +13 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/decisions.md +471 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/design.md +127 -1
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/review-0.11.0.md +134 -0
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/run-10-release-0-11-0/report.md +153 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-10-release-0-11-0/validate-exit.sh +31 -2
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/run-7-portal-read/report.md +297 -0
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/run-7-portal-read/review-findings.md +130 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-7-portal-read/validate-exit.sh +8 -1
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/run-8-portal-write/report.md +346 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-8-portal-write/validate-exit.sh +29 -2
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/run-9-portal-page/report.md +231 -0
- vocalize_cli-0.11.0/docs/plans/2026-09-next-features/run-9-portal-page/review-findings.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-9-portal-page/validate-exit.sh +25 -1
- vocalize_cli-0.11.0/docs/research/2026-09-04-app-roadmap-analysis.md +290 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/roadmap.md +3 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/conftest.py +33 -0
- vocalize_cli-0.11.0/tests/portal_page_harness.js +713 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_auth.py +10 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_cli.py +222 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_config.py +21 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_dictate.py +17 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_elevenlabs_provider.py +1 -1
- vocalize_cli-0.11.0/tests/test_portal.py +3835 -0
- vocalize_cli-0.11.0/tests/test_portal_assets.py +393 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_readiness.py +44 -15
- vocalize_cli-0.11.0/tests/test_tts.py +411 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_wizard.py +375 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/__init__.py +1 -1
- vocalize_cli-0.11.0/vocalize/assets/portal.html +295 -0
- vocalize_cli-0.11.0/vocalize/assets/portal.js +1486 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/auth.py +8 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/cli.py +58 -9
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/config.py +22 -8
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/exceptions.py +15 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/local/install.py +10 -0
- vocalize_cli-0.11.0/vocalize/portal.py +1693 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/readiness.py +31 -6
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/tts.py +44 -5
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/wizard.py +183 -17
- vocalize_cli-0.10.2/tests/test_tts.py +0 -201
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/.env.example +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/.github/workflows/ci.yml +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/.gitignore +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/LICENSE +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/dictation.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/next-features-analysis.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/choreography.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/review-0.10.0.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-1-status/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-1-status/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-1-status/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-10-release-0-11-0/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-3-recorder/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-3-recorder/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-3-recorder/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-4-dictation/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-4-dictation/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-4-dictation/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-5-resume/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-5-resume/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-5-resume/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-6-release-0-10-0/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-6-release-0-10-0/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-6-release-0-10-0/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-7-portal-read/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-8-portal-write/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/run-9-portal-page/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/spike-2026-09-01.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/split-assessment.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/plans/2026-09-next-features/verification.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/provider-credentials.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/research/2026-09-01-config-portal-design.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/research/2026-09-01-dictation-design.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/docs/research/2026-09-01-voicebox-findings.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/claude_stop_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/install_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/install_quick_action.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Dictate with Vocalize.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Dictate with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Speak Latest Plan.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Speak Latest Plan.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Stop Vocalize.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/quick_actions/Stop Vocalize.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/speak_options.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/hooks/speak_url_gate.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/pyproject.toml +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_audio.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_cache.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_chain.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_claude_stop_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_clipboard.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_cue_assets.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_exceptions.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_google_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_http.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_install_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_install_quick_action.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_kokoro_manifest.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_kokoro_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_kokoro_worker.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_ledger.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_listen_check.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_local_install.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_openai_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_polly_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_preprocess.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_providers_registry.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_recorder_build.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_say_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_speak_options.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_speak_url_gate.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_uv_path.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_whisper_manifest.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/tests/test_whisper_worker.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/__main__.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/assets/cues/README.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/assets/cues/ready.wav +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/assets/cues/start.wav +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/assets/cues/stopped.wav +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/audio.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/cache.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/chain.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/clipboard.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/dictate.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/interrupted.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/ledger.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/local/__init__.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/local/kokoro_manifest.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/local/kokoro_worker.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/local/whisper_manifest.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/local/whisper_worker.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/preprocess.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/__init__.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/_http.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/elevenlabs.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/google.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/kokoro.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/openai.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/polly.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/providers/say.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/recorder/Info.plist.in +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/recorder/Recorder.entitlements +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.11.0}/vocalize/recorder/VocalizeRecorder.swift +0 -0
|
@@ -3,6 +3,102 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## 0.11.0 - 2026-09-05
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **A web portal page** (`vocalize portal`) for the whole config surface: reorder
|
|
11
|
+
the provider chain; set each provider's voice, speed and monthly budget with a
|
|
12
|
+
live preview; store and check API keys (masked, `autocomplete="off"`); watch
|
|
13
|
+
usage; and drive the local Kokoro and whisper installs with progress — no more
|
|
14
|
+
hand-editing `config.toml`. Five tabs (Chain, Providers, Keys, Usage, Local)
|
|
15
|
+
plus a persistent readiness sidebar reading `/api/state`, with buttons on the
|
|
16
|
+
rows that map to a vocalize command the portal already runs. Every save goes
|
|
17
|
+
through the same compare-and-swap the CLI uses, so a change made elsewhere
|
|
18
|
+
while the page was open is refused rather than silently overwritten. Ships as
|
|
19
|
+
static HTML and JS with no framework, no inline script and no external
|
|
20
|
+
resource — the whole surface has to work with the network cable pulled — over
|
|
21
|
+
a loopback-only, one-time-code session (DEC-004).
|
|
22
|
+
- **A voice picker on the Providers tab.** `GET /api/voices/<name>` returns the
|
|
23
|
+
provider's own `list_voices()`, paginated and cached for the life of the
|
|
24
|
+
portal process, so the field is a real dropdown of the account's actual
|
|
25
|
+
voices with the current value pre-selected, and `"Type a voice id…"` for an
|
|
26
|
+
id the list doesn't carry — rather than a hand-typed guess.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`vocalize voices` and `vocalize usage` printed the ElevenLabs API key to
|
|
31
|
+
stderr when the API quoted it back.** The SDK's `ApiError` renders the whole
|
|
32
|
+
response body and header dict, and both commands printed that text verbatim —
|
|
33
|
+
so a key the API named in an error reached terminal scrollback, any session
|
|
34
|
+
log, and whatever the user pasted into a bug report. `auth.scrub` already
|
|
35
|
+
existed for exactly this, but three callers never reached it. The scrub now
|
|
36
|
+
happens inside `tts` itself, below every caller, against the key the client
|
|
37
|
+
was built with. Found by the 0.11.0 release review (DEC-021).
|
|
38
|
+
- **The portal's `say` voice list failed on the first ask.** The route gave every
|
|
39
|
+
provider the two-second budget meant for network probes, and enumerating the
|
|
40
|
+
system voices takes longer than that on a current Mac — so the one provider
|
|
41
|
+
needing no key, no account and no network was the one that said "couldn't
|
|
42
|
+
fetch the list". Offline lists get their own budget now.
|
|
43
|
+
- **A key of four characters or fewer was shown in full** by `vocalize auth
|
|
44
|
+
status` and on the portal's Keys tab — a truncated paste, or another tool's
|
|
45
|
+
short secret in the same environment variable, reproduced whole in output the
|
|
46
|
+
design treats as safe to screenshot. Anything under eight characters now masks
|
|
47
|
+
to `…` alone.
|
|
48
|
+
- **The portal's Keys tab pointed at Keychain Access** to remove a stored key.
|
|
49
|
+
It now names `vocalize auth logout --provider <name>`, which reads the entry
|
|
50
|
+
back and refuses to claim a removal it cannot verify.
|
|
51
|
+
- **The ElevenLabs SDK client followed a redirect and re-sent the API key
|
|
52
|
+
wherever it pointed, cross-origin, over plain http.** The SDK's httpx client
|
|
53
|
+
follows a 3xx by default; every ElevenLabs endpoint is a fixed URL, so a
|
|
54
|
+
redirect was never legitimate and the key had no business leaving the
|
|
55
|
+
original origin. Present since 0.9.0, when the ElevenLabs provider first
|
|
56
|
+
shipped. Closed by building the client with `follow_redirects=False` — the
|
|
57
|
+
same rule the urllib-based providers already held via `_http._NoRedirects` —
|
|
58
|
+
and proven on two real loopback HTTP servers: the second origin never sees
|
|
59
|
+
the request.
|
|
60
|
+
- **Nothing could rewrite a config file containing an `[stt]` table** —
|
|
61
|
+
which is every config a dictation user has had since 0.10.0 added the
|
|
62
|
+
table. The serialiser wrote flat keys and `[providers.*]` and had no
|
|
63
|
+
case for any other table, so `stt` reached the scalar renderer as a
|
|
64
|
+
`dict` and raised: "The config file has a table under 'stt'. The wizard
|
|
65
|
+
only manages flat keys, so it will not rewrite this file — edit that
|
|
66
|
+
file by hand." That refusal came out of `vocalize wizard` before it
|
|
67
|
+
asked its first question, and out of `vocalize chain google say` instead
|
|
68
|
+
of a write. The only way out was to delete the `[stt]` table, run the
|
|
69
|
+
command, and put it back. `[stt]` is now rendered as its own table, so
|
|
70
|
+
both commands rewrite the file and leave the dictation settings alone.
|
|
71
|
+
The rewritten file puts `[stt]` ahead of `[providers.*]` whatever order
|
|
72
|
+
they were in before — identical TOML, a one-off cosmetic diff.
|
|
73
|
+
- **Two writers saving at the same moment could lose one of the two
|
|
74
|
+
changes.** The config write compared the file against the fingerprint it
|
|
75
|
+
had read and then renamed a temp file over it, with the two steps not
|
|
76
|
+
held together, so two saves that overlapped could both see an unchanged
|
|
77
|
+
file, both write, and both report success while only the second survived.
|
|
78
|
+
The compare and the rename now happen under one lock. Two `vocalize`
|
|
79
|
+
*processes* saving in the same instant can still race — the fingerprint
|
|
80
|
+
check narrows that to a window of microseconds, and closing it entirely
|
|
81
|
+
needs a lock file, which is deliberately not what this is.
|
|
82
|
+
- **A config save rendered through a fixed `config.toml.tmp`**, so two
|
|
83
|
+
writers at once could render through the same temp file and leave one of
|
|
84
|
+
them writing a file the other had already renamed away — reported as
|
|
85
|
+
"Could not write config file", or worse, reported as success for bytes
|
|
86
|
+
that were not in the file. Each writer now gets its own temp file, made
|
|
87
|
+
with `mkstemp` in the same directory, still `0600`, and still renamed
|
|
88
|
+
into place atomically. The predictable name a local process could
|
|
89
|
+
pre-plant as a symlink is gone with it.
|
|
90
|
+
|
|
91
|
+
### Known limitation
|
|
92
|
+
|
|
93
|
+
- **Any other process on the Mac can close the portal with five wrong or
|
|
94
|
+
missing-origin requests to `/api/session`**, no code and no token needed —
|
|
95
|
+
the same guard that stops a cross-origin web page from doing it has no way
|
|
96
|
+
to tell that traffic apart from a local script's. This is availability
|
|
97
|
+
only: nothing is read or changed, the portal just exits. Rerun
|
|
98
|
+
`vocalize portal` and carry on. Accepted rather than fixed, since anything
|
|
99
|
+
able to send that traffic locally could kill the process outright anyway
|
|
100
|
+
(DEC-018).
|
|
101
|
+
|
|
6
102
|
## 0.10.2 - 2026-09-02
|
|
7
103
|
|
|
8
104
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vocalize-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: A CLI that turns text, markdown, or piped stdin into speech via the ElevenLabs API, with markdown-table-aware preprocessing.
|
|
5
5
|
Project-URL: Homepage, https://github.com/matthager12-collab/vocalize
|
|
6
6
|
Project-URL: Repository, https://github.com/matthager12-collab/vocalize
|
|
@@ -216,6 +216,9 @@ Hotkeys: `↑`/`↓` or `k`/`j` move, `Enter` selects, `p` previews the
|
|
|
216
216
|
highlighted voice, `m` types a value by hand, `q` or `Esc` cancels without
|
|
217
217
|
writing anything.
|
|
218
218
|
|
|
219
|
+
There's also a settings page in the browser — see
|
|
220
|
+
[Settings portal](#settings-portal) below.
|
|
221
|
+
|
|
219
222
|
| Setting | Flag | Env var | Config file key | Default |
|
|
220
223
|
|---|---|---|---|---|
|
|
221
224
|
| API key | `--api-key` | `ELEVENLABS_API_KEY` | not read from the config file | stored via `vocalize auth` |
|
|
@@ -295,6 +298,41 @@ configures a TTS provider — see
|
|
|
295
298
|
[Dictation](#dictation-speech-to-text) below for every key and its
|
|
296
299
|
allowlist.
|
|
297
300
|
|
|
301
|
+
## Settings portal
|
|
302
|
+
|
|
303
|
+
A page in your own browser for the whole config surface above, so you don't
|
|
304
|
+
have to hand-edit `config.toml` or click through `vocalize config` one
|
|
305
|
+
setting at a time.
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
vocalize portal # opens your browser at a one-time link
|
|
309
|
+
vocalize portal --no-browser # prints the link instead (headless, SSH)
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Five tabs:
|
|
313
|
+
|
|
314
|
+
- **Chain** — reorder providers, or add and remove one.
|
|
315
|
+
- **Providers** — each provider's voice, model, speed, and monthly budget,
|
|
316
|
+
with a live preview of the voice you're looking at.
|
|
317
|
+
- **Keys** — store or check an API key; the field is masked and never
|
|
318
|
+
offers to autocomplete.
|
|
319
|
+
- **Usage** — this month's spend and quota per provider.
|
|
320
|
+
- **Local** — install or update the on-device Kokoro and whisper models,
|
|
321
|
+
with progress.
|
|
322
|
+
|
|
323
|
+
The opening link works once and for 60 seconds; after that, run the command
|
|
324
|
+
again for a new one. The server only ever listens on `127.0.0.1`, and it
|
|
325
|
+
closes itself on Ctrl-C or after fifteen minutes with nothing to do. Nothing
|
|
326
|
+
you change on the page reaches disk until you press Save on that tab — and
|
|
327
|
+
a save made against a config file that's changed on disk since the page
|
|
328
|
+
loaded is refused rather than silently overwritten, the same protection
|
|
329
|
+
`vocalize chain` and `vocalize config` already have.
|
|
330
|
+
|
|
331
|
+
It assumes a single-user machine: everything that reads or changes your
|
|
332
|
+
settings is behind that one-time token, but any other process on the Mac
|
|
333
|
+
can still reach the port and close the portal under you. Nothing leaks if
|
|
334
|
+
that happens — it's a shutdown, not a read — just run the command again.
|
|
335
|
+
|
|
298
336
|
## Providers and fallback
|
|
299
337
|
|
|
300
338
|
vocalize tries providers in order — a **chain** — until one speaks. The
|
|
@@ -180,6 +180,9 @@ Hotkeys: `↑`/`↓` or `k`/`j` move, `Enter` selects, `p` previews the
|
|
|
180
180
|
highlighted voice, `m` types a value by hand, `q` or `Esc` cancels without
|
|
181
181
|
writing anything.
|
|
182
182
|
|
|
183
|
+
There's also a settings page in the browser — see
|
|
184
|
+
[Settings portal](#settings-portal) below.
|
|
185
|
+
|
|
183
186
|
| Setting | Flag | Env var | Config file key | Default |
|
|
184
187
|
|---|---|---|---|---|
|
|
185
188
|
| API key | `--api-key` | `ELEVENLABS_API_KEY` | not read from the config file | stored via `vocalize auth` |
|
|
@@ -259,6 +262,41 @@ configures a TTS provider — see
|
|
|
259
262
|
[Dictation](#dictation-speech-to-text) below for every key and its
|
|
260
263
|
allowlist.
|
|
261
264
|
|
|
265
|
+
## Settings portal
|
|
266
|
+
|
|
267
|
+
A page in your own browser for the whole config surface above, so you don't
|
|
268
|
+
have to hand-edit `config.toml` or click through `vocalize config` one
|
|
269
|
+
setting at a time.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
vocalize portal # opens your browser at a one-time link
|
|
273
|
+
vocalize portal --no-browser # prints the link instead (headless, SSH)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Five tabs:
|
|
277
|
+
|
|
278
|
+
- **Chain** — reorder providers, or add and remove one.
|
|
279
|
+
- **Providers** — each provider's voice, model, speed, and monthly budget,
|
|
280
|
+
with a live preview of the voice you're looking at.
|
|
281
|
+
- **Keys** — store or check an API key; the field is masked and never
|
|
282
|
+
offers to autocomplete.
|
|
283
|
+
- **Usage** — this month's spend and quota per provider.
|
|
284
|
+
- **Local** — install or update the on-device Kokoro and whisper models,
|
|
285
|
+
with progress.
|
|
286
|
+
|
|
287
|
+
The opening link works once and for 60 seconds; after that, run the command
|
|
288
|
+
again for a new one. The server only ever listens on `127.0.0.1`, and it
|
|
289
|
+
closes itself on Ctrl-C or after fifteen minutes with nothing to do. Nothing
|
|
290
|
+
you change on the page reaches disk until you press Save on that tab — and
|
|
291
|
+
a save made against a config file that's changed on disk since the page
|
|
292
|
+
loaded is refused rather than silently overwritten, the same protection
|
|
293
|
+
`vocalize chain` and `vocalize config` already have.
|
|
294
|
+
|
|
295
|
+
It assumes a single-user machine: everything that reads or changes your
|
|
296
|
+
settings is behind that one-time token, but any other process on the Mac
|
|
297
|
+
can still reach the port and close the portal under you. Nothing leaks if
|
|
298
|
+
that happens — it's a shutdown, not a read — just run the command again.
|
|
299
|
+
|
|
262
300
|
## Providers and fallback
|
|
263
301
|
|
|
264
302
|
vocalize tries providers in order — a **chain** — until one speaks. The
|
|
@@ -75,6 +75,19 @@ Smoke test (also proves stdin + preprocessing):
|
|
|
75
75
|
echo "vocalize is alive" | vocalize speak-file -
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### Or: finish setup in a browser
|
|
79
|
+
|
|
80
|
+
Everything above — keys, the chain, the local models — can be done from one
|
|
81
|
+
page instead:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
vocalize portal
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
It opens a one-time link at `127.0.0.1` (valid for 60 seconds), and writes
|
|
88
|
+
nothing until you save. See [README § Settings portal](../README.md#settings-portal)
|
|
89
|
+
for what each tab does.
|
|
90
|
+
|
|
78
91
|
## Layer 2 — the `/speak` command in Claude Code
|
|
79
92
|
|
|
80
93
|
Until the repo ships this (tracked as an open gap), create it by hand at
|