Macaron Artifacts
Architecture

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

src/types.ts
src/sse.ts
  • sharedtypes.ts holds the REST + domain types, sse.ts the streaming contract.
  • serverindex.ts is the Fastify bootstrap, config.ts the ports / hosts / derived paths. Under src/lib, claude-runner.ts drives the Claude Agent SDK loop, codex-app-server.ts is the Codex app-server client, kimi-runner.ts spawns the Kimi Code CLI per turn, session-store.ts reads ~/.claude transcripts and codex-store.ts reads ~/.codex rollouts. src/routes mounts the API slices — most under /api/* (sessions, codex, kimi, …), plus the provider relay at /relay/*.
  • webindex.html is the Claude SPA entry, codex.html the Codex one, and kimi.html the Kimi one; src/main.tsx bootstraps the router + GenUI runtime, src/macaron-vendor/ is the vendored GenUI renderer.
  • bin / mcx / mkxmcc.mjs is the Claude launcher; the mcx and mkx members 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.

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:

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.

On this page