@elizaos/interop 2.0.0-alpha
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/LICENSE +21 -0
- package/README.md +436 -0
- package/dist/packages/interop/tsconfig.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/typescript/index.d.ts +33 -0
- package/dist/typescript/index.d.ts.map +1 -0
- package/dist/typescript/index.js +121 -0
- package/dist/typescript/python-bridge.d.ts +80 -0
- package/dist/typescript/python-bridge.d.ts.map +1 -0
- package/dist/typescript/python-bridge.js +334 -0
- package/dist/typescript/types.d.ts +301 -0
- package/dist/typescript/types.d.ts.map +1 -0
- package/dist/typescript/types.js +10 -0
- package/dist/typescript/wasm-loader.d.ts +32 -0
- package/dist/typescript/wasm-loader.d.ts.map +1 -0
- package/dist/typescript/wasm-loader.js +269 -0
- package/package.json +43 -0
- package/python/__init__.py +50 -0
- package/python/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/__pycache__/rust_ffi.cpython-313.pyc +0 -0
- package/python/__pycache__/ts_bridge.cpython-313.pyc +0 -0
- package/python/__pycache__/wasm_loader.cpython-313.pyc +0 -0
- package/python/bridge_server.py +505 -0
- package/python/rust_ffi.py +418 -0
- package/python/tests/__init__.py +1 -0
- package/python/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/tests/__pycache__/test_bridge_server.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/__pycache__/test_interop.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/__pycache__/test_rust_ffi.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/__pycache__/test_rust_ffi_loader.cpython-313-pytest-9.0.2.pyc +0 -0
- package/python/tests/test_bridge_server.py +525 -0
- package/python/tests/test_interop.py +319 -0
- package/python/tests/test_rust_ffi.py +352 -0
- package/python/tests/test_rust_ffi_loader.py +71 -0
- package/python/ts_bridge.py +452 -0
- package/python/ts_bridge_runner.mjs +284 -0
- package/python/wasm_loader.py +517 -0
- package/rust/ffi_exports.rs +362 -0
- package/rust/mod.rs +21 -0
- package/rust/py_loader.rs +412 -0
- package/rust/ts_loader.rs +467 -0
- package/rust/wasm_plugin.rs +320 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TypeScript Plugin Bridge Runner for Python
|
|
5
|
+
*
|
|
6
|
+
* Loads a TypeScript/JavaScript plugin and communicates with Python via
|
|
7
|
+
* newline-delimited JSON messages over stdin/stdout.
|
|
8
|
+
*
|
|
9
|
+
* This file is intentionally committed (not generated at runtime) to avoid
|
|
10
|
+
* “performative” behavior and to make audits/reviews reproducible.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
import { createInterface } from "node:readline";
|
|
16
|
+
|
|
17
|
+
const pluginPath = process.argv[2];
|
|
18
|
+
if (!pluginPath) {
|
|
19
|
+
process.stderr.write("Usage: ts_bridge_runner.mjs <plugin_path>\n");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const INCLUDE_DETAILS =
|
|
24
|
+
["1", "true", "yes", "on"].includes(
|
|
25
|
+
String(process.env.ELIZA_INTEROP_DEBUG ?? process.env.LOG_DIAGNOSTIC ?? "")
|
|
26
|
+
.trim()
|
|
27
|
+
.toLowerCase(),
|
|
28
|
+
) || false;
|
|
29
|
+
|
|
30
|
+
const MAX_MESSAGE_BYTES = Number.parseInt(
|
|
31
|
+
String(process.env.ELIZA_INTEROP_MAX_MESSAGE_BYTES ?? "1000000"),
|
|
32
|
+
10,
|
|
33
|
+
);
|
|
34
|
+
const MAX_BUFFER_BYTES = Number.parseInt(
|
|
35
|
+
String(process.env.ELIZA_INTEROP_MAX_BUFFER_BYTES ?? "2000000"),
|
|
36
|
+
10,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/** @param {string} s */
|
|
40
|
+
function byteLen(s) {
|
|
41
|
+
return Buffer.byteLength(s, "utf8");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** @param {unknown} error */
|
|
45
|
+
function formatError(error) {
|
|
46
|
+
if (error instanceof Error) {
|
|
47
|
+
return {
|
|
48
|
+
message: error.message,
|
|
49
|
+
stack: INCLUDE_DETAILS ? error.stack : undefined,
|
|
50
|
+
name: INCLUDE_DETAILS ? error.name : undefined,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { message: String(error) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function loadPlugin(absPath) {
|
|
57
|
+
// Prefer ESM import, fall back to CJS require.
|
|
58
|
+
try {
|
|
59
|
+
const mod = await import(absPath);
|
|
60
|
+
return mod.default ?? mod.plugin ?? mod;
|
|
61
|
+
} catch (_e) {
|
|
62
|
+
const require = createRequire(import.meta.url);
|
|
63
|
+
const mod = require(absPath);
|
|
64
|
+
return mod.default ?? mod.plugin ?? mod;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function indexByName(items) {
|
|
69
|
+
/** @type {Record<string, any>} */
|
|
70
|
+
const out = {};
|
|
71
|
+
for (const item of items ?? []) {
|
|
72
|
+
if (item && typeof item.name === "string") out[item.name] = item;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function buildManifest(plugin, actions, providers, evaluators) {
|
|
78
|
+
return {
|
|
79
|
+
name: plugin?.name ?? "unknown",
|
|
80
|
+
description: plugin?.description ?? "",
|
|
81
|
+
version: plugin?.version ?? "1.0.0",
|
|
82
|
+
language: "typescript",
|
|
83
|
+
config: plugin?.config,
|
|
84
|
+
dependencies: plugin?.dependencies,
|
|
85
|
+
actions: Object.values(actions).map((a) => ({
|
|
86
|
+
name: a.name,
|
|
87
|
+
description: a.description,
|
|
88
|
+
similes: a.similes,
|
|
89
|
+
})),
|
|
90
|
+
providers: Object.values(providers).map((p) => ({
|
|
91
|
+
name: p.name,
|
|
92
|
+
description: p.description,
|
|
93
|
+
dynamic: p.dynamic,
|
|
94
|
+
position: p.position,
|
|
95
|
+
private: p.private,
|
|
96
|
+
})),
|
|
97
|
+
evaluators: Object.values(evaluators).map((e) => ({
|
|
98
|
+
name: e.name,
|
|
99
|
+
description: e.description,
|
|
100
|
+
alwaysRun: e.alwaysRun,
|
|
101
|
+
similes: e.similes,
|
|
102
|
+
})),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {any} request
|
|
108
|
+
* @param {any} plugin
|
|
109
|
+
* @param {Record<string, any>} actions
|
|
110
|
+
* @param {Record<string, any>} providers
|
|
111
|
+
* @param {Record<string, any>} evaluators
|
|
112
|
+
*/
|
|
113
|
+
async function handleRequest(request, plugin, actions, providers, evaluators) {
|
|
114
|
+
const type = request?.type;
|
|
115
|
+
const id = request?.id ?? "";
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
switch (type) {
|
|
119
|
+
case "plugin.init": {
|
|
120
|
+
if (typeof plugin?.init === "function") {
|
|
121
|
+
await plugin.init(request.config ?? {}, null);
|
|
122
|
+
}
|
|
123
|
+
return { type: "plugin.init.result", id, success: true };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case "action.validate": {
|
|
127
|
+
const action = actions[request.action];
|
|
128
|
+
if (!action || typeof action.validate !== "function") {
|
|
129
|
+
return { type: "validate.result", id, valid: false };
|
|
130
|
+
}
|
|
131
|
+
const valid = await action.validate(
|
|
132
|
+
null,
|
|
133
|
+
request.memory,
|
|
134
|
+
request.state,
|
|
135
|
+
);
|
|
136
|
+
return { type: "validate.result", id, valid: Boolean(valid) };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case "action.invoke": {
|
|
140
|
+
const action = actions[request.action];
|
|
141
|
+
if (!action || typeof action.handler !== "function") {
|
|
142
|
+
return {
|
|
143
|
+
type: "action.result",
|
|
144
|
+
id,
|
|
145
|
+
result: {
|
|
146
|
+
success: false,
|
|
147
|
+
error: `Action not found: ${request.action}`,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const result = await action.handler(
|
|
152
|
+
null,
|
|
153
|
+
request.memory,
|
|
154
|
+
request.state,
|
|
155
|
+
request.options ?? null,
|
|
156
|
+
null,
|
|
157
|
+
null,
|
|
158
|
+
);
|
|
159
|
+
return {
|
|
160
|
+
type: "action.result",
|
|
161
|
+
id,
|
|
162
|
+
result: {
|
|
163
|
+
success: Boolean(result?.success ?? true),
|
|
164
|
+
text: result?.text ?? null,
|
|
165
|
+
error: result?.error?.message ?? result?.error ?? null,
|
|
166
|
+
data: result?.data ?? null,
|
|
167
|
+
values: result?.values ?? null,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
case "provider.get": {
|
|
173
|
+
const provider = providers[request.provider];
|
|
174
|
+
if (!provider || typeof provider.get !== "function") {
|
|
175
|
+
return {
|
|
176
|
+
type: "provider.result",
|
|
177
|
+
id,
|
|
178
|
+
result: { text: null, values: null, data: null },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const result = await provider.get(null, request.memory, request.state);
|
|
182
|
+
return {
|
|
183
|
+
type: "provider.result",
|
|
184
|
+
id,
|
|
185
|
+
result: result ?? { text: null, values: null, data: null },
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case "evaluator.invoke": {
|
|
190
|
+
const evaluator = evaluators[request.evaluator];
|
|
191
|
+
if (!evaluator || typeof evaluator.handler !== "function") {
|
|
192
|
+
return { type: "action.result", id, result: null };
|
|
193
|
+
}
|
|
194
|
+
const result = await evaluator.handler(
|
|
195
|
+
null,
|
|
196
|
+
request.memory,
|
|
197
|
+
request.state,
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
type: "action.result",
|
|
201
|
+
id,
|
|
202
|
+
result: result
|
|
203
|
+
? {
|
|
204
|
+
success: Boolean(result.success ?? true),
|
|
205
|
+
text: result.text ?? null,
|
|
206
|
+
error: result.error?.message ?? result.error ?? null,
|
|
207
|
+
data: result.data ?? null,
|
|
208
|
+
values: result.values ?? null,
|
|
209
|
+
}
|
|
210
|
+
: null,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
default:
|
|
215
|
+
return {
|
|
216
|
+
type: "error",
|
|
217
|
+
id,
|
|
218
|
+
error: `Unknown request type: ${String(type)}`,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
return {
|
|
223
|
+
type: "error",
|
|
224
|
+
id,
|
|
225
|
+
error: formatError(e).message,
|
|
226
|
+
details: INCLUDE_DETAILS ? formatError(e) : undefined,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
(async () => {
|
|
232
|
+
const absPath = resolve(pluginPath);
|
|
233
|
+
const plugin = await loadPlugin(absPath);
|
|
234
|
+
|
|
235
|
+
const actions = indexByName(plugin?.actions);
|
|
236
|
+
const providers = indexByName(plugin?.providers);
|
|
237
|
+
const evaluators = indexByName(plugin?.evaluators);
|
|
238
|
+
|
|
239
|
+
const manifest = buildManifest(plugin, actions, providers, evaluators);
|
|
240
|
+
|
|
241
|
+
process.stdout.write(`${JSON.stringify({ type: "ready", manifest })}\n`);
|
|
242
|
+
|
|
243
|
+
const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
244
|
+
|
|
245
|
+
let bufferedBytes = 0;
|
|
246
|
+
rl.on("line", async (line) => {
|
|
247
|
+
const trimmed = line.trim();
|
|
248
|
+
if (!trimmed) return;
|
|
249
|
+
|
|
250
|
+
const len = byteLen(trimmed);
|
|
251
|
+
if (len > MAX_MESSAGE_BYTES) {
|
|
252
|
+
process.stderr.write(`IPC message too large (${len} bytes)\n`);
|
|
253
|
+
process.exit(2);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
bufferedBytes += len;
|
|
257
|
+
if (bufferedBytes > MAX_BUFFER_BYTES) {
|
|
258
|
+
process.stderr.write("IPC buffer exceeded limit\n");
|
|
259
|
+
process.exit(2);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** @type {any} */
|
|
263
|
+
let request;
|
|
264
|
+
try {
|
|
265
|
+
request = JSON.parse(trimmed);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
// Protocol violation: fail closed.
|
|
268
|
+
process.stderr.write(`Invalid JSON from stdin: ${String(e)}\n`);
|
|
269
|
+
process.exit(2);
|
|
270
|
+
return;
|
|
271
|
+
} finally {
|
|
272
|
+
bufferedBytes -= len;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const response = await handleRequest(
|
|
276
|
+
request,
|
|
277
|
+
plugin,
|
|
278
|
+
actions,
|
|
279
|
+
providers,
|
|
280
|
+
evaluators,
|
|
281
|
+
);
|
|
282
|
+
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
283
|
+
});
|
|
284
|
+
})();
|