@holokai/neuron-sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +166 -14
- package/dist/index.d.mts +54 -668
- package/dist/index.d.ts +54 -668
- package/dist/index.js +227 -209
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +227 -209
- package/dist/index.mjs.map +1 -1
- package/dist/ipc.d.mts +88 -0
- package/dist/ipc.d.ts +88 -0
- package/dist/ipc.js +86 -0
- package/dist/ipc.js.map +1 -0
- package/dist/ipc.mjs +84 -0
- package/dist/ipc.mjs.map +1 -0
- package/dist/transport-QqkooS7K.d.mts +742 -0
- package/dist/transport-QqkooS7K.d.ts +742 -0
- package/package.json +15 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Holokai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
SDK for building neurons — processes that connect to the BigBrain gateway and execute tasks on behalf of workflows.
|
|
4
4
|
|
|
5
|
-
A neuron advertises one or more **capabilities** (typed task handlers), opens a persistent connection to the gateway, and receives work via
|
|
5
|
+
A neuron advertises one or more **capabilities** (typed task handlers), opens a persistent connection to the gateway, and receives work via a pluggable `Transport`. Two transports ship in the SDK:
|
|
6
|
+
|
|
7
|
+
- **`HttpTransport`** — SSE for inbound, HTTPS POST for outbound. The default for any neuron talking to a remote BigBrain gateway. Re-exported from the package root.
|
|
8
|
+
- **`IpcTransport`** — Electron renderer ↔ main IPC. For neurons running inside an app that embeds BigBrain in main; no network hop, no JWT in the renderer. Imported from the `/ipc` subpath.
|
|
9
|
+
|
|
10
|
+
The gateway routes tasks to the right neuron based on capability type and scope. The built-in `apps/worker` uses this SDK over HTTP; the in-process Holokai Desktop renderer uses it over IPC; both share the same `Neuron` class and handler API.
|
|
6
11
|
|
|
7
12
|
---
|
|
8
13
|
|
|
@@ -115,19 +120,35 @@ neuronId: await readOrCreateStoredId('/var/lib/myapp/neuron-id')
|
|
|
115
120
|
|
|
116
121
|
### `new Neuron(opts: NeuronOptions)`
|
|
117
122
|
|
|
123
|
+
`NeuronOptions` is a discriminated union — pick the convenience shape (HTTP) or pass a transport explicitly.
|
|
124
|
+
|
|
118
125
|
```ts
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
type NeuronOptions = NeuronOptionsWithHttp | NeuronOptionsWithTransport;
|
|
127
|
+
|
|
128
|
+
interface NeuronCommonOptions {
|
|
121
129
|
neuronId: string; // Stable per process/device
|
|
122
|
-
auth: AuthCallback; // Token provider (see below)
|
|
123
130
|
logger?: NeuronLogger; // Plug in pino, console, etc. Default: silent
|
|
124
131
|
heartbeatIntervalMs?: number; // Default 10_000 (10s)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Convenience: SDK builds an HttpTransport for you. Most common path.
|
|
135
|
+
interface NeuronOptionsWithHttp extends NeuronCommonOptions {
|
|
136
|
+
gatewayUrl: string; // Base URL, e.g. 'https://api.example.com'
|
|
137
|
+
auth: AuthCallback; // Token provider (see below)
|
|
125
138
|
initialReconnectDelayMs?: number; // Default 500ms
|
|
126
139
|
maxReconnectDelayMs?: number; // Default 30_000 (30s)
|
|
127
140
|
fetch?: typeof fetch; // Override for tests
|
|
128
141
|
}
|
|
142
|
+
|
|
143
|
+
// Power-user: bring your own Transport. Used for IpcTransport, custom
|
|
144
|
+
// transports, or HttpTransport instances built explicitly.
|
|
145
|
+
interface NeuronOptionsWithTransport extends NeuronCommonOptions {
|
|
146
|
+
transport: Transport;
|
|
147
|
+
}
|
|
129
148
|
```
|
|
130
149
|
|
|
150
|
+
See the [Transports](#transports) section for `HttpTransport`, `IpcTransport`, and the `Transport` interface.
|
|
151
|
+
|
|
131
152
|
---
|
|
132
153
|
|
|
133
154
|
### `neuron.handle(reg, handler)`
|
|
@@ -358,6 +379,128 @@ The event surface is designed to grow — future entries (e.g. `'workflow:task-c
|
|
|
358
379
|
|
|
359
380
|
---
|
|
360
381
|
|
|
382
|
+
## Transports
|
|
383
|
+
|
|
384
|
+
A `Transport` is the wire-level abstraction the rest of the SDK builds on. It carries typed frames in both directions: inbound `ServerToNeuronFrame`s via callbacks, outbound `ClientFrame`s via `post(...)`. The `Neuron` class only ever talks to a `Transport` — adding a new transport (e.g. WebSocket, native messaging) means implementing one interface.
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
interface Transport {
|
|
388
|
+
subscribe(callbacks: TransportCallbacks): void;
|
|
389
|
+
start(): Promise<void>;
|
|
390
|
+
stop(): Promise<void>;
|
|
391
|
+
post<T extends ClientFrame>(path: PostPath, frame: T): Promise<unknown>;
|
|
392
|
+
isConnected(): boolean;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
interface TransportCallbacks {
|
|
396
|
+
onOpen?: () => void;
|
|
397
|
+
onClose?: (reason: string) => void;
|
|
398
|
+
onFrame?: (frame: ServerToNeuronFrame) => void;
|
|
399
|
+
onError?: (err: Error) => void;
|
|
400
|
+
onReconnecting?: (info: { attempt: number; delayMs: number; reason: string }) => void;
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Both `Transport` and `TransportCallbacks` are exported from the package root.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
### `HttpTransport`
|
|
409
|
+
|
|
410
|
+
Default transport. SSE for inbound, HTTPS POST for outbound. Reconnects with exponential backoff (default: 500ms → 30s ceiling). Refreshes the auth token via your `AuthCallback` and retries once on 401.
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
import { Neuron, HttpTransport } from '@holokai/neuron-sdk';
|
|
414
|
+
|
|
415
|
+
const transport = new HttpTransport({
|
|
416
|
+
gatewayUrl: 'https://api.example.com',
|
|
417
|
+
neuronId: 'my-worker-001',
|
|
418
|
+
auth: () => process.env.GATEWAY_TOKEN!,
|
|
419
|
+
// optional:
|
|
420
|
+
initialReconnectDelayMs: 500,
|
|
421
|
+
maxReconnectDelayMs: 30_000,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const neuron = new Neuron({ neuronId: 'my-worker-001', transport });
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
In practice you rarely construct `HttpTransport` directly — passing `gatewayUrl` + `auth` to `Neuron` builds it for you. Construct explicitly when you want to share a fetch implementation, swap the auth strategy, or instrument the transport in tests.
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
interface HttpTransportOptions {
|
|
431
|
+
gatewayUrl: string;
|
|
432
|
+
neuronId: string;
|
|
433
|
+
auth: AuthCallback;
|
|
434
|
+
logger?: NeuronLogger;
|
|
435
|
+
initialReconnectDelayMs?: number; // default 500ms
|
|
436
|
+
maxReconnectDelayMs?: number; // default 30_000ms
|
|
437
|
+
fetch?: typeof fetch;
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
### `IpcTransport`
|
|
444
|
+
|
|
445
|
+
For renderer-process neurons running inside an Electron app that hosts BigBrain in the main process. Leases arrive over Electron IPC, not over the network. Imported from the `/ipc` subpath so non-Electron consumers don't pay for it:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
import { Neuron } from '@holokai/neuron-sdk';
|
|
449
|
+
import { IpcTransport } from '@holokai/neuron-sdk/ipc';
|
|
450
|
+
|
|
451
|
+
// `bridge` comes from the renderer's preload — see below.
|
|
452
|
+
const transport = new IpcTransport({ bridge: window.bigbrainNeuron, neuronId });
|
|
453
|
+
const neuron = new Neuron({ neuronId, transport });
|
|
454
|
+
|
|
455
|
+
neuron.handle({ type: 'desktop/local-tool', scope: { onBehalfOf: userId }, concurrency: 1 }, handler);
|
|
456
|
+
await neuron.start();
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Differences from `HttpTransport`:
|
|
460
|
+
|
|
461
|
+
- **No reconnect logic** — IPC has no transient socket. If the bridge throws, the failure surfaces via `onError` and the consumer decides what to do.
|
|
462
|
+
- **No auth callback** — the renderer never sees the JWT. The Electron main process attaches `Authorization` headers when it forwards POSTs to the embedded Fastify gateway.
|
|
463
|
+
- **No keep-alive** — IPC channels are process-local; nothing to time out.
|
|
464
|
+
|
|
465
|
+
The SDK does **not** depend on Electron. The renderer's preload script is responsible for constructing a concrete bridge that satisfies the `NeuronIpcBridge` shape and exposing it via `contextBridge`:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
interface NeuronIpcBridge {
|
|
469
|
+
attach(neuronId: string): Promise<{ channel: string }>;
|
|
470
|
+
detach(neuronId: string): Promise<void>;
|
|
471
|
+
subscribe(channel: string, cb: (frame: ServerToNeuronFrame) => void): () => void;
|
|
472
|
+
invoke<T extends ClientFrame>(path: PostPath, frame: T): Promise<unknown>;
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Typical preload wiring (Electron-side, app code — not part of the SDK):
|
|
477
|
+
|
|
478
|
+
```ts
|
|
479
|
+
// preload.ts
|
|
480
|
+
import { contextBridge, ipcRenderer } from 'electron';
|
|
481
|
+
|
|
482
|
+
contextBridge.exposeInMainWorld('bigbrainNeuron', {
|
|
483
|
+
attach: (neuronId) => ipcRenderer.invoke('bigbrain:attach', neuronId),
|
|
484
|
+
detach: (neuronId) => ipcRenderer.invoke('bigbrain:detach', neuronId),
|
|
485
|
+
subscribe: (channel, cb) => {
|
|
486
|
+
const listener = (_e, frame) => cb(frame);
|
|
487
|
+
ipcRenderer.on(channel, listener);
|
|
488
|
+
return () => ipcRenderer.off(channel, listener);
|
|
489
|
+
},
|
|
490
|
+
invoke: (path, frame) => ipcRenderer.invoke('bigbrain:post', path, frame),
|
|
491
|
+
});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
interface IpcTransportOptions {
|
|
496
|
+
bridge: NeuronIpcBridge;
|
|
497
|
+
neuronId: string;
|
|
498
|
+
logger?: NeuronLogger;
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
361
504
|
## Examples
|
|
362
505
|
|
|
363
506
|
### Simple any-scoped capability
|
|
@@ -493,15 +636,17 @@ powerMonitor.on('on-battery', async () => {
|
|
|
493
636
|
|
|
494
637
|
## Configuration reference
|
|
495
638
|
|
|
496
|
-
| Option | Default | Notes |
|
|
497
|
-
|
|
498
|
-
| `
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
501
|
-
| `
|
|
502
|
-
| `
|
|
503
|
-
| `
|
|
504
|
-
| `
|
|
639
|
+
| Option | Default | Applies to | Notes |
|
|
640
|
+
|---|---|---|---|
|
|
641
|
+
| `neuronId` | — | both | Stable per device/process; reused on reconnect |
|
|
642
|
+
| `logger` | silent no-op | both | Plug in pino or console |
|
|
643
|
+
| `heartbeatIntervalMs` | `10_000` | both | Sent every 10s; renews lease expiries |
|
|
644
|
+
| `transport` | — | with-transport shape | Pre-built `Transport` (e.g. `IpcTransport` or a custom `HttpTransport`) |
|
|
645
|
+
| `gatewayUrl` | — | with-http shape | Base URL, no trailing slash |
|
|
646
|
+
| `auth` | — | with-http shape | Called on every POST + SSE open |
|
|
647
|
+
| `initialReconnectDelayMs` | `500` | with-http shape | First reconnect delay (HTTP only — IPC doesn't reconnect) |
|
|
648
|
+
| `maxReconnectDelayMs` | `30_000` | with-http shape | Reconnect delay ceiling (exponential backoff) |
|
|
649
|
+
| `fetch` | global `fetch` | with-http shape | Override for tests |
|
|
505
650
|
|
|
506
651
|
---
|
|
507
652
|
|
|
@@ -511,10 +656,17 @@ This section covers enough to debug; the wire-level frame schemas are bundled in
|
|
|
511
656
|
|
|
512
657
|
### Transport
|
|
513
658
|
|
|
659
|
+
The SDK abstracts the wire as a `Transport` (see [Transports](#transports) above). The frame types below are the same regardless of transport — only the carrier differs.
|
|
660
|
+
|
|
661
|
+
**`HttpTransport` (default):**
|
|
514
662
|
- **Server → Neuron**: SSE stream at `GET /neuron/events?neuronId={id}`
|
|
515
663
|
- **Neuron → Server**: HTTPS POST endpoints (one per frame type)
|
|
664
|
+
- No WebSockets — SSE + HTTPS POST traverses enterprise proxies cleanly.
|
|
516
665
|
|
|
517
|
-
|
|
666
|
+
**`IpcTransport` (Electron renderer):**
|
|
667
|
+
- **Server → Neuron**: per-neuron Electron IPC channel obtained from `bridge.attach()`.
|
|
668
|
+
- **Neuron → Server**: `bridge.invoke(path, frame)`, which the main process forwards to the embedded Fastify gateway via `app.inject(...)` — same auth and validation chain as HTTP.
|
|
669
|
+
- No reconnect, no auth callback, no keep-alive.
|
|
518
670
|
|
|
519
671
|
### Frame types
|
|
520
672
|
|