npm package: @inventhq/plugin-sdk
Description: TypeScript SDK for plugin-runtime connector and event-handler plugins
Latest version: 0.1.1

# Plugin Runtime SDK

> TypeScript SDK + CLI for building event-handler and connector plugins that run inside secure V8 isolates.

## What's in this package

```
sdk/
├── cli/                 CLI tool (plugin init, build, deploy, dev, logs)
├── src/                 SDK source (types, helpers, providers, validation)
├── dist/                Built type definitions (ambient types for plugins)
├── examples/            Working example plugins
├── AI_CONTEXT.md        Machine-readable reference for AI coding assistants
├── DEVELOPER_GUIDE.md   Full human developer guide
└── package.json
```

## Quick Start

### 1. Install

```bash
npm install -g .    # from inside this sdk/ folder
# or link for development:
npm link
```

### 2. Create a plugin

```bash
plugin init my-plugin
# Prompts: pick a template, set your tenant prefix, set runtime URL
cd my-plugin
```

### 3. Edit → Build → Deploy

```bash
# Edit src/plugin.ts — full autocomplete via ambient types, no imports needed
plugin validate       # type-check
plugin build          # compile TS → JS
plugin deploy         # register with runtime (saves plugin_id to plugin.json)
plugin logs           # view execution logs
plugin dev            # watch mode — rebuild + deploy on save
```

## Runtime URL

By default the CLI points to `http://localhost:3050`. To change it:

- During `plugin init` — interactive prompt asks for the URL
- In `plugin.json` — set `"runtime_url": "https://your-runtime.example.com"`
- Via environment variable — `PLUGIN_RUNTIME_URL=https://...`

## Key Concepts

- **No imports** — `definePlugin`, `defineConnector`, `RuntimeHelper`, `defineTable`, etc. are globals injected into every plugin's V8 isolate
- **`plugin.json`** — config file created by `plugin init`. Contains name, events, tenant, runtime_url, allowed_domains
- **`allowed_domains`** — `runtime.fetch()` is blocked by default. List the API hostnames your plugin needs in `plugin.json`
- **Params are strings** — all event params and emit params must be strings. `RuntimeHelper.emit()` auto-coerces for you

## Plugin Types

**Event Handler** — reacts to events from the SSE pipeline:
```typescript
export default definePlugin({
  name: "my-plugin",
  events: ["order.created"],
  async onEvent(event: Event, runtime: Runtime) {
    const rt = new RuntimeHelper(runtime);
    await rt.emit("order.processed", { order_id: event.params.order_id });
  },
});
```

**Connector** — ingests data via webhooks, polling, and backfill:
```typescript
export default defineConnector({
  name: "my-connector",
  webhook: { verify(...) {}, normalize(...) {} },
  poller:  { fetch(...) {}, normalize(...) {} },
});
```

## Available Runtime APIs

| Method | Description |
|--------|-------------|
| `runtime.emit(type, params)` | Emit a derived event |
| `runtime.fetch(url, opts?)` | HTTP request (domain-restricted) |
| `runtime.dbExec(sql, params?)` | Write to plugin database |
| `runtime.dbQuery(sql, params?)` | Read from plugin database |
| `runtime.getState(key)` | Read plugin state (KV) |
| `runtime.setState(key, value)` | Write plugin state (KV) |
| `runtime.getConfig()` | Read plugin config JSON |
| `runtime.log.info/warn/error(msg)` | Structured logging |

## Documentation

- **[DEVELOPER_GUIDE.md](./DEVELOPER_GUIDE.md)** — full reference with examples for every feature
- **[AI_CONTEXT.md](./AI_CONTEXT.md)** — machine-readable context for AI coding assistants

## Examples

- `examples/e2e-stripe-charge-processor/` — event handler that processes Stripe charges
- `examples/stripe-connector/` — full connector with webhooks, polling, and backfill