@an-sdk/cli 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,19 @@
1
+ # @an-sdk/cli
2
+
3
+ CLI tool for deploying AI agents to the AN platform. Two commands: `an login` and `an deploy`.
4
+
5
+ ## Docs
6
+
7
+ Full documentation: `./docs/` directory (8 guides covering the entire AN platform).
8
+
9
+ ## Source
10
+
11
+ Source code: `./src/` directory.
12
+
13
+ ## Key Entry Points
14
+
15
+ - `src/index.ts` — Command router (dispatches to login/deploy)
16
+ - `src/login.ts` — API key auth flow, saves to `~/.an/credentials`
17
+ - `src/deploy.ts` — Bundles agent code with esbuild, deploys to AN platform
18
+ - `src/config.ts` — Reads/writes credentials and project config
19
+ - `src/bundler.ts` — esbuild wrapper (externalizes `@an-sdk/agent`)
package/dist/index.js CHANGED
@@ -81,7 +81,7 @@ async function findAgentEntryPoints() {
81
81
  const { existsSync, readdirSync, statSync } = await import("fs");
82
82
  const { join: join2, basename: basename2, extname } = await import("path");
83
83
  if (!existsSync("agents") || !statSync("agents").isDirectory()) {
84
- throw new Error("No agents found. Create agents in the `agents/` directory.");
84
+ throw new Error("No agents/ directory found. See https://an.dev/docs to get started.");
85
85
  }
86
86
  const entries = [];
87
87
  const items = readdirSync("agents");
@@ -104,7 +104,7 @@ async function findAgentEntryPoints() {
104
104
  }
105
105
  }
106
106
  if (entries.length === 0) {
107
- throw new Error("No agents found. Create agents in the `agents/` directory.");
107
+ throw new Error("No agents found in agents/ directory. See https://an.dev/docs to get started.");
108
108
  }
109
109
  return entries;
110
110
  }
@@ -261,20 +261,62 @@ Deployed before failure:`);
261
261
  console.log(` ${agent.slug} \u2192 ${AN_BASE}/a/${projectSlug}/${agent.slug}`);
262
262
  }
263
263
  console.log();
264
- console.log(` Dashboard \u2192 ${AN_BASE}/an/deployments`);
264
+ p2.log.info("Next steps:");
265
+ console.log(" \xB7 Open the link above to test your agent");
266
+ console.log(" \xB7 Run `an deploy` again after changes");
267
+ console.log(` \xB7 View all deployments: ${AN_BASE}/an/deployments`);
265
268
  p2.outro("Done");
266
269
  }
267
270
 
268
271
  // src/index.ts
272
+ import { createRequire } from "module";
273
+ var require2 = createRequire(import.meta.url);
274
+ var { version } = require2("../package.json");
269
275
  var command = process.argv[2];
276
+ var args = process.argv.slice(3);
277
+ var hasFlag = (flag) => args.includes(flag);
278
+ function showHelp() {
279
+ console.log(`AN CLI v${version} \u2014 deploy AI agents
280
+ `);
281
+ console.log("Usage: an <command>\n");
282
+ console.log("Commands:");
283
+ console.log(" an login Authenticate with AN platform");
284
+ console.log(" an deploy Bundle and deploy your agent");
285
+ console.log("\nOptions:");
286
+ console.log(" --help, -h Show help");
287
+ console.log(" --version, -v Show version");
288
+ console.log("\nGet started: an login");
289
+ console.log("Docs: https://an.dev/docs");
290
+ }
291
+ function showLoginHelp() {
292
+ console.log("Usage: an login\n");
293
+ console.log("Authenticate with the AN platform using an API key.");
294
+ console.log("Get your API key at https://an.dev/api-keys");
295
+ }
296
+ function showDeployHelp() {
297
+ console.log("Usage: an deploy\n");
298
+ console.log("Bundle and deploy agents from the ./agents/ directory.\n");
299
+ console.log("Agent structure:");
300
+ console.log(" agents/");
301
+ console.log(" my-agent/");
302
+ console.log(" index.ts agent entry point");
303
+ console.log(" another.ts single-file agent");
304
+ console.log("\nDocs: https://an.dev/docs");
305
+ }
270
306
  if (command === "login") {
271
- await login();
307
+ if (hasFlag("--help") || hasFlag("-h")) {
308
+ showLoginHelp();
309
+ } else {
310
+ await login();
311
+ }
272
312
  } else if (command === "deploy") {
273
- await deploy();
313
+ if (hasFlag("--help") || hasFlag("-h")) {
314
+ showDeployHelp();
315
+ } else {
316
+ await deploy();
317
+ }
318
+ } else if (command === "--version" || command === "-v") {
319
+ console.log(version);
274
320
  } else {
275
- console.log("AN CLI \u2014 deploy AI agents\n");
276
- console.log("Commands:");
277
- console.log(" an login Authenticate with AN platform");
278
- console.log(" an deploy Bundle and deploy your agent");
279
- console.log("\nGet started: an login");
321
+ showHelp();
280
322
  }
@@ -0,0 +1,61 @@
1
+ # AN Platform Overview
2
+
3
+ AN is a platform for building, deploying, and embedding AI agents. You define an agent in TypeScript, deploy it with one command, and embed a chat UI in any React app.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ Your Code (agent.ts)
9
+ |
10
+ v
11
+ an deploy (CLI)
12
+ |
13
+ v
14
+ AN Platform
15
+ |
16
+ +---> E2B Sandbox (isolated Node.js environment)
17
+ | |
18
+ | +---> Claude Agent SDK / Codex (executes your agent)
19
+ | |
20
+ | +---> Your custom tools run here
21
+ |
22
+ +---> AN Relay (relay.an.dev)
23
+ |
24
+ +---> SSE streaming to clients
25
+ |
26
+ +---> Token exchange (API key -> short-lived JWT)
27
+ ```
28
+
29
+ ## Packages
30
+
31
+ | Package | Purpose |
32
+ |---------|---------|
33
+ | `@an-sdk/agent` | Define agents and tools with type inference |
34
+ | `@an-sdk/cli` | Deploy agents from your terminal |
35
+ | `@an-sdk/react` | Chat UI components (theming, tool renderers) |
36
+ | `@an-sdk/nextjs` | Next.js server-side token handler |
37
+ | `@an-sdk/node` | Server-side SDK (sandboxes, threads, tokens) |
38
+
39
+ ## Key Concepts
40
+
41
+ - **Agent**: A TypeScript config defining model, system prompt, tools, and hooks. Runs in a cloud sandbox.
42
+ - **Tool**: A function your agent can call, with a Zod schema for input validation and full type inference.
43
+ - **Sandbox**: An isolated E2B cloud environment where your agent executes. Has Node.js, git, and system tools.
44
+ - **Thread**: A conversation within a sandbox. One sandbox can have multiple threads.
45
+ - **Relay**: The streaming gateway at `relay.an.dev`. Handles auth, routing, and SSE streaming.
46
+
47
+ ## Runtimes
48
+
49
+ AN supports two runtimes:
50
+
51
+ - **`claude-code`** (default) — Uses the Claude Agent SDK. Full tool use, file editing, bash execution.
52
+ - **`codex`** — Uses OpenAI Codex via ACP provider.
53
+
54
+ ## Auth Model
55
+
56
+ Two layers of authentication:
57
+
58
+ 1. **Client -> Relay**: API key (`an_sk_...`) or short-lived JWT (via token exchange)
59
+ 2. **Sandbox -> AI Provider**: Handled internally by the platform (Claude Proxy)
60
+
61
+ For web apps, use `@an-sdk/nextjs` to exchange your API key for a short-lived JWT on the server, so the key never reaches the browser.
@@ -0,0 +1,110 @@
1
+ # Getting Started
2
+
3
+ ## 1. Get an API Key
4
+
5
+ Sign up at [an.dev](https://an.dev) and get your API key from [an.dev/agents/dashboard/api](https://an.dev/agents/dashboard/api).
6
+
7
+ ## 2. Install
8
+
9
+ ```bash
10
+ npm install @an-sdk/agent zod
11
+ ```
12
+
13
+ ## 3. Define Your Agent
14
+
15
+ Create `src/agent.ts`:
16
+
17
+ ```ts
18
+ import { agent, tool } from "@an-sdk/agent"
19
+ import { z } from "zod"
20
+
21
+ export default agent({
22
+ model: "claude-sonnet-4-6",
23
+ systemPrompt: "You are a helpful coding assistant.",
24
+ tools: {
25
+ greet: tool({
26
+ description: "Greet a user by name",
27
+ inputSchema: z.object({ name: z.string() }),
28
+ execute: async ({ name }) => ({
29
+ content: [{ type: "text", text: `Hello, ${name}!` }],
30
+ }),
31
+ }),
32
+ },
33
+ })
34
+ ```
35
+
36
+ ## 4. Login & Deploy
37
+
38
+ ```bash
39
+ npx @an-sdk/cli login
40
+ # Enter your API key: an_sk_...
41
+
42
+ npx @an-sdk/cli deploy
43
+ # Bundling src/agent.ts...
44
+ # Deploying my-agent...
45
+ # https://api.an.dev/v1/chat/my-agent
46
+ ```
47
+
48
+ Your agent is live.
49
+
50
+ ## 5. Embed in a React App
51
+
52
+ ```bash
53
+ npm install @an-sdk/nextjs @an-sdk/react ai @ai-sdk/react
54
+ ```
55
+
56
+ Create a token route (keeps your API key on the server):
57
+
58
+ ```ts
59
+ // app/api/an/token/route.ts
60
+ import { createAnTokenHandler } from "@an-sdk/nextjs/server"
61
+
62
+ export const POST = createAnTokenHandler({
63
+ apiKey: process.env.AN_API_KEY!,
64
+ })
65
+ ```
66
+
67
+ Add the chat UI:
68
+
69
+ ```tsx
70
+ // app/page.tsx
71
+ "use client"
72
+
73
+ import { useChat } from "@ai-sdk/react"
74
+ import { AnAgentChat, createAnChat } from "@an-sdk/nextjs"
75
+ import "@an-sdk/react/styles.css"
76
+ import { useMemo } from "react"
77
+
78
+ export default function Chat() {
79
+ const chat = useMemo(
80
+ () => createAnChat({
81
+ agent: "your-agent-slug",
82
+ tokenUrl: "/api/an/token",
83
+ }),
84
+ [],
85
+ )
86
+
87
+ const { messages, sendMessage, status, stop, error } = useChat({ chat })
88
+
89
+ return (
90
+ <AnAgentChat
91
+ messages={messages}
92
+ onSend={(msg) =>
93
+ sendMessage({ parts: [{ type: "text", text: msg.content }] })
94
+ }
95
+ status={status}
96
+ onStop={stop}
97
+ error={error}
98
+ />
99
+ )
100
+ }
101
+ ```
102
+
103
+ ## Next Steps
104
+
105
+ - [Defining Agents](./03-defining-agents.md) — Models, tools, hooks, permissions
106
+ - [React UI](./04-react-ui.md) — Theming, slots, tool renderers
107
+ - [Next.js Integration](./05-nextjs.md) — Server-side token handler
108
+ - [Node SDK](./06-node-sdk.md) — Sandboxes, threads, tokens from server code
109
+ - [CLI Reference](./07-cli.md) — All CLI commands
110
+ - [Custom Tools](./08-custom-tools.md) — Build custom tool renderers
@@ -0,0 +1,159 @@
1
+ # Defining Agents
2
+
3
+ Agents are defined with the `agent()` and `tool()` functions from `@an-sdk/agent`. These are config-only — they return exactly what you pass in, with type inference added. The actual execution happens in the AN runtime (E2B sandbox).
4
+
5
+ ## `agent(config)`
6
+
7
+ ```ts
8
+ import { agent } from "@an-sdk/agent"
9
+
10
+ export default agent({
11
+ // Model (default: "claude-sonnet-4-6")
12
+ model: "claude-sonnet-4-6",
13
+
14
+ // System prompt
15
+ systemPrompt: "You are a PR reviewer...",
16
+
17
+ // Custom tools (see below)
18
+ tools: { /* ... */ },
19
+
20
+ // Runtime: "claude-code" (default) or "codex"
21
+ runtime: "claude-code",
22
+
23
+ // Permission mode: "default", "acceptEdits", or "bypassPermissions"
24
+ permissionMode: "default",
25
+
26
+ // Max conversation turns (default: 50)
27
+ maxTurns: 50,
28
+
29
+ // Max budget in USD
30
+ maxBudgetUsd: 5,
31
+
32
+ // Lifecycle hooks (see below)
33
+ onStart: async () => {},
34
+ onToolCall: async ({ toolName, input }) => {},
35
+ onToolResult: async ({ toolName, input, output }) => {},
36
+ onStepFinish: async ({ step }) => {},
37
+ onFinish: async ({ result }) => {},
38
+ onError: async ({ error }) => {},
39
+ })
40
+ ```
41
+
42
+ ### Defaults
43
+
44
+ | Field | Default |
45
+ |-------|---------|
46
+ | `model` | `"claude-sonnet-4-6"` |
47
+ | `runtime` | `"claude-code"` |
48
+ | `permissionMode` | `"default"` |
49
+ | `maxTurns` | `50` |
50
+ | `tools` | `{}` |
51
+
52
+ ## `tool(definition)`
53
+
54
+ ```ts
55
+ import { tool } from "@an-sdk/agent"
56
+ import { z } from "zod"
57
+
58
+ const myTool = tool({
59
+ description: "What this tool does",
60
+ inputSchema: z.object({
61
+ path: z.string(),
62
+ verbose: z.boolean().optional(),
63
+ }),
64
+ execute: async (args) => {
65
+ // args is fully typed: { path: string; verbose?: boolean }
66
+ return {
67
+ content: [{ type: "text", text: "result" }],
68
+ }
69
+ },
70
+ })
71
+ ```
72
+
73
+ The `execute` function receives args typed from the Zod schema. Return value must have `content` array with `{ type: "text", text: string }` items.
74
+
75
+ ## Hooks
76
+
77
+ ### `onToolCall`
78
+
79
+ Runs before a tool executes. Return `false` to block it.
80
+
81
+ ```ts
82
+ onToolCall: async ({ toolName, input }) => {
83
+ if (toolName === "Bash" && input.command?.includes("rm -rf")) {
84
+ return false // blocked
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### `onToolResult`
90
+
91
+ Runs after a tool executes with the result.
92
+
93
+ ```ts
94
+ onToolResult: async ({ toolName, input, output }) => {
95
+ console.log(`Tool ${toolName} returned:`, output)
96
+ }
97
+ ```
98
+
99
+ ### `onFinish`
100
+
101
+ Runs when the agent completes successfully.
102
+
103
+ ```ts
104
+ onFinish: async ({ result }) => {
105
+ await fetch("https://my-api.com/webhook", {
106
+ method: "POST",
107
+ body: JSON.stringify({ done: true }),
108
+ })
109
+ }
110
+ ```
111
+
112
+ ### `onError`
113
+
114
+ Runs when the agent encounters an error.
115
+
116
+ ```ts
117
+ onError: async ({ error }) => {
118
+ console.error("Agent failed:", error)
119
+ }
120
+ ```
121
+
122
+ ## Permission Modes
123
+
124
+ | Mode | Behavior |
125
+ |------|----------|
126
+ | `"default"` | Agent asks for confirmation on risky operations |
127
+ | `"acceptEdits"` | Auto-approve file edits, still confirm other actions |
128
+ | `"bypassPermissions"` | Auto-approve everything (use with caution) |
129
+
130
+ ## Type Reference
131
+
132
+ ```ts
133
+ // Agent config (all fields required — defaults filled by agent())
134
+ interface AgentConfig {
135
+ model: string
136
+ systemPrompt: string
137
+ tools: ToolSet
138
+ runtime: "claude-code" | "codex"
139
+ permissionMode: "default" | "acceptEdits" | "bypassPermissions"
140
+ maxTurns: number
141
+ maxBudgetUsd?: number
142
+ onStart?: () => Promise<void>
143
+ onToolCall?: (payload: { toolName: string; input: any }) => Promise<boolean | void>
144
+ onToolResult?: (payload: { toolName: string; input: any; output: any }) => Promise<void>
145
+ onStepFinish?: (payload: { step: any }) => Promise<void>
146
+ onFinish?: (payload: { result: any }) => Promise<void>
147
+ onError?: (payload: { error: Error }) => Promise<void>
148
+ }
149
+
150
+ // Tool definition — generic over Zod schema
151
+ interface ToolDefinition<TInput> {
152
+ description: string
153
+ inputSchema: ZodType<TInput>
154
+ execute: (args: TInput) => Promise<{ content: { type: string; text: string }[] }>
155
+ }
156
+
157
+ // Tool set
158
+ type ToolSet = Record<string, ToolDefinition<any>>
159
+ ```
@@ -0,0 +1,186 @@
1
+ # React UI
2
+
3
+ `@an-sdk/react` provides a full chat UI for AN agents. Built on [Vercel AI SDK v5](https://sdk.vercel.ai) — uses standard `useChat()` from `@ai-sdk/react`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @an-sdk/react ai @ai-sdk/react
9
+ ```
10
+
11
+ ## Basic Usage
12
+
13
+ ```tsx
14
+ "use client"
15
+
16
+ import { useChat } from "@ai-sdk/react"
17
+ import { AnAgentChat, createAnChat } from "@an-sdk/react"
18
+ import "@an-sdk/react/styles.css"
19
+ import { useMemo } from "react"
20
+
21
+ export default function Chat() {
22
+ const chat = useMemo(
23
+ () => createAnChat({
24
+ agent: "your-agent-slug",
25
+ getToken: async () => "your_an_sk_token",
26
+ }),
27
+ [],
28
+ )
29
+
30
+ const { messages, sendMessage, status, stop, error } = useChat({ chat })
31
+
32
+ return (
33
+ <AnAgentChat
34
+ messages={messages}
35
+ onSend={(msg) =>
36
+ sendMessage({ parts: [{ type: "text", text: msg.content }] })
37
+ }
38
+ status={status}
39
+ onStop={stop}
40
+ error={error}
41
+ />
42
+ )
43
+ }
44
+ ```
45
+
46
+ ## `createAnChat(options)`
47
+
48
+ Creates an AI SDK `Chat` instance pointed at the AN Relay.
49
+
50
+ ```ts
51
+ createAnChat({
52
+ agent: string // Agent slug from dashboard
53
+ getToken: () => Promise<string> // Returns an_sk_ key or JWT
54
+ apiUrl?: string // Default: "https://relay.an.dev"
55
+ projectId?: string // Session persistence key
56
+ onFinish?: () => void
57
+ onError?: (error: Error) => void
58
+ })
59
+ ```
60
+
61
+ For Next.js apps, use `tokenUrl` instead of `getToken` — see [Next.js Integration](./05-nextjs.md).
62
+
63
+ ## `<AnAgentChat />` Props
64
+
65
+ | Prop | Type | Description |
66
+ |------|------|-------------|
67
+ | `messages` | `UIMessage[]` | From `useChat()` |
68
+ | `onSend` | `(msg) => void` | Send handler |
69
+ | `status` | `ChatStatus` | `"ready" \| "submitted" \| "streaming" \| "error"` |
70
+ | `onStop` | `() => void` | Stop generation |
71
+ | `error` | `Error` | Error to display |
72
+ | `theme` | `AnTheme` | Theme from playground |
73
+ | `colorMode` | `"light" \| "dark" \| "auto"` | Color mode |
74
+ | `classNames` | `Partial<AnClassNames>` | Per-element CSS overrides |
75
+ | `slots` | `Partial<AnSlots>` | Component swapping |
76
+ | `className` | `string` | Root element class |
77
+ | `style` | `CSSProperties` | Root element style |
78
+
79
+ ## Customization
80
+
81
+ Four levels, from simple to full control:
82
+
83
+ ### 1. Theme tokens
84
+
85
+ Apply a theme JSON from the [AN Playground](https://an.dev/an/playground):
86
+
87
+ ```tsx
88
+ <AnAgentChat theme={playgroundTheme} colorMode="dark" />
89
+ ```
90
+
91
+ ### 2. Class overrides
92
+
93
+ ```tsx
94
+ <AnAgentChat
95
+ classNames={{
96
+ root: "rounded-2xl border",
97
+ messageList: "px-8",
98
+ inputBar: "bg-gray-50",
99
+ userMessage: "bg-blue-100",
100
+ }}
101
+ />
102
+ ```
103
+
104
+ ### 3. Slot components
105
+
106
+ Swap sub-components:
107
+
108
+ ```tsx
109
+ <AnAgentChat
110
+ slots={{
111
+ InputBar: MyCustomInput,
112
+ UserMessage: MyUserBubble,
113
+ ToolRenderer: MyToolRenderer,
114
+ }}
115
+ />
116
+ ```
117
+
118
+ ### 4. Individual component imports
119
+
120
+ ```tsx
121
+ import { MessageList, InputBar, ToolRenderer } from "@an-sdk/react"
122
+ ```
123
+
124
+ ## CSS
125
+
126
+ Import once in your app:
127
+
128
+ ```tsx
129
+ import "@an-sdk/react/styles.css"
130
+ ```
131
+
132
+ No Tailwind peer dependency — CSS is pre-compiled. All elements have stable `an-*` class names:
133
+
134
+ ```css
135
+ .an-root { }
136
+ .an-message-list { }
137
+ .an-user-message { }
138
+ .an-assistant-message { }
139
+ .an-input-bar { }
140
+ .an-send-button { }
141
+ .an-tool-bash { }
142
+ .an-tool-edit { }
143
+ ```
144
+
145
+ ## Theme Type
146
+
147
+ ```ts
148
+ interface AnTheme {
149
+ theme: Record<string, string> // Shared: font, spacing, accent
150
+ light: Record<string, string> // Light mode CSS vars
151
+ dark: Record<string, string> // Dark mode CSS vars
152
+ }
153
+ ```
154
+
155
+ ## `applyTheme(element, theme, colorMode?)`
156
+
157
+ Manually inject CSS variables from a theme JSON onto a DOM element.
158
+
159
+ ## Components
160
+
161
+ | Component | Description |
162
+ |-----------|-------------|
163
+ | `MessageList` | Auto-scrolling message container with grouping |
164
+ | `UserMessage` | User message bubble |
165
+ | `AssistantMessage` | Assistant response with parts routing |
166
+ | `StreamingMarkdown` | Markdown renderer with syntax highlighting |
167
+ | `InputBar` | Text input with send/stop buttons |
168
+ | `MessageActions` | Copy button |
169
+ | `ToolRenderer` | Routes tool parts to the correct component |
170
+
171
+ ## Built-in Tool Renderers
172
+
173
+ | Component | Renders |
174
+ |-----------|---------|
175
+ | `BashTool` | Terminal commands with output |
176
+ | `EditTool` | File edits with diff display |
177
+ | `WriteTool` | File creation |
178
+ | `SearchTool` | Web search results |
179
+ | `TodoTool` | Task checklists |
180
+ | `PlanTool` | Step-by-step plans |
181
+ | `TaskTool` | Sub-agent tasks |
182
+ | `McpTool` | MCP protocol calls |
183
+ | `ThinkingTool` | Reasoning/thinking indicator |
184
+ | `GenericTool` | Fallback for unknown tools |
185
+
186
+ See [Custom Tools](./08-custom-tools.md) for building your own tool renderers.