Skip to content

Nuabase

View as Markdown

Get typed LLM responses in your TypeScript app, or hand off complex tasks directly to the user's coding agent.

Nuabase lets you call an LLM just like an ordinary, type-safe TypeScript function. You can use it to get quick, structured answers—validated by a Zod schema—or to hand off complex tasks to a coding agent installed on the user’s machine (such as Claude Code, Codex, or Pi).

You can run Nuabase in three ways:

1. Headless Coding Agent API

Runs Claude Code, Codex, or Pi headless on the user's machine to get a single typed response. It uses their existing login—no API keys required. (Node.js only)

Nua.direct({
  localAgent:
    await findLocalAgent(),
})

2. Interactive Coding Agent Session

Opens the user's coding agent inside a terminal in their project directory, pre-loaded with a prompt from your app. The user can watch and guide the agent as it works.

startAgentSession({
  agent, cwd, prompt, dir,
})

3. LLM Provider APIs

Calls Groq, Cerebras, Gemini, OpenRouter, or DeepSeek directly over HTTPS using your own API keys. Works in both Node.js and edge runtimes.

Nua.direct({
  providers: {
    openrouter: { apiKey },
  },
})

Combining headless calls and interactive sessions

Section titled “Combining headless calls and interactive sessions”

Most real-world AI workflows need two things: fast, structured decisions made on their own, and a way to hand off open-ended problems when something goes wrong. Nuabase is designed to let you combine both in the same app.

For example, look at how dbu6—an open-source personal bookkeeping app—uses Nuabase to import bank and credit card statements into double-entry books:

  • Headless calls for routine work: Every imported transaction needs a category. dbu6 sends uncategorized rows to the user’s local coding agent using nua.list() in batches of 50. It gets back a Zod-validated account for each row and updates the UI as responses arrive. During initial setup, the user describes their finances in a few sentences, and nua.get() generates a typed chart of accounts for them to review.
  • Interactive sessions for complex problems: Sometimes a bank statement uses an unfamiliar format, or the imported balances don’t match the bank’s totals. You can’t fix that with a single API call. Instead, dbu6 gathers the relevant context into a prompt and calls startAgentSession(). This opens Claude Code right in the user’s project directory so the agent can write a new parser or debug the balance mismatch while the user watches and steers.

Because both modes run through the user’s existing Claude or ChatGPT subscription, your app doesn’t need API keys or a backend server, and the user’s financial data goes only to their own agent and the model provider it already uses.

import { Nua, z } from 'nuabase';
import { findLocalAgent, startAgentSession } from 'nuabase/local-agent';
const agent = await findLocalAgent();
const nua = Nua.direct({ localAgent: agent.with({ effort: 'low' }) });
// 1. Headless: get one typed answer per row
const result = await nua.list(`Pick the account for each transaction from: ${accounts.join(', ')}.`, {
input: transactions, // [{ id, description, amount }, ...]
primaryKey: 'id',
output: { name: 'account', schema: z.enum(accounts) },
});
// 2. Interactive: hand off a complex job to the agent in the user's project
if (unknownStatement) {
await startAgentSession({
agent: agent.agent,
cwd: projectDir,
prompt: `Write a parser for ${unknownStatement}, a statement format no parser recognizes.`,
approval: 'auto',
dir: `${projectDir}/tmp/agent-sessions`,
});
}

nua.get() and nua.list() take the same arguments, validate against the same Zod schemas, and return the exact same result structure regardless of which engine you use under the hood.

This makes it easy to adapt your app to different environments:

  • On the desktop or CLI: Use the user’s local coding agent so they don’t have to manage API keys.
  • On the server or edge: Pass your own API keys to Nua.direct(), and the same get() and list() calls run in Node.js, Cloudflare Workers, or Vercel Edge.

Everything runs directly inside your application process—there is no hosted Nuabase proxy or middleman service in between.

  • Local-first and desktop apps: Give users AI features powered by the Claude or ChatGPT subscriptions they already pay for—no “paste your API key” settings screen required. You can use detectLocalAgents() to check which agents are installed and logged in on their machine.
  • Developer tools and CLIs: Parse logs, summarize diffs, or transform files headless, then launch an interactive terminal session when a fix requires editing code across the project.
  • Data extraction: Turn unstructured emails, support tickets, or notes into strictly typed objects ready to pre-fill a form or insert into a database.
  • Bulk classification and enrichment: Pass an array of records to nua.list() to categorize or enrich an entire batch in a single request.

When you pass a Zod schema to Nuabase, it converts that schema into JSON Schema, tells the model the exact structure to return (through the engine’s native structured output when it has one, or in the prompt otherwise), and validates the response with Zod before returning it to you.

All of your Zod defaults, transforms, and refinements work out of the box, and result.data is automatically typed as z.infer<typeof schema>.

const SentimentSchema = z.object({
sentiment: z.enum(['positive', 'neutral', 'negative']),
confidence: z.number().min(0).max(1),
});
const result = await nua.get('Analyze the sentiment', {
input: 'I love this product!',
output: { name: 'analysis', schema: SentimentSchema },
});
// result.data is typed as:
// { sentiment: 'positive' | 'neutral' | 'negative'; confidence: number }

Instead of looping over items and making dozens of separate requests, use nua.list() to send an array of records in a single call. Nuabase returns one validated output per input row, preserving each row’s primaryKey so you can easily match results back to your original data.

const result = await nua.list('Classify each lead', {
input: [
{ id: 'lead-1', notes: 'CEO at Acme Corp, interested in enterprise plan' },
{ id: 'lead-2', notes: 'Freelancer, looking for starter tier' },
],
primaryKey: 'id',
output: { name: 'tier', schema: z.enum(['starter', 'team', 'enterprise']) },
});
// result.data:
// [{ id: 'lead-1', tier: 'enterprise' }, { id: 'lead-2', tier: 'starter' }]

Predictable error handling (results, not exceptions)

Section titled “Predictable error handling (results, not exceptions)”

LLM calls can fail for reasons outside your control: network drops, expired logins, rate limits, or outputs that still fail schema validation after automatic retries.

Instead of throwing runtime exceptions for these operational failures, every Nuabase call resolves to a discriminated union that you check with result.success. If a call fails, you get { success: false, reason }, where reason explains what went wrong (including the raw error message from the agent or provider when available).

Nuabase only throws for invalid arguments, such as an empty prompt.

Nuabase doesn’t maintain any hidden state: there is no database, no cache server, and no background job queue to manage. Every call is simply a single CLI process or HTTPS request (plus any configured retries) executed directly from your process.

Install Nuabase and Zod via npm:

Terminal window
npm install nuabase zod

Then explore the documentation to start building:

  • Quick Start: Make your first get() and list() calls in under five minutes.
  • Headless Coding Agent API: Run typed calls through Claude Code, Codex, or Pi.
  • Interactive Coding Agent Session: Hand off multi-step tasks to the user’s coding agent in their terminal.
  • LLM Provider APIs: Call cloud providers directly over HTTPS with your own API keys.
  • API Reference: Look up every configuration option, result field, and error type.
  • How It Works: Learn how Nuabase handles schemas, prompt construction, retries, and prompt caching under the hood.
  • Examples: Browse complete, real-world code examples.

Nuabase is open source and available on npm as nuabase. Have questions or feedback? Reach out at [email protected].