@basenative/station

中文文档
3.0.0发布于 2026/10/31Apache-2.0TypeScriptES Module零依赖构建来源已验证

Queue-driven local-inference primitive — drives a cloudflared-tunneled vLLM tower (or any OpenAI-compatible endpoint) with an SQLite-backed job queue, iteration loop runtime, fallback to Workers AI, and reusable job templates.

原始使用文档
文档目录16 节
  1. What Station is
  2. How it differs from frontier inference
  3. When to use Station
  4. Install
  5. Quick start
  6. API
  7. defineStation(opts)
  8. Client / OpenAICompatClient
  9. Queue
  10. Runner
  11. templates
  12. ops
  13. CLI
  14. Ops loop
  15. Worker-runtime support
  16. License

@basenative/station

Queue-driven local-inference primitive — drives a cloudflared-tunneled vLLM tower (or any OpenAI-compatible endpoint) with an SQLite-backed job queue, an iteration-loop runtime, automatic Workers AI fallback, and a registry of pre-built job templates.

Part of the BaseNative ecosystem. Specified in DUGANLABS_ORCHESTRATOR_SPEC.md §9.

What Station is

A small, opinionated software primitive for running loop-pattern workloads against a local 7B-class coder model behind an OpenAI-compatible HTTP endpoint (in our reference stack: an RTX 5070 box serving Qwen2.5-Coder-7B INT4 via vLLM, exposed over a cloudflared tunnel).

It is not the tower hardware. It is the software that drives the tower.

How it differs from frontier inference

The cost-model from spec §9, restated:

TierPer-token marginal costQuality on novel codeQuality on loop-pattern code
Frontier (Sonnet 4.6 / Opus 4.7)Posted Anthropic ratesExcellentExcellent — wasted
Workers AI (gpt-oss-120b free tier)Free up to 10K Neurons/dayGoodGood — capped throughput
Station (local 7B INT4)$0 above electricity (~$5–10/mo)Poor one-shotAcceptable on narrow templated iteration

Idle tower: ~20W × 24h × $0.30/kWh × 30d ≈ $4.30/mo. Bursty inference: ~$5–10/mo all-in. Sonnet-equivalent for 5K Station iterations on docstring-coverage: ~$15–25.

Breakeven is roughly 50K tokens/day. Above that, Station wins on cost. Below it, just use Sonnet.

When to use Station

Use Station for tasks where:

  • The per-iteration stakes are low.
  • The template is narrow and the success check is deterministic.
  • You're going to do this a lot (≥hundreds of iterations).

Concrete fits:

  • Write one test for one TODO, mark done, advance.
  • Add one JSDoc to one undocumented export.
  • Fix one lint warning.
  • Apply one mechanical refactor step in one file.
  • Classify one inbound SMS into one FSM transition (Greenput intake).

Do not use Station for: planner tasks (Opus), novel implementer code (Sonnet), PR review (Opus), or anything that touches BaseNative axioms (frontier-only — drift is unrecoverable).

Install

pnpm add @basenative/station
# Optional, for production persistence:
pnpm add better-sqlite3

Quick start

import { defineStation } from '@basenative/station';

const station = defineStation({
  tunnelUrl: 'https://tower.your-account.cloudflareaccess.com',
  model: 'qwen2.5-coder-7b-instruct',
  queueDb: '/var/lib/dugan/station.db',
  fallback: {
    url: 'https://api.cloudflare.com/client/v4/accounts/<id>/ai/run',
    model: 'gpt-oss-120b',
  },
});

// Enqueue a job
const id = station.queue.enqueue({
  venture: 'basenative',
  intent: 'docstring-coverage',
  payload: {
    filePath: 'packages/runtime/src/signal.js',
    fileContents: '/* ... */',
    exportName: 'createSignal',
    exportKind: 'function',
  },
});

// Drain the queue (runs all queued jobs to completion or escalation)
const counters = await station.runner.run({ maxJobs: 50 });
console.log(counters);
// { processed: 50, completed: 47, escalated: 2, failed: 1 }

API

defineStation(opts)

Returns { client, queue, runner, templates }. The four pieces are also exported individually if you want to wire them differently (e.g. enqueue from a Worker, run from a Node daemon).

Client / OpenAICompatClient

import { OpenAICompatClient } from '@basenative/station';

const client = new OpenAICompatClient({
  baseUrl: 'https://tower.example.com',
  model: 'qwen2.5-coder-7b-instruct',
  fallbackUrl: 'https://api.cloudflare.com/.../ai/run',
});

const { text, latencyMs, source } = await client.chat({
  messages: [{ role: 'user', content: 'Write one JSDoc for foo.' }],
  temperature: 0.2,
  maxTokens: 400,
});
// source === 'primary' | 'fallback'

Fallback behavior: every chat() call hits the primary first. On any failure (HTTP error, malformed response, timeout) it retries on the fallback. If both fail, throws StationUnavailable with both error chains attached. 30-second hard timeout per call.

Queue

SQLite-schema-compatible (see src/queue.js for the DDL). Default driver is in-memory; pass betterSqlite3 to createQueue for persistence:

import Database from 'better-sqlite3';
import { createQueue } from '@basenative/station';
const queue = createQueue({ path: './station.db', betterSqlite3: Database });

API: enqueue, claim, recordIteration, complete, escalate, fail, list, iterationsFor, depth.

Runner

The loop runtime. Per iteration it:

  1. Looks up the job's template by intent.
  2. Builds a prompt via template.buildPrompt(payload).
  3. Calls the model.
  4. (Consumer hook) Applies the response as a diff in a sandboxed worktree.
  5. Runs template.successCheck(diff, payload).
  6. Records the iteration; advances or stalls.

Stall threshold and maxIterations are per-job; escalateTo is the upstream tier the orchestrator should route the job to when it stalls.

templates

Pre-built, frozen registry. Each template has name, description, buildPrompt, successCheck, maxIterations, escalateTo. The five included templates are:

  • tests-from-todos
  • docstring-coverage
  • lint-bankruptcy
  • refactor-migration
  • fsm-classifier

You can add your own — defineStation({ ..., templates: { ...templates, mine } }).

ops

Health-check helpers consumed by the orchestrator's station-ops agent:

  • tunnelHealth(url) — GET /health
  • modelHealth(client) — GET /v1/models, verifies expected model present
  • gpuHealth(url) — optional GET /gpu probe (stubbed; tower-side endpoint TBD)
  • queueHealth(queue) — depth + oldest-queued age
  • summary({ tunnelUrl, client, queue, gpuUrl }) — composite

CLI

bn-station enqueue <intent> [--venture x] [--payload @file.json]
bn-station list    [--status queued|running|done|escalated|failed]
bn-station run     [--max-iterations N] [--max-jobs N]
bn-station ops
bn-station drain

Reads config from STATION_TUNNEL_URL, STATION_MODEL, STATION_QUEUE_DB, STATION_FALLBACK_URL.

Ops loop

The intended deployment shape, per spec §5.5:

  1. A cron / systemd timer runs bn-station ops every 60s on the dispatcher host.
  2. On tunnel.ok === false, the station-ops agent flips the queue's effective fallback to Workers AI, posts to the TUI, and writes an incident.
  3. On queue.oldestQueuedAgeMs > 1h, escalate the oldest queued job per its escalateTo policy.
  4. On power/ISP outage, all in-flight jobs are escalated and surfaced to the user.

Worker-runtime support

Client and templates are pure fetch + pure functions and run unmodified in Cloudflare Workers. The default Queue driver is in-memory (Workers have no filesystem) — if you need durable queueing in a Worker, persist to D1/KV at a higher layer and call enqueue/claim from there.

Runner is portable and runs in either Node or a Worker.

License

Apache-2.0.

版本与发布记录

@basenative/station 共发布 11 个版本,首次发布于 2026/10/18;过去 12 个月发布了 11 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.0.0latest2026/10/312 天前
2.4.02026/10/268 天前
2.3.22026/10/2212 天前
2.3.12026/10/2212 天前
2.3.02026/10/2212 天前
2.2.02026/10/2113 天前

关于 @basenative/station 的常见问题

@basenative/station 是什么?

队列驱动的本地推理原语——驱动带有sqlite支持的作业队列、迭代循环运行时、回退到Workers AI和可重用的作业模板的cloudflare隧道vLLM塔(或任何openai兼容的端点)。

如何安装 @basenative/station?

在项目目录运行 npm install @basenative/station,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 bn-station,可以用 npx bn-station 直接运行。

@basenative/station 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@basenative/station 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@basenative/station 可以商用吗?

@basenative/station 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@basenative/station 还在维护吗?

最新版本 3.0.0 发布于 2026/10/31(2 天前);至今共发布 11 个版本,其中过去 12 个月发布了 11 个;首次发布于 2026/10/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。