PAIPAI

Codegen

Generate a typed HTTP client and React bindings from the server app.

The usual TypeScript path is import type from the server PAI module. That is what React chat does.

Use codegen when the frontend cannot safely type-import from the server package, or when you want a generated folder that stays in lockstep with the app contract.

pnpm add -D @pai/cli

Export an inferred app contract from the server:

server/pai.ts
import { createPai, type InferPaiContract } from "@pai/core";

export const pai = createPai({
  agents: { assistant: assistantAgent, sales: salesAgent },
  scopeKey: (identity) => identity.workspaceId,
});

export type AppPai = InferPaiContract<typeof pai>;

Keep the agents (and tools / commands) maps as object literals. Widening them to Record<string, ...> discards the keys, and the CLI rejects the contract instead of generating an incomplete one.

pnpm exec pai generate client \
  --source server/pai.ts \
  --type AppPai \
  --out-dir web/src/generated/pai \
  --name Pai

--type is the exported server alias. --name is the generated API stem (Pai, PaiContract, createPaiClient, …).

FileContents
types.d.tsFlattened runtime-free contract types
contract.tsRuntime contract metadata
client.tsHTTP client factory and React bindings
index.tsBarrel
web/src/pai.ts
import {
  AssistantAgent,
  Pai,
  createPaiClient,
} from "./generated/pai";

export const paiClient = createPaiClient({
  url: "http://localhost:3001/api/pai",
  headers: () => ({
    "x-user-id": "dev-user",
    "x-workspace-id": "dev",
  }),
});

export { AssistantAgent, Pai };

Add a check script so CI fails when the generated folder is stale:

{
  "scripts": {
    "pai:generate": "pai generate client --source server/pai.ts --type AppPai --out-dir web/src/generated/pai --name Pai",
    "pai:check": "pai generate client --source server/pai.ts --type AppPai --out-dir web/src/generated/pai --name Pai --check"
  }
}

Keep the server typecheck as a separate CI step; codegen validates generated contracts and file freshness.

Next

On this page