@elizaos/interop 2.0.0-alpha → 2.0.0-alpha.10

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.
@@ -1,80 +0,0 @@
1
- /**
2
- * Python Plugin Bridge for elizaOS
3
- *
4
- * Loads Python plugins via subprocess IPC and adapts them
5
- * to the TypeScript Plugin interface.
6
- */
7
- import { EventEmitter } from "node:events";
8
- import type { Plugin } from "@elizaos/core";
9
- import type { IPCRequest, IPCResponse, PluginManifest } from "./types";
10
- /**
11
- * Options for loading a Python plugin
12
- */
13
- export interface PythonBridgeOptions {
14
- /** Python module name to import */
15
- moduleName: string;
16
- /** Path to Python executable (defaults to 'python3') */
17
- pythonPath?: string;
18
- /** Working directory for the subprocess */
19
- cwd?: string;
20
- /** Additional environment variables */
21
- env?: Record<string, string>;
22
- /** Path to the bridge script */
23
- bridgeScriptPath?: string;
24
- /** Connection timeout in milliseconds */
25
- timeout?: number;
26
- }
27
- /**
28
- * Python plugin bridge that communicates via subprocess
29
- */
30
- export declare class PythonPluginBridge extends EventEmitter {
31
- private options;
32
- private process;
33
- private pendingRequests;
34
- private messageBuffer;
35
- private manifest;
36
- private initialized;
37
- private requestCounter;
38
- constructor(options: PythonBridgeOptions);
39
- /**
40
- * Start the Python subprocess
41
- */
42
- start(): Promise<void>;
43
- /**
44
- * Wait for the Python process to send ready message
45
- */
46
- private waitForReady;
47
- /**
48
- * Handle incoming data from subprocess
49
- */
50
- private handleData;
51
- /**
52
- * Handle a parsed IPC message
53
- */
54
- private handleMessage;
55
- /**
56
- * Send a request and wait for response
57
- */
58
- sendRequest<T extends IPCResponse>(request: IPCRequest): Promise<T>;
59
- /**
60
- * Get the plugin manifest
61
- */
62
- getManifest(): PluginManifest | null;
63
- /**
64
- * Stop the Python subprocess
65
- */
66
- stop(): Promise<void>;
67
- /**
68
- * Cleanup resources
69
- */
70
- private cleanup;
71
- }
72
- /**
73
- * Load a Python plugin and return an elizaOS Plugin interface
74
- */
75
- export declare function loadPythonPlugin(options: PythonBridgeOptions): Promise<Plugin>;
76
- /**
77
- * Stop a Python plugin bridge
78
- */
79
- export declare function stopPythonPlugin(plugin: Plugin): Promise<void>;
80
- //# sourceMappingURL=python-bridge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"python-bridge.d.ts","sourceRoot":"","sources":["../../typescript/python-bridge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EASV,MAAM,EAKP,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,cAAc,EAGf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAWD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAStC,OAAO,CAAC,OAAO;IAR3B,OAAO,CAAC,OAAO,CACR;IACP,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAa;gBAEf,OAAO,EAAE,mBAAmB;IAIhD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkD5B;;OAEG;YACW,YAAY;IAsB1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAmBlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACG,WAAW,CAAC,CAAC,SAAS,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BzE;;OAEG;IACH,WAAW,IAAI,cAAc,GAAG,IAAI;IAIpC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B3B;;OAEG;IACH,OAAO,CAAC,OAAO;CAWhB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAUjB;AA8KD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE"}
@@ -1,334 +0,0 @@
1
- /**
2
- * Python Plugin Bridge for elizaOS
3
- *
4
- * Loads Python plugins via subprocess IPC and adapts them
5
- * to the TypeScript Plugin interface.
6
- */
7
- import { EventEmitter } from "node:events";
8
- /**
9
- * Python plugin bridge that communicates via subprocess
10
- */
11
- export class PythonPluginBridge extends EventEmitter {
12
- options;
13
- process = null;
14
- pendingRequests = new Map();
15
- messageBuffer = "";
16
- manifest = null;
17
- initialized = false;
18
- requestCounter = 0;
19
- constructor(options) {
20
- super();
21
- this.options = options;
22
- }
23
- /**
24
- * Start the Python subprocess
25
- */
26
- async start() {
27
- const { spawn } = await import("node:child_process");
28
- const pythonPath = this.options.pythonPath ?? "python3";
29
- const bridgeScript = this.options.bridgeScriptPath ??
30
- new URL("../python/bridge_server.py", import.meta.url).pathname;
31
- this.process = spawn(pythonPath, ["-u", bridgeScript, "--module", this.options.moduleName], {
32
- cwd: this.options.cwd,
33
- env: { ...process.env, ...this.options.env },
34
- stdio: ["pipe", "pipe", "pipe"],
35
- });
36
- // Handle stdout (JSON-RPC messages)
37
- if (this.process.stdout) {
38
- this.process.stdout.on("data", (data) => {
39
- this.handleData(data.toString());
40
- });
41
- }
42
- // Handle stderr (logging)
43
- if (this.process.stderr) {
44
- this.process.stderr.on("data", (data) => {
45
- console.error(`[Python Plugin ${this.options.moduleName}]`, data.toString());
46
- });
47
- }
48
- // Handle process exit
49
- this.process.on("exit", (code, signal) => {
50
- this.emit("exit", { code, signal });
51
- this.cleanup();
52
- });
53
- this.process.on("error", (error) => {
54
- this.emit("error", error);
55
- });
56
- // Wait for the ready message with manifest
57
- await this.waitForReady();
58
- this.initialized = true;
59
- }
60
- /**
61
- * Wait for the Python process to send ready message
62
- */
63
- async waitForReady() {
64
- const timeout = this.options.timeout ?? 30000;
65
- return new Promise((resolve, reject) => {
66
- const timer = setTimeout(() => {
67
- reject(new Error(`Python plugin startup timeout after ${timeout}ms`));
68
- }, timeout);
69
- const handler = (msg) => {
70
- if (msg.type === "ready" && "manifest" in msg) {
71
- clearTimeout(timer);
72
- this.manifest = msg.manifest;
73
- resolve();
74
- }
75
- };
76
- this.once("message", handler);
77
- });
78
- }
79
- /**
80
- * Handle incoming data from subprocess
81
- */
82
- handleData(data) {
83
- this.messageBuffer += data;
84
- // Process complete JSON messages (newline-delimited)
85
- const lines = this.messageBuffer.split("\n");
86
- this.messageBuffer = lines.pop() ?? "";
87
- for (const line of lines) {
88
- if (line.trim()) {
89
- try {
90
- const message = JSON.parse(line);
91
- this.handleMessage(message);
92
- }
93
- catch (error) {
94
- console.error("Failed to parse IPC message:", line, error);
95
- }
96
- }
97
- }
98
- }
99
- /**
100
- * Handle a parsed IPC message
101
- */
102
- handleMessage(message) {
103
- this.emit("message", message);
104
- // Check if this is a response to a pending request
105
- if (message.id && this.pendingRequests.has(message.id)) {
106
- const pending = this.pendingRequests.get(message.id);
107
- if (!pending)
108
- return;
109
- this.pendingRequests.delete(message.id);
110
- clearTimeout(pending.timeout);
111
- if (message.type === "error") {
112
- pending.reject(new Error(message.error));
113
- }
114
- else {
115
- pending.resolve(message);
116
- }
117
- }
118
- }
119
- /**
120
- * Send a request and wait for response
121
- */
122
- async sendRequest(request) {
123
- if (!this.process || !this.initialized) {
124
- throw new Error("Python bridge not started");
125
- }
126
- const id = `req_${++this.requestCounter}`;
127
- const requestWithId = { ...request, id };
128
- return new Promise((resolve, reject) => {
129
- const timeout = setTimeout(() => {
130
- this.pendingRequests.delete(id);
131
- reject(new Error(`Request timeout for ${request.type}`));
132
- }, this.options.timeout ?? 30000);
133
- this.pendingRequests.set(id, {
134
- resolve: resolve,
135
- reject,
136
- timeout,
137
- });
138
- const json = `${JSON.stringify(requestWithId)}\n`;
139
- if (this.process?.stdin) {
140
- this.process.stdin.write(json);
141
- }
142
- });
143
- }
144
- /**
145
- * Get the plugin manifest
146
- */
147
- getManifest() {
148
- return this.manifest;
149
- }
150
- /**
151
- * Stop the Python subprocess
152
- */
153
- async stop() {
154
- if (this.process) {
155
- this.process.kill("SIGTERM");
156
- // Wait for graceful shutdown
157
- await new Promise((resolve) => {
158
- const timeout = setTimeout(() => {
159
- if (this.process) {
160
- this.process.kill("SIGKILL");
161
- }
162
- resolve();
163
- }, 5000);
164
- if (this.process) {
165
- this.process.on("exit", () => {
166
- clearTimeout(timeout);
167
- resolve();
168
- });
169
- }
170
- else {
171
- resolve();
172
- }
173
- });
174
- }
175
- this.cleanup();
176
- }
177
- /**
178
- * Cleanup resources
179
- */
180
- cleanup() {
181
- this.process = null;
182
- this.initialized = false;
183
- // Reject all pending requests
184
- for (const pending of this.pendingRequests.values()) {
185
- clearTimeout(pending.timeout);
186
- pending.reject(new Error("Bridge closed"));
187
- }
188
- this.pendingRequests.clear();
189
- }
190
- }
191
- /**
192
- * Load a Python plugin and return an elizaOS Plugin interface
193
- */
194
- export async function loadPythonPlugin(options) {
195
- const bridge = new PythonPluginBridge(options);
196
- await bridge.start();
197
- const manifest = bridge.getManifest();
198
- if (!manifest) {
199
- throw new Error("Failed to get plugin manifest");
200
- }
201
- return createPluginFromBridge(manifest, bridge);
202
- }
203
- /**
204
- * Create a Plugin from a Python bridge
205
- */
206
- function createPluginFromBridge(manifest, bridge) {
207
- // Create action wrappers
208
- const actions = (manifest.actions ?? []).map((actionDef) => ({
209
- name: actionDef.name,
210
- description: actionDef.description,
211
- similes: actionDef.similes,
212
- examples: actionDef.examples,
213
- validate: async (_runtime, message, state) => {
214
- const response = await bridge.sendRequest({
215
- type: "action.validate",
216
- id: "",
217
- action: actionDef.name,
218
- memory: message,
219
- state: state ?? null,
220
- });
221
- return response.valid;
222
- },
223
- handler: async (_runtime, message, state, options, _callback) => {
224
- const response = await bridge.sendRequest({
225
- type: "action.invoke",
226
- id: "",
227
- action: actionDef.name,
228
- memory: message,
229
- state: state ?? null,
230
- options: options ?? null,
231
- });
232
- const result = response.result;
233
- return {
234
- success: result.success,
235
- text: result.text,
236
- error: result.error ? new Error(result.error) : undefined,
237
- data: result.data,
238
- values: result.values,
239
- };
240
- },
241
- }));
242
- // Create provider wrappers
243
- const providers = (manifest.providers ?? []).map((providerDef) => ({
244
- name: providerDef.name,
245
- description: providerDef.description,
246
- dynamic: providerDef.dynamic,
247
- position: providerDef.position,
248
- private: providerDef.private,
249
- get: async (_runtime, message, state) => {
250
- const response = await bridge.sendRequest({
251
- type: "provider.get",
252
- id: "",
253
- provider: providerDef.name,
254
- memory: message,
255
- state: state,
256
- });
257
- return {
258
- text: response.result.text,
259
- values: response.result.values,
260
- data: response.result.data,
261
- };
262
- },
263
- }));
264
- // Create evaluator wrappers
265
- const evaluators = (manifest.evaluators ?? []).map((evalDef) => ({
266
- name: evalDef.name,
267
- description: evalDef.description,
268
- alwaysRun: evalDef.alwaysRun,
269
- similes: evalDef.similes,
270
- examples: [],
271
- validate: async (_runtime, message, state) => {
272
- const response = await bridge.sendRequest({
273
- type: "action.validate",
274
- id: "",
275
- action: evalDef.name,
276
- memory: message,
277
- state: state ?? null,
278
- });
279
- return response.valid;
280
- },
281
- handler: async (_runtime, message, state) => {
282
- const response = await bridge.sendRequest({
283
- type: "evaluator.invoke",
284
- id: "",
285
- evaluator: evalDef.name,
286
- memory: message,
287
- state: state ?? null,
288
- });
289
- if (!response.result) {
290
- return undefined;
291
- }
292
- return {
293
- success: response.result.success,
294
- text: response.result.text,
295
- error: response.result.error
296
- ? new Error(response.result.error)
297
- : undefined,
298
- data: response.result.data,
299
- values: response.result.values,
300
- };
301
- },
302
- }));
303
- // Store bridge reference for cleanup
304
- const bridgeRef = { current: bridge };
305
- return {
306
- name: manifest.name,
307
- description: manifest.description,
308
- config: manifest.config ?? {},
309
- dependencies: manifest.dependencies,
310
- actions,
311
- providers,
312
- evaluators,
313
- routes: [],
314
- services: [],
315
- async init(config) {
316
- await bridge.sendRequest({
317
- type: "plugin.init",
318
- id: "",
319
- config,
320
- });
321
- },
322
- // Extension for cleanup
323
- _bridge: bridgeRef,
324
- };
325
- }
326
- /**
327
- * Stop a Python plugin bridge
328
- */
329
- export async function stopPythonPlugin(plugin) {
330
- const extended = plugin;
331
- if (extended._bridge?.current) {
332
- await extended._bridge.current.stop();
333
- }
334
- }