Repository navigation
korekore Phase 2d: account, own keys and making a Character - #196
Merged
Merged
Conversation
…ADR-0018) Brings ADR-0018 and the Account and Daily allowance terms onto main, and points ADR-0017's account line at it: voice is free, with a daily allowance per account. The API document is docs/api.md. Closes #159 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: accounts ADR-0018 onto main
How the daemon signs in through the browser with a loopback redirect and PKCE, exchanges the code for a session, and uses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: the account part of the VibeBuddy API; GitHub sign-in first
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: the voice part of the VibeBuddy API
POST /v1/account/sign-in returns the service's sign-in URL with a PKCE challenge and a loopback redirect to the daemon's own /v1/account/callback, which exchanges the code for a session and keeps it in config_dir()/account (0600). Sign-out revokes the session and forgets it even offline; deleting the account forgets it only once the service has deleted it. /v1/status gains the account, with the GitHub login looked up for display. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mac and Linux alike: signed out, Sign in opens the browser on the URL the daemon gives and waits for the status stream to say signed in; signed in, it names the GitHub login (or the id, or the email) with Sign out and Delete account, which asks first. A build that can't sign in says so, and an absent daemon points to Advanced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recognition and synthesis go to /v1/voice/* with the session. A 401 forgets it, which signs out; a 429 is passed to the box as voice.error allowance or global_cap, and no session as signed_out. With the voice service, voice is offered only while signed in, and signing in or out tells the box again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-cloud#8, #10) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The box was to call the voice service itself over the Relay, with a voice token. That needs an HTTP client and a second TLS session in the firmware, a token endpoint and a token handout, and a box that follows every change to the voice API, to save one hop of a few hundred milliseconds; the Relay carries only ciphertext since ADR-0015. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The box sends its recording over the Relay too, as it does over USB and the local network, leaving 8 places in the Relay's outgoing queue to its other lines so VOICE END isn't lost behind audio; the daemon offers voice and takes the turn there as anywhere but the UART bridge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to the VibeBuddy account from the Apps
Voice through the account: the daemon uses voice v1 with the session
docs: the global cap in the VibeBuddy API
The daemon keeps today's use from the service (read at sign-in and when the App opens the tab, POST /v1/account/refresh), counts each command the voice service recognizes, and remembers an allowance or global-cap refusal until the UTC day ends; /v1/status carries both. Both Apps show "Voice today: 7 of 100", or why voice is refused until midnight UTC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
We charge only for what runs on our servers. Free keeps everything over USB and the local network, and making Characters with the user's own key, which stays on the computer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For vibebuddy-cloud#17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For vibebuddy-cloud#18. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For vibebuddy-cloud#19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user's own key for a speech provider (ADR-0020) is kept by the daemon in config_dir()/voice-keys.json (mode 0600) and set with PUT /v1/voice-keys. A reply whose voice is from a provider with an own key goes straight to that provider and never to vibebuddy-api: ElevenLabs as mp3_24000_48, Volcano Engine's V3 stream as 24 kHz MP3 with its leading ID3 tag dropped. A 401 or 403 marks the key refused in /v1/status and leaves that reply silent, with no fallback to the service. The status says only which keys are set. The Mac and Linux Account tabs get "Your own keys": a field for each, a link to each provider's key page, "Set" in place of the value, and Clear. Closes #182 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The daemon reads the account's plan from GET /v1/account wherever it reads today's usage (sign-in, POST /v1/account/refresh), and on the Paid plan counts each reply spoken through the service against the month, in UTF-16 code units as the service does, until the limit is crossed. /v1/status `account` gains `plan`. Both Apps show "Free" or "Paid plan", and on Paid "This month: 12,300 of 100,000 characters · renews 24 Nov"; the daily line stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /v1/characters makes a Character's lines in a voice from the new
characters/voices.tsv (GET /v1/voices): with the user's own key for the
voice's provider, 16 kHz PCM straight from ElevenLabs or Volcano Engine;
otherwise from the voice service with "for":"character". Lines with
{address} are made once per form of address, named and laid out as
tools/make-character.sh does. Each line is trimmed, normalized to -1 dBFS
and packed with the box's ADPCM encoder into pack.bin and
address/<form>.bin under config_dir()/characters/<id>/, byte for byte as
tools/character_pack.py packs them. A refusal stops the job and keeps the
lines made; making the same id again resumes. Progress is `making` in
/v1/status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sal (#181) The computer's hello carries the account session when signed in. After the Relay refuses with signed_out or plan (close 4006 or 4007, at hello or on its hourly check), the daemon doesn't try it again until the account signs in or out or a read of the account finds the Paid plan; unavailable (close 1013) backs off like any refusal. /v1/status reports why as device.relay_refused, and both Apps' Device tab says "Sign in to use the Relay.", "The Relay needs the Paid plan." or that it can't check the account right now, while the box is away. docs/relay-protocol.md describes the session, the three refusals and what the daemon does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ve a panic GET /v1/characters lists the Characters made here from their manifests, with whether their packs are made. A box wearing a made Character's pack gets spoken replies in the voice it was made with, not Jessica. A job that panics ends as failed with reason "internal" instead of making forever. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DELETE /v1/characters/<id> removes config_dir()/characters/<id>/: 404 for an id that isn't valid (so nothing outside characters/ is reached) or wasn't made here, 409 while its job runs. Both Apps get Delete… on each made Character, asking first, and off with a hint for the one on the box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR-0020: one Paid plan carries the Relay and a monthly voice allowance
docs/api.md: plans, the monthly allowance, and Character lines
Show today's voice use in the Account tab
Paid plan: show the plan and the monthly allowance in the App
Paid plan: the daemon presents the account to the Relay; the App explains a refusal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Own keys: ElevenLabs and Volcano Engine keys for spoken replies
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Making a Character: the daemon makes the line audio
Making a Character: in the Mac and Linux Apps
Voice over the Relay through the link
GET /v1/voices/<id>/sample returns one line in that voice as a WAV file:
the first done line without {address} of the preset it comes from,
decoded from the preset's pack (now compiled into the daemon) as 16 kHz
mono; Jessica's is the firmware's built-in done line, 24 kHz stereo.
404 for anything not in voices.tsv.
The make-a-Character form lists the voices as rows, each with a play
button: AVAudioPlayer on the Mac, pw-play/paplay/aplay on Linux as the
Character previews already do. One plays at a time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The voice picker's play button now plays the preview of the preset the voice comes from, with the card's player (Jessica's is the robot's), and hides itself when this App has no such preset. GET /v1/voices/<id>/sample and the packs compiled into the daemon go: the daemon is as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Making a Character: hear a voice before picking it
# Conflicts: # app/Localization/zh-Hans.lproj/Localizable.strings # app/Sources/VibeBuddy/AppModel.swift # app/Sources/VibeBuddyCore/Status.swift # desktop/src/status.rs
The merge of main brought the Account tab, own keys and Making a Character to the current UI only. The General page now opens with the account and own keys, the Character page shows made Characters, Make a Character and the making progress, and the Device page says why the Relay refuses. Make your own look scrolls to the card it opens below the grid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The design hands over no app icon, so the Dock shows the General page's badge: Kore at a whole-pixel scale on a kore-pink tile with an ink outline. The bundle's icon stays the robot until the current UI goes. Snapshots gain the icon and a signed-in account with own keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The App shows in the Dock only while a window is open, and the tile then takes the bundle's icon, so the icon set at launch never showed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Linux counterpart of the Mac change: the General page opens with the account and own keys, the Character page shows made Characters, Make a Character with its form in the page's place, and the making progress, and the Device page says why the Relay refuses. After drawings are picked for Make your own look, the page goes down to the card it opens below the grid. Snapshots gain a signed-in account, own keys and a made Character. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2d of the korekore UI (ADR-0021): the account, own keys and making a Character, which main gained in the current UI only, now in the korekore look, on the Mac and on Linux. Also the Dock icon, and a fix to Make your own look. Behind
VIBEBUDDY_UI=korekore; without it both apps behave exactly as before.Stacked on #177 (2c) → #176 (2b) → #175 (2a) → #173 (Phase 1).
This branch merges main (717de01), so the diff against 2c also shows main's commits since 2c branched (#160 to #193). This PR's own change is the commits after that merge. The merge had four conflicts, all where both sides added fields or strings; both sides are kept:
Status.swiftanddesktop/src/status.rs(korekore'sactivitiesandrolledBackbeside main'saccount,voiceKeys,makingandrelayRefused),AppModel.swift(both new methods) andLocalizable.strings(both sets).What it does
sunwith an ink outline, the General page's badge at icon size, set each time the Dock tile appears (the App shows in the Dock only while a window is open). The bundle's own icon stays the robot until the current UI goes. Linux has no counterpart: its launcher icon comes from the installed icon theme, and a Wayland window can't change it while running. Its tray already shows Kore.What each part does is the current UI's, copied (
AccountView,OwnKeyRow,MadeCharacterCard,MakingRow,MakeCharacterSheet; on Linuxaccount(),own_keys(),making_row(),make_form()).Screenshots
Mac, the real window drawn off screen in a debug build with sample data (a sample signed-in account):
Linux, drawn headless by iced_test on Omarchy (a sample account and a made Character):
Where it departs from the canvas
There are no boards for these parts, so there is nothing to match pixel for pixel. They use only the design's components and tokens: cards, rows, pills, pixel tags, the dashed card of Make your own look.
New files
app/Sources/VibeBuddy/Korekore/Pages/AccountCard.swift: the account and own keysapp/Sources/VibeBuddy/Korekore/Pages/MakingCharacter.swift: made Character cards, the making row, the Make a Character sheetdesktop/src/korekore/account.rs: the same on Linuxdocs/design/korekore/screenshots/phase-2d/*.pngChanges to existing files
After the merge:
app/Sources/VibeBuddy/AppDelegate.swiftapp/Sources/KorekoreUI/MenuPanel.swiftKorekoreAppIconapp/Sources/VibeBuddy/Korekore/Pages/CharacterPage.swiftapp/Sources/VibeBuddy/Korekore/Pages/GeneralPage.swiftapp/Sources/VibeBuddy/Korekore/Pages/DevicePage.swiftapp/Sources/VibeBuddy/Korekore/KorekoreSnapshots.swiftdesktop/src/korekore/pages.rsdashed()shared by both new-item cardsdesktop/src/korekore/mod.rsdesktop/src/korekore/snapshots.rsdesktop/src/main.rsiffrom 2c collapsed for clippyapp/Localization/zh-Hans.lproj/Localizable.stringsdocs/design/korekore/README.mdLESSONS.mdpkill -fpatterns over SSHTests
just test(Swift build and SelfTest): passed.tools/check-localization.py: 576 keys, OK.cargo clippy -p vibebuddy-desktop --all-targets(macOS): no warnings.cargo test -p vibebuddy-desktop: 58 passed on macOS, 60 on Omarchy (Linux). Clippy is not installed on Omarchy./Applicationswith the switch on and used; the Dock icon checked by eye.🤖 Generated with Claude Code