@net-mesh/sdk 0.19.0
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/README.md +1684 -0
- package/dist/_internal.d.ts +25 -0
- package/dist/_internal.js +60 -0
- package/dist/capabilities.d.ts +271 -0
- package/dist/capabilities.js +186 -0
- package/dist/capability-enhancements.d.ts +574 -0
- package/dist/capability-enhancements.js +1324 -0
- package/dist/capability-schema.d.ts +112 -0
- package/dist/capability-schema.js +317 -0
- package/dist/channel.d.ts +56 -0
- package/dist/channel.js +95 -0
- package/dist/compute.d.ts +546 -0
- package/dist/compute.js +741 -0
- package/dist/cortex.d.ts +236 -0
- package/dist/cortex.js +584 -0
- package/dist/deck.d.ts +342 -0
- package/dist/deck.js +717 -0
- package/dist/groups.d.ts +208 -0
- package/dist/groups.js +431 -0
- package/dist/identity.d.ts +149 -0
- package/dist/identity.js +264 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +147 -0
- package/dist/mesh.d.ts +369 -0
- package/dist/mesh.js +433 -0
- package/dist/meshdb.d.ts +87 -0
- package/dist/meshdb.js +111 -0
- package/dist/meshos.d.ts +277 -0
- package/dist/meshos.js +359 -0
- package/dist/node.d.ts +120 -0
- package/dist/node.js +246 -0
- package/dist/redis-dedup.d.ts +48 -0
- package/dist/redis-dedup.js +52 -0
- package/dist/stream.d.ts +47 -0
- package/dist/stream.js +118 -0
- package/dist/subnets.d.ts +75 -0
- package/dist/subnets.js +54 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.js +5 -0
- package/package.json +43 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute surface — `MeshDaemon` + `DaemonRuntime`.
|
|
3
|
+
*
|
|
4
|
+
* Stage 3 of `SDK_COMPUTE_SURFACE_PLAN.md`. Sub-step 1 lands the
|
|
5
|
+
* skeleton: a caller can build a runtime against an existing
|
|
6
|
+
* {@link MeshNode}, register a factory (stored but not yet
|
|
7
|
+
* invoked), start the runtime, and shut it down. Event delivery,
|
|
8
|
+
* migration, and snapshot/restore land in subsequent sub-steps.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { MeshNode, DaemonRuntime } from '@net-mesh/sdk';
|
|
13
|
+
*
|
|
14
|
+
* const mesh = await MeshNode.create({ bindAddr: '127.0.0.1:0', psk: '...' });
|
|
15
|
+
* const rt = DaemonRuntime.create(mesh);
|
|
16
|
+
*
|
|
17
|
+
* // Sub-step 1: register a factory shape the TS side can see.
|
|
18
|
+
* // Sub-step 2+ will actually invoke the returned object on
|
|
19
|
+
* // events delivered by Rust.
|
|
20
|
+
* rt.registerFactory('echo', () => ({
|
|
21
|
+
* name: 'echo',
|
|
22
|
+
* process: (event) => [event.payload],
|
|
23
|
+
* }));
|
|
24
|
+
*
|
|
25
|
+
* await rt.start();
|
|
26
|
+
* // ... daemons would run here (sub-step 3+) ...
|
|
27
|
+
* await rt.shutdown();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { Identity } from './identity.js';
|
|
31
|
+
import { MeshNode } from './mesh.js';
|
|
32
|
+
/**
|
|
33
|
+
* Base class for daemon-layer errors: factory registration, runtime
|
|
34
|
+
* lifecycle, spawn/stop, migration. The Rust side prefixes every
|
|
35
|
+
* message with `daemon:`; this file peels the prefix and rethrows
|
|
36
|
+
* the typed class so TS callers can `catch (e: DaemonError)`.
|
|
37
|
+
*/
|
|
38
|
+
export declare class DaemonError extends Error {
|
|
39
|
+
constructor(message: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Stable discriminator for migration-layer failures. Use
|
|
43
|
+
* `err.kind` in catch blocks rather than parsing messages.
|
|
44
|
+
*
|
|
45
|
+
* - `not-ready` — target runtime exists but hasn't called
|
|
46
|
+
* `start()`. Retriable; source auto-retries by default.
|
|
47
|
+
* - `factory-not-found` — target has no factory for the daemon's
|
|
48
|
+
* origin_hash. Terminal — target mis-configured.
|
|
49
|
+
* - `compute-not-supported` — target node is a bare `Mesh` with
|
|
50
|
+
* no `DaemonRuntime`. Terminal.
|
|
51
|
+
* - `state-failed` — snapshot encode/decode or restore failed.
|
|
52
|
+
* Terminal. `detail` carries the underlying message.
|
|
53
|
+
* - `already-migrating` — a migration is already in flight for
|
|
54
|
+
* the same origin_hash. Terminal on the duplicate attempt.
|
|
55
|
+
* - `identity-transport-failed` — envelope signature/unseal
|
|
56
|
+
* failure. Terminal. `detail` carries the underlying message.
|
|
57
|
+
* - `not-ready-timeout` — source exhausted its NotReady-retry
|
|
58
|
+
* budget. Terminal. `attempts` is the retry count.
|
|
59
|
+
* - `daemon-not-found` — orchestrator couldn't locate the daemon
|
|
60
|
+
* on the source node. Carries `originHash`.
|
|
61
|
+
* - `target-unavailable` — target node ID isn't in the source's
|
|
62
|
+
* peer table. Carries `nodeId`.
|
|
63
|
+
* - `wrong-phase` — internal phase-machine violation (shouldn't
|
|
64
|
+
* surface in practice; carries expected + actual phase).
|
|
65
|
+
* - `snapshot-too-large` — snapshot exceeds the transfer limit;
|
|
66
|
+
* carries `size` and `max`.
|
|
67
|
+
*/
|
|
68
|
+
export type MigrationErrorKind = 'not-ready' | 'factory-not-found' | 'compute-not-supported' | 'state-failed' | 'already-migrating' | 'identity-transport-failed' | 'not-ready-timeout' | 'daemon-not-found' | 'target-unavailable' | 'wrong-phase' | 'snapshot-too-large' | 'unknown';
|
|
69
|
+
/**
|
|
70
|
+
* Typed migration failure. Subclass of {@link DaemonError} so
|
|
71
|
+
* `catch (e: DaemonError)` still matches; callers who want to
|
|
72
|
+
* discriminate use `e instanceof MigrationError` + `e.kind`.
|
|
73
|
+
*
|
|
74
|
+
* **Retriability:** only `kind === 'not-ready'` is retriable
|
|
75
|
+
* (the source SDK auto-retries on this by default). Everything
|
|
76
|
+
* else is terminal — a caller's own retry loop won't help.
|
|
77
|
+
*/
|
|
78
|
+
export declare class MigrationError extends DaemonError {
|
|
79
|
+
readonly kind: MigrationErrorKind;
|
|
80
|
+
/** Number of NotReady retries on `not-ready-timeout`. */
|
|
81
|
+
readonly attempts?: number;
|
|
82
|
+
/** Daemon origin on `daemon-not-found` / `already-migrating`. */
|
|
83
|
+
readonly originHash?: number;
|
|
84
|
+
/** Node ID on `target-unavailable`. */
|
|
85
|
+
readonly nodeId?: bigint;
|
|
86
|
+
/** Size / max on `snapshot-too-large`. */
|
|
87
|
+
readonly size?: number;
|
|
88
|
+
readonly max?: number;
|
|
89
|
+
/** Underlying string detail on `state-failed` / `identity-transport-failed`. */
|
|
90
|
+
readonly detail?: string;
|
|
91
|
+
constructor(kind: MigrationErrorKind, message: string, extras?: {
|
|
92
|
+
attempts?: number;
|
|
93
|
+
originHash?: number;
|
|
94
|
+
nodeId?: bigint;
|
|
95
|
+
size?: number;
|
|
96
|
+
max?: number;
|
|
97
|
+
detail?: string;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Phase a migration is currently in. Order is monotonic:
|
|
102
|
+
* `snapshot` → `transfer` → `restore` → `replay` → `cutover` →
|
|
103
|
+
* `complete`. Once complete (or aborted), the orchestrator drops
|
|
104
|
+
* its record and {@link MigrationHandle.phase} returns `null`.
|
|
105
|
+
*/
|
|
106
|
+
export type MigrationPhase = 'snapshot' | 'transfer' | 'restore' | 'replay' | 'cutover' | 'complete';
|
|
107
|
+
/**
|
|
108
|
+
* Options for {@link DaemonRuntime.startMigrationWith}. Omit any
|
|
109
|
+
* field to take the runtime default.
|
|
110
|
+
*/
|
|
111
|
+
export interface MigrationOptions {
|
|
112
|
+
/**
|
|
113
|
+
* Seal the daemon's ed25519 seed into the outbound snapshot so
|
|
114
|
+
* the target keeps full signing capability. Default `true`;
|
|
115
|
+
* set `false` for pure compute daemons that only consume events
|
|
116
|
+
* and don't need to sign anything on the target.
|
|
117
|
+
*/
|
|
118
|
+
readonly transportIdentity?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Retry budget for `NotReady` targets, in milliseconds. Default
|
|
121
|
+
* 30_000 (30 s). Pass `0` to disable retry — the first
|
|
122
|
+
* `NotReady` surfaces as a terminal failure.
|
|
123
|
+
*/
|
|
124
|
+
readonly retryNotReadyMs?: bigint;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A causal event delivered to a daemon's `process`. Sub-step 3
|
|
128
|
+
* will plumb this through NAPI; sub-step 1 declares the shape so
|
|
129
|
+
* the factory signature is callable today.
|
|
130
|
+
*/
|
|
131
|
+
export interface CausalEvent {
|
|
132
|
+
/** 64-bit origin hash of the emitting entity. */
|
|
133
|
+
readonly originHash: bigint;
|
|
134
|
+
/** Sequence number in the emitter's causal chain. */
|
|
135
|
+
readonly sequence: bigint;
|
|
136
|
+
/** Opaque payload bytes. */
|
|
137
|
+
readonly payload: Buffer;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* User-implemented daemon. The object returned by the factory
|
|
141
|
+
* passed to {@link DaemonRuntime.registerFactory}.
|
|
142
|
+
*
|
|
143
|
+
* `process` is synchronous by contract — do not return a Promise.
|
|
144
|
+
* Snapshot/restore are optional; stateless daemons omit them.
|
|
145
|
+
*/
|
|
146
|
+
export interface MeshDaemon {
|
|
147
|
+
/** Stable human-readable name. Used only for diagnostics. */
|
|
148
|
+
readonly name: string;
|
|
149
|
+
/**
|
|
150
|
+
* Handle one inbound event. Return zero or more output payloads
|
|
151
|
+
* (buffers); each is wrapped in a fresh causal link by the host.
|
|
152
|
+
*
|
|
153
|
+
* Must be synchronous — the core's `process` contract is sync,
|
|
154
|
+
* and the TSFN bridge in sub-step 3 blocks the calling tokio
|
|
155
|
+
* task until this returns.
|
|
156
|
+
*/
|
|
157
|
+
process(event: CausalEvent): Buffer[];
|
|
158
|
+
/** Optional: serialize current state for migration / persistence. */
|
|
159
|
+
snapshot?(): Buffer | null;
|
|
160
|
+
/** Optional: restore state from a snapshot produced by `snapshot`. */
|
|
161
|
+
restore?(state: Buffer): void;
|
|
162
|
+
/**
|
|
163
|
+
* Phase 6 of `CAPABILITY_SYSTEM_SDK_PLAN.md` — hard placement
|
|
164
|
+
* requirements declared at factory time. Drives the substrate's
|
|
165
|
+
* `MeshDaemon::required_capabilities`; missing tags veto
|
|
166
|
+
* placement (`StandardPlacement` returns `None` for any
|
|
167
|
+
* candidate node missing a required tag).
|
|
168
|
+
*
|
|
169
|
+
* Static — captured once when the factory returns; not
|
|
170
|
+
* re-queried per placement decision. Omit for "runs anywhere"
|
|
171
|
+
* defaults.
|
|
172
|
+
*
|
|
173
|
+
* Example:
|
|
174
|
+
* ```ts
|
|
175
|
+
* rt.registerFactory('inference', () => ({
|
|
176
|
+
* name: 'inference',
|
|
177
|
+
* process: (ev) => doWork(ev.payload),
|
|
178
|
+
* requiredCapabilities: {
|
|
179
|
+
* tags: ['hardware.gpu', 'hardware.gpu.vram_gb=24'],
|
|
180
|
+
* },
|
|
181
|
+
* optionalCapabilities: {
|
|
182
|
+
* tags: ['hardware.gpu.vram_gb=80'],
|
|
183
|
+
* },
|
|
184
|
+
* }));
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
requiredCapabilities?: import('./capabilities').CapabilitySet;
|
|
188
|
+
/**
|
|
189
|
+
* Phase 6 of `CAPABILITY_SYSTEM_SDK_PLAN.md` — soft placement
|
|
190
|
+
* preferences. Factor into per-axis scoring; missing optional
|
|
191
|
+
* tags don't veto placement (unlike `requiredCapabilities`).
|
|
192
|
+
* Omit for "no preferences" default.
|
|
193
|
+
*/
|
|
194
|
+
optionalCapabilities?: import('./capabilities').CapabilitySet;
|
|
195
|
+
}
|
|
196
|
+
/** A zero-arg function returning a {@link MeshDaemon} or a Promise of one. */
|
|
197
|
+
export type DaemonFactory = () => MeshDaemon | Promise<MeshDaemon>;
|
|
198
|
+
/**
|
|
199
|
+
* Runtime statistics for a single daemon. Read via
|
|
200
|
+
* {@link DaemonHandle.stats}.
|
|
201
|
+
*
|
|
202
|
+
* All counters are monotonic for the daemon's lifetime. They reset
|
|
203
|
+
* to zero when the daemon is stopped and respawned — the core
|
|
204
|
+
* rebuilds the host, including on {@link DaemonRuntime.spawnFromSnapshot}.
|
|
205
|
+
*/
|
|
206
|
+
export interface DaemonStats {
|
|
207
|
+
/** Total events processed since spawn. */
|
|
208
|
+
readonly eventsProcessed: bigint;
|
|
209
|
+
/** Total output events emitted since spawn. */
|
|
210
|
+
readonly eventsEmitted: bigint;
|
|
211
|
+
/** Total processing errors surfaced from `process`. */
|
|
212
|
+
readonly errors: bigint;
|
|
213
|
+
/** Number of snapshots taken (manual + auto combined). */
|
|
214
|
+
readonly snapshotsTaken: bigint;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Host configuration for a daemon. Omit a field to take the
|
|
218
|
+
* runtime default.
|
|
219
|
+
*/
|
|
220
|
+
export interface DaemonHostConfig {
|
|
221
|
+
/**
|
|
222
|
+
* Auto-snapshot cadence in events processed. `0` (the default) =
|
|
223
|
+
* manual snapshots only.
|
|
224
|
+
*/
|
|
225
|
+
readonly autoSnapshotInterval?: bigint;
|
|
226
|
+
/** Maximum events to buffer before forcing a snapshot. */
|
|
227
|
+
readonly maxLogEntries?: number;
|
|
228
|
+
/**
|
|
229
|
+
* Maximum time (milliseconds) the Rust side will wait for a JS
|
|
230
|
+
* `process` / `snapshot` / `restore` callback to return before
|
|
231
|
+
* surfacing a `DaemonError` with a timeout message. Default
|
|
232
|
+
* `60_000` (60 s).
|
|
233
|
+
*
|
|
234
|
+
* **Why it exists.** The core daemon registry holds a per-daemon
|
|
235
|
+
* mutex across `process`. If a user callback re-enters the runtime
|
|
236
|
+
* synchronously on that same daemon, or the Node main thread is
|
|
237
|
+
* blocked and the TSFN callback can't fire, an unbounded wait
|
|
238
|
+
* would deadlock silently. A bounded wait converts the deadlock
|
|
239
|
+
* into a typed error so the daemon's event becomes one failure
|
|
240
|
+
* instead of a frozen runtime.
|
|
241
|
+
*
|
|
242
|
+
* Set a shorter value (e.g. 500) in tests that intentionally
|
|
243
|
+
* stall the callback and assert the timeout path. Set a longer
|
|
244
|
+
* value for daemons that legitimately do heavy sync work per
|
|
245
|
+
* event.
|
|
246
|
+
*/
|
|
247
|
+
readonly callbackTimeoutMs?: number;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Handle to a running daemon. Returned by
|
|
251
|
+
* {@link DaemonRuntime.spawn}; pass its `originHash` back to
|
|
252
|
+
* {@link DaemonRuntime.stop} to tear the daemon down.
|
|
253
|
+
*
|
|
254
|
+
* Cloning the JS object shares the same underlying daemon.
|
|
255
|
+
* Dropping the handle does **not** stop the daemon — callers must
|
|
256
|
+
* call `stop` explicitly.
|
|
257
|
+
*/
|
|
258
|
+
export declare class DaemonHandle {
|
|
259
|
+
private readonly inner;
|
|
260
|
+
/**
|
|
261
|
+
* 64-bit hash of the daemon's identity — the key used by the
|
|
262
|
+
* registry, factory registry, and migration dispatcher.
|
|
263
|
+
*/
|
|
264
|
+
get originHash(): bigint;
|
|
265
|
+
/**
|
|
266
|
+
* Full 32-byte `EntityId` (ed25519 public key) of the daemon's
|
|
267
|
+
* identity. Returned as a `Buffer` to match the convention used
|
|
268
|
+
* by `Identity.entityId`.
|
|
269
|
+
*/
|
|
270
|
+
get entityId(): Buffer;
|
|
271
|
+
/**
|
|
272
|
+
* Current runtime statistics for this daemon. Reads a live
|
|
273
|
+
* atomic snapshot from the registry — cheap enough to poll.
|
|
274
|
+
*
|
|
275
|
+
* Throws {@link DaemonError} if the daemon has been stopped.
|
|
276
|
+
*/
|
|
277
|
+
stats(): DaemonStats;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Handle to an in-flight migration. Returned by
|
|
281
|
+
* {@link DaemonRuntime.startMigration} /
|
|
282
|
+
* {@link DaemonRuntime.startMigrationWith}.
|
|
283
|
+
*
|
|
284
|
+
* Dropping the handle does NOT cancel the migration — the
|
|
285
|
+
* orchestrator keeps driving it to completion in the background.
|
|
286
|
+
* Keep the handle to observe phase transitions or request abort.
|
|
287
|
+
*/
|
|
288
|
+
export declare class MigrationHandle {
|
|
289
|
+
private readonly inner;
|
|
290
|
+
/** 64-bit origin hash of the daemon being migrated. */
|
|
291
|
+
get originHash(): bigint;
|
|
292
|
+
/** Node ID of the source (currently hosting) node. */
|
|
293
|
+
get sourceNode(): bigint;
|
|
294
|
+
/** Node ID of the target (post-cutover) node. */
|
|
295
|
+
get targetNode(): bigint;
|
|
296
|
+
/**
|
|
297
|
+
* Current migration phase, or `null` once the migration has
|
|
298
|
+
* left the orchestrator's records (terminal success or abort).
|
|
299
|
+
* Callers distinguish success from abort by remembering the
|
|
300
|
+
* last non-null phase they observed.
|
|
301
|
+
*/
|
|
302
|
+
phase(): MigrationPhase | null;
|
|
303
|
+
/**
|
|
304
|
+
* Async iterator that yields each distinct migration phase as
|
|
305
|
+
* the orchestrator transitions through them, and terminates
|
|
306
|
+
* cleanly once the migration reaches a terminal state (either
|
|
307
|
+
* `complete` on success, or abort / failure — the orchestrator
|
|
308
|
+
* record is gone either way).
|
|
309
|
+
*
|
|
310
|
+
* **Usage pattern:**
|
|
311
|
+
* ```ts
|
|
312
|
+
* const mig = await rt.startMigration(origin, a, b);
|
|
313
|
+
* const phases: MigrationPhase[] = [];
|
|
314
|
+
* for await (const phase of mig.phases()) {
|
|
315
|
+
* phases.push(phase);
|
|
316
|
+
* }
|
|
317
|
+
* // Inspect `phases.at(-1)` — `'complete'` vs anything else
|
|
318
|
+
* // distinguishes success from abort / failure.
|
|
319
|
+
* ```
|
|
320
|
+
*
|
|
321
|
+
* **Call site ordering:** iterate as soon as the handle is
|
|
322
|
+
* returned. If you await `wait()` first and then call
|
|
323
|
+
* `phases()`, the orchestrator record may already be cleared
|
|
324
|
+
* and the iterator yields nothing.
|
|
325
|
+
*
|
|
326
|
+
* **Sampling cadence:** polls every 50 ms — matching the Rust
|
|
327
|
+
* SDK's `wait()` cadence. Phase transitions faster than that
|
|
328
|
+
* may be missed; acceptable for Stage 1 since real migrations
|
|
329
|
+
* spend hundreds of ms per phase on network round-trips. A
|
|
330
|
+
* broadcast-channel push replacement is documented as future
|
|
331
|
+
* work in `DAEMON_IDENTITY_MIGRATION_PLAN.md`.
|
|
332
|
+
*/
|
|
333
|
+
phases(): AsyncGenerator<MigrationPhase, void, void>;
|
|
334
|
+
/**
|
|
335
|
+
* Block until the migration reaches a terminal state. Resolves
|
|
336
|
+
* on `complete`; rejects with {@link DaemonError} on abort or
|
|
337
|
+
* structured failure (target unavailable, restore failed, etc.).
|
|
338
|
+
*
|
|
339
|
+
* No wall-clock timeout — a migration stalled against an
|
|
340
|
+
* unresponsive peer blocks indefinitely. Use
|
|
341
|
+
* {@link MigrationHandle.waitWithTimeout} for a bound.
|
|
342
|
+
*/
|
|
343
|
+
wait(): Promise<void>;
|
|
344
|
+
/**
|
|
345
|
+
* Like {@link wait} with a caller-controlled timeout (in
|
|
346
|
+
* milliseconds). On timeout the orchestrator record is aborted
|
|
347
|
+
* and the promise rejects with {@link DaemonError}.
|
|
348
|
+
*/
|
|
349
|
+
waitWithTimeout(timeoutMs: bigint): Promise<void>;
|
|
350
|
+
/**
|
|
351
|
+
* Request cancellation of the migration. Best-effort: past
|
|
352
|
+
* `cutover` the routing flip cannot be undone cleanly, and
|
|
353
|
+
* this call resolves without aborting.
|
|
354
|
+
*/
|
|
355
|
+
cancel(): Promise<void>;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Per-mesh compute runtime. Holds the kind-keyed factory table and
|
|
359
|
+
* drives the `Registering → Ready → ShuttingDown` lifecycle.
|
|
360
|
+
*
|
|
361
|
+
* Construct via {@link create}; the runtime shares the given mesh's
|
|
362
|
+
* underlying `MeshNode` (no second socket). Shutting down the
|
|
363
|
+
* runtime does NOT shut down the mesh — the caller owns that.
|
|
364
|
+
*/
|
|
365
|
+
export declare class DaemonRuntime {
|
|
366
|
+
private readonly inner;
|
|
367
|
+
/**
|
|
368
|
+
* TS-side factory table, keyed by `kind`. `registerFactory`
|
|
369
|
+
* inserts here; `spawn` looks up and invokes. Duplicates the
|
|
370
|
+
* kind set that lives on the NAPI side — the NAPI copy drives
|
|
371
|
+
* migration-targeting and the `already registered` check at
|
|
372
|
+
* registration time; this map is what actually gets *called*.
|
|
373
|
+
*/
|
|
374
|
+
private readonly factories;
|
|
375
|
+
private constructor();
|
|
376
|
+
/**
|
|
377
|
+
* Build a compute runtime against an existing {@link MeshNode}.
|
|
378
|
+
*/
|
|
379
|
+
static create(mesh: MeshNode): DaemonRuntime;
|
|
380
|
+
/**
|
|
381
|
+
* Promote to `Ready`. Installs the migration subprotocol handler.
|
|
382
|
+
* Idempotent on an already-ready runtime; rejects on a runtime
|
|
383
|
+
* that has been shut down.
|
|
384
|
+
*/
|
|
385
|
+
start(): Promise<void>;
|
|
386
|
+
/**
|
|
387
|
+
* Tear down the runtime. Drains daemons, clears factory
|
|
388
|
+
* registrations, uninstalls the migration handler. Idempotent:
|
|
389
|
+
* a second call on an already-shut-down runtime is a no-op.
|
|
390
|
+
*/
|
|
391
|
+
shutdown(): Promise<void>;
|
|
392
|
+
/**
|
|
393
|
+
* `true` iff the runtime has transitioned to `Ready` and has not
|
|
394
|
+
* yet begun shutting down.
|
|
395
|
+
*/
|
|
396
|
+
isReady(): boolean;
|
|
397
|
+
/** Number of daemons currently registered with the runtime. */
|
|
398
|
+
daemonCount(): number;
|
|
399
|
+
/**
|
|
400
|
+
* Register a factory closure under `kind`. The factory returns a
|
|
401
|
+
* {@link MeshDaemon}-shaped object. Second registration of the
|
|
402
|
+
* same `kind` throws {@link DaemonError}.
|
|
403
|
+
*
|
|
404
|
+
* Sub-step 1 stores the factory but does not invoke it — event
|
|
405
|
+
* dispatch to daemon `process` lands in sub-step 3.
|
|
406
|
+
*
|
|
407
|
+
* ## Migration targeting
|
|
408
|
+
*
|
|
409
|
+
* `registerFactory` alone is **not sufficient** to accept
|
|
410
|
+
* inbound migrations — it registers the kind-to-factory mapping
|
|
411
|
+
* only on the SDK side. Migrations lookup by `origin_hash`, not
|
|
412
|
+
* by kind. Future sub-steps will surface `expectMigration` and
|
|
413
|
+
* `registerMigrationTargetIdentity` for that wiring.
|
|
414
|
+
*/
|
|
415
|
+
registerFactory(kind: string, factory: DaemonFactory): void;
|
|
416
|
+
/**
|
|
417
|
+
* Spawn a daemon of `kind` under the given {@link Identity}.
|
|
418
|
+
*
|
|
419
|
+
* Invokes the user-supplied factory (registered via
|
|
420
|
+
* {@link DaemonRuntime.registerFactory}), extracts the
|
|
421
|
+
* returned daemon's `process` / `snapshot` / `restore`
|
|
422
|
+
* methods, and hands each to NAPI as a separate JS function.
|
|
423
|
+
* NAPI builds a `ThreadsafeFunction` per method so the
|
|
424
|
+
* eventual event-dispatch path (sub-step 3) can call them
|
|
425
|
+
* from any tokio task.
|
|
426
|
+
*
|
|
427
|
+
* **Sub-step 2b** (current): method TSFNs are stored on the
|
|
428
|
+
* Rust side but **not yet invoked**. `process` / `snapshot` /
|
|
429
|
+
* `restore` behave as no-ops. Sub-step 3 wires the full
|
|
430
|
+
* round-trip so events land in the JS daemon.
|
|
431
|
+
*
|
|
432
|
+
* `kind` must have been registered first — spawning an
|
|
433
|
+
* unregistered kind throws {@link DaemonError}.
|
|
434
|
+
*/
|
|
435
|
+
spawn(kind: string, identity: Identity, config?: DaemonHostConfig): Promise<DaemonHandle>;
|
|
436
|
+
/**
|
|
437
|
+
* Spawn a daemon of `kind` from a previously-taken snapshot.
|
|
438
|
+
* Parallel to {@link DaemonRuntime.spawn} but seeds the
|
|
439
|
+
* daemon's initial state from `snapshotBytes` by calling its
|
|
440
|
+
* `restore` method before any events land.
|
|
441
|
+
*
|
|
442
|
+
* `snapshotBytes` must be the exact `Buffer` returned by a
|
|
443
|
+
* prior call to {@link DaemonRuntime.snapshot}; mismatched or
|
|
444
|
+
* corrupted bytes surface as `daemon: snapshot decode failed`.
|
|
445
|
+
*
|
|
446
|
+
* `kind` must be registered and the caller's {@link Identity}
|
|
447
|
+
* must match the snapshot's `entityId` — a mismatch throws
|
|
448
|
+
* {@link DaemonError} before any side effects.
|
|
449
|
+
*/
|
|
450
|
+
spawnFromSnapshot(kind: string, identity: Identity, snapshotBytes: Buffer, config?: DaemonHostConfig): Promise<DaemonHandle>;
|
|
451
|
+
/**
|
|
452
|
+
* Take a snapshot of a running daemon by `originHash`. Returns
|
|
453
|
+
* the daemon's serialized state bytes, or `null` if the daemon
|
|
454
|
+
* is stateless (no `snapshot` method, or it returned `null`).
|
|
455
|
+
*
|
|
456
|
+
* The returned `Buffer` is opaque to the caller — the wire
|
|
457
|
+
* format is the core's `StateSnapshot` encoding, including
|
|
458
|
+
* version headers and the chain link at the snapshot point.
|
|
459
|
+
* Feed it unchanged to {@link DaemonRuntime.spawnFromSnapshot}
|
|
460
|
+
* to restore the daemon on another node or after a restart.
|
|
461
|
+
*/
|
|
462
|
+
snapshot(originHash: bigint): Promise<Buffer | null>;
|
|
463
|
+
/**
|
|
464
|
+
* Stop a daemon, removing it from the runtime's registry.
|
|
465
|
+
* Idempotent during `ShuttingDown`; rejects with
|
|
466
|
+
* {@link DaemonError} during `Registering` or when the origin
|
|
467
|
+
* is unknown.
|
|
468
|
+
*/
|
|
469
|
+
stop(originHash: bigint): Promise<void>;
|
|
470
|
+
/**
|
|
471
|
+
* Deliver a single causal event to a live daemon and return
|
|
472
|
+
* the daemon's output buffers. Routes through the core
|
|
473
|
+
* `DaemonRegistry::deliver` → `MeshDaemon::process` path,
|
|
474
|
+
* which invokes the JS `process(event)` callback registered
|
|
475
|
+
* at spawn time and waits for its return.
|
|
476
|
+
*
|
|
477
|
+
* Direct ingress — Stage 1 convenience. Mesh-dispatched
|
|
478
|
+
* delivery (via the causal subprotocol on an inbound packet)
|
|
479
|
+
* lands in a later stage; this method stays as test sugar + a
|
|
480
|
+
* manual-trigger surface.
|
|
481
|
+
*
|
|
482
|
+
* Throws {@link DaemonError} if `originHash` doesn't match a
|
|
483
|
+
* live daemon, if the daemon's `process` throws, or if the
|
|
484
|
+
* runtime is shutting down.
|
|
485
|
+
*/
|
|
486
|
+
deliver(originHash: bigint, event: CausalEvent): Promise<Buffer[]>;
|
|
487
|
+
/**
|
|
488
|
+
* Initiate a migration for the daemon identified by
|
|
489
|
+
* `originHash`, moving it from `sourceNode` to `targetNode`.
|
|
490
|
+
*
|
|
491
|
+
* Returns a {@link MigrationHandle} whose `wait()` resolves
|
|
492
|
+
* when the migration reaches a terminal state. On local-source
|
|
493
|
+
* migrations (`sourceNode === mesh.nodeId`) the snapshot is
|
|
494
|
+
* taken synchronously inside this call; on remote-source
|
|
495
|
+
* migrations the orchestrator drives the state machine via
|
|
496
|
+
* inbound wire messages.
|
|
497
|
+
*
|
|
498
|
+
* Both node IDs are `u64` — pass as `bigint` to avoid silent
|
|
499
|
+
* precision loss past 2^53.
|
|
500
|
+
*/
|
|
501
|
+
startMigration(originHash: bigint, sourceNode: bigint, targetNode: bigint): Promise<MigrationHandle>;
|
|
502
|
+
/**
|
|
503
|
+
* {@link startMigration} with caller-supplied options. Use this
|
|
504
|
+
* to opt out of identity transport (when the daemon doesn't
|
|
505
|
+
* need to sign on the target) or to tune the NotReady-retry
|
|
506
|
+
* budget.
|
|
507
|
+
*/
|
|
508
|
+
startMigrationWith(originHash: bigint, sourceNode: bigint, targetNode: bigint, opts: MigrationOptions): Promise<MigrationHandle>;
|
|
509
|
+
/**
|
|
510
|
+
* Declare that a migration will land on this node for the given
|
|
511
|
+
* `originHash` of `kind`. Registers a placeholder factory; the
|
|
512
|
+
* migration snapshot's identity envelope supplies the real
|
|
513
|
+
* keypair at restore time.
|
|
514
|
+
*
|
|
515
|
+
* Must be called BEFORE the source initiates the migration —
|
|
516
|
+
* the target dispatcher checks for a factory entry when the
|
|
517
|
+
* inbound `SnapshotReady` lands, and rejects with
|
|
518
|
+
* `FactoryNotFound` if nothing is registered.
|
|
519
|
+
*
|
|
520
|
+
* The source must migrate with `transportIdentity: true`
|
|
521
|
+
* (default). Without the envelope the dispatcher emits
|
|
522
|
+
* `IdentityTransportFailed` because the placeholder has no
|
|
523
|
+
* keypair. Use {@link registerMigrationTargetIdentity} for the
|
|
524
|
+
* explicit public-identity-migration case.
|
|
525
|
+
*/
|
|
526
|
+
expectMigration(kind: string, originHash: bigint, config?: DaemonHostConfig): void;
|
|
527
|
+
/**
|
|
528
|
+
* Pre-register a target-side identity for a migration that
|
|
529
|
+
* will NOT carry an identity envelope (source used
|
|
530
|
+
* `transportIdentity: false`). The target holds the matching
|
|
531
|
+
* {@link Identity}; the dispatcher restores the daemon with
|
|
532
|
+
* that identity instead of overriding it from an envelope.
|
|
533
|
+
*
|
|
534
|
+
* For the common envelope-transport case, prefer
|
|
535
|
+
* {@link expectMigration} — the caller doesn't need to know
|
|
536
|
+
* the daemon's private key ahead of time.
|
|
537
|
+
*/
|
|
538
|
+
registerMigrationTargetIdentity(kind: string, identity: Identity, config?: DaemonHostConfig): void;
|
|
539
|
+
/**
|
|
540
|
+
* Query the orchestrator's current migration phase for
|
|
541
|
+
* `originHash`, or `null` if no migration is in flight for
|
|
542
|
+
* that origin. Works on any node — source, target, or an
|
|
543
|
+
* observer that heard the migration on the mesh.
|
|
544
|
+
*/
|
|
545
|
+
migrationPhase(originHash: bigint): MigrationPhase | null;
|
|
546
|
+
}
|