Overview
A tour of how Macaron Artifacts fits together — one server binary, three SPA entries, and a live GenUI renderer.
Macaron Artifacts is a local WebUI for driving agent sessions from the browser. Under the hood it is a small monorepo: a Fastify server that fronts the Claude Agent SDK, Codex (via the codex app-server transport by default), and the Kimi Code CLI, a React SPA that renders sessions and streams turns, and a shared package that pins the wire contract between them. This section walks the whole system top to bottom.
Who this is for
These pages document the implementation, not day-to-day usage. If you just want to install and run the WebUI, start with Usage. Come here when you want to understand how a turn flows from the browser to the SDK and back, or how the three engines share one binary.
The Monorepo
- shared —
types.tsholds the REST + domain types,sse.tsthe streaming contract. - server —
index.tsis the Fastify bootstrap,config.tsthe ports / hosts / derived paths. Undersrc/lib,claude-runner.tsdrives the Claude Agent SDK loop,codex-app-server.tsis the Codex app-server client,kimi-runner.tsspawns the Kimi Code CLI per turn,session-store.tsreads~/.claudetranscripts andcodex-store.tsreads~/.codexrollouts.src/routesmounts the API slices — most under/api/*(sessions, codex, kimi, …), plus the provider relay at/relay/*. - web —
index.htmlis the Claude SPA entry,codex.htmlthe Codex one, andkimi.htmlthe Kimi one;src/main.tsxbootstraps the router + GenUI runtime,src/macaron-vendor/is the vendored GenUI renderer. - bin / mcx / mkx —
mcc.mjsis the Claude launcher; themcxandmkxmembers each ship their own self-contained Codex / Kimi launcher.
The workspace (pnpm-workspace.yaml) has five members: shared, server, web, mcx, and mkx. The root package is published as mcc (the Claude launcher); mcx and mkx are self-contained packages that ship their own copy of the built server and web bundles.
The Big Picture
Three layers, one data contract. The shared package sits between the other two: it defines the REST and SSE types both the server and the web build import, so neither side can drift from the wire format.
Server
A Fastify app that owns the agent runtimes, reads on-disk transcripts, relays provider traffic, and serves the SPA.
Web
A React SPA with a canvas of session tiles, a fetch-based SSE reader, and a live GenUI preview.
Shared
The types.ts + sse.ts contract both layers depend on — one union type published once, consumed on both ends.
The Web layer has three entries that boot from the same server: the Claude UI, the Codex UI, and the Kimi UI. Three cross-cutting concerns span them:
Codex Variant
How the same server binary boots a second SPA and drives Codex (default codex app-server transport) instead of Claude.
Kimi Variant
How the same binary boots the Kimi SPA and drives the Kimi Code CLI (kimi -p --output-format stream-json).
GenUI
The render_ui tool + vendored renderer that compile model-authored TSX to a live component as tokens stream.
Packaging
Three publishable launchers, prebuilt bundles, and the pkg.pr.new per-commit release flow.
One Binary, Three Front Ends
The single most important design choice: there is exactly one server binary, and an environment variable decides which SPA it serves at /.
Launch picks the engine
mcc runs with the default engine (Claude, port 7878); mcx sets MACARON_ENGINE=codex and defaults to port 7979; mkx sets MACARON_ENGINE=kimi and defaults to port 7980. All three import('../server/dist/index.js') — the same compiled server.
The server chooses an SPA entry
At boot the server reads MACARON_ENGINE and picks codex.html, kimi.html, or index.html as the / document and the SPA fallback. Static assets (JS/CSS chunks) are shared between the entries.
const spaEntry = process.env.MACARON_ENGINE === 'codex' ? 'codex.html' : process.env.MACARON_ENGINE === 'kimi' ? 'kimi.html' : 'index.html';
app.get('/', (_req, reply) => reply.sendFile(spaEntry));The browser loads one runtime
Each HTML entry boots its own React tree — the Claude UI, the ChatGPT-style Codex UI, or the Kimi UI — talking to its engine's /api/* namespace over the same SSE contract.
Read on for how each layer works, starting with the server.