kimi-code/apps/web
liruifengv d96494db06
feat(settings): add Lab tab with a multi-tab sidebar toggle (#265)
* feat(settings): add Lab tab with a multi-tab sidebar toggle

* feat(settings): revert the chat-header archive action in single-list form
2026-08-19 01:13:05 +08:00
..
public refactor: P1 lib migration to app-core + rename shared packages web-* → app-* (#185) 2026-08-11 11:29:33 +08:00
scripts feat(chat): restyle dock work pills and redesign work panels (#209) 2026-08-13 03:41:18 +08:00
src feat(settings): add Lab tab with a multi-tab sidebar toggle (#265) 2026-08-19 01:13:05 +08:00
test feat(chat): keep the subagent detail transcript fully expanded without the run-end footer (#260) 2026-08-19 00:04:39 +08:00
AGENTS.md refactor: pinia domain stores for sessions, notifications, models, approvals, files (P8-P12) (#258) 2026-08-18 10:51:03 +08:00
CHANGELOG.md chore: import kimi-web as apps/web 2026-07-10 11:38:49 +08:00
index.html feat: stepped font scale, kimi skin palette, and macOS frosted sidebar (#91) 2026-07-24 10:48:20 +08:00
package.json feat(desktop): mention pills for files, folders, and skills (#226) 2026-08-18 14:21:12 +08:00
README.md refactor: P1 lib migration to app-core + rename shared packages web-* → app-* (#185) 2026-08-11 11:29:33 +08:00
tsconfig.json feat(chat): add on-demand subagent transcripts (#137) 2026-07-28 23:06:53 +08:00
vite.config.ts refactor: unify the icon assets/registry and absorb mediaPreview into app-client (P5) (#199) 2026-08-11 15:46:06 +08:00
vitest.config.ts refactor: unify the icon assets/registry and absorb mediaPreview into app-client (P5) (#199) 2026-08-11 15:46:06 +08:00

Kimi Web

Browser client for Kimi Code — a peer to the TUI that talks to a local server over REST + WebSocket. Vue 3 + Vite + TypeScript.

Since phase 3 this app is no longer a monolith: it aggregates the three source-only shared packages (exports → ./src/*, transpiled by this app's bundler) and keeps only the web-specific glue:

  • @moonshot-ai/app-ui — presentational components + design tokens.
  • @moonshot-ai/app-markdown — chat Markdown renderer.
  • @moonshot-ai/app-core — daemon transport, session state machine, state container, appearance composables.

Quick start

# from the repo root
pnpm dev:web                 # vite dev server (proxies /api/v1 to KIMI_SERVER_URL)
pnpm -C apps/web run dev:stub  # offline stub faking the server API + event stream

# checks
pnpm -C apps/web run typecheck   # vue-tsc --noEmit
pnpm -C apps/web run test        # vitest (pure logic only)
pnpm -C apps/web run build       # vite build → apps/web/dist

How it connects to the server

The browser cannot reach the server cross-origin (no CORS), so Vite same-origin proxies /api/v1 (HTTP + WS) to the server (vite.config.ts):

env var default meaning
WEB_PORT 5175 port the dev server listens on
KIMI_SERVER_URL http://127.0.0.1:58627 where /api/v1 (and /api/v1/ws) is forwarded

Behind a corporate HTTP proxy, also set NO_PROXY=<server-host> (for example, NO_PROXY=127.0.0.1,localhost) so the proxy forward reaches the server directly.

vite.config.ts also carries the two consumer requirements the shared packages need: the unplugin-icons kimi collection (~icons/kimi/*) and worker: { format: 'es' } for app-markdown's off-thread KaTeX / Mermaid workers.


Architecture

src/api/bootstrap.ts is the only module that knows both sides: it composes app-core's DaemonKimiWebApi with this app's bridges — tracer (debug/trace), credentialStore (lib/serverAuth), and the required projectorFactory (src/api/daemon/agentEventProjector.ts) — and exposes the shared api singleton (plus a getKimiWebApi() back-compat accessor).

src/main.ts stays thin: createApp(App).use(i18n), an app.provide(IconResolverKey, …) that bridges app-ui's <Icon> to this app's icon registry (lib/icons.ts), and a desktop-only theme-IPC bridge that mirrors <html data-color-scheme> to the host's nativeTheme. There is no client factory / provide of an un-singletoned client here — that is deferred to a later phase.

src/api keeps only the web-specific glue (bootstrap, config, daemon/agentEventProjector, errors, index, types); the transport, reducer, and state container live in @moonshot-ai/app-core. i18n and tool metadata stay in this app.

server (REST + WS)
  └─ @moonshot-ai/app-core/api   DaemonKimiWebApi (transport + projector + reducer)
        └─ src/api/bootstrap.ts  injects tracer / credentialStore / projectorFactory → api
  └─ @moonshot-ai/app-core       createKimiWebClientCore({ api, t }) → reactive state
  └─ @moonshot-ai/app-ui         <Button> / <Dialog> / <Icon> / tokens
  └─ @moonshot-ai/app-markdown   <Markdown>
  └─ src/components/*.vue        render props, emit intents (no transport access)

Server contract — non-obvious notes

The server's wire protocol has a few things that will bite you if forgotten:

  • Envelope: every response is { code, msg, data, request_id } and the HTTP status is always 200 — check code (0 = ok), not the status.
  • Persisted sessions are directly promptable — selecting an old session and sending a message just works; there is no :activate step.
  • Creating a session needs a registered workspace. workspace_id must be a wd_<slug>_<hash> id that exists in the server's registry.

Release & deployment

build → dist produces apps/web/dist. It is consumed two ways, never published as a standalone package:

  1. Desktopapps/desktop/scripts/copy-web-dist.mjs copies apps/web/dist into apps/desktop/web-dist/ at desktop build time.
  2. SEA / CLI embedpnpm run sync:web builds the web and syncs dist into the kimi-code submodule's apps/kimi-code/dist-web/ for SEA embedding.