get2knowio / get2knowio/airframe

n8n community node + `airframe serve` HTTP service mode

Open
#60 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Summary

Two linked opportunities, surfaced while thinking through what an **n8n node for Airframe** would look like:

1. **`airframe serve`** — a small, optional HTTP service mode over the existing runtime/discovery surface. The reusable artifact.
2. **`n8n-nodes-airframe`** — a thin TypeScript community node that consumes Airframe (via the service, or the CLI as a local fallback).

The node is mostly glue; the service is the real deliverable, and it's reusable well beyond n8n (Zapier, Make, Retool, any non-Python consumer).

## Why a service (not just the CLI)

n8n is a Node.js app and community nodes are authored in **TypeScript** — there is no Python node SDK, and the inline "Code" node is Pyodide (WASM) which cannot run Airframe (no real CPython / subprocess SDKs / native deps). So a TS node must **bridge** to Airframe out-of-process. Two bridges, and the viable one depends on where n8n runs:

| n8n deployment | CLI subprocess (#58) | HTTP sidecar (`airframe serve`) |
|---|---|---|
| Self-hosted, Python/uv present | ✅ simplest | ✅ |
| Self-hosted container, no Python | ⚠️ must bake Python in | ✅ |
| **n8n Cloud** | ❌ can't add binaries | ✅ only option |

Two forces push toward the service:
- **n8n executes per-item.** A `uvx`/Python cold-start *per input item* is brutal for batches; a warm service amortizes import + SDK load. (The GitHub Action runs once per job, so cold-start was fine there — n8n's item loop is not.)
- **n8n Cloud** can't add system binaries, so only the HTTP client path works there.

Conclusion: "build an n8n node" largely reduces to "give Airframe a service mode, then write a thin HTTP-client node."

## `airframe serve` — proposed surface

Optional extra (`pip install airframe-agents[serve]`, FastAPI/uvicorn — kept out of core, lazy like the adapter extras). Thin HTTP veneer over `runtime_for` / `execute` / `list_providers` / `list_models`:

- `GET /healthz` — liveness.
- `GET /providers?installed_only=true` — mirrors `list_providers`.
- `GET /models?provider=claude` — mirrors `list_models` (needs auth).
- `POST /run` — body `{provider, model?, prompt, system?, schema?, format, timeout}`; returns `{text, structured, finish, cost_usd}`. (Same shape as the CLI's JSON envelope.)
- Later: `GET /capabilities?provider=…` (integrates #57), SSE streaming on `/run`.

### Service auth model (key open question)

Two modes, ideally both supported:
- **Single-tenant / personal** — service configured with provider creds via env (same chain as the CLI). Node just calls it.
- **Multi-tenant / BYO-subscription** — node passes the *user's* provider credential per request; service uses it per-request and does not store it. Maps onto the Musaic / per-user-subscription pattern. Requires TLS + a **service-level bearer token** so `/run` isn't open to anyone.

## n8n node — shape

An **action/transform node** (not a trigger — Airframe is request/response), sitting mid-workflow: input items → agent step → output items.

```ts
export class Airframe implements INodeType {
description = {
displayName: 'Airframe', name: 'airframe', group: ['transform'],
inputs: ['main'], outputs: ['main'],
credentials: [{ name: 'airframeProviderApi', required: true }],
properties: [
{ displayName: 'Provider', name: 'provider', type: 'options',
typeOptions: { loadOptionsMethod: 'getProviders' }, default: '' }, // -> GET /providers
{ displayName: 'Model', name: 'model', type: 'options',
typeOptions: { loadOptionsMethod: 'getModels',
loadOptionsDependsOn: ['provider'] }, default: '' }, // -> GET /models
{ displayName: 'Prompt', name: 'prompt', type: 'string',
typeOptions: { rows: 4 }, default: '' }, // supports {{$json.x}} expressions
{ displayName: 'System Prompt', name: 'system', type: 'string', default: '' },
{ displayName: 'Output', name: 'format', type: 'options',
options: [{name:'Text',value:'text'},{name:'JSON',value:'json'}], default: 'text' },
{ displayName: 'Timeout (s)', name: 'timeout', type: 'number', default: 600 },
],
};
methods = { loadOptions: { getProviders(){/* GET /providers */}, getModels(){/* GET /models */} } };
async execute() { /* per item: read params (expressions resolve), POST /run, attach result */ }
}
```

Design points:
- **Dynamic dropdowns map cleanly** onto `/providers` and `/models` (n8n `loadOptionsMethod`). Needs a **`list_models` exposure** — the library has `list_models()`; the CLI currently has only `run`/`providers`, so add a `models` command/endpoint.
- **Credentials = the BYO-subscription story, natively.** n8n credentials are per-instance and shareable per user; one user configures a Claude credential, another Copilot, and Provider-selector + matching credential routes accordingly. **#57** (capability matching) would let the Provider dropdown show only providers that satisfy the node's needs.
- **Do NOT flatten into n8n's LangChain "Chat Model" sub-node.** That interface expects a bare completions model and would throw away Airframe's agentic-runtime + provider-routing value. Keep it a standalone "Airframe: Run Agent" action node.

## Distribution

- `airframe serve` ships in `airframe-agents` under a `[serve]` extra.
- The node ships as an npm community node **`n8n-nodes-airframe`** (separate repo or subdir). Self-hosted n8n installs any community node; n8n Cloud requires verification.

## Open questions

1. **Service auth** — env creds vs per-request BYO creds; service bearer token; TLS expectations.
2. **Streaming** — SSE on `/run` now or later.
3. **Sessions** — Airframe sessions are stateful; start single-shot `execute` only, or expose session endpoints? (Start single-shot.)
4. **Process/concurrency model** — async single process; subprocess-based adapters (Claude/Kimi) spawn child processes and need their vendor CLI present on the host (same caveat as the GitHub Action).
5. **Packaging** — `[serve]` extra deps (FastAPI/uvicorn); node repo placement.

## Prerequisites / related

- **#58** — the `airframe` CLI (subprocess bridge + reference behavior for the service).
- Needs a **`list_models` surface** (CLI `models` command and/or service `/models`).
- **#57** — capability-matching surface makes the Provider/Model pickers smart and supports the BYO-subscription selection.
- **#56** — CLI `--cwd` + read-only tool mode (relevant if the service runs filesystem-capable adapters).
- Pattern siblings: this is the third consumer of the same spine (CLI → GitHub Action → n8n), each reinforcing "stable out-of-process interface + capability-aware selection."

Contributor guide

Open the contributing guide

Research direction

The issue names runtime_for, execute, list_providers, and list_models as existing entry points; begin there and review #58 for CLI behavior. Define the optional FastAPI service, its authentication and endpoint contract, then the separate TypeScript n8n node around that contract. Done means the documented service and node flow is implemented with the open packaging, streaming, session, and concurrency decisions resolved.

Written by the indexing model from the issue text.

Assessment

Tech stack
fastapi, python, typescript
Domain
api, backend, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.