@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.
- package/README.md +2 -2
- package/dist/packages/interop/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/python/tests/test_rust_ffi_loader.py +2 -2
- package/LICENSE +0 -21
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/typescript/index.d.ts +0 -33
- package/dist/typescript/index.d.ts.map +0 -1
- package/dist/typescript/index.js +0 -121
- package/dist/typescript/python-bridge.d.ts +0 -80
- package/dist/typescript/python-bridge.d.ts.map +0 -1
- package/dist/typescript/python-bridge.js +0 -334
- package/dist/typescript/types.d.ts +0 -301
- package/dist/typescript/types.d.ts.map +0 -1
- package/dist/typescript/types.js +0 -10
- package/dist/typescript/wasm-loader.d.ts +0 -32
- package/dist/typescript/wasm-loader.d.ts.map +0 -1
- package/dist/typescript/wasm-loader.js +0 -269
- 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/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
|
@@ -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
|
-
}
|