@easynet-run/node 0.27.14 → 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 +39 -1
- package/native/dendrite-bridge-manifest.json +5 -4
- package/native/dendrite-bridge.json +1 -1
- package/native/include/axon_dendrite_bridge.h +18 -4
- package/native/libaxon_dendrite_bridge.so +0 -0
- package/package.json +9 -5
- 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 +12 -1
- package/src/ability_lifecycle.js +117 -31
- package/src/capability_request.js +3 -1
- package/src/dendrite_bridge/bridge.d.ts +10 -2
- package/src/dendrite_bridge/bridge.js +75 -14
- package/src/dendrite_bridge/ffi.d.ts +4 -0
- package/src/dendrite_bridge/ffi.js +194 -18
- package/src/dendrite_bridge/types.d.ts +4 -0
- package/src/errors.js +9 -3
- package/src/index.d.ts +3 -3
- package/src/index.js +9 -10
- package/src/mcp/server.d.ts +24 -2
- package/src/mcp/server.js +218 -18
- package/src/mcp/server.test.js +100 -0
- package/src/presets/ability_dispatch/workflow.js +8 -30
- package/src/presets/remote_control/config.d.ts +3 -0
- package/src/presets/remote_control/config.js +22 -24
- package/src/presets/remote_control/descriptor.d.ts +36 -0
- package/src/presets/remote_control/descriptor.js +267 -11
- package/src/presets/remote_control/handlers.d.ts +8 -0
- package/src/presets/remote_control/handlers.js +230 -26
- package/src/presets/remote_control/kit.d.ts +4 -2
- package/src/presets/remote_control/kit.js +106 -1
- package/src/presets/remote_control/kit.test.js +994 -0
- package/src/presets/remote_control/orchestrator.d.ts +6 -0
- package/src/presets/remote_control/orchestrator.js +36 -1
- package/src/presets/remote_control/specs.js +217 -61
- package/src/receipt.js +6 -3
- package/runtime/easynet-runtime-rs-0.27.14-x86_64-unknown-linux-gnu.tar.gz +0 -0
package/README.md
CHANGED
|
@@ -33,6 +33,43 @@ 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
|
+
|
|
61
|
+
## Development
|
|
62
|
+
|
|
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.
|
|
64
|
+
Edit `src/**/*.ts`, then run:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm run build
|
|
68
|
+
npm run generated:check
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`npm pack` and git-based installs run `prepare`/`prepack`, so published tarballs still include the generated runtime files.
|
|
72
|
+
|
|
36
73
|
## Quick Start
|
|
37
74
|
|
|
38
75
|
### Expose an ability
|
|
@@ -94,7 +131,7 @@ No public IP required — the local runtime connects outbound to the Hub and rec
|
|
|
94
131
|
|
|
95
132
|
Full lifecycle management — not just invocation:
|
|
96
133
|
|
|
97
|
-
- `
|
|
134
|
+
- `buildAbilityDescriptor()` / `exportAbility()` — define and register abilities with schemas
|
|
98
135
|
- `deployToNode()` — install + activate on target nodes
|
|
99
136
|
- `listAbilities()` / `invokeAbility()` / `uninstallAbility()`
|
|
100
137
|
- `discoverNodes()` / `executeCommand()` / `disconnectDevice()` / `drainDevice()`
|
|
@@ -119,6 +156,7 @@ First-class voice call lifecycle and transport negotiation (19+ FFI bindings):
|
|
|
119
156
|
|
|
120
157
|
- `startServer()` spawns a local Axon runtime and joins the Hub — all traffic is outbound.
|
|
121
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.
|
|
122
160
|
|
|
123
161
|
### Remote Control & Orchestration
|
|
124
162
|
|
|
@@ -1,15 +1,15 @@
|
|
|
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",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "bb6fdce765a6b1c2fb91a1cee037d00f7ec4297c9a1fd03b39fb829edf2d0fc4"
|
|
13
13
|
},
|
|
14
14
|
"protocol_coverage": {
|
|
15
15
|
"rpc_shapes": ["unary", "server_stream", "client_stream", "bidi_stream"],
|
|
@@ -29,10 +29,11 @@
|
|
|
29
29
|
"axon_dendrite_deploy_mcp_list_dir_json",
|
|
30
30
|
"axon_dendrite_list_mcp_tools_json",
|
|
31
31
|
"axon_dendrite_call_mcp_tool_json",
|
|
32
|
+
"axon_dendrite_call_mcp_tool_stream_open_json",
|
|
32
33
|
"axon_dendrite_uninstall_capability_json",
|
|
33
34
|
"axon_dendrite_update_mcp_list_dir_json",
|
|
34
35
|
"axon_dendrite_protocol_coverage_json",
|
|
35
36
|
"axon_dendrite_string_free"
|
|
36
37
|
],
|
|
37
|
-
"generated_at_utc": "2026-03-
|
|
38
|
+
"generated_at_utc": "2026-03-30T16:23:47Z"
|
|
38
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",
|
|
@@ -251,6 +251,20 @@ char *axon_dendrite_list_mcp_tools_json(uint64_t handle,
|
|
|
251
251
|
char *axon_dendrite_call_mcp_tool_json(uint64_t handle,
|
|
252
252
|
const char *request_json);
|
|
253
253
|
|
|
254
|
+
// Control helper: open streaming MCP tool call. Returns {"stream_handle": <u64>}.
|
|
255
|
+
// Chunks are read via axon_dendrite_stream_next_json.
|
|
256
|
+
// IMPORTANT: Caller MUST call axon_dendrite_stream_close_json on the returned
|
|
257
|
+
// stream_handle to release the background task and channel. Failure to close
|
|
258
|
+
// will leak resources until the session itself is closed.
|
|
259
|
+
// timeout_ms is split as follows:
|
|
260
|
+
// - setup: min(timeout_ms, 30000) — capped at 30s for stream open
|
|
261
|
+
// - per-chunk: timeout_ms — each stream_next call waits up to this value
|
|
262
|
+
// Per-session limit: max 16 concurrent MCP tool streams.
|
|
263
|
+
// input JSON:
|
|
264
|
+
// {"tenant_id":"...","tool_name":"...","target_node_id":"...","arguments_json":{},"timeout_ms":60000}
|
|
265
|
+
char *axon_dendrite_call_mcp_tool_stream_open_json(uint64_t handle,
|
|
266
|
+
const char *request_json);
|
|
267
|
+
|
|
254
268
|
// Control helper: deactivate (optional) and uninstall installed capability.
|
|
255
269
|
// input JSON:
|
|
256
270
|
// {
|
|
@@ -392,8 +406,8 @@ char *axon_dendrite_voice_watch_transport_events_json(uint64_t handle,
|
|
|
392
406
|
// "path":"/axon.v1.Invocation/WatchInvocation",
|
|
393
407
|
// "request_base64":"...",
|
|
394
408
|
// "metadata":{"x-foo":"bar"},
|
|
395
|
-
// "timeout_ms":10000,
|
|
396
|
-
// "chunk_timeout_ms":30000,
|
|
409
|
+
// "timeout_ms":10000, // setup/open timeout
|
|
410
|
+
// "chunk_timeout_ms":30000, // per-chunk wait timeout
|
|
397
411
|
// "chunk_buffer_size":64
|
|
398
412
|
// }
|
|
399
413
|
// output JSON:
|
|
@@ -426,8 +440,8 @@ char *axon_dendrite_stream_close_json(uint64_t stream_handle);
|
|
|
426
440
|
// "request_base64":"...",
|
|
427
441
|
// "request_chunks_base64":["..."],
|
|
428
442
|
// "metadata":{"x-foo":"bar"},
|
|
429
|
-
// "timeout_ms":10000,
|
|
430
|
-
// "chunk_timeout_ms":30000,
|
|
443
|
+
// "timeout_ms":10000, // setup/open timeout
|
|
444
|
+
// "chunk_timeout_ms":30000, // per-chunk wait timeout
|
|
431
445
|
// "chunk_buffer_size":64,
|
|
432
446
|
// "request_buffer_size":64
|
|
433
447
|
// }
|
|
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,12 +15,16 @@
|
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "tsc",
|
|
19
|
-
"check": "tsc --noEmit",
|
|
18
|
+
"build": "node ./scripts/clean-generated.mjs && tsc -p tsconfig.json",
|
|
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
|
-
"types:verify": "npm run types:build &&
|
|
23
|
-
"
|
|
22
|
+
"types:verify": "npm run types:build && node ./scripts/verify-dist-types.mjs",
|
|
23
|
+
"generated:check": "npm run build && node ./scripts/verify-generated.mjs",
|
|
24
|
+
"types": "npm run check && npm run types:verify",
|
|
25
|
+
"verify": "npm run types && npm run generated:check",
|
|
26
|
+
"prepare": "npm run build",
|
|
27
|
+
"prepack": "npm run build"
|
|
24
28
|
},
|
|
25
29
|
"main": "src/index.js",
|
|
26
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
|
}
|
|
@@ -3,6 +3,7 @@ import { type DeployTrace } from "./receipt.js";
|
|
|
3
3
|
import type { ToolSpec } from "./tool_adapter.js";
|
|
4
4
|
export interface AbilityDescriptor {
|
|
5
5
|
name: string;
|
|
6
|
+
toolName: string;
|
|
6
7
|
description: string;
|
|
7
8
|
commandTemplate: string;
|
|
8
9
|
inputSchema: Record<string, unknown>;
|
|
@@ -10,6 +11,11 @@ export interface AbilityDescriptor {
|
|
|
10
11
|
version: string;
|
|
11
12
|
tags: string[];
|
|
12
13
|
resourceUri: string;
|
|
14
|
+
instructions: string;
|
|
15
|
+
inputExamples: unknown[];
|
|
16
|
+
prerequisites: string[];
|
|
17
|
+
contextBindings: Record<string, string>;
|
|
18
|
+
category: string;
|
|
13
19
|
}
|
|
14
20
|
export type AbilityTarget = "claude" | "codex" | "openclaw" | "agent_skills";
|
|
15
21
|
export interface AbilityExportResult {
|
|
@@ -71,7 +77,7 @@ export declare function startServer(endpoint?: string, options?: {
|
|
|
71
77
|
/** Pre-shared join token used for outbound federation authentication. */
|
|
72
78
|
hubJoinToken?: string;
|
|
73
79
|
}): Promise<ServerHandle>;
|
|
74
|
-
export declare function
|
|
80
|
+
export declare function buildAbilityDescriptor(opts: {
|
|
75
81
|
name: string;
|
|
76
82
|
description: string;
|
|
77
83
|
commandTemplate: string;
|
|
@@ -80,6 +86,11 @@ export declare function createAbility(opts: {
|
|
|
80
86
|
version?: string;
|
|
81
87
|
tags?: string[];
|
|
82
88
|
resourceUri?: string;
|
|
89
|
+
instructions?: string;
|
|
90
|
+
inputExamples?: unknown[];
|
|
91
|
+
prerequisites?: string[];
|
|
92
|
+
contextBindings?: Record<string, string>;
|
|
93
|
+
category?: string;
|
|
83
94
|
}): AbilityDescriptor;
|
|
84
95
|
export declare function toToolSpec(descriptor: AbilityDescriptor): ToolSpec;
|
|
85
96
|
export declare function exportAbility(descriptor: AbilityDescriptor, target?: AbilityTarget, axonEndpoint?: string): AbilityExportResult;
|
package/src/ability_lifecycle.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
//
|
|
1
|
+
// EasyNet Axon for AgentNet
|
|
2
|
+
// =========================
|
|
2
3
|
//
|
|
4
|
+
// File: sdk/node/src/ability_lifecycle.ts
|
|
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.
|
|
21
|
+
//
|
|
22
|
+
// Author: Silan.Hu
|
|
23
|
+
// Email: silan.hu@u.nus.edu
|
|
3
24
|
// Copyright (c) 2026-2027 easynet. All rights reserved.
|
|
4
25
|
import { spawn } from "node:child_process";
|
|
5
26
|
import { webcrypto } from "node:crypto";
|
|
@@ -8,7 +29,7 @@ import { existsSync, mkdirSync, openSync } from "node:fs";
|
|
|
8
29
|
import { hostname as localHostname } from "node:os";
|
|
9
30
|
import { join, dirname } from "node:path";
|
|
10
31
|
import { env } from "node:process";
|
|
11
|
-
import { buildPythonSubprocessTemplate } from "./presets/remote_control/descriptor.js";
|
|
32
|
+
import { buildDescriptor as buildRemotePackageDescriptor, buildPythonSubprocessTemplate, serializeDescriptor as serializeRemotePackageDescriptor, } from "./presets/remote_control/descriptor.js";
|
|
12
33
|
import { AxonConfigError, AxonBridgeError, AxonInvocationError, AxonPartialSuccessError } from "./errors.js";
|
|
13
34
|
import { DEFAULT_SIGNATURE } from "./presets/remote_control/config.js";
|
|
14
35
|
import { beginPhase, buildDeployTrace } from "./receipt.js";
|
|
@@ -33,7 +54,10 @@ function normalizeAbilityName(raw) {
|
|
|
33
54
|
.replace(/[^a-z0-9_\-]/g, "-")
|
|
34
55
|
.replace(/-{2,}/g, "-")
|
|
35
56
|
.replace(/^-+|-+$/g, "");
|
|
36
|
-
|
|
57
|
+
if (!result) {
|
|
58
|
+
throw new AxonConfigError(`identifier contains no valid characters: ${JSON.stringify(raw)}`);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
37
61
|
}
|
|
38
62
|
function firstNonEmpty(...values) {
|
|
39
63
|
for (const value of values) {
|
|
@@ -209,9 +233,9 @@ export async function startServer(endpoint, options = {}) {
|
|
|
209
233
|
};
|
|
210
234
|
}
|
|
211
235
|
// ---------------------------------------------------------------------------
|
|
212
|
-
//
|
|
236
|
+
// build
|
|
213
237
|
// ---------------------------------------------------------------------------
|
|
214
|
-
export function
|
|
238
|
+
export function buildAbilityDescriptor(opts) {
|
|
215
239
|
if (!opts.name?.trim())
|
|
216
240
|
throw new AxonConfigError("ability name cannot be empty");
|
|
217
241
|
if (!opts.commandTemplate?.trim())
|
|
@@ -219,6 +243,7 @@ export function createAbility(opts) {
|
|
|
219
243
|
const token = normalizeAbilityName(opts.name);
|
|
220
244
|
return {
|
|
221
245
|
name: opts.name,
|
|
246
|
+
toolName: token,
|
|
222
247
|
description: opts.description,
|
|
223
248
|
commandTemplate: opts.commandTemplate,
|
|
224
249
|
inputSchema: opts.inputSchema ?? { type: "object", properties: {} },
|
|
@@ -226,15 +251,19 @@ export function createAbility(opts) {
|
|
|
226
251
|
version: opts.version ?? "1.0.0",
|
|
227
252
|
tags: opts.tags ?? [],
|
|
228
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 ?? "",
|
|
229
259
|
};
|
|
230
260
|
}
|
|
231
261
|
// ---------------------------------------------------------------------------
|
|
232
262
|
// toToolSpec
|
|
233
263
|
// ---------------------------------------------------------------------------
|
|
234
264
|
export function toToolSpec(descriptor) {
|
|
235
|
-
const token = normalizeAbilityName(descriptor.name);
|
|
236
265
|
return {
|
|
237
|
-
name:
|
|
266
|
+
name: descriptor.toolName,
|
|
238
267
|
description: descriptor.description,
|
|
239
268
|
resourceUri: descriptor.resourceUri,
|
|
240
269
|
parameters: descriptor.inputSchema,
|
|
@@ -244,7 +273,7 @@ export function toToolSpec(descriptor) {
|
|
|
244
273
|
// exportAbility
|
|
245
274
|
// ---------------------------------------------------------------------------
|
|
246
275
|
export function exportAbility(descriptor, target = "agent_skills", axonEndpoint) {
|
|
247
|
-
const token =
|
|
276
|
+
const token = descriptor.toolName;
|
|
248
277
|
const endpoint = axonEndpoint ?? `http://127.0.0.1:${DEFAULT_AXON_PORT}`;
|
|
249
278
|
const invokeScript = generateInvokeScript(descriptor.resourceUri, endpoint);
|
|
250
279
|
const abilityMd = generateAbilityMd(descriptor, target, token);
|
|
@@ -270,9 +299,9 @@ export function exportAbility(descriptor, target = "agent_skills", axonEndpoint)
|
|
|
270
299
|
* ```
|
|
271
300
|
*/
|
|
272
301
|
export async function deployToNode(bridge, tenant, nodeId, descriptor, signature) {
|
|
273
|
-
const token =
|
|
302
|
+
const token = descriptor.toolName;
|
|
274
303
|
const abilityId = token;
|
|
275
|
-
const
|
|
304
|
+
const deployArgs = {
|
|
276
305
|
ability_name: descriptor.name,
|
|
277
306
|
tool_name: token,
|
|
278
307
|
description: descriptor.description,
|
|
@@ -280,8 +309,20 @@ export async function deployToNode(bridge, tenant, nodeId, descriptor, signature
|
|
|
280
309
|
version: descriptor.version,
|
|
281
310
|
input_schema: descriptor.inputSchema,
|
|
282
311
|
output_schema: descriptor.outputSchema,
|
|
283
|
-
|
|
284
|
-
|
|
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);
|
|
285
326
|
const builder = beginPhase("deploy", tenant, nodeId, abilityId);
|
|
286
327
|
try {
|
|
287
328
|
const result = await bridge.deployAbilityPackage(tenant, nodeId, pkg);
|
|
@@ -308,7 +349,7 @@ export async function listAbilities(bridge, tenant, nodeId) {
|
|
|
308
349
|
// invokeAbility
|
|
309
350
|
// ---------------------------------------------------------------------------
|
|
310
351
|
export async function invokeAbility(bridge, tenant, nodeId, toolName, args = {}) {
|
|
311
|
-
return await bridge.callMcpToolWithArgs(tenant, toolName, nodeId, args);
|
|
352
|
+
return await bridge.callMcpToolWithArgs(tenant, normalizeAbilityName(toolName), nodeId, args);
|
|
312
353
|
}
|
|
313
354
|
// ---------------------------------------------------------------------------
|
|
314
355
|
// uninstallAbility
|
|
@@ -374,14 +415,17 @@ export async function forgetAll(bridge, tenant, nodeId, confirm = false, options
|
|
|
374
415
|
// without performing any uninstalls.
|
|
375
416
|
if (dryRun) {
|
|
376
417
|
const wouldRemove = [];
|
|
418
|
+
const wouldFail = [];
|
|
377
419
|
for (const tool of tools) {
|
|
378
420
|
const installId = String(tool.install_id ?? "");
|
|
379
|
-
const toolName = String(tool.tool_name ?? "");
|
|
380
|
-
if (!installId)
|
|
421
|
+
const toolName = String(tool.tool_name ?? "unknown");
|
|
422
|
+
if (!installId) {
|
|
423
|
+
wouldFail.push({ tool_name: toolName, error: "missing install_id" });
|
|
381
424
|
continue;
|
|
425
|
+
}
|
|
382
426
|
wouldRemove.push(toolName);
|
|
383
427
|
}
|
|
384
|
-
return { removed: wouldRemove, removed_count: wouldRemove.length, failed:
|
|
428
|
+
return { removed: wouldRemove, removed_count: wouldRemove.length, failed: wouldFail, failed_count: wouldFail.length };
|
|
385
429
|
}
|
|
386
430
|
const removed = [];
|
|
387
431
|
const failed = [];
|
|
@@ -428,19 +472,10 @@ export async function listRemoteTools(bridge, tenant, namePattern = "", nodeId =
|
|
|
428
472
|
// buildDeployPackage
|
|
429
473
|
// ---------------------------------------------------------------------------
|
|
430
474
|
export function buildDeployPackage(args, signature) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
tool_name: toolName,
|
|
436
|
-
description: args.description ?? "",
|
|
437
|
-
command_template: args.command_template ?? "",
|
|
438
|
-
input_schema: args.input_schema ?? { type: "object", properties: {} },
|
|
439
|
-
output_schema: args.output_schema ?? { type: "object", properties: {} },
|
|
440
|
-
version: args.version ?? "1.0.0",
|
|
441
|
-
tags: args.tags ?? [],
|
|
442
|
-
signature_base64: signature,
|
|
443
|
-
};
|
|
475
|
+
return serializeRemotePackageDescriptor(buildRemotePackageDescriptor({
|
|
476
|
+
...args,
|
|
477
|
+
signature_base64: args.signature_base64 ?? signature,
|
|
478
|
+
}, signature));
|
|
444
479
|
}
|
|
445
480
|
// ---------------------------------------------------------------------------
|
|
446
481
|
// deployPackage
|
|
@@ -451,12 +486,21 @@ export async function deployPackage(bridge, tenant, nodeId, packageDescriptor) {
|
|
|
451
486
|
// ---------------------------------------------------------------------------
|
|
452
487
|
// Internal generators
|
|
453
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
|
+
}
|
|
454
496
|
function generateInvokeScript(resourceUri, endpoint) {
|
|
497
|
+
const safeEP = sanitizeShellValue(endpoint, "endpoint");
|
|
498
|
+
const safeURI = sanitizeShellValue(resourceUri, "resource_uri");
|
|
455
499
|
return `#!/usr/bin/env bash
|
|
456
500
|
set -euo pipefail
|
|
457
|
-
AXON_ENDPOINT="\${AXON_ENDPOINT:-${
|
|
501
|
+
AXON_ENDPOINT="\${AXON_ENDPOINT:-${safeEP}}"
|
|
458
502
|
TENANT="\${AXON_TENANT:-default}"
|
|
459
|
-
RESOURCE_URI="${
|
|
503
|
+
RESOURCE_URI="${safeURI}"
|
|
460
504
|
ARGS="\${1:-{}}"
|
|
461
505
|
curl -sS -X POST "\${AXON_ENDPOINT}/v1/invoke" \\
|
|
462
506
|
-H "Content-Type: application/json" \\
|
|
@@ -492,6 +536,20 @@ function generateAbilityMd(descriptor, target, token) {
|
|
|
492
536
|
lines.push("");
|
|
493
537
|
lines.push(descriptor.description);
|
|
494
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
|
+
}
|
|
495
553
|
lines.push("## Parameters");
|
|
496
554
|
lines.push("");
|
|
497
555
|
lines.push("| Name | Type | Required | Description |");
|
|
@@ -504,6 +562,31 @@ function generateAbilityMd(descriptor, target, token) {
|
|
|
504
562
|
const isRequired = required.includes(name) ? "Yes" : "No";
|
|
505
563
|
lines.push(`| ${name} | ${propType} | ${isRequired} | ${propDesc} |`);
|
|
506
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
|
+
}
|
|
507
590
|
lines.push("");
|
|
508
591
|
lines.push("## Invoke");
|
|
509
592
|
lines.push("");
|
|
@@ -520,6 +603,9 @@ function generateAbilityMd(descriptor, target, token) {
|
|
|
520
603
|
lines.push("");
|
|
521
604
|
lines.push(`- **URI**: \`${descriptor.resourceUri}\``);
|
|
522
605
|
lines.push(`- **Version**: ${descriptor.version}`);
|
|
606
|
+
if (descriptor.category) {
|
|
607
|
+
lines.push(`- **Category**: ${descriptor.category}`);
|
|
608
|
+
}
|
|
523
609
|
lines.push("");
|
|
524
610
|
return lines.join("\n");
|
|
525
611
|
}
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
// Author: Silan.Hu
|
|
24
24
|
// Email: silan.hu@u.nus.edu
|
|
25
25
|
// Copyright (c) 2026-2027 easynet. All rights reserved.
|
|
26
|
+
// DendriteError re-exported from errors.ts to break the circular dependency:
|
|
27
|
+
// dendrite_bridge imports validate/build helpers from this module, so we
|
|
28
|
+
// cannot import from dendrite_bridge here. errors.ts has no such dependency.
|
|
26
29
|
import { AxonConfigError } from "./errors.js";
|
|
27
|
-
// DendriteError kept for backward compatibility; validation now uses AxonConfigError.
|
|
28
30
|
export class DendriteError extends Error {
|
|
29
31
|
constructor(message) {
|
|
30
32
|
super(message);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DendriteBridgeOptions, DendriteUnaryCallOptions, DendriteAbilityCallOptions, DendriteStreamCallOptions, DendriteClientStreamCallOptions, DendriteBidiStreamCallOptions, DendriteListNodesOptions, DendriteRegisterNodeOptions, DendritePublishCapabilityOptions, DendriteInstallCapabilityOptions, DendriteListA2aAgentsOptions, DendriteSendA2aTaskOptions, DendriteDeployMcpListDirOptions, DendriteListMcpToolsOptions, DendriteCallMcpToolOptions, DendriteUninstallCapabilityOptions, DendriteUpdateMcpListDirOptions, DendriteVoiceCreateCallOptions, DendriteVoiceJoinCallOptions, DendriteVoiceUpdateMediaPathOptions, DendriteVoiceMetricsOptions, DendriteVoiceEndCallOptions, DendriteVoiceWatchOptions, DendriteVoiceCreateTransportSessionOptions, DendriteVoiceDescriptionOptions, DendriteVoiceCandidateOptions, DendriteVoiceEndTransportSessionOptions, DendriteProtocolInvokeOptions } from "./types.js";
|
|
1
|
+
import type { DendriteBridgeOptions, DendriteUnaryCallOptions, DendriteAbilityCallOptions, DendriteStreamCallOptions, DendriteClientStreamCallOptions, DendriteBidiStreamCallOptions, DendriteListNodesOptions, DendriteRegisterNodeOptions, DendritePublishCapabilityOptions, DendriteInstallCapabilityOptions, DendriteListA2aAgentsOptions, DendriteSendA2aTaskOptions, DendriteDeployMcpListDirOptions, DendriteListMcpToolsOptions, DendriteCallMcpToolOptions, DendriteCallMcpToolStreamOptions, DendriteUninstallCapabilityOptions, DendriteUpdateMcpListDirOptions, DendriteVoiceCreateCallOptions, DendriteVoiceJoinCallOptions, DendriteVoiceUpdateMediaPathOptions, DendriteVoiceMetricsOptions, DendriteVoiceEndCallOptions, DendriteVoiceWatchOptions, DendriteVoiceCreateTransportSessionOptions, DendriteVoiceDescriptionOptions, DendriteVoiceCandidateOptions, DendriteVoiceEndTransportSessionOptions, DendriteProtocolInvokeOptions } from "./types.js";
|
|
2
2
|
import type { DendriteBridgeLib } from "./ffi.js";
|
|
3
3
|
export declare class DendriteBridge {
|
|
4
4
|
private readonly lib;
|
|
@@ -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>;
|
|
@@ -33,6 +34,12 @@ export declare class DendriteBridge {
|
|
|
33
34
|
deployMcpListDir(tenantId: string, nodeId: string, options?: DendriteDeployMcpListDirOptions): Record<string, unknown>;
|
|
34
35
|
listMcpTools(tenantId: string, options?: DendriteListMcpToolsOptions): Record<string, unknown>[];
|
|
35
36
|
callMcpTool(tenantId: string, toolName: string, options?: DendriteCallMcpToolOptions): Record<string, unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* Open a streaming MCP tool call that returns incremental response chunks.
|
|
39
|
+
* Requires native library support for streaming symbols.
|
|
40
|
+
* Uses DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS (60s) per chunk if no timeout specified.
|
|
41
|
+
*/
|
|
42
|
+
callMcpToolStream(tenantId: string, toolName: string, options?: DendriteCallMcpToolStreamOptions): DendriteServerStream;
|
|
36
43
|
uninstallCapability(tenantId: string, nodeId: string, installId: string, options?: DendriteUninstallCapabilityOptions): Record<string, unknown>;
|
|
37
44
|
updateMcpListDir(tenantId: string, nodeId: string, options?: DendriteUpdateMcpListDirOptions): Record<string, unknown>;
|
|
38
45
|
createVoiceCall(tenantId: string, options?: DendriteVoiceCreateCallOptions): Record<string, unknown>;
|
|
@@ -69,9 +76,10 @@ export declare class DendriteBridge {
|
|
|
69
76
|
export declare class DendriteServerStream implements AsyncIterable<Uint8Array> {
|
|
70
77
|
private readonly lib;
|
|
71
78
|
private readonly streamHandle;
|
|
79
|
+
private readonly defaultChunkTimeoutMs;
|
|
72
80
|
private done;
|
|
73
81
|
private nativeClosed;
|
|
74
|
-
constructor(lib: DendriteBridgeLib, streamHandle: bigint);
|
|
82
|
+
constructor(lib: DendriteBridgeLib, streamHandle: bigint, defaultChunkTimeoutMs?: number);
|
|
75
83
|
next(timeoutMs?: number): {
|
|
76
84
|
chunk: Uint8Array | null;
|
|
77
85
|
done: boolean;
|