@olsisoft/pulse-client
v2.6.1
Published
Official JavaScript / TypeScript client for StreamFlow Pulse — AI Agent Platform
Downloads
302
Maintainers
Readme
@olsisoft/pulse-client — JavaScript / TypeScript SDK for StreamFlow Pulse
Official client for the Pulse AI Agent Platform.
import { PulseClient } from '@olsisoft/pulse-client';
const client = new PulseClient({ baseUrl: 'http://localhost:9090' });
await client.auth.login('alice', 'secret');
for (const pipeline of await client.pipelines.list()) {
console.log(pipeline.name);
}Install
npm install @olsisoft/pulse-clientWorks on Node 20+ (native fetch) and modern browsers. Zero runtime dependencies. Ships ESM + CJS + .d.ts so TypeScript consumers get full type completion without installing @types/anything.
Why @olsisoft/pulse-client
- Environment-agnostic — single package for Node services, serverless functions, Vite/Next.js apps, browser scripts. Pass the JWT as a constructor arg; no cookies / no
localStoragecoupling. - Lightweight — pure TypeScript, single file at runtime (~9 kB ESM gzipped), zero peer deps. No Axios. No node-fetch polyfill. Just
fetch. - Spec-aligned — every method corresponds 1:1 to an endpoint in the Pulse OpenAPI 3.1 spec. Drift caught at PR time by the in-tree invariant tests (B-103).
- Typed errors —
PulseAuthError(401),PulseNotFoundError(404),PulseValidationError(400),PulseRateLimitError(429, with.retryAfterSeconds),PulseAPIError(everything else). All extendPulseClientError.
Quick start
import { PulseClient, PulseAuthError } from '@olsisoft/pulse-client';
const client = new PulseClient({ baseUrl: 'http://localhost:9090' });
// Authenticate — JWT is cached on the client automatically
try {
const response = await client.auth.login('alice', 'secret');
console.log(`Logged in as ${response.user?.username}`);
} catch (e) {
if (e instanceof PulseAuthError) console.error('Login failed:', e.message);
else throw e;
}
// List + inspect
for (const pipeline of await client.pipelines.list()) {
console.log(pipeline.name, pipeline.status);
}
// Create a pipeline from a template
const newPipeline = await client.pipelines.create({
name: 'my-fraud-detector',
templateId: 'fintech-fraud-detection-realtime',
nodes: [
{ id: 'source', type: 'source', subType: 'kafka-source' },
{ id: 'agent', type: 'agent', subType: 'streaming' },
{ id: 'sink', type: 'sink', subType: 'telegram' },
],
});
console.log('created', newPipeline.id);
// Inspect deployed agents
for (const agent of await client.agents.list()) {
console.log(` ${agent.name} — ${agent.engineType} — ${agent.status}`);
}Supported surfaces (v2.6.0)
| Resource | Methods | Notes |
|---|---|---|
| client.auth | login(), refresh(), organizations(), switchOrg() | Auto-caches JWT after login / refresh / switchOrg. |
| client.pipelines | list(), get(id), create(definition), delete(id) | definition follows the CreatePipelineRequest schema (see OpenAPI spec). |
| client.agents | list(), get(id) | Read-only — agents are owned by pipelines. |
| client.templates | list() | The 223+ first-party templates. |
| client.users | list() | Requires USERS_LIST permission (Owner / Platform Admin personas). |
| client.version() | top-level | Public — no JWT required. |
The full ~112-endpoint surface is documented in the OpenAPI spec at <pulse-server>/api-docs. SDK methods for the rest land opportunistically as user-facing demand surfaces.
Embedded ML inference & duplex
Score events with an uploaded ONNX model in-process (B-112), and open a bidirectional duplex channel for synchronous decisions (B-114). Full guide: ML inference & duplex.
// Upload + score with an ONNX model (no model-server hop)
await client.models.upload({ name: 'fraud', path: './fraud.onnx',
inputSchema: { amount: 'float', country: 'float' } });
builder.fromTopic('transactions')
.mlPredict({ model: 'fraud', inputFields: ['amount', 'country'], outputField: 'prediction' })
.filter('prediction.fraud_score > 0.8').toTopic('flagged');
// Duplex: one connection, send in / receive the correlated output
// (global WebSocket on Node 22+/browsers; `npm i ws` on Node < 22)
const ch = await client.duplex('fraud-detector');
await ch.send({ amount: 5000 }, 'tx-1');
const signal = await ch.recv(); // signal.correlationId === 'tx-1'
await ch.close();Authentication
// 1. Username + password (interactive / CLI tools)
const client = new PulseClient({ baseUrl: 'http://localhost:9090' });
await client.auth.login('alice', 'secret');
// 2. Pre-minted JWT (CI / service accounts)
const client = new PulseClient({
baseUrl: 'http://localhost:9090',
token: 'ey...',
});
// 3. JWT from environment (12-factor apps)
const client = new PulseClient({
baseUrl: process.env.PULSE_URL!,
token: process.env.PULSE_TOKEN,
});For long-running daemons, store the refreshToken from login() and call client.auth.refresh(refreshToken) when the JWT nears expiry (default 1 h TTL).
Error handling
import {
PulseClientError, // base — catches every client-side error
PulseAuthError, // 401 — invalid / missing / expired JWT
PulseNotFoundError, // 404
PulseValidationError,// 400 — malformed request
PulseRateLimitError, // 429 — carries .retryAfterSeconds
PulseAPIError, // everything else (5xx, etc.)
} from '@olsisoft/pulse-client';
try {
await client.pipelines.get('nope');
} catch (e) {
if (e instanceof PulseNotFoundError) {
console.log("Doesn't exist — fine");
} else if (e instanceof PulseRateLimitError) {
const wait = (e.retryAfterSeconds ?? 60) * 1000;
console.log(`Backing off ${wait}ms`);
await new Promise((r) => setTimeout(r, wait));
} else if (e instanceof PulseClientError) {
console.error('Pulse error:', e.message);
} else {
throw e;
}
}Every exception carries .statusCode, .path, and .body so log lines + bug reports are actionable.
Browser / Edge runtime usage
The package ships ESM as the primary build, so modern bundlers (Vite, Next.js, esbuild, webpack 5+) tree-shake unused resources cleanly. No node-specific imports → works in:
- Browser (via bundler — Vite, Next.js, etc.)
- Cloudflare Workers / Pages
- Deno (via
npm:specifier oresm.sh) - Bun
- Node 20+
If you're on a runtime that doesn't expose a global fetch, pass your own:
import nodeFetch from 'node-fetch';
const client = new PulseClient({
baseUrl: 'http://localhost:9090',
fetch: nodeFetch as unknown as typeof fetch,
});Development
git clone https://github.com/olsisoft/pulse-js.git
cd pulse-js
npm install
npm run typecheck # tsc --noEmit
npm run test # vitest run
npm run test:watch # vitest in watch mode
npm run build # tsup → dist/ (ESM + CJS + .d.ts)CI runs the same on every push touching pulse-js/ — see .github/workflows/pulse-js.yaml.
Roadmap
- v2.5.x — current API, 5 core resources (auth, pipelines, agents, templates, users),
version(). - v2.6.x — expanded resource coverage: backups, schedules, credentials, settings, approvals, chat.
- v3.0 — event-stream consumer (
client.events.stream()returns an async iterable wrapping the SSE endpoint at/api/pulse/events/stream). - B-098 satellite — once
olsisoft/pulse-jsexists as its own repo, this in-tree code lifts out wholesale.npm installwill switch to the satellite; in-tree continues to mirror for one release cycle so the migration is non-breaking.
Track progress in docs/STREAMFLOW-BACKLOG.md under item B-098.
License
Apache 2.0 — same as the parent Pulse repository.
