# @routecraft/routecraft

Where your AI gets to work.

Routecraft is the open source AI automation platform your teams build on together. Write a capability as a typed TypeScript route, and the same code is an MCP tool for any agent, an HTTP endpoint or a scheduled job. Prove it on your laptop, promote it to a team harness, and every team can use it on service credentials instead of their own.

## Installation

```bash
# Bun (recommended)
bun add @routecraft/routecraft

# npm / pnpm / yarn
npm install @routecraft/routecraft
pnpm add @routecraft/routecraft
yarn add @routecraft/routecraft
```

## Quick Start

```typescript
// capabilities/timer-ping.ts
import { craft, timer, log } from '@routecraft/routecraft';

export default craft()
  .id('timer-ping')
  .from(timer({ interval: "1s" }))
  .transform((ex) => ({ timestamp: Date.now() }))
  .to(log());
```

Run it directly with the `craft` CLI (requires Bun on the host):

```bash
bunx @routecraft/cli run capabilities/timer-ping.ts
```

For Node-based projects, embed the library programmatically instead of using the CLI -- see [Programmatic Invocation](https://routecraft.dev/docs/advanced/programmatic-invocation).

## Core Concepts

### Capabilities

A capability is a named, typed pipeline that defines exactly what an agent or system can trigger. You compose capabilities using a chainable DSL:

```typescript
import { craft, timer, http } from '@routecraft/routecraft';

craft()
  .id('sync-users')
  .from(timer({ interval: "1m" }))
  .transform((ex) => ({ ...ex.body, syncedAt: Date.now() }))
  .to(http({ url: 'https://api.example.com/sync' }));
```

### The Exchange

Every message flowing through a capability is an `Exchange` -- a typed envelope with `body`, `headers`, and metadata:

```json
{
  "id": "ex_abc",
  "body": { "userId": "u_123", "action": "sync" },
  "headers": { "routecraft.route": "sync-users" }
}
```

### Operations

Operations transform or gate the data flowing through a capability:

| Operation | Purpose |
|-----------|---------|
| `.transform(fn)` | Reshape the exchange body |
| `.process(fn)` | Full exchange access for side effects |
| `.validate(schema)` | Validate the body against a Standard Schema (Zod, Valibot, ArkType); a failure throws `RC5002` and fails the exchange |
| `.filter(fn)` | Conditionally stop an exchange |
| `.enrich(adapter)` | Augment the body with data from an external source |
| `.to(adapter)` | Send to a destination |
| `.defer({ schema, ttl })` | Defer the exchange in a store and answer with a `Deferred` acknowledgment; `.resume()` continues it later by token |
| `.enabled(fn)` | Declare whether the route runs at all; a disabled route is known but not started |

### Context

Group capabilities into a context for lifecycle management:

```typescript
import { ContextBuilder } from '@routecraft/routecraft';

const ctx = new ContextBuilder()
  .routes([sendEmail, syncUsers, processWebhook])
  .build();

await ctx.start();
```

## Features

- Type-safe by default: the entire DSL uses TypeScript generics for end-to-end inference
- AI-authorable: the predictable, chainable DSL is easy for code generators like Claude or Cursor to write
- Run on a schedule or hand to an agent as a tool -- the same capability file works for both
- Secure by design: agents can only invoke the capabilities you expose; no filesystem or shell access unless a capability you wrote grants it
- Work that survives a restart: `.defer()` parks an exchange in a store and `.resume()` revives it by signed token, hours or days later, from any transport
- Operable: named `servers` carry HTTP routes, MCP and the ops surface; the ops plugin serves `/health`, `/ops/routes`, `/ops/deferrals` and `/ops/events`, and `remotes` make another instance's routes direct endpoints here
- Minimal dependencies

## Event System

Routecraft emits structured events throughout a capability's lifecycle, useful for observability, cost tracking, and debugging.

### Event Naming

Event names are a fixed, colon-separated set; identity (route id, plugin
id) lives in the payload, never in the name:

```text
context:started
route:registered
route:exchange:started        (details.routeId)
route:step:completed          (details.routeId, details.operation)
plugin:applied                (details.pluginId)
```

### Subscribing

`ctx.on()` / `ctx.once()` subscriptions use exact names plus payload
filtering. The `forRoute()` helper scopes a handler to one route; the
only pattern is the catch-all `"*"`, which observes every event. (The
`event()` source adapter additionally supports `*` / `**` patterns in
its filter.)

```typescript
import { ContextBuilder, forRoute } from '@routecraft/routecraft';

const ctx = new ContextBuilder()
  .routes([...])
  .on('route:exchange:completed', ({ details }) => {
    console.log('Exchange completed on', details.routeId);
  })
  .on('route:step:completed', forRoute('orders', ({ details }) => {
    console.log(`orders step ${details.operation} took ${details.duration}ms`);
  }))
  .build();
```

### Common events

```text
context:starting / started / stopping / stopped / error
route:registered / starting / started / stopping / stopped
route:error / route:error:caught
route:exchange:started / completed / failed / dropped / restored
route:step:started / completed / failed / error
route:batch:started / flushed / stopped
route:error-handler:invoked / recovered / failed
route:cache:hit / miss / stored / failed
route:exchange:deferred / resumed / expired
route:enablement:changed
server:listening / failed / closed
plugin:applying / applied / starting / started / stopping / stopped
```

See the [events reference](https://routecraft.dev/docs/reference/events)
for every name and payload shape.

### Adapter Metadata

Adapters populate `details.metadata` for filtering and metrics. Include small values (IDs, counts, codes) and exclude large bodies or sensitive data.

Examples by adapter type:
- **LLM**: model, provider, inputTokens, outputTokens, temperature
- **HTTP**: method, url, statusCode, contentLength
- **MCP**: toolName, transport, userId, serverId
- **Kafka**: topic, partition, offset, messageSize

### Event Source Adapter

Trigger a capability from internal events:

```typescript
import { craft, event, log } from '@routecraft/routecraft';

craft()
  .from(event('route:exchange:completed'))
  .process((ex) => {
    console.log('Route completed:', ex.body.details.routeId);
    return ex;
  })
  .to(log());
```

Note: the event adapter does not filter its own events. A route that subscribes to `route:step:*` or `route:exchange:*` events and then emits more of them loops; subscribe to lifecycle events, or filter in the route.

## Logging

Logs go to stdout at `warn` level by default.

- **Env vars**: `LOG_LEVEL` / `CRAFT_LOG_LEVEL`, `LOG_FILE` / `CRAFT_LOG_FILE`
- **CLI flags**: `craft --log-level info --log-file craft.log run <file>`
- **Config file**: `craft.log.js` (or `craft.log.cjs`) in the working directory or `~/.routecraft/` sets defaults; env vars and CLI flags win

## Documentation

For full guides, adapter reference, and examples, visit [routecraft.dev](https://routecraft.dev).

## Contributing

Contributions are welcome. See the [Contributing Guide](https://github.com/routecraftjs/routecraft/blob/main/CONTRIBUTING.md).

## License

Apache-2.0

## Links

- [Documentation](https://routecraft.dev)
- [GitHub Repository](https://github.com/routecraftjs/routecraft)
- [Issue Tracker](https://github.com/routecraftjs/routecraft/issues)
