@holokai/neuron-sdk 0.2.0 → 0.4.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 +63 -3
- package/dist/auth.d.mts +187 -0
- package/dist/auth.d.ts +187 -0
- package/dist/auth.js +354 -0
- package/dist/auth.js.map +1 -0
- package/dist/auth.mjs +343 -0
- package/dist/auth.mjs.map +1 -0
- package/dist/cli.js +436 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.mts +21 -8
- package/dist/index.d.ts +21 -8
- package/dist/index.js +100 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +100 -29
- package/dist/index.mjs.map +1 -1
- package/dist/ipc.d.mts +1 -1
- package/dist/ipc.d.ts +1 -1
- package/dist/{transport-QqkooS7K.d.mts → transport-Cs5tY2IO.d.mts} +91 -2
- package/dist/{transport-QqkooS7K.d.ts → transport-Cs5tY2IO.d.ts} +91 -2
- package/package.json +12 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { J as JsonSchema, C as Capability, a as CapabilityHandleOptions, S as ServerToNeuronFrame, N as NeuronLogger, T as Transport, A as AuthCallback, H as Handler, b as CapabilityRegistration, c as TransportCallbacks, d as ClientFrame, P as PostPath } from './transport-
|
|
2
|
-
export { e as HandlerContext, f as HeartbeatInFlightEntry, L as LeasePayload, g as TaskPayload } from './transport-
|
|
1
|
+
import { J as JsonSchema, C as Capability, a as CapabilityHandleOptions, S as ServerToNeuronFrame, N as NeuronLogger, T as Transport, A as AuthCallback, H as Handler, b as CapabilityRegistration, c as TransportCallbacks, d as ClientFrame, P as PostPath } from './transport-Cs5tY2IO.js';
|
|
2
|
+
export { e as HandlerContext, f as HeartbeatInFlightEntry, L as LeasePayload, g as TaskPayload } from './transport-Cs5tY2IO.js';
|
|
3
3
|
import { EventEmitter } from 'eventemitter3';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { ErrorObject } from 'ajv';
|
|
@@ -97,6 +97,11 @@ declare function toPlanShape(def: CapabilityDef): CapabilityPlanShape;
|
|
|
97
97
|
* required at registration time — if neither the def nor `opts` provides
|
|
98
98
|
* them, this throws so the misconfiguration surfaces at start time
|
|
99
99
|
* rather than as a confusing wire-validation error.
|
|
100
|
+
*
|
|
101
|
+
* Since HOL-2926 the frame also carries the contract (`description` +
|
|
102
|
+
* compiled JSON Schemas): the gateway forwards it to Moku's capability
|
|
103
|
+
* catalog so registry-sourced capabilities render with a real contract
|
|
104
|
+
* in the supervisor catalog instead of type-only stubs.
|
|
100
105
|
*/
|
|
101
106
|
declare function toRegistrationCapability(def: CapabilityDef, opts?: CapabilityHandleOptions): Capability;
|
|
102
107
|
|
|
@@ -141,7 +146,12 @@ interface NeuronEvents {
|
|
|
141
146
|
delayMs: number;
|
|
142
147
|
reason: string;
|
|
143
148
|
}) => void;
|
|
144
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Fired on transport / parse errors (reconnect handling is automatic)
|
|
151
|
+
* and on register rejection (HOL-2926 — NOT automatically recovered:
|
|
152
|
+
* the SSE stream stays open, so the neuron sits connected but
|
|
153
|
+
* lease-less until the app reacts, e.g. by calling `stop()`).
|
|
154
|
+
*/
|
|
145
155
|
error: (err: Error) => void;
|
|
146
156
|
/** Pass-through of every parsed inbound SSE frame. */
|
|
147
157
|
frame: (frame: ServerToNeuronFrame) => void;
|
|
@@ -176,7 +186,7 @@ interface NeuronOptionsWithTransport extends NeuronCommonOptions {
|
|
|
176
186
|
* surface so existing call sites keep compiling unchanged.
|
|
177
187
|
*/
|
|
178
188
|
interface NeuronOptionsWithHttp extends NeuronCommonOptions {
|
|
179
|
-
/** Base URL of the gateway, e.g. `https://
|
|
189
|
+
/** Base URL of the gateway, e.g. `https://bigbrain.holokai.dev`. */
|
|
180
190
|
gatewayUrl: string;
|
|
181
191
|
/** Token provider — see `AuthCallback` for refresh semantics. */
|
|
182
192
|
auth: AuthCallback;
|
|
@@ -276,7 +286,7 @@ declare const SSE_EVENT_NAMES: {
|
|
|
276
286
|
};
|
|
277
287
|
|
|
278
288
|
interface HttpTransportOptions {
|
|
279
|
-
/** Base URL of the gateway (e.g. `https://
|
|
289
|
+
/** Base URL of the gateway (e.g. `https://bigbrain.holokai.dev`). */
|
|
280
290
|
gatewayUrl: string;
|
|
281
291
|
/** Stable per-process identifier sent as `?neuronId=` on the SSE GET. */
|
|
282
292
|
neuronId: string;
|
|
@@ -395,12 +405,15 @@ declare class ShutdownError extends Error {
|
|
|
395
405
|
* the same schemas so a misbehaving party can't sneak by either side.
|
|
396
406
|
*/
|
|
397
407
|
declare class SchemaValidator {
|
|
408
|
+
private readonly onCompileError?;
|
|
398
409
|
private readonly ajv;
|
|
399
410
|
private readonly cache;
|
|
400
|
-
|
|
411
|
+
private readonly uncompilable;
|
|
412
|
+
constructor(onCompileError?: ((message: string) => void) | undefined);
|
|
401
413
|
/**
|
|
402
|
-
* Returns null if the value matches the schema
|
|
403
|
-
*
|
|
414
|
+
* Returns null if the value matches the schema, no schema was given, or
|
|
415
|
+
* the schema cannot be compiled in this runtime (fail-open — the gateway
|
|
416
|
+
* remains the enforcement point); otherwise an array of structured errors.
|
|
404
417
|
*/
|
|
405
418
|
validate(schema: JsonSchema | undefined, data: unknown): ErrorObject[] | null;
|
|
406
419
|
}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ var CapabilityTypeSchema = zod.z.string().min(1).regex(
|
|
|
17
17
|
"capability type must be namespaced: {owner}/{package}[/{name}][.{variant}]"
|
|
18
18
|
);
|
|
19
19
|
var UserIdSchema = zod.z.string().min(1);
|
|
20
|
+
var JsonSchemaSchema = zod.z.unknown();
|
|
20
21
|
var CapabilityScopeSchema = zod.z.union([
|
|
21
22
|
zod.z.literal("any"),
|
|
22
23
|
zod.z.object({ onBehalfOf: UserIdSchema }).strict()
|
|
@@ -25,6 +26,36 @@ var CapabilitySchema = zod.z.object({
|
|
|
25
26
|
type: CapabilityTypeSchema,
|
|
26
27
|
scope: CapabilityScopeSchema,
|
|
27
28
|
concurrency: zod.z.number().int().positive(),
|
|
29
|
+
/**
|
|
30
|
+
* Plain-language contract advertisement (HOL-2926). All three fields
|
|
31
|
+
* are optional and additive: older neurons keep sending
|
|
32
|
+
* `{ type, scope, concurrency }` and the gateway accepts both shapes.
|
|
33
|
+
* When present they flow (fire-and-forget) to Moku's
|
|
34
|
+
* `capability_catalog` so registry-sourced capabilities render with a
|
|
35
|
+
* real contract in the supervisor catalog instead of type-only stubs.
|
|
36
|
+
*
|
|
37
|
+
* Omit rather than null — the fields are `.optional()`, not
|
|
38
|
+
* `.nullable()`, and a `null` description fails validation.
|
|
39
|
+
*/
|
|
40
|
+
description: zod.z.string().optional(),
|
|
41
|
+
/**
|
|
42
|
+
* JSON Schema for the capability's input payload — opaque on the wire,
|
|
43
|
+
* but `null` is rejected: the reconcile push to Moku is last-writer-wins
|
|
44
|
+
* per present key, so a null here could wipe a stored schema.
|
|
45
|
+
* (`description` already rejects null via `z.string()`.)
|
|
46
|
+
*/
|
|
47
|
+
// The `: boolean` return annotations stop TS (5.5+) from inferring the
|
|
48
|
+
// lambdas as type predicates, which would narrow the field from
|
|
49
|
+
// `unknown` to `{}` and ripple into every consumer.
|
|
50
|
+
inputSchema: JsonSchemaSchema.refine(
|
|
51
|
+
(v) => v !== null,
|
|
52
|
+
"omit inputSchema instead of sending null"
|
|
53
|
+
).optional(),
|
|
54
|
+
/** JSON Schema for the capability's output payload — same rules as inputSchema. */
|
|
55
|
+
outputSchema: JsonSchemaSchema.refine(
|
|
56
|
+
(v) => v !== null,
|
|
57
|
+
"omit outputSchema instead of sending null"
|
|
58
|
+
).optional(),
|
|
28
59
|
/**
|
|
29
60
|
* Per-capability lease TTL override in milliseconds (HOL-356).
|
|
30
61
|
* The gateway uses this when minting a lease's `expiresAt`; absent
|
|
@@ -40,7 +71,6 @@ var CapabilitySchema = zod.z.object({
|
|
|
40
71
|
*/
|
|
41
72
|
leaseTtlMs: zod.z.number().int().min(1e3).max(60 * 60 * 1e3).optional()
|
|
42
73
|
}).strict();
|
|
43
|
-
var JsonSchemaSchema = zod.z.unknown();
|
|
44
74
|
var LeaseIdSchema = zod.z.string().min(1);
|
|
45
75
|
var TaskPayloadSchema = zod.z.object({
|
|
46
76
|
taskId: zod.z.string().min(1),
|
|
@@ -62,9 +92,10 @@ var NeuronIdSchema = zod.z.string().min(1);
|
|
|
62
92
|
var RegisterFrameSchema = zod.z.object({
|
|
63
93
|
type: zod.z.literal("register"),
|
|
64
94
|
neuronId: NeuronIdSchema,
|
|
65
|
-
// Free-form by design — the gateway
|
|
66
|
-
//
|
|
67
|
-
//
|
|
95
|
+
// Free-form by design — the gateway records this verbatim and performs
|
|
96
|
+
// no version negotiation; compatibility comes from additive-only frame
|
|
97
|
+
// evolution (see PROTOCOL_VERSION in transport.ts). Do not pin this to
|
|
98
|
+
// the exported constant.
|
|
68
99
|
protocolVersion: zod.z.string().min(1),
|
|
69
100
|
capabilities: zod.z.array(CapabilitySchema).min(1).refine(
|
|
70
101
|
(caps) => new Set(caps.map((c) => c.type)).size === caps.length,
|
|
@@ -178,7 +209,7 @@ zod.z.discriminatedUnion("type", [
|
|
|
178
209
|
]);
|
|
179
210
|
|
|
180
211
|
// ../neuron-protocol/src/transport.ts
|
|
181
|
-
var PROTOCOL_VERSION = "1.
|
|
212
|
+
var PROTOCOL_VERSION = "1.1.0";
|
|
182
213
|
var SSE_PATH = "/neuron/events";
|
|
183
214
|
var SSE_EVENT = {
|
|
184
215
|
registered: "registered",
|
|
@@ -233,6 +264,12 @@ function toRegistrationCapability(def, opts) {
|
|
|
233
264
|
type: def.type,
|
|
234
265
|
scope,
|
|
235
266
|
concurrency,
|
|
267
|
+
// Contract advertisement (HOL-2926). `description` is required on the
|
|
268
|
+
// def, so it is always present here; `opts.description` lets a
|
|
269
|
+
// deployment reword it without redefining the capability.
|
|
270
|
+
description: opts?.description ?? def.description,
|
|
271
|
+
inputSchema: def.inputSchema,
|
|
272
|
+
outputSchema: def.outputSchema,
|
|
236
273
|
...typeof leaseTtlMs === "number" ? { leaseTtlMs } : {}
|
|
237
274
|
};
|
|
238
275
|
}
|
|
@@ -252,7 +289,6 @@ var HeartbeatLoop = class {
|
|
|
252
289
|
this.timer = setInterval(() => {
|
|
253
290
|
void this.tick();
|
|
254
291
|
}, this.intervalMs);
|
|
255
|
-
if (typeof this.timer.unref === "function") this.timer.unref();
|
|
256
292
|
}
|
|
257
293
|
stop() {
|
|
258
294
|
if (this.timer) {
|
|
@@ -503,9 +539,6 @@ var HttpTransport = class {
|
|
|
503
539
|
this.reconnectTimer = null;
|
|
504
540
|
void this.openStream();
|
|
505
541
|
}, delay);
|
|
506
|
-
if (typeof this.reconnectTimer === "object" && this.reconnectTimer !== null && "unref" in this.reconnectTimer && typeof this.reconnectTimer.unref === "function") {
|
|
507
|
-
this.reconnectTimer.unref();
|
|
508
|
-
}
|
|
509
542
|
}
|
|
510
543
|
};
|
|
511
544
|
var TransportError = class extends Error {
|
|
@@ -537,8 +570,19 @@ function validateInput(zod, validator, jsonSchema, data) {
|
|
|
537
570
|
return errors === null ? { ok: true, value: data } : { ok: false, errors };
|
|
538
571
|
}
|
|
539
572
|
async function runLease(opts) {
|
|
540
|
-
const {
|
|
541
|
-
|
|
573
|
+
const {
|
|
574
|
+
lease,
|
|
575
|
+
handler,
|
|
576
|
+
zodInput,
|
|
577
|
+
zodOutput,
|
|
578
|
+
inputSchema,
|
|
579
|
+
outputSchema,
|
|
580
|
+
transport,
|
|
581
|
+
validator,
|
|
582
|
+
logger,
|
|
583
|
+
signal
|
|
584
|
+
} = opts;
|
|
585
|
+
const inputCheck = validateInput(zodInput, validator, inputSchema, lease.task.input);
|
|
542
586
|
if (!inputCheck.ok) {
|
|
543
587
|
await sendNack(
|
|
544
588
|
transport,
|
|
@@ -600,7 +644,7 @@ async function runLease(opts) {
|
|
|
600
644
|
);
|
|
601
645
|
return;
|
|
602
646
|
}
|
|
603
|
-
const outputCheck = validateInput(zodOutput, validator,
|
|
647
|
+
const outputCheck = validateInput(zodOutput, validator, outputSchema, output);
|
|
604
648
|
if (!outputCheck.ok) {
|
|
605
649
|
await sendNack(
|
|
606
650
|
transport,
|
|
@@ -667,32 +711,39 @@ function scopedLogger(logger, lease) {
|
|
|
667
711
|
var Ajv = AjvDefault__default.default.default ?? AjvDefault__default.default;
|
|
668
712
|
var addFormats = addFormatsDefault__default.default.default ?? addFormatsDefault__default.default;
|
|
669
713
|
var SchemaValidator = class {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
constructor() {
|
|
714
|
+
constructor(onCompileError) {
|
|
715
|
+
this.onCompileError = onCompileError;
|
|
673
716
|
this.ajv = new Ajv({ allErrors: true, strict: false });
|
|
674
717
|
addFormats(this.ajv);
|
|
675
718
|
}
|
|
719
|
+
ajv;
|
|
720
|
+
cache = /* @__PURE__ */ new WeakMap();
|
|
721
|
+
// Schemas whose compilation threw. Cannot-compile is an environment or
|
|
722
|
+
// schema-authoring problem, not evidence about the data — so validate()
|
|
723
|
+
// FAILS OPEN for these (returns null) instead of reporting a mismatch.
|
|
724
|
+
// Nacking on compile failure turned every lease into an
|
|
725
|
+
// INPUT_SCHEMA_MISMATCH retry loop on runtimes where ajv's codegen is
|
|
726
|
+
// unavailable (Electron renderers with CSP `script-src 'self'` block
|
|
727
|
+
// `new Function`) — HOL-2937.
|
|
728
|
+
uncompilable = /* @__PURE__ */ new WeakSet();
|
|
676
729
|
/**
|
|
677
|
-
* Returns null if the value matches the schema
|
|
678
|
-
*
|
|
730
|
+
* Returns null if the value matches the schema, no schema was given, or
|
|
731
|
+
* the schema cannot be compiled in this runtime (fail-open — the gateway
|
|
732
|
+
* remains the enforcement point); otherwise an array of structured errors.
|
|
679
733
|
*/
|
|
680
734
|
validate(schema, data) {
|
|
681
735
|
if (!schema || typeof schema !== "object") return null;
|
|
736
|
+
if (this.uncompilable.has(schema)) return null;
|
|
682
737
|
let validator = this.cache.get(schema);
|
|
683
738
|
if (!validator) {
|
|
684
739
|
try {
|
|
685
740
|
validator = this.ajv.compile(schema);
|
|
686
741
|
} catch (err) {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
params: {},
|
|
693
|
-
message: `schema failed to compile: ${err instanceof Error ? err.message : String(err)}`
|
|
694
|
-
}
|
|
695
|
-
];
|
|
742
|
+
this.uncompilable.add(schema);
|
|
743
|
+
this.onCompileError?.(
|
|
744
|
+
`schema failed to compile \u2014 skipping SDK-side validation for this schema: ${err instanceof Error ? err.message : String(err)}`
|
|
745
|
+
);
|
|
746
|
+
return null;
|
|
696
747
|
}
|
|
697
748
|
this.cache.set(schema, validator);
|
|
698
749
|
}
|
|
@@ -825,7 +876,9 @@ var Neuron = class extends eventemitter3.EventEmitter {
|
|
|
825
876
|
super();
|
|
826
877
|
this.logger = opts.logger ?? noopLogger;
|
|
827
878
|
this.neuronId = opts.neuronId;
|
|
828
|
-
this.validator = new SchemaValidator(
|
|
879
|
+
this.validator = new SchemaValidator(
|
|
880
|
+
(message) => this.logger.warn({ neuronId: this.neuronId }, message)
|
|
881
|
+
);
|
|
829
882
|
this.transport = isWithTransport(opts) ? opts.transport : new HttpTransport({
|
|
830
883
|
gatewayUrl: opts.gatewayUrl,
|
|
831
884
|
neuronId: opts.neuronId,
|
|
@@ -897,7 +950,15 @@ var Neuron = class extends eventemitter3.EventEmitter {
|
|
|
897
950
|
);
|
|
898
951
|
}
|
|
899
952
|
handler = optsOrHandler;
|
|
900
|
-
capability = {
|
|
953
|
+
capability = {
|
|
954
|
+
type: reg.type,
|
|
955
|
+
scope: reg.scope,
|
|
956
|
+
concurrency: reg.concurrency,
|
|
957
|
+
...reg.description !== void 0 ? { description: reg.description } : {},
|
|
958
|
+
...reg.inputSchema !== void 0 ? { inputSchema: reg.inputSchema } : {},
|
|
959
|
+
...reg.outputSchema !== void 0 ? { outputSchema: reg.outputSchema } : {},
|
|
960
|
+
...typeof reg.leaseTtlMs === "number" ? { leaseTtlMs: reg.leaseTtlMs } : {}
|
|
961
|
+
};
|
|
901
962
|
inputSchema = reg.inputSchema;
|
|
902
963
|
outputSchema = reg.outputSchema;
|
|
903
964
|
}
|
|
@@ -1008,7 +1069,15 @@ var Neuron = class extends eventemitter3.EventEmitter {
|
|
|
1008
1069
|
this.hasOpenedOnce = true;
|
|
1009
1070
|
this.heartbeat.start();
|
|
1010
1071
|
} catch (err) {
|
|
1011
|
-
|
|
1072
|
+
const cause = err instanceof Error ? err : new Error(String(err));
|
|
1073
|
+
this.logger.error(
|
|
1074
|
+
{ err: cause },
|
|
1075
|
+
"register POST rejected; SSE is open but the neuron is NOT registered and will receive no leases"
|
|
1076
|
+
);
|
|
1077
|
+
this.emit(
|
|
1078
|
+
"error",
|
|
1079
|
+
new Error(`registration rejected by gateway: ${cause.message}`, { cause })
|
|
1080
|
+
);
|
|
1012
1081
|
}
|
|
1013
1082
|
}
|
|
1014
1083
|
handleConnectionClose(reason) {
|
|
@@ -1085,6 +1154,8 @@ var Neuron = class extends eventemitter3.EventEmitter {
|
|
|
1085
1154
|
handler: handler.handler,
|
|
1086
1155
|
zodInput: handler.zodInput,
|
|
1087
1156
|
zodOutput: handler.zodOutput,
|
|
1157
|
+
inputSchema: handler.inputSchema,
|
|
1158
|
+
outputSchema: handler.outputSchema,
|
|
1088
1159
|
transport: this.transport,
|
|
1089
1160
|
validator: this.validator,
|
|
1090
1161
|
logger: this.logger,
|