@easynet-run/node 0.36.9 → 0.39.29
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 +29 -3
- package/native/dendrite-bridge-manifest.json +3 -3
- package/native/dendrite-bridge.json +1 -1
- package/native/libaxon_dendrite_bridge.so +0 -0
- package/package.json +5 -3
- package/runtime/easynet-runtime-rs-0.39.29-x86_64-unknown-linux-gnu.tar.gz +0 -0
- package/runtime/runtime-bridge-manifest.json +4 -4
- package/runtime/runtime-bridge.json +3 -3
- package/src/ability_lifecycle.d.ts +11 -1
- package/src/ability_lifecycle.js +106 -26
- package/src/dendrite_bridge/bridge.d.ts +1 -0
- package/src/dendrite_bridge/bridge.js +21 -9
- package/src/dendrite_bridge/ffi.d.ts +1 -0
- package/src/dendrite_bridge/ffi.js +192 -18
- package/src/index.d.ts +1 -1
- package/src/index.js +9 -10
- package/src/mcp/server.test.js +55 -17
- package/src/presets/ability_dispatch/workflow.js +8 -30
- package/src/presets/remote_control/config.js +6 -22
- package/src/presets/remote_control/descriptor.d.ts +36 -0
- package/src/presets/remote_control/descriptor.js +243 -4
- package/src/presets/remote_control/handlers.d.ts +6 -0
- package/src/presets/remote_control/handlers.js +208 -9
- package/src/presets/remote_control/kit.js +37 -1
- package/src/presets/remote_control/kit.test.js +545 -0
- package/src/presets/remote_control/orchestrator.d.ts +1 -0
- package/src/presets/remote_control/orchestrator.js +27 -0
- package/src/presets/remote_control/specs.js +137 -0
- package/runtime/easynet-runtime-rs-0.36.9-x86_64-unknown-linux-gnu.tar.gz +0 -0
package/README.md
CHANGED
|
@@ -33,9 +33,34 @@ The SDK ships a native Dendrite bridge binary (loaded via `koffi`) that provides
|
|
|
33
33
|
npm install @easynet-run/node
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
## Platform / Native Bundle Notes
|
|
37
|
+
|
|
38
|
+
- The published npm artifact currently comes from the Linux x86_64 SDK pack, so the embedded native file is host-matched to that pack.
|
|
39
|
+
- If you need other native targets (iOS/Android/macOS/Linux arm64/Windows), use the target SDK pack from GitHub Releases:
|
|
40
|
+
- `sdk-packs-<target>.tar.gz`
|
|
41
|
+
- extract the `node/` directory and install the `*.tgz` artifact from that directory.
|
|
42
|
+
- For local source development:
|
|
43
|
+
- `SDK_VERSION=<version>` (optional; resolver checks this and then fallback `0.1.0` in the same root)
|
|
44
|
+
- `EASYNET_DENDRITE_BRIDGE_LIB` can force an explicit native path.
|
|
45
|
+
- `EASYNET_DENDRITE_BRIDGE_HOME=<pack-root>` can be set to a shared extracted SDK pack path that contains `native/`.
|
|
46
|
+
- set `EASYNET_DENDRITE_BRIDGE_SOURCE=local` if you want preset/CLI helpers to auto-probe local source-tree bridge artifacts.
|
|
47
|
+
- `EASYNET_DENDRITE_BRIDGE_PLATFORM=ios|android|linux|windows|macos` if host/build target mismatch (for example macOS host + iOS pack).
|
|
48
|
+
- For macOS-hosted iOS dev workflows, prefer a pack-native `.a` and explicit `EASYNET_DENDRITE_BRIDGE_LIB` path binding.
|
|
49
|
+
|
|
50
|
+
Target examples:
|
|
51
|
+
|
|
52
|
+
- macOS arm64: `sdk-packs-aarch64-apple-darwin.tar.gz`
|
|
53
|
+
- macOS x86_64: `sdk-packs-x86_64-apple-darwin.tar.gz`
|
|
54
|
+
- iOS arm64 simulator/device: `sdk-packs-aarch64-apple-ios.tar.gz`
|
|
55
|
+
- iOS x86_64 simulator: `sdk-packs-x86_64-apple-ios.tar.gz`
|
|
56
|
+
- Android arm64: `sdk-packs-aarch64-linux-android.tar.gz`
|
|
57
|
+
- Linux arm64: `sdk-packs-aarch64-unknown-linux-gnu.tar.gz`
|
|
58
|
+
- Linux x86_64: `sdk-packs-x86_64-unknown-linux-gnu.tar.gz`
|
|
59
|
+
- Windows x64: `sdk-packs-x86_64-pc-windows-msvc.tar.gz`
|
|
60
|
+
|
|
36
61
|
## Development
|
|
37
62
|
|
|
38
|
-
Runtime `.js` files and package-facing `.d.ts` files under `src/` are generated from the TypeScript sources with `tsc
|
|
63
|
+
Runtime `.js` files and package-facing `.d.ts` files under `src/` are generated locally from the TypeScript sources with `tsc`, but they are intentionally gitignored and must not be committed.
|
|
39
64
|
Edit `src/**/*.ts`, then run:
|
|
40
65
|
|
|
41
66
|
```bash
|
|
@@ -43,7 +68,7 @@ npm run build
|
|
|
43
68
|
npm run generated:check
|
|
44
69
|
```
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
`npm pack` and git-based installs run `prepare`/`prepack`, so published tarballs still include the generated runtime files.
|
|
47
72
|
|
|
48
73
|
## Quick Start
|
|
49
74
|
|
|
@@ -106,7 +131,7 @@ No public IP required — the local runtime connects outbound to the Hub and rec
|
|
|
106
131
|
|
|
107
132
|
Full lifecycle management — not just invocation:
|
|
108
133
|
|
|
109
|
-
- `
|
|
134
|
+
- `buildAbilityDescriptor()` / `exportAbility()` — define and register abilities with schemas
|
|
110
135
|
- `deployToNode()` — install + activate on target nodes
|
|
111
136
|
- `listAbilities()` / `invokeAbility()` / `uninstallAbility()`
|
|
112
137
|
- `discoverNodes()` / `executeCommand()` / `disconnectDevice()` / `drainDevice()`
|
|
@@ -131,6 +156,7 @@ First-class voice call lifecycle and transport negotiation (19+ FFI bindings):
|
|
|
131
156
|
|
|
132
157
|
- `startServer()` spawns a local Axon runtime and joins the Hub — all traffic is outbound.
|
|
133
158
|
- Federated node discovery and cross-network invocation dispatch.
|
|
159
|
+
- Preset status: the dedicated federation preset helper is currently Python-only; Node exposes federation through runtime bootstrap and transport-facing APIs.
|
|
134
160
|
|
|
135
161
|
### Remote Control & Orchestration
|
|
136
162
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"dendrite_bridge_version": "0.
|
|
2
|
+
"dendrite_bridge_version": "0.39.29",
|
|
3
3
|
"target": "x86_64-unknown-linux-gnu",
|
|
4
4
|
"os": "linux",
|
|
5
5
|
"arch": "x86_64",
|
|
6
6
|
"library": {
|
|
7
7
|
"path": "native/libaxon_dendrite_bridge.so",
|
|
8
|
-
"sha256": "
|
|
8
|
+
"sha256": "65e28058d2937cc457b28eb52e86c8f0ad376ce00c0e524cd49eac3372fb8ddf"
|
|
9
9
|
},
|
|
10
10
|
"header": {
|
|
11
11
|
"path": "native/include/axon_dendrite_bridge.h",
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"axon_dendrite_protocol_coverage_json",
|
|
36
36
|
"axon_dendrite_string_free"
|
|
37
37
|
],
|
|
38
|
-
"generated_at_utc": "2026-03-
|
|
38
|
+
"generated_at_utc": "2026-03-30T16:23:47Z"
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"language": "node",
|
|
3
|
-
"dendrite_bridge_version": "0.
|
|
3
|
+
"dendrite_bridge_version": "0.39.29",
|
|
4
4
|
"target": "x86_64-unknown-linux-gnu",
|
|
5
5
|
"dendrite_manifest_path": "native/dendrite-bridge-manifest.json",
|
|
6
6
|
"dendrite_library_path": "native/libaxon_dendrite_bridge.so",
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easynet-run/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.29",
|
|
4
4
|
"description": "EasyNet Axon Node.js SDK (sidecar-first with Dendrite bridge payload).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://easynet.run",
|
|
@@ -15,14 +15,16 @@
|
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"build": "node ./scripts/clean-generated.mjs && tsc -p tsconfig.json",
|
|
19
19
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
20
20
|
"types:check": "tsc -p tsconfig.types.json --noEmit",
|
|
21
21
|
"types:build": "tsc -p tsconfig.types.json",
|
|
22
22
|
"types:verify": "npm run types:build && node ./scripts/verify-dist-types.mjs",
|
|
23
23
|
"generated:check": "npm run build && node ./scripts/verify-generated.mjs",
|
|
24
24
|
"types": "npm run check && npm run types:verify",
|
|
25
|
-
"verify": "npm run types && npm run generated:check"
|
|
25
|
+
"verify": "npm run types && npm run generated:check",
|
|
26
|
+
"prepare": "npm run build",
|
|
27
|
+
"prepack": "npm run build"
|
|
26
28
|
},
|
|
27
29
|
"main": "src/index.js",
|
|
28
30
|
"types": "src/index.d.ts",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"runtime_version": "0.
|
|
2
|
+
"runtime_version": "0.39.29",
|
|
3
3
|
"target": "x86_64-unknown-linux-gnu",
|
|
4
4
|
"os": "linux",
|
|
5
5
|
"arch": "x86_64",
|
|
6
6
|
"artifact": {
|
|
7
|
-
"path": "runtime/easynet-runtime-rs-0.
|
|
8
|
-
"sha256": "
|
|
7
|
+
"path": "runtime/easynet-runtime-rs-0.39.29-x86_64-unknown-linux-gnu.tar.gz",
|
|
8
|
+
"sha256": "6234d8b5fa7d71e918c3648e8b627b93d767271f6486a003ffe836fb5ea3bfd1",
|
|
9
9
|
"binary_name": "axon-runtime"
|
|
10
10
|
},
|
|
11
11
|
"language_bridge_descriptors": [
|
|
@@ -16,5 +16,5 @@
|
|
|
16
16
|
"java/runtime-bridge.json",
|
|
17
17
|
"swift/runtime-bridge.json"
|
|
18
18
|
],
|
|
19
|
-
"generated_at_utc": "2026-03-
|
|
19
|
+
"generated_at_utc": "2026-03-30T16:23:47Z"
|
|
20
20
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"language": "node",
|
|
3
|
-
"runtime_version": "0.
|
|
3
|
+
"runtime_version": "0.39.29",
|
|
4
4
|
"target": "x86_64-unknown-linux-gnu",
|
|
5
|
-
"runtime_artifact_path": "runtime/easynet-runtime-rs-0.
|
|
5
|
+
"runtime_artifact_path": "runtime/easynet-runtime-rs-0.39.29-x86_64-unknown-linux-gnu.tar.gz",
|
|
6
6
|
"binary_relative_path": "axon-runtime",
|
|
7
|
-
"recommended_extract": "tar -xzf runtime/easynet-runtime-rs-0.
|
|
7
|
+
"recommended_extract": "tar -xzf runtime/easynet-runtime-rs-0.39.29-x86_64-unknown-linux-gnu.tar.gz -C <runtime_dir>",
|
|
8
8
|
"recommended_launch": "AXON_ENFORCE_MTLS=false <runtime_dir>/axon-runtime"
|
|
9
9
|
}
|
|
@@ -11,6 +11,11 @@ export interface AbilityDescriptor {
|
|
|
11
11
|
version: string;
|
|
12
12
|
tags: string[];
|
|
13
13
|
resourceUri: string;
|
|
14
|
+
instructions: string;
|
|
15
|
+
inputExamples: unknown[];
|
|
16
|
+
prerequisites: string[];
|
|
17
|
+
contextBindings: Record<string, string>;
|
|
18
|
+
category: string;
|
|
14
19
|
}
|
|
15
20
|
export type AbilityTarget = "claude" | "codex" | "openclaw" | "agent_skills";
|
|
16
21
|
export interface AbilityExportResult {
|
|
@@ -72,7 +77,7 @@ export declare function startServer(endpoint?: string, options?: {
|
|
|
72
77
|
/** Pre-shared join token used for outbound federation authentication. */
|
|
73
78
|
hubJoinToken?: string;
|
|
74
79
|
}): Promise<ServerHandle>;
|
|
75
|
-
export declare function
|
|
80
|
+
export declare function buildAbilityDescriptor(opts: {
|
|
76
81
|
name: string;
|
|
77
82
|
description: string;
|
|
78
83
|
commandTemplate: string;
|
|
@@ -81,6 +86,11 @@ export declare function createAbility(opts: {
|
|
|
81
86
|
version?: string;
|
|
82
87
|
tags?: string[];
|
|
83
88
|
resourceUri?: string;
|
|
89
|
+
instructions?: string;
|
|
90
|
+
inputExamples?: unknown[];
|
|
91
|
+
prerequisites?: string[];
|
|
92
|
+
contextBindings?: Record<string, string>;
|
|
93
|
+
category?: string;
|
|
84
94
|
}): AbilityDescriptor;
|
|
85
95
|
export declare function toToolSpec(descriptor: AbilityDescriptor): ToolSpec;
|
|
86
96
|
export declare function exportAbility(descriptor: AbilityDescriptor, target?: AbilityTarget, axonEndpoint?: string): AbilityExportResult;
|
package/src/ability_lifecycle.js
CHANGED
|
@@ -2,7 +2,22 @@
|
|
|
2
2
|
// =========================
|
|
3
3
|
//
|
|
4
4
|
// File: sdk/node/src/ability_lifecycle.ts
|
|
5
|
-
// Description:
|
|
5
|
+
// Description: Node SDK ability lifecycle API for descriptor creation, deployment, invocation, and skill export workflows.
|
|
6
|
+
//
|
|
7
|
+
// Protocol Responsibility:
|
|
8
|
+
// - Defines typed ability descriptors, export targets, and deployment helpers exposed to SDK users.
|
|
9
|
+
// - Keeps cross-language ability naming, resource-uri, and tool-spec semantics aligned.
|
|
10
|
+
//
|
|
11
|
+
// Implementation Approach:
|
|
12
|
+
// - Separates pure descriptor/export helpers from bridge-backed deployment and invocation flows.
|
|
13
|
+
// - Uses explicit validation and stable defaults so generated ability artifacts stay deterministic.
|
|
14
|
+
//
|
|
15
|
+
// Usage Contract:
|
|
16
|
+
// - Callers must provide valid command templates, schema objects, and runtime context for deployment helpers.
|
|
17
|
+
// - Returned descriptors and exported skill artifacts should be treated as stable SDK-level contracts.
|
|
18
|
+
//
|
|
19
|
+
// Architectural Position:
|
|
20
|
+
// - High-level SDK facade above preset/orchestrator layers and below application examples.
|
|
6
21
|
//
|
|
7
22
|
// Author: Silan.Hu
|
|
8
23
|
// Email: silan.hu@u.nus.edu
|
|
@@ -14,7 +29,7 @@ import { existsSync, mkdirSync, openSync } from "node:fs";
|
|
|
14
29
|
import { hostname as localHostname } from "node:os";
|
|
15
30
|
import { join, dirname } from "node:path";
|
|
16
31
|
import { env } from "node:process";
|
|
17
|
-
import { buildPythonSubprocessTemplate } from "./presets/remote_control/descriptor.js";
|
|
32
|
+
import { buildDescriptor as buildRemotePackageDescriptor, buildPythonSubprocessTemplate, serializeDescriptor as serializeRemotePackageDescriptor, } from "./presets/remote_control/descriptor.js";
|
|
18
33
|
import { AxonConfigError, AxonBridgeError, AxonInvocationError, AxonPartialSuccessError } from "./errors.js";
|
|
19
34
|
import { DEFAULT_SIGNATURE } from "./presets/remote_control/config.js";
|
|
20
35
|
import { beginPhase, buildDeployTrace } from "./receipt.js";
|
|
@@ -39,7 +54,10 @@ function normalizeAbilityName(raw) {
|
|
|
39
54
|
.replace(/[^a-z0-9_\-]/g, "-")
|
|
40
55
|
.replace(/-{2,}/g, "-")
|
|
41
56
|
.replace(/^-+|-+$/g, "");
|
|
42
|
-
|
|
57
|
+
if (!result) {
|
|
58
|
+
throw new AxonConfigError(`identifier contains no valid characters: ${JSON.stringify(raw)}`);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
43
61
|
}
|
|
44
62
|
function firstNonEmpty(...values) {
|
|
45
63
|
for (const value of values) {
|
|
@@ -215,9 +233,9 @@ export async function startServer(endpoint, options = {}) {
|
|
|
215
233
|
};
|
|
216
234
|
}
|
|
217
235
|
// ---------------------------------------------------------------------------
|
|
218
|
-
//
|
|
236
|
+
// build
|
|
219
237
|
// ---------------------------------------------------------------------------
|
|
220
|
-
export function
|
|
238
|
+
export function buildAbilityDescriptor(opts) {
|
|
221
239
|
if (!opts.name?.trim())
|
|
222
240
|
throw new AxonConfigError("ability name cannot be empty");
|
|
223
241
|
if (!opts.commandTemplate?.trim())
|
|
@@ -233,6 +251,11 @@ export function createAbility(opts) {
|
|
|
233
251
|
version: opts.version ?? "1.0.0",
|
|
234
252
|
tags: opts.tags ?? [],
|
|
235
253
|
resourceUri: opts.resourceUri ?? `easynet:///r/org/${token}`,
|
|
254
|
+
instructions: opts.instructions ?? "",
|
|
255
|
+
inputExamples: opts.inputExamples ?? [],
|
|
256
|
+
prerequisites: opts.prerequisites ?? [],
|
|
257
|
+
contextBindings: opts.contextBindings ?? {},
|
|
258
|
+
category: opts.category ?? "",
|
|
236
259
|
};
|
|
237
260
|
}
|
|
238
261
|
// ---------------------------------------------------------------------------
|
|
@@ -278,7 +301,7 @@ export function exportAbility(descriptor, target = "agent_skills", axonEndpoint)
|
|
|
278
301
|
export async function deployToNode(bridge, tenant, nodeId, descriptor, signature) {
|
|
279
302
|
const token = descriptor.toolName;
|
|
280
303
|
const abilityId = token;
|
|
281
|
-
const
|
|
304
|
+
const deployArgs = {
|
|
282
305
|
ability_name: descriptor.name,
|
|
283
306
|
tool_name: token,
|
|
284
307
|
description: descriptor.description,
|
|
@@ -286,8 +309,20 @@ export async function deployToNode(bridge, tenant, nodeId, descriptor, signature
|
|
|
286
309
|
version: descriptor.version,
|
|
287
310
|
input_schema: descriptor.inputSchema,
|
|
288
311
|
output_schema: descriptor.outputSchema,
|
|
289
|
-
|
|
290
|
-
|
|
312
|
+
};
|
|
313
|
+
if (descriptor.tags.length > 0)
|
|
314
|
+
deployArgs.tags = descriptor.tags;
|
|
315
|
+
if (descriptor.instructions)
|
|
316
|
+
deployArgs.instructions = descriptor.instructions;
|
|
317
|
+
if (descriptor.inputExamples.length > 0)
|
|
318
|
+
deployArgs.input_examples = descriptor.inputExamples;
|
|
319
|
+
if (descriptor.prerequisites.length > 0)
|
|
320
|
+
deployArgs.prerequisites = descriptor.prerequisites;
|
|
321
|
+
if (Object.keys(descriptor.contextBindings).length > 0)
|
|
322
|
+
deployArgs.context_bindings = descriptor.contextBindings;
|
|
323
|
+
if (descriptor.category)
|
|
324
|
+
deployArgs.category = descriptor.category;
|
|
325
|
+
const pkg = buildDeployPackage(deployArgs, signature);
|
|
291
326
|
const builder = beginPhase("deploy", tenant, nodeId, abilityId);
|
|
292
327
|
try {
|
|
293
328
|
const result = await bridge.deployAbilityPackage(tenant, nodeId, pkg);
|
|
@@ -380,14 +415,17 @@ export async function forgetAll(bridge, tenant, nodeId, confirm = false, options
|
|
|
380
415
|
// without performing any uninstalls.
|
|
381
416
|
if (dryRun) {
|
|
382
417
|
const wouldRemove = [];
|
|
418
|
+
const wouldFail = [];
|
|
383
419
|
for (const tool of tools) {
|
|
384
420
|
const installId = String(tool.install_id ?? "");
|
|
385
|
-
const toolName = String(tool.tool_name ?? "");
|
|
386
|
-
if (!installId)
|
|
421
|
+
const toolName = String(tool.tool_name ?? "unknown");
|
|
422
|
+
if (!installId) {
|
|
423
|
+
wouldFail.push({ tool_name: toolName, error: "missing install_id" });
|
|
387
424
|
continue;
|
|
425
|
+
}
|
|
388
426
|
wouldRemove.push(toolName);
|
|
389
427
|
}
|
|
390
|
-
return { removed: wouldRemove, removed_count: wouldRemove.length, failed:
|
|
428
|
+
return { removed: wouldRemove, removed_count: wouldRemove.length, failed: wouldFail, failed_count: wouldFail.length };
|
|
391
429
|
}
|
|
392
430
|
const removed = [];
|
|
393
431
|
const failed = [];
|
|
@@ -434,19 +472,10 @@ export async function listRemoteTools(bridge, tenant, namePattern = "", nodeId =
|
|
|
434
472
|
// buildDeployPackage
|
|
435
473
|
// ---------------------------------------------------------------------------
|
|
436
474
|
export function buildDeployPackage(args, signature) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
tool_name: toolName,
|
|
442
|
-
description: args.description ?? "",
|
|
443
|
-
command_template: args.command_template ?? "",
|
|
444
|
-
input_schema: args.input_schema ?? { type: "object", properties: {} },
|
|
445
|
-
output_schema: args.output_schema ?? { type: "object", properties: {} },
|
|
446
|
-
version: args.version ?? "1.0.0",
|
|
447
|
-
tags: args.tags ?? [],
|
|
448
|
-
signature_base64: signature,
|
|
449
|
-
};
|
|
475
|
+
return serializeRemotePackageDescriptor(buildRemotePackageDescriptor({
|
|
476
|
+
...args,
|
|
477
|
+
signature_base64: args.signature_base64 ?? signature,
|
|
478
|
+
}, signature));
|
|
450
479
|
}
|
|
451
480
|
// ---------------------------------------------------------------------------
|
|
452
481
|
// deployPackage
|
|
@@ -457,12 +486,21 @@ export async function deployPackage(bridge, tenant, nodeId, packageDescriptor) {
|
|
|
457
486
|
// ---------------------------------------------------------------------------
|
|
458
487
|
// Internal generators
|
|
459
488
|
// ---------------------------------------------------------------------------
|
|
489
|
+
const SHELL_UNSAFE_RE = /[`$\\"'\n\r;|&<>(){}!#\x00-\x1f]/;
|
|
490
|
+
function sanitizeShellValue(value, label) {
|
|
491
|
+
if (SHELL_UNSAFE_RE.test(value)) {
|
|
492
|
+
throw new AxonConfigError(`${label} contains disallowed shell characters: ${value}`);
|
|
493
|
+
}
|
|
494
|
+
return value;
|
|
495
|
+
}
|
|
460
496
|
function generateInvokeScript(resourceUri, endpoint) {
|
|
497
|
+
const safeEP = sanitizeShellValue(endpoint, "endpoint");
|
|
498
|
+
const safeURI = sanitizeShellValue(resourceUri, "resource_uri");
|
|
461
499
|
return `#!/usr/bin/env bash
|
|
462
500
|
set -euo pipefail
|
|
463
|
-
AXON_ENDPOINT="\${AXON_ENDPOINT:-${
|
|
501
|
+
AXON_ENDPOINT="\${AXON_ENDPOINT:-${safeEP}}"
|
|
464
502
|
TENANT="\${AXON_TENANT:-default}"
|
|
465
|
-
RESOURCE_URI="${
|
|
503
|
+
RESOURCE_URI="${safeURI}"
|
|
466
504
|
ARGS="\${1:-{}}"
|
|
467
505
|
curl -sS -X POST "\${AXON_ENDPOINT}/v1/invoke" \\
|
|
468
506
|
-H "Content-Type: application/json" \\
|
|
@@ -498,6 +536,20 @@ function generateAbilityMd(descriptor, target, token) {
|
|
|
498
536
|
lines.push("");
|
|
499
537
|
lines.push(descriptor.description);
|
|
500
538
|
lines.push("");
|
|
539
|
+
// Agent extension: instructions
|
|
540
|
+
if (descriptor.instructions) {
|
|
541
|
+
lines.push(descriptor.instructions);
|
|
542
|
+
lines.push("");
|
|
543
|
+
}
|
|
544
|
+
// Agent extension: prerequisites
|
|
545
|
+
if (descriptor.prerequisites.length > 0) {
|
|
546
|
+
lines.push("## Prerequisites");
|
|
547
|
+
lines.push("");
|
|
548
|
+
for (const p of descriptor.prerequisites) {
|
|
549
|
+
lines.push(`- ${p}`);
|
|
550
|
+
}
|
|
551
|
+
lines.push("");
|
|
552
|
+
}
|
|
501
553
|
lines.push("## Parameters");
|
|
502
554
|
lines.push("");
|
|
503
555
|
lines.push("| Name | Type | Required | Description |");
|
|
@@ -510,6 +562,31 @@ function generateAbilityMd(descriptor, target, token) {
|
|
|
510
562
|
const isRequired = required.includes(name) ? "Yes" : "No";
|
|
511
563
|
lines.push(`| ${name} | ${propType} | ${isRequired} | ${propDesc} |`);
|
|
512
564
|
}
|
|
565
|
+
// Agent extension: examples
|
|
566
|
+
if (descriptor.inputExamples.length > 0) {
|
|
567
|
+
lines.push("");
|
|
568
|
+
lines.push("## Examples");
|
|
569
|
+
lines.push("");
|
|
570
|
+
for (let i = 0; i < descriptor.inputExamples.length; i++) {
|
|
571
|
+
lines.push(`**Example ${i + 1}:**`);
|
|
572
|
+
lines.push("");
|
|
573
|
+
lines.push("```json");
|
|
574
|
+
lines.push(JSON.stringify(descriptor.inputExamples[i]));
|
|
575
|
+
lines.push("```");
|
|
576
|
+
lines.push("");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// Agent extension: context bindings
|
|
580
|
+
if (Object.keys(descriptor.contextBindings).length > 0) {
|
|
581
|
+
lines.push("## Context Bindings");
|
|
582
|
+
lines.push("");
|
|
583
|
+
lines.push("| Key | Value |");
|
|
584
|
+
lines.push("|-----|-------|");
|
|
585
|
+
for (const [k, v] of Object.entries(descriptor.contextBindings)) {
|
|
586
|
+
lines.push(`| \`${k}\` | ${v} |`);
|
|
587
|
+
}
|
|
588
|
+
lines.push("");
|
|
589
|
+
}
|
|
513
590
|
lines.push("");
|
|
514
591
|
lines.push("## Invoke");
|
|
515
592
|
lines.push("");
|
|
@@ -526,6 +603,9 @@ function generateAbilityMd(descriptor, target, token) {
|
|
|
526
603
|
lines.push("");
|
|
527
604
|
lines.push(`- **URI**: \`${descriptor.resourceUri}\``);
|
|
528
605
|
lines.push(`- **Version**: ${descriptor.version}`);
|
|
606
|
+
if (descriptor.category) {
|
|
607
|
+
lines.push(`- **Category**: ${descriptor.category}`);
|
|
608
|
+
}
|
|
529
609
|
lines.push("");
|
|
530
610
|
return lines.join("\n");
|
|
531
611
|
}
|
|
@@ -23,6 +23,7 @@ export declare class DendriteBridge {
|
|
|
23
23
|
listNodes(tenantId: string, options?: DendriteListNodesOptions): Record<string, unknown>[];
|
|
24
24
|
registerNode(tenantId: string, nodeId: string, options?: DendriteRegisterNodeOptions): Record<string, unknown>;
|
|
25
25
|
deregisterNode(tenantId: string, nodeId: string, reason?: string): Record<string, unknown>;
|
|
26
|
+
drainNode(tenantId: string, nodeId: string, reason?: string): Record<string, unknown>;
|
|
26
27
|
heartbeat(tenantId: string, nodeId: string): Record<string, unknown>;
|
|
27
28
|
publishCapability(tenantId: string, packageId: string, capabilityName: string, options: DendritePublishCapabilityOptions): Record<string, unknown>;
|
|
28
29
|
installCapability(tenantId: string, nodeId: string, packageId: string, options: DendriteInstallCapabilityOptions): Record<string, unknown>;
|
|
@@ -2,23 +2,22 @@
|
|
|
2
2
|
// =========================
|
|
3
3
|
//
|
|
4
4
|
// File: sdk/node/src/dendrite_bridge/bridge.ts
|
|
5
|
-
// Description: DendriteBridge
|
|
5
|
+
// Description: Node DendriteBridge implementation that exposes typed protocol methods over the native Axon bridge.
|
|
6
6
|
//
|
|
7
7
|
// Protocol Responsibility:
|
|
8
|
-
// - Implements the DendriteBridge
|
|
9
|
-
// - Preserves
|
|
8
|
+
// - Implements the high-level DendriteBridge API for Node callers across RPC, capability, interop, and voice surfaces.
|
|
9
|
+
// - Preserves JSON request and error semantics expected by the Node SDK public API.
|
|
10
10
|
//
|
|
11
11
|
// Implementation Approach:
|
|
12
|
-
// - Delegates
|
|
13
|
-
// -
|
|
12
|
+
// - Delegates native symbol loading to the FFI layer while keeping method-level payload shaping near the class boundary.
|
|
13
|
+
// - Uses typed options and explicit method families so transport intent remains readable at call sites.
|
|
14
14
|
//
|
|
15
15
|
// Usage Contract:
|
|
16
|
-
// -
|
|
17
|
-
// -
|
|
16
|
+
// - Construct bridge instances with valid endpoint or library configuration before invoking protocol methods.
|
|
17
|
+
// - Callers should prefer this layer over direct FFI usage unless they are extending native bindings.
|
|
18
18
|
//
|
|
19
19
|
// Architectural Position:
|
|
20
|
-
// -
|
|
21
|
-
// - Should not embed FFI loading or type declarations outside this class's responsibility.
|
|
20
|
+
// - Node SDK bridge facade above FFI bindings and below application or preset code.
|
|
22
21
|
//
|
|
23
22
|
// Author: Silan.Hu
|
|
24
23
|
// Email: silan.hu@u.nus.edu
|
|
@@ -202,6 +201,19 @@ export class DendriteBridge {
|
|
|
202
201
|
});
|
|
203
202
|
return callJson(this.lib.axon_dendrite_deregister_node_json, this.handle, req);
|
|
204
203
|
}
|
|
204
|
+
drainNode(tenantId, nodeId, reason = "") {
|
|
205
|
+
if (this.handle <= 0n)
|
|
206
|
+
throw new DendriteError("dendrite bridge already closed");
|
|
207
|
+
if (!this.lib.axon_dendrite_drain_node_json) {
|
|
208
|
+
throw new DendriteError("dendrite bridge symbol not available: axon_dendrite_drain_node_json");
|
|
209
|
+
}
|
|
210
|
+
const req = JSON.stringify({
|
|
211
|
+
tenant_id: tenantId,
|
|
212
|
+
node_id: nodeId,
|
|
213
|
+
reason,
|
|
214
|
+
});
|
|
215
|
+
return callJson(this.lib.axon_dendrite_drain_node_json, this.handle, req);
|
|
216
|
+
}
|
|
205
217
|
heartbeat(tenantId, nodeId) {
|
|
206
218
|
if (this.handle <= 0n)
|
|
207
219
|
throw new DendriteError("dendrite bridge already closed");
|
|
@@ -21,6 +21,7 @@ export type DendriteBridgeLib = {
|
|
|
21
21
|
axon_dendrite_list_nodes_json: DendriteJsonFn<[bigint, string]>;
|
|
22
22
|
axon_dendrite_register_node_json: DendriteJsonFn<[bigint, string]>;
|
|
23
23
|
axon_dendrite_deregister_node_json?: DendriteJsonFn<[bigint, string]>;
|
|
24
|
+
axon_dendrite_drain_node_json?: DendriteJsonFn<[bigint, string]>;
|
|
24
25
|
axon_dendrite_heartbeat_json: DendriteJsonFn<[bigint, string]>;
|
|
25
26
|
axon_dendrite_publish_capability_json: DendriteJsonFn<[bigint, string]>;
|
|
26
27
|
axon_dendrite_install_capability_json: DendriteJsonFn<[bigint, string]>;
|