@todoforai/edge 0.12.14 → 0.12.15
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/dist/index.js +80 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48418,8 +48418,12 @@ class BrowserExtensionBridge {
|
|
|
48418
48418
|
pending.ws.send(JSON.stringify({ type: "browser.command.result", requestId, error: "Browser bridge stopped" }));
|
|
48419
48419
|
}
|
|
48420
48420
|
this.pending.clear();
|
|
48421
|
+
if (isOpen(this.extensionWs))
|
|
48422
|
+
this.extensionWs.terminate();
|
|
48421
48423
|
this.extensionWs = null;
|
|
48424
|
+
this.wss?.clients.forEach((ws) => ws.terminate());
|
|
48422
48425
|
this.wss?.close();
|
|
48426
|
+
this.server?.closeAllConnections();
|
|
48423
48427
|
this.server?.close();
|
|
48424
48428
|
this.wss = undefined;
|
|
48425
48429
|
this.server = undefined;
|
|
@@ -48608,7 +48612,7 @@ import path4 from "path";
|
|
|
48608
48612
|
import fs3 from "fs";
|
|
48609
48613
|
import os3 from "os";
|
|
48610
48614
|
import path3 from "path";
|
|
48611
|
-
import { execSync, spawnSync } from "child_process";
|
|
48615
|
+
import { execSync, spawnSync, execFile } from "child_process";
|
|
48612
48616
|
|
|
48613
48617
|
// src/tool-catalog.ts
|
|
48614
48618
|
import os2 from "os";
|
|
@@ -49344,27 +49348,39 @@ function uninstallTool(name) {
|
|
|
49344
49348
|
return false;
|
|
49345
49349
|
}
|
|
49346
49350
|
}
|
|
49347
|
-
function
|
|
49351
|
+
function execShellAsync(cmd, env, timeout) {
|
|
49352
|
+
return new Promise((resolve) => {
|
|
49353
|
+
execFile("sh", ["-c", cmd], { env, timeout, encoding: "utf-8", maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
49354
|
+
resolve({ status: err ? 1 : 0, stdout: (stdout || "").toString(), stderr: (stderr || "").toString() });
|
|
49355
|
+
});
|
|
49356
|
+
});
|
|
49357
|
+
}
|
|
49358
|
+
async function scanCatalogTools() {
|
|
49348
49359
|
const result = {};
|
|
49349
49360
|
const env = buildEnvWithTools();
|
|
49350
|
-
|
|
49361
|
+
const entries = Object.entries(TOOL_CATALOG);
|
|
49362
|
+
const installed = [];
|
|
49363
|
+
for (const [name, entry] of entries) {
|
|
49351
49364
|
if (!isToolInstalled(name)) {
|
|
49352
49365
|
result[name] = { installed: false };
|
|
49353
|
-
|
|
49366
|
+
} else {
|
|
49367
|
+
installed.push([name, entry]);
|
|
49354
49368
|
}
|
|
49369
|
+
}
|
|
49370
|
+
await Promise.all(installed.map(async ([name, entry]) => {
|
|
49355
49371
|
const state = { installed: true };
|
|
49356
49372
|
if (entry.versionCmd) {
|
|
49357
49373
|
try {
|
|
49358
|
-
const r =
|
|
49374
|
+
const r = await execShellAsync(entry.versionCmd, env, 5000);
|
|
49359
49375
|
if (r.status === 0)
|
|
49360
|
-
state.version =
|
|
49376
|
+
state.version = r.stdout.trim().slice(0, 100);
|
|
49361
49377
|
} catch {}
|
|
49362
49378
|
}
|
|
49363
49379
|
if (entry.statusCmd) {
|
|
49364
49380
|
try {
|
|
49365
|
-
const r =
|
|
49381
|
+
const r = await execShellAsync(entry.statusCmd, env, 1e4);
|
|
49366
49382
|
state.authenticated = r.status === 0;
|
|
49367
|
-
state.statusOutput = (r.stdout || r.stderr
|
|
49383
|
+
state.statusOutput = (r.stdout || r.stderr).trim().slice(0, 200);
|
|
49368
49384
|
} catch {
|
|
49369
49385
|
state.authenticated = false;
|
|
49370
49386
|
}
|
|
@@ -49372,7 +49388,7 @@ function scanCatalogTools() {
|
|
|
49372
49388
|
state.authenticated = true;
|
|
49373
49389
|
}
|
|
49374
49390
|
result[name] = state;
|
|
49375
|
-
}
|
|
49391
|
+
}));
|
|
49376
49392
|
return result;
|
|
49377
49393
|
}
|
|
49378
49394
|
var MOUNT_FLAGS = [
|
|
@@ -51272,6 +51288,9 @@ function waitForCompletion(blockId, timeoutMs) {
|
|
|
51272
51288
|
function getBlockOutput(blockId) {
|
|
51273
51289
|
return outputBuffers.get(blockId)?.getOutput() ?? "";
|
|
51274
51290
|
}
|
|
51291
|
+
function getBlockRawOutput(blockId) {
|
|
51292
|
+
return outputBuffers.get(blockId)?.getRawIfComplete() ?? null;
|
|
51293
|
+
}
|
|
51275
51294
|
function clearBlockOutput(blockId) {
|
|
51276
51295
|
outputBuffers.delete(blockId);
|
|
51277
51296
|
}
|
|
@@ -51327,7 +51346,7 @@ register("install_tool", async (args) => {
|
|
|
51327
51346
|
if (!name || !(name in TOOL_CATALOG)) {
|
|
51328
51347
|
return { success: false, error: `Unknown tool: ${name}` };
|
|
51329
51348
|
}
|
|
51330
|
-
const scan = scanCatalogTools();
|
|
51349
|
+
const scan = await scanCatalogTools();
|
|
51331
51350
|
if (scan[name]?.installed) {
|
|
51332
51351
|
return { success: true, alreadyInstalled: true, tool: name };
|
|
51333
51352
|
}
|
|
@@ -51337,7 +51356,7 @@ register("install_tool", async (args) => {
|
|
|
51337
51356
|
}
|
|
51338
51357
|
const edge = getGlobalEdgeInstance();
|
|
51339
51358
|
if (edge) {
|
|
51340
|
-
await edge.updateConfig({ installedTools: scanCatalogTools() });
|
|
51359
|
+
await edge.updateConfig({ installedTools: await scanCatalogTools() });
|
|
51341
51360
|
}
|
|
51342
51361
|
return { success: true, tool: name, label: TOOL_CATALOG[name].label };
|
|
51343
51362
|
});
|
|
@@ -51350,7 +51369,7 @@ register("uninstall_tool", async (args) => {
|
|
|
51350
51369
|
if (success) {
|
|
51351
51370
|
const edge = getGlobalEdgeInstance();
|
|
51352
51371
|
if (edge)
|
|
51353
|
-
await edge.updateConfig({ installedTools: scanCatalogTools() });
|
|
51372
|
+
await edge.updateConfig({ installedTools: await scanCatalogTools() });
|
|
51354
51373
|
}
|
|
51355
51374
|
return { success, tool: name };
|
|
51356
51375
|
});
|
|
@@ -51486,6 +51505,19 @@ function extractTrailingTail(cmd) {
|
|
|
51486
51505
|
`).slice(-n).join(`
|
|
51487
51506
|
`) };
|
|
51488
51507
|
}
|
|
51508
|
+
var DATA_URL_IMAGE_REGEX = /^data:(image\/[^;]+);base64,[A-Za-z0-9+/]+=*$/;
|
|
51509
|
+
function detectContentType(output, cmd) {
|
|
51510
|
+
const trimmed = output.trim();
|
|
51511
|
+
const match = trimmed.match(DATA_URL_IMAGE_REGEX);
|
|
51512
|
+
if (match) {
|
|
51513
|
+
console.log(`
|
|
51514
|
+
\uD83D\uDDBC️ [edge] Image output detected! type=${match[1]} size=${trimmed.length} chars${cmd ? `
|
|
51515
|
+
cmd: ${cmd}` : ""}
|
|
51516
|
+
`);
|
|
51517
|
+
return { result: trimmed, contentType: match[1] };
|
|
51518
|
+
}
|
|
51519
|
+
return { result: output };
|
|
51520
|
+
}
|
|
51489
51521
|
register("execute_shell_command", async (args, client) => {
|
|
51490
51522
|
const { cmd, timeout = 120, root_path = "", todoId = "", messageId = "", blockId = "" } = args;
|
|
51491
51523
|
const canStream = !!(todoId && blockId && client);
|
|
@@ -51496,7 +51528,7 @@ register("execute_shell_command", async (args, client) => {
|
|
|
51496
51528
|
resolve((stdout || "") + (stderr || ""));
|
|
51497
51529
|
});
|
|
51498
51530
|
});
|
|
51499
|
-
return { cmd, result };
|
|
51531
|
+
return { cmd, ...detectContentType(result, cmd) };
|
|
51500
51532
|
}
|
|
51501
51533
|
const { execCmd, postFilter } = extractTrailingTail(cmd);
|
|
51502
51534
|
try {
|
|
@@ -51507,11 +51539,12 @@ register("execute_shell_command", async (args, client) => {
|
|
|
51507
51539
|
return { __awaiting_approval__: true };
|
|
51508
51540
|
}
|
|
51509
51541
|
await waitForCompletion(blockId, (timeout + 5) * 1000);
|
|
51510
|
-
|
|
51542
|
+
const rawOutput = getBlockRawOutput(blockId);
|
|
51543
|
+
let output = rawOutput ?? getBlockOutput(blockId);
|
|
51511
51544
|
clearBlockOutput(blockId);
|
|
51512
51545
|
if (postFilter)
|
|
51513
51546
|
output = postFilter(output);
|
|
51514
|
-
return { cmd, result: output };
|
|
51547
|
+
return rawOutput !== null ? { cmd, ...detectContentType(output, cmd) } : { cmd, result: output };
|
|
51515
51548
|
} catch (e) {
|
|
51516
51549
|
throw e;
|
|
51517
51550
|
} finally {
|
|
@@ -51939,6 +51972,8 @@ class TODOforAIEdge {
|
|
|
51939
51972
|
addWorkspacePath;
|
|
51940
51973
|
frontendWs = null;
|
|
51941
51974
|
browserExtensionBridge;
|
|
51975
|
+
stopping = false;
|
|
51976
|
+
reconnectTimer;
|
|
51942
51977
|
edgeConfig = {
|
|
51943
51978
|
id: "",
|
|
51944
51979
|
name: "Name uninitialized",
|
|
@@ -52117,7 +52152,7 @@ class TODOforAIEdge {
|
|
|
52117
52152
|
id2 += String.fromCharCode(frame[i10]);
|
|
52118
52153
|
const data = frame.slice(36);
|
|
52119
52154
|
this.pendingBinaries.set(id2, data);
|
|
52120
|
-
setTimeout(() => this.pendingBinaries.delete(id2), 60000);
|
|
52155
|
+
setTimeout(() => this.pendingBinaries.delete(id2), 60000).unref();
|
|
52121
52156
|
}
|
|
52122
52157
|
async handleMessage(raw) {
|
|
52123
52158
|
let data;
|
|
@@ -52149,7 +52184,7 @@ class TODOforAIEdge {
|
|
|
52149
52184
|
this.edgeConfig.id = this.edgeId;
|
|
52150
52185
|
console.log(`\x1B[32m\x1B[1m\uD83D\uDD17 Connected edge=${this.edgeId} user=${this.userId}\x1B[0m`);
|
|
52151
52186
|
run(async () => {
|
|
52152
|
-
this.updateConfig({ installedTools: scanCatalogTools() });
|
|
52187
|
+
this.updateConfig({ installedTools: await scanCatalogTools() });
|
|
52153
52188
|
autoMountRcloneRemotes();
|
|
52154
52189
|
});
|
|
52155
52190
|
break;
|
|
@@ -52289,10 +52324,12 @@ class TODOforAIEdge {
|
|
|
52289
52324
|
console.log(`\x1B[36m\x1B[1m\uD83D\uDC46 Fingerprint:\x1B[0m ${this.fingerprint}`);
|
|
52290
52325
|
const maxAttempts = 20;
|
|
52291
52326
|
let attempt = 0;
|
|
52292
|
-
while (attempt < maxAttempts) {
|
|
52327
|
+
while (attempt < maxAttempts && !this.stopping) {
|
|
52293
52328
|
console.log(`[info] Connecting (attempt ${attempt + 1}/${maxAttempts})`);
|
|
52294
52329
|
try {
|
|
52295
52330
|
await this.connect();
|
|
52331
|
+
if (this.stopping)
|
|
52332
|
+
break;
|
|
52296
52333
|
attempt = 0;
|
|
52297
52334
|
} catch (e) {
|
|
52298
52335
|
if (e instanceof AuthenticationError) {
|
|
@@ -52309,16 +52346,26 @@ class TODOforAIEdge {
|
|
|
52309
52346
|
this.connected = false;
|
|
52310
52347
|
this.ws = null;
|
|
52311
52348
|
}
|
|
52312
|
-
if (attempt > 0 && attempt < maxAttempts) {
|
|
52349
|
+
if (attempt > 0 && attempt < maxAttempts && !this.stopping) {
|
|
52313
52350
|
const delay = Math.min(4 + attempt, 20);
|
|
52314
52351
|
console.log(`[info] Reconnecting in ${delay}s...`);
|
|
52315
|
-
await new Promise((r) =>
|
|
52352
|
+
await new Promise((r) => {
|
|
52353
|
+
this.reconnectTimer = setTimeout(r, delay * 1000);
|
|
52354
|
+
});
|
|
52316
52355
|
}
|
|
52317
52356
|
}
|
|
52318
52357
|
if (attempt >= maxAttempts) {
|
|
52319
52358
|
console.error("\x1B[31mMax reconnection attempts reached.\x1B[0m");
|
|
52320
52359
|
}
|
|
52321
52360
|
}
|
|
52361
|
+
stop() {
|
|
52362
|
+
this.stopping = true;
|
|
52363
|
+
clearTimeout(this.reconnectTimer);
|
|
52364
|
+
this.stopHeartbeat();
|
|
52365
|
+
this.browserExtensionBridge.stop();
|
|
52366
|
+
this.frontendWs?.close();
|
|
52367
|
+
this.ws?.terminate();
|
|
52368
|
+
}
|
|
52322
52369
|
async getFrontendWs() {
|
|
52323
52370
|
if (!this.frontendWs || !this.frontendWs.connected) {
|
|
52324
52371
|
this.frontendWs = new FrontendWebSocket(this.api.apiUrl, this.api.apiKey);
|
|
@@ -52429,13 +52476,24 @@ async function main() {
|
|
|
52429
52476
|
console.error("\x1B[31mAnother edge is already running for this user+server. Use --kill to replace it.\x1B[0m");
|
|
52430
52477
|
process.exit(1);
|
|
52431
52478
|
}
|
|
52479
|
+
let cleaned = false;
|
|
52432
52480
|
const cleanup = () => {
|
|
52481
|
+
if (cleaned)
|
|
52482
|
+
return;
|
|
52483
|
+
cleaned = true;
|
|
52484
|
+
edge.stop();
|
|
52433
52485
|
unmountAllRclone();
|
|
52434
52486
|
releaseLock(lp2);
|
|
52435
52487
|
};
|
|
52436
52488
|
process.on("exit", cleanup);
|
|
52437
|
-
process.on("SIGINT", () =>
|
|
52438
|
-
|
|
52489
|
+
process.on("SIGINT", () => {
|
|
52490
|
+
cleanup();
|
|
52491
|
+
process.exit(0);
|
|
52492
|
+
});
|
|
52493
|
+
process.on("SIGTERM", () => {
|
|
52494
|
+
cleanup();
|
|
52495
|
+
process.exit(0);
|
|
52496
|
+
});
|
|
52439
52497
|
await edge.start();
|
|
52440
52498
|
}
|
|
52441
52499
|
main().catch((e) => {
|