@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
|
@@ -2,29 +2,28 @@
|
|
|
2
2
|
// =========================
|
|
3
3
|
//
|
|
4
4
|
// File: sdk/node/src/dendrite_bridge/ffi.ts
|
|
5
|
-
// Description: FFI
|
|
5
|
+
// Description: Node FFI loader and raw JSON-call bindings for the native Axon dendrite bridge.
|
|
6
6
|
//
|
|
7
7
|
// Protocol Responsibility:
|
|
8
|
-
// - Loads the native dendrite
|
|
9
|
-
// -
|
|
8
|
+
// - Loads the native dendrite shared library and exposes the raw JSON-call symbol set to Node code.
|
|
9
|
+
// - Normalizes low-level FFI failures into stable Node-side error contracts.
|
|
10
10
|
//
|
|
11
11
|
// Implementation Approach:
|
|
12
|
-
// -
|
|
13
|
-
// -
|
|
12
|
+
// - Keeps library path resolution, symbol binding, and JSON marshalling isolated from higher-level SDK classes.
|
|
13
|
+
// - Defines timeout and transport constants close to the native boundary for consistent reuse.
|
|
14
14
|
//
|
|
15
15
|
// Usage Contract:
|
|
16
|
-
// -
|
|
17
|
-
// -
|
|
16
|
+
// - Treat this module as the lowest-level Node native binding layer and avoid embedding application logic here.
|
|
17
|
+
// - Regenerate or update bindings here before adding new native bridge capabilities to higher layers.
|
|
18
18
|
//
|
|
19
19
|
// Architectural Position:
|
|
20
|
-
// -
|
|
21
|
-
// - Should not embed class or interface logic outside FFI concerns.
|
|
20
|
+
// - Native binding boundary underneath the Node SDK bridge facade.
|
|
22
21
|
//
|
|
23
22
|
// Author: Silan.Hu
|
|
24
23
|
// Email: silan.hu@u.nus.edu
|
|
25
24
|
// Copyright (c) 2026-2027 easynet. All rights reserved.
|
|
26
25
|
import { createRequire } from "node:module";
|
|
27
|
-
import { existsSync } from "node:fs";
|
|
26
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
28
27
|
import { dirname, resolve } from "node:path";
|
|
29
28
|
import { fileURLToPath } from "node:url";
|
|
30
29
|
import { DendriteError } from "../capability_request.js";
|
|
@@ -34,22 +33,196 @@ export const DEFAULT_TIMEOUT_MS = 30000;
|
|
|
34
33
|
export const DEFAULT_CONNECT_TIMEOUT_MS = 5000;
|
|
35
34
|
export const DEFAULT_MCP_TOOL_STREAM_TIMEOUT_MS = 60_000;
|
|
36
35
|
export { DendriteError };
|
|
36
|
+
function bridgeDebugLog(tried) {
|
|
37
|
+
if ((process.env.EASYNET_DENDRITE_BRIDGE_DEBUG || "").trim() === "1") {
|
|
38
|
+
process.stderr.write(`[dendrite-bridge] tried ${tried.length} candidate(s):\n`);
|
|
39
|
+
for (const p of tried) {
|
|
40
|
+
process.stderr.write(` - ${p}\n`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
37
44
|
export function resolveLibraryPath(explicitPath) {
|
|
38
45
|
if (explicitPath)
|
|
39
46
|
return explicitPath;
|
|
40
47
|
if (process.env.EASYNET_DENDRITE_BRIDGE_LIB)
|
|
41
48
|
return process.env.EASYNET_DENDRITE_BRIDGE_LIB;
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
const names = libraryFileNames();
|
|
50
|
+
const versions = sdkVersionCandidates();
|
|
51
|
+
const tried = [];
|
|
52
|
+
for (const c of homeLibraryCandidates(names, versions)) {
|
|
53
|
+
tried.push(c);
|
|
54
|
+
if (existsSync(c))
|
|
55
|
+
return c;
|
|
56
|
+
}
|
|
57
|
+
if (useLocalSource()) {
|
|
58
|
+
for (const c of sourceLibraryCandidates(names, versions)) {
|
|
59
|
+
tried.push(c);
|
|
60
|
+
if (existsSync(c))
|
|
61
|
+
return c;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const c of packageLibraryCandidates(names, versions)) {
|
|
65
|
+
tried.push(c);
|
|
49
66
|
if (existsSync(c))
|
|
50
67
|
return c;
|
|
51
68
|
}
|
|
52
|
-
|
|
69
|
+
bridgeDebugLog(tried);
|
|
70
|
+
throw new DendriteError("dendrite bridge library not found; set EASYNET_DENDRITE_BRIDGE_LIB or EASYNET_DENDRITE_BRIDGE_HOME — run with EASYNET_DENDRITE_BRIDGE_DEBUG=1 for candidate details");
|
|
71
|
+
}
|
|
72
|
+
function dedupeCandidates(values) {
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const value of values) {
|
|
76
|
+
if (seen.has(value)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
seen.add(value);
|
|
80
|
+
out.push(value);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function libraryFileNames() {
|
|
85
|
+
const rawHint = (process.env.EASYNET_DENDRITE_BRIDGE_PLATFORM || "").trim().toLowerCase();
|
|
86
|
+
if (rawHint === "ios") {
|
|
87
|
+
return [
|
|
88
|
+
"libaxon_dendrite_bridge.dylib",
|
|
89
|
+
"libaxon_dendrite_bridge.so",
|
|
90
|
+
"axon_dendrite_bridge.dll",
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
if (["android", "linux", "linux-gnu"].includes(rawHint)) {
|
|
94
|
+
return [
|
|
95
|
+
"libaxon_dendrite_bridge.so",
|
|
96
|
+
"libaxon_dendrite_bridge.dylib",
|
|
97
|
+
"axon_dendrite_bridge.dll",
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
if (["macos", "darwin", "mac"].includes(rawHint)) {
|
|
101
|
+
return [
|
|
102
|
+
"libaxon_dendrite_bridge.dylib",
|
|
103
|
+
"libaxon_dendrite_bridge.so",
|
|
104
|
+
"axon_dendrite_bridge.dll",
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
if (["windows", "win", "win32", "win64"].includes(rawHint)) {
|
|
108
|
+
return [
|
|
109
|
+
"axon_dendrite_bridge.dll",
|
|
110
|
+
"libaxon_dendrite_bridge.dylib",
|
|
111
|
+
"libaxon_dendrite_bridge.so",
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
const primary = process.platform === "win32"
|
|
115
|
+
? "axon_dendrite_bridge.dll"
|
|
116
|
+
: process.platform === "darwin"
|
|
117
|
+
? "libaxon_dendrite_bridge.dylib"
|
|
118
|
+
: "libaxon_dendrite_bridge.so";
|
|
119
|
+
const all = [
|
|
120
|
+
"libaxon_dendrite_bridge.dylib",
|
|
121
|
+
"libaxon_dendrite_bridge.so",
|
|
122
|
+
"axon_dendrite_bridge.dll",
|
|
123
|
+
];
|
|
124
|
+
return [primary, ...all.filter((n) => n !== primary)];
|
|
125
|
+
}
|
|
126
|
+
function projectRoot() {
|
|
127
|
+
let cursor = process.cwd();
|
|
128
|
+
for (let depth = 0; depth < 12; depth += 1) {
|
|
129
|
+
if (existsSync(resolve(cursor, "core/runtime-rs"))) {
|
|
130
|
+
return cursor;
|
|
131
|
+
}
|
|
132
|
+
const parent = dirname(cursor);
|
|
133
|
+
if (parent === cursor)
|
|
134
|
+
break;
|
|
135
|
+
cursor = parent;
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
function sdkVersionCandidates() {
|
|
140
|
+
const versions = [];
|
|
141
|
+
const envVersion = (process.env.SDK_VERSION || "").trim();
|
|
142
|
+
if (envVersion) {
|
|
143
|
+
versions.push(envVersion);
|
|
144
|
+
}
|
|
145
|
+
const root = projectRoot();
|
|
146
|
+
if (root) {
|
|
147
|
+
try {
|
|
148
|
+
const raw = readFileSync(resolve(root, "VERSION"), "utf8").trim();
|
|
149
|
+
if (raw) {
|
|
150
|
+
versions.push(raw.split("\n")[0].trim());
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// no-op
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (versions.length === 0) {
|
|
158
|
+
if ((process.env.EASYNET_DENDRITE_BRIDGE_DEBUG || "").trim() === "1") {
|
|
159
|
+
console.error("warning: no SDK version detected, falling back to 0.1.0");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
versions.push("0.1.0");
|
|
163
|
+
return dedupeCandidates(versions).filter(Boolean);
|
|
164
|
+
}
|
|
165
|
+
function addLibraryCandidates(targets, names, roots) {
|
|
166
|
+
for (const root of roots) {
|
|
167
|
+
for (const name of names) {
|
|
168
|
+
targets.push(resolve(root, name));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function homeLibraryCandidates(names, versions) {
|
|
173
|
+
const raw = (process.env.EASYNET_DENDRITE_BRIDGE_HOME || "").trim();
|
|
174
|
+
if (!raw)
|
|
175
|
+
return [];
|
|
176
|
+
const homeRoot = resolve(raw);
|
|
177
|
+
const candidateRoots = [
|
|
178
|
+
homeRoot,
|
|
179
|
+
resolve(homeRoot, "native"),
|
|
180
|
+
...versions.map((version) => resolve(homeRoot, "dist", "sdk-packs", version, "native")),
|
|
181
|
+
];
|
|
182
|
+
const candidates = [];
|
|
183
|
+
addLibraryCandidates(candidates, names, candidateRoots);
|
|
184
|
+
return dedupeCandidates(candidates);
|
|
185
|
+
}
|
|
186
|
+
function useLocalSource() {
|
|
187
|
+
const raw = (process.env.EASYNET_DENDRITE_BRIDGE_SOURCE || "").trim().toLowerCase();
|
|
188
|
+
return new Set(["1", "true", "yes", "on", "local", "source"]).has(raw);
|
|
189
|
+
}
|
|
190
|
+
function sourceLibraryCandidates(names, versions) {
|
|
191
|
+
const root = projectRoot();
|
|
192
|
+
if (!root)
|
|
193
|
+
return [];
|
|
194
|
+
const candidates = [];
|
|
195
|
+
// Versioned dist paths
|
|
196
|
+
for (const version of versions) {
|
|
197
|
+
addLibraryCandidates(candidates, names, [resolve(root, "dist", "sdk-packs", version, "native")]);
|
|
198
|
+
}
|
|
199
|
+
// Build artifacts
|
|
200
|
+
const bridgeTarget = resolve(root, "core/runtime-rs/dendrite-bridge/target");
|
|
201
|
+
const roots = [
|
|
202
|
+
resolve(bridgeTarget, "release"),
|
|
203
|
+
resolve(bridgeTarget, "debug"),
|
|
204
|
+
];
|
|
205
|
+
const sdkTarget = (process.env.SDK_TARGET || "").trim();
|
|
206
|
+
if (sdkTarget) {
|
|
207
|
+
roots.push(resolve(bridgeTarget, sdkTarget, "release"));
|
|
208
|
+
roots.push(resolve(bridgeTarget, sdkTarget, "debug"));
|
|
209
|
+
}
|
|
210
|
+
addLibraryCandidates(candidates, names, roots);
|
|
211
|
+
return dedupeCandidates(candidates);
|
|
212
|
+
}
|
|
213
|
+
function packageLibraryCandidates(names, versions) {
|
|
214
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
215
|
+
const pkgBase = resolve(here, "../..");
|
|
216
|
+
const roots = [
|
|
217
|
+
pkgBase,
|
|
218
|
+
resolve(pkgBase, "native"),
|
|
219
|
+
];
|
|
220
|
+
for (const version of versions) {
|
|
221
|
+
roots.push(resolve(pkgBase, "dist", "sdk-packs", version, "native"));
|
|
222
|
+
}
|
|
223
|
+
const candidates = [];
|
|
224
|
+
addLibraryCandidates(candidates, names, roots);
|
|
225
|
+
return dedupeCandidates(candidates);
|
|
53
226
|
}
|
|
54
227
|
export function loadDendriteLib(libraryPath) {
|
|
55
228
|
const lib = koffi.load(libraryPath);
|
|
@@ -79,6 +252,7 @@ export function loadDendriteLib(libraryPath) {
|
|
|
79
252
|
axon_dendrite_list_nodes_json: lib.func("axon_dendrite_list_nodes_json", bridgeString, ["uint64_t", "str"]),
|
|
80
253
|
axon_dendrite_register_node_json: lib.func("axon_dendrite_register_node_json", bridgeString, ["uint64_t", "str"]),
|
|
81
254
|
axon_dendrite_deregister_node_json: optionalJsonFn("axon_dendrite_deregister_node_json", ["uint64_t", "str"]),
|
|
255
|
+
axon_dendrite_drain_node_json: optionalJsonFn("axon_dendrite_drain_node_json", ["uint64_t", "str"]),
|
|
82
256
|
axon_dendrite_heartbeat_json: lib.func("axon_dendrite_heartbeat_json", bridgeString, ["uint64_t", "str"]),
|
|
83
257
|
axon_dendrite_publish_capability_json: lib.func("axon_dendrite_publish_capability_json", bridgeString, ["uint64_t", "str"]),
|
|
84
258
|
axon_dendrite_install_capability_json: lib.func("axon_dendrite_install_capability_json", bridgeString, ["uint64_t", "str"]),
|
package/src/index.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ export declare class Client {
|
|
|
48
48
|
callRaw(payload: JsonValue): Promise<Record<string, unknown>>;
|
|
49
49
|
}
|
|
50
50
|
export declare function client(transport?: Transport): Client;
|
|
51
|
-
export {
|
|
51
|
+
export { buildAbilityDescriptor, exportAbility, deployToNode, listAbilities, invokeAbility, uninstallAbility, discoverNodes, executeCommand, forgetAll, disconnectDevice, drainDevice, listRemoteTools, buildDeployPackage, deployPackage, startServer, toToolSpec, type AbilityDescriptor, type AbilityTarget, type AbilityExportResult, type ServerHandle, type BridgeInterface, type ForgetAllResult, type ForgetAllFailure, type DeployResult, } from "./ability_lifecycle.js";
|
|
52
52
|
export { AxonError, AxonConfigError, AxonBridgeError, AxonNotInstalledError, AxonNotActivatedError, AxonInvocationError, AxonStreamError, AxonPolicyDeniedError, AxonMcpError, AxonPartialSuccessError, AxonJsonError, AxonIoError, AxonSymbolNotFoundError, } from "./errors.js";
|
|
53
53
|
export { beginPhase, skippedReceipt, buildDeployTrace, phaseFromTrace, PhaseReceiptBuilder, type Phase, type PhaseStatus, type PhaseReceipt, type DeployTrace, } from "./receipt.js";
|
|
54
54
|
export { AbilityToolAdapter, type ToolSpec as AdapterToolSpec, type OpenAITool, type OpenAIChatTool, type AnthropicTool, type ToolParameters, type LocalHandler, type RegisterOptions, type AbilityToolAdapterOptions, } from "./tool_adapter.js";
|
package/src/index.js
CHANGED
|
@@ -2,23 +2,22 @@
|
|
|
2
2
|
// =========================
|
|
3
3
|
//
|
|
4
4
|
// File: sdk/node/src/index.ts
|
|
5
|
-
// Description:
|
|
5
|
+
// Description: Node SDK package-root facade for tenant-aware clients, sidecar transport, MCP helpers, and bridge exports.
|
|
6
6
|
//
|
|
7
7
|
// Protocol Responsibility:
|
|
8
|
-
// -
|
|
9
|
-
// -
|
|
8
|
+
// - Defines the package-root SDK facade and re-export surface consumed by application code.
|
|
9
|
+
// - Keeps tenant-aware client, bridge, and tool APIs reachable through one stable import boundary.
|
|
10
10
|
//
|
|
11
11
|
// Implementation Approach:
|
|
12
|
-
// -
|
|
13
|
-
// -
|
|
12
|
+
// - Composes smaller modules rather than re-implementing transport or protocol logic at the package root.
|
|
13
|
+
// - Uses explicit exports so public surface changes remain auditable.
|
|
14
14
|
//
|
|
15
15
|
// Usage Contract:
|
|
16
|
-
// -
|
|
17
|
-
// -
|
|
16
|
+
// - Prefer importing from this boundary when consuming the SDK from application code.
|
|
17
|
+
// - Public export changes here should be synchronized with language-specific packaging and documentation.
|
|
18
18
|
//
|
|
19
19
|
// Architectural Position:
|
|
20
|
-
// -
|
|
21
|
-
// - Should not embed unrelated orchestration logic outside this file's responsibility.
|
|
20
|
+
// - Top-level SDK package boundary for application-facing imports.
|
|
22
21
|
//
|
|
23
22
|
// Author: Silan.Hu
|
|
24
23
|
// Email: silan.hu@u.nus.edu
|
|
@@ -157,7 +156,7 @@ export class Client {
|
|
|
157
156
|
export function client(transport) {
|
|
158
157
|
return new Client(transport);
|
|
159
158
|
}
|
|
160
|
-
export {
|
|
159
|
+
export { buildAbilityDescriptor, exportAbility, deployToNode, listAbilities, invokeAbility, uninstallAbility, discoverNodes, executeCommand, forgetAll, disconnectDevice, drainDevice, listRemoteTools, buildDeployPackage, deployPackage, startServer, toToolSpec, } from "./ability_lifecycle.js";
|
|
161
160
|
export { AxonError, AxonConfigError, AxonBridgeError, AxonNotInstalledError, AxonNotActivatedError, AxonInvocationError, AxonStreamError, AxonPolicyDeniedError, AxonMcpError, AxonPartialSuccessError, AxonJsonError, AxonIoError, AxonSymbolNotFoundError, } from "./errors.js";
|
|
162
161
|
export { beginPhase, skippedReceipt, buildDeployTrace, phaseFromTrace, PhaseReceiptBuilder, } from "./receipt.js";
|
|
163
162
|
export { AbilityToolAdapter, } from "./tool_adapter.js";
|
package/src/mcp/server.test.js
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
// EasyNet Axon for AgentNet
|
|
2
|
+
// =========================
|
|
3
|
+
//
|
|
4
|
+
// File: sdk/node/src/mcp/server.test.js
|
|
5
|
+
// Description: JavaScript runtime mirror for `sdk/node/src/mcp/server.test.js` in the published Node SDK artifact set.
|
|
6
|
+
//
|
|
7
|
+
// Protocol Responsibility:
|
|
8
|
+
// - Preserves the exported API surface generated from the canonical TypeScript source module.
|
|
9
|
+
// - Keeps the distributable Node package consistent for consumers that import built artifacts directly.
|
|
10
|
+
//
|
|
11
|
+
// Implementation Approach:
|
|
12
|
+
// - Mirrors the canonical `.ts` module output and should not diverge semantically from source.
|
|
13
|
+
// - Exists as a distribution artifact so package consumers do not need the TypeScript authoring tree at runtime.
|
|
14
|
+
//
|
|
15
|
+
// Usage Contract:
|
|
16
|
+
// - Update this file through the Node SDK build pipeline when the corresponding TypeScript source changes.
|
|
17
|
+
// - Treat manual edits here as temporary at most; source-of-truth behavior lives in the `.ts` module.
|
|
18
|
+
//
|
|
19
|
+
// Architectural Position:
|
|
20
|
+
// - Published artifact boundary for the Node SDK distribution.
|
|
21
|
+
//
|
|
22
|
+
// Author: Silan.Hu
|
|
23
|
+
// Email: silan.hu@u.nus.edu
|
|
24
|
+
// Copyright (c) 2026-2027 easynet. All rights reserved.
|
|
25
|
+
|
|
1
26
|
import assert from "node:assert/strict";
|
|
2
27
|
import { describe, it } from "node:test";
|
|
3
28
|
import { PassThrough } from "node:stream";
|
|
@@ -6,34 +31,47 @@ import { StdioMcpServer } from "./server.js";
|
|
|
6
31
|
|
|
7
32
|
describe("StdioMcpServer", () => {
|
|
8
33
|
it("keeps buffered stream responses intact when close throws", async () => {
|
|
34
|
+
const originalConsoleError = console.error;
|
|
35
|
+
const consoleErrors = [];
|
|
36
|
+
console.error = (...args) => {
|
|
37
|
+
consoleErrors.push(args);
|
|
38
|
+
};
|
|
39
|
+
|
|
9
40
|
const server = new StdioMcpServer({
|
|
10
41
|
toolSpecs: () => [],
|
|
11
42
|
handleToolCall: () => ({ payload: { ok: true }, isError: false }),
|
|
12
43
|
handleToolCallStream: () => ({
|
|
44
|
+
stream: {
|
|
45
|
+
async *[Symbol.asyncIterator]() {
|
|
46
|
+
yield Buffer.from("chunk-1");
|
|
47
|
+
},
|
|
48
|
+
},
|
|
13
49
|
close() {
|
|
14
50
|
throw new Error("close failed");
|
|
15
51
|
},
|
|
16
|
-
async *[Symbol.asyncIterator]() {
|
|
17
|
-
yield Buffer.from("chunk-1");
|
|
18
|
-
},
|
|
19
52
|
}),
|
|
20
53
|
});
|
|
21
54
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
55
|
+
try {
|
|
56
|
+
const response = await server.handleRequest({
|
|
57
|
+
jsonrpc: "2.0",
|
|
58
|
+
id: 1,
|
|
59
|
+
method: "tools/call",
|
|
60
|
+
params: {
|
|
61
|
+
name: "streamed_tool",
|
|
62
|
+
arguments: {},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
31
65
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
66
|
+
assert.equal(response.jsonrpc, "2.0");
|
|
67
|
+
const payload = JSON.parse(response.result.content[0].text);
|
|
68
|
+
assert.equal(payload.ok, true);
|
|
69
|
+
assert.deepEqual(payload.chunks, ["chunk-1"]);
|
|
70
|
+
assert.equal(response.result.isError, undefined);
|
|
71
|
+
assert.equal(consoleErrors.length, 1);
|
|
72
|
+
} finally {
|
|
73
|
+
console.error = originalConsoleError;
|
|
74
|
+
}
|
|
37
75
|
});
|
|
38
76
|
|
|
39
77
|
it("serializes writes through drain backpressure", async () => {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
// Copyright (c) 2026-2027 easynet. All rights reserved.
|
|
25
25
|
// Node server flow: publish/install/activate media capability on selected client and invoke tasks.
|
|
26
26
|
import { Orchestrator } from "./orchestrator.js";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
27
|
+
import { resolveLibraryPath } from "../../dendrite_bridge/ffi.js";
|
|
28
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
29
|
+
import { resolve } from "node:path";
|
|
30
30
|
import { parseArgs, parsePositiveInt } from "./args.js";
|
|
31
31
|
import { readBundleVersion, resolveBundlePath, sha256Prefixed } from "./bundle.js";
|
|
32
32
|
import { decodeMediaBase64, defaultOutputDir, persistMediaFile } from "./media.js";
|
|
@@ -53,36 +53,14 @@ function resolveConfig(cfg) {
|
|
|
53
53
|
taskPayloadBuilder: cfg?.taskPayloadBuilder ?? mediaCaptureTaskPayload,
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
function defaultBridgeLibName() {
|
|
57
|
-
if (process.platform === "darwin")
|
|
58
|
-
return "libaxon_dendrite_bridge.dylib";
|
|
59
|
-
if (process.platform === "win32")
|
|
60
|
-
return "axon_dendrite_bridge.dll";
|
|
61
|
-
return "libaxon_dendrite_bridge.so";
|
|
62
|
-
}
|
|
63
|
-
function findRepoRootForBridge(startDir) {
|
|
64
|
-
let current = resolve(startDir);
|
|
65
|
-
for (let steps = 0; steps < 10; steps += 1) {
|
|
66
|
-
const candidate = resolve(current, "core/runtime-rs/dendrite-bridge/target/release", defaultBridgeLibName());
|
|
67
|
-
if (existsSync(candidate)) {
|
|
68
|
-
return current;
|
|
69
|
-
}
|
|
70
|
-
const parent = dirname(current);
|
|
71
|
-
if (parent === current) {
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
current = parent;
|
|
75
|
-
}
|
|
76
|
-
return "";
|
|
77
|
-
}
|
|
78
56
|
function ensureBridgeLibEnv() {
|
|
79
57
|
if (process.env.EASYNET_DENDRITE_BRIDGE_LIB)
|
|
80
58
|
return;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
59
|
+
try {
|
|
60
|
+
process.env.EASYNET_DENDRITE_BRIDGE_LIB = resolveLibraryPath();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Non-fatal: library may be resolved later by the bridge constructor.
|
|
86
64
|
}
|
|
87
65
|
}
|
|
88
66
|
function extractInstallId(lifecycle) {
|
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
// Author: Silan.Hu
|
|
8
8
|
// Email: silan.hu@u.nus.edu
|
|
9
9
|
// Copyright (c) 2026-2027 easynet. All rights reserved.
|
|
10
|
-
import {
|
|
11
|
-
import { dirname, resolve } from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { resolveLibraryPath } from "../../dendrite_bridge/ffi.js";
|
|
13
11
|
export const DEFAULT_SIGNATURE = "__AXON_EPHEMERAL_DO_NOT_USE_IN_PROD__";
|
|
14
12
|
export const DEFAULT_VERSION = "1.0.0";
|
|
15
13
|
export const DEFAULT_INSTALL_TIMEOUT_SECONDS = 45;
|
|
@@ -30,16 +28,11 @@ export function ensureRemoteControlNativeLibEnv() {
|
|
|
30
28
|
if (process.env.EASYNET_DENDRITE_BRIDGE_LIB) {
|
|
31
29
|
return;
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for (const candidate of candidates) {
|
|
39
|
-
if (existsSync(candidate)) {
|
|
40
|
-
process.env.EASYNET_DENDRITE_BRIDGE_LIB = candidate;
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
31
|
+
try {
|
|
32
|
+
process.env.EASYNET_DENDRITE_BRIDGE_LIB = resolveLibraryPath();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Non-fatal: library may be resolved later by the bridge constructor.
|
|
43
36
|
}
|
|
44
37
|
}
|
|
45
38
|
export function ensureNativeLibEnv() {
|
|
@@ -66,12 +59,3 @@ export function asNumber(raw) {
|
|
|
66
59
|
const value = Number.parseInt(String(raw ?? "0"), 10);
|
|
67
60
|
return Number.isFinite(value) ? value : 0;
|
|
68
61
|
}
|
|
69
|
-
function defaultNativeLibName() {
|
|
70
|
-
if (process.platform === "darwin") {
|
|
71
|
-
return "libaxon_dendrite_bridge.dylib";
|
|
72
|
-
}
|
|
73
|
-
if (process.platform === "win32") {
|
|
74
|
-
return "axon_dendrite_bridge.dll";
|
|
75
|
-
}
|
|
76
|
-
return "libaxon_dendrite_bridge.so";
|
|
77
|
-
}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
export type AbilityTarget = "claude" | "codex" | "openclaw" | "agent_skills";
|
|
2
|
+
export interface AbilityDescriptorLite {
|
|
3
|
+
name: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
description: string;
|
|
6
|
+
commandTemplate: string;
|
|
7
|
+
inputSchema: JsonRecord;
|
|
8
|
+
outputSchema: JsonRecord;
|
|
9
|
+
version: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
resourceUri: string;
|
|
12
|
+
instructions: string;
|
|
13
|
+
inputExamples: JsonRecord[];
|
|
14
|
+
prerequisites: string[];
|
|
15
|
+
contextBindings: Record<string, string>;
|
|
16
|
+
category: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AbilityExportResult {
|
|
19
|
+
abilityName: string;
|
|
20
|
+
abilityMd: string;
|
|
21
|
+
invokeScript: string;
|
|
22
|
+
}
|
|
1
23
|
export interface JsonRecord {
|
|
2
24
|
[key: string]: unknown;
|
|
3
25
|
}
|
|
@@ -31,4 +53,18 @@ export declare function defaultOutputSchema(): JsonRecord;
|
|
|
31
53
|
export declare function serializeDescriptor(descriptor: AbilityPackageDescriptor): JsonRecord;
|
|
32
54
|
export declare function requireString(payload: JsonRecord, key: string): string;
|
|
33
55
|
export declare function parseDescriptor(raw: unknown): AbilityPackageDescriptor;
|
|
56
|
+
/**
|
|
57
|
+
* Build an `AbilityPackageDescriptor` from handler arguments.
|
|
58
|
+
*
|
|
59
|
+
* Normalises names, generates a package ID, encodes the command template
|
|
60
|
+
* into Base64 package bytes, and serialises agent extension properties
|
|
61
|
+
* (instructions, input_examples, prerequisites, context_bindings, category)
|
|
62
|
+
* into the metadata map under the `mcp.*` prefix.
|
|
63
|
+
*
|
|
64
|
+
* @throws {Error} If `ability_name`, `command_template`, or `signature_base64` is missing.
|
|
65
|
+
*/
|
|
34
66
|
export declare function buildDescriptor(args: JsonRecord, defaultSignature: string): AbilityPackageDescriptor;
|
|
67
|
+
export declare function createAbilityDescriptor(args: JsonRecord): AbilityDescriptorLite;
|
|
68
|
+
declare function parseAbilityTarget(raw: string): AbilityTarget;
|
|
69
|
+
export declare function exportAbilitySkill(descriptor: AbilityDescriptorLite, target?: AbilityTarget, axonEndpoint?: string): AbilityExportResult;
|
|
70
|
+
export { parseAbilityTarget };
|