feat(server): add GUI store API mirroring localStorage - #1231
Conversation
- add /api/v1/gui/store/* endpoints (getItem/setItem/removeItem/clear/length) mirroring the browser localStorage interface - add IGuiStoreService persisting opaque string values to ~/.kimi-code/gui.toml via smol-toml with atomic writes and an in-process write lock - wire protocol schema, service, routes, and DI registration; add e2e tests and update the API surface snapshot
🦋 Changeset detectedLatest commit: caa5725 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 124ecf9f1e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| async getItem(key: string): Promise<string | null> { | ||
| const all = await this.readAll(); | ||
| return all[key] ?? null; |
There was a problem hiding this comment.
Use own-key storage for GUI store keys
When a caller uses a legal localStorage key that exists on Object.prototype (toString, constructor, hasOwnProperty) on an empty store, this returns the inherited function instead of null; for __proto__, setItem also goes through the prototype setter and does not persist the entry. The route schema accepts any non-empty string key, so these valid keys produce malformed responses or cannot be stored; use a null-prototype object/Map and an own-property check for reads.
Useful? React with 👍 / 👎.
| // Atomic replace via temp file + rename (POSIX-atomic), so readers never | ||
| // observe a half-written file. | ||
| const tmp = `${this.filePath}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`; | ||
| await writeFile(tmp, text, 'utf-8'); |
There was a problem hiding this comment.
Create gui.toml with private permissions
On POSIX systems where <homeDir> already exists, this writeFile creates the temp file with the process umask (commonly 0644), and the later rename preserves that mode for gui.toml. This store mirrors web localStorage, which includes composer drafts and input history (useComposerDraft, useInputHistory), so on multi-user hosts another local user can read unsent prompts/preferences from <homeDir>/gui.toml; write the temp file as 0600 or reuse the existing private-file helper.
Useful? React with 👍 / 👎.
- use a null-prototype record and an own-property check so keys that exist on Object.prototype (toString, constructor, __proto__) behave like ordinary keys - write gui.toml with 0600 permissions so unsent drafts and input history stay private to the owning user
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Summary
Add a server-side key/value store API that mirrors the browser
localStorageinterface, so the web UI can persist its preferences to the user's data directory (~/.kimi-code/gui.toml) and sync them across browsers and devices. This PR only adds the server endpoint; web-side integration will follow separately.Related Issue
No related issue. See "Problem" below.
Problem
The web UI currently keeps all of its preferences (theme, font size, locale, layout, notification toggles, etc.) only in the browser's
localStorage. They are lost when the user switches browsers or devices, and there is no server-side source of truth. Because the browser cannot access the user filesystem directly, the server needs to proxy a small persistence API that writes under~/.kimi-code.What changed
/api/v1/gui/store/*that map 1:1 tolocalStorage:getItem,setItem,removeItem,clear, andlength. Values are opaque strings, so the server does not interpret them; keys travel in the query/body so any string is allowed.IGuiStoreServicethat persists entries to~/.kimi-code/gui.tomlviasmol-toml, with atomic writes and an in-process write lock so concurrent writes from multiple tabs cannot clobber each other. Missing or corrupt files are treated as empty.gui.tomlis kept separate fromconfig.toml(agent/runtime settings) andtui.toml(terminal preferences), following the existing per-client preferences convention.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.