@rajat-rastogi/maestro 0.5.6 → 0.7.0
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/app/out/main/main.js
CHANGED
|
@@ -503,6 +503,15 @@ function registerSessionsIpc(sessionManager2) {
|
|
|
503
503
|
electron.ipcMain.handle("sessions:resize", async (_e, id, cols, rows) => {
|
|
504
504
|
sessionManager2.resize(id, cols, rows);
|
|
505
505
|
});
|
|
506
|
+
electron.ipcMain.handle("sessions:savedList", async () => {
|
|
507
|
+
return sessionManager2.listSaved();
|
|
508
|
+
});
|
|
509
|
+
electron.ipcMain.handle("sessions:savedResume", async (_e, ids, resumeAI) => {
|
|
510
|
+
return sessionManager2.resumeSaved(ids, resumeAI);
|
|
511
|
+
});
|
|
512
|
+
electron.ipcMain.handle("sessions:savedDismiss", async () => {
|
|
513
|
+
sessionManager2.dismissSaved();
|
|
514
|
+
});
|
|
506
515
|
}
|
|
507
516
|
const execAsync$2 = util.promisify(child_process.exec);
|
|
508
517
|
class AgentConnectionPool {
|
|
@@ -580,6 +589,29 @@ class AgentConnectionPool {
|
|
|
580
589
|
entry.ready = true;
|
|
581
590
|
entry.queue.forEach((cb) => cb(ws));
|
|
582
591
|
entry.queue = [];
|
|
592
|
+
entry.heartbeat = setInterval(() => {
|
|
593
|
+
if (ws.readyState !== WebSocket.OPEN) return;
|
|
594
|
+
let gotPong = false;
|
|
595
|
+
const handler = (data) => {
|
|
596
|
+
try {
|
|
597
|
+
const msg = JSON.parse(data.toString());
|
|
598
|
+
if (msg.type === "pong") {
|
|
599
|
+
gotPong = true;
|
|
600
|
+
ws.removeListener("message", handler);
|
|
601
|
+
}
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
ws.on("message", handler);
|
|
606
|
+
ws.send(JSON.stringify({ type: "ping" }));
|
|
607
|
+
setTimeout(() => {
|
|
608
|
+
ws.removeListener("message", handler);
|
|
609
|
+
if (!gotPong && ws.readyState === WebSocket.OPEN) {
|
|
610
|
+
console.log(`[AgentPool] heartbeat failed for "${tunnelId}" — zombie connection, closing`);
|
|
611
|
+
ws.terminate();
|
|
612
|
+
}
|
|
613
|
+
}, 1e4);
|
|
614
|
+
}, 6e4);
|
|
583
615
|
resolve(ws);
|
|
584
616
|
});
|
|
585
617
|
ws.on("error", (err) => {
|
|
@@ -685,6 +717,7 @@ class AgentConnectionPool {
|
|
|
685
717
|
teardown(tunnelId) {
|
|
686
718
|
const entry = this.pool.get(tunnelId);
|
|
687
719
|
if (entry) {
|
|
720
|
+
if (entry.heartbeat) clearInterval(entry.heartbeat);
|
|
688
721
|
try {
|
|
689
722
|
entry.ws?.close();
|
|
690
723
|
} catch {
|
|
@@ -692,6 +725,9 @@ class AgentConnectionPool {
|
|
|
692
725
|
this.pool.delete(tunnelId);
|
|
693
726
|
}
|
|
694
727
|
}
|
|
728
|
+
releaseOne(tunnelId) {
|
|
729
|
+
this.teardown(tunnelId);
|
|
730
|
+
}
|
|
695
731
|
releaseAll() {
|
|
696
732
|
for (const id of Array.from(this.pool.keys())) {
|
|
697
733
|
this.teardown(id);
|
|
@@ -1157,11 +1193,20 @@ class LocalPtyAdapter {
|
|
|
1157
1193
|
} catch {
|
|
1158
1194
|
}
|
|
1159
1195
|
}
|
|
1160
|
-
autoLaunch(provider) {
|
|
1196
|
+
autoLaunch(provider, skipPermissions, resumeAI) {
|
|
1161
1197
|
if (provider === "claude") {
|
|
1162
|
-
|
|
1198
|
+
const flags = [
|
|
1199
|
+
resumeAI ? "--continue" : "",
|
|
1200
|
+
skipPermissions ? "--dangerously-skip-permissions" : ""
|
|
1201
|
+
].filter(Boolean).join(" ");
|
|
1202
|
+
setTimeout(() => this.write(`claude${flags ? " " + flags : ""}\r`), 800);
|
|
1163
1203
|
} else if (provider === "copilot") {
|
|
1164
|
-
|
|
1204
|
+
const flags = [
|
|
1205
|
+
"--output-format json",
|
|
1206
|
+
resumeAI ? "--continue" : "",
|
|
1207
|
+
skipPermissions ? "--yolo" : ""
|
|
1208
|
+
].filter(Boolean).join(" ");
|
|
1209
|
+
setTimeout(() => this.write(`copilot ${flags}\r`), 800);
|
|
1165
1210
|
}
|
|
1166
1211
|
}
|
|
1167
1212
|
}
|
|
@@ -1356,11 +1401,20 @@ class SshPtyAdapter {
|
|
|
1356
1401
|
this.sshClient = null;
|
|
1357
1402
|
this.devtunnelProc = null;
|
|
1358
1403
|
}
|
|
1359
|
-
autoLaunch(provider) {
|
|
1404
|
+
autoLaunch(provider, skipPermissions, resumeAI) {
|
|
1360
1405
|
if (provider === "claude") {
|
|
1361
|
-
|
|
1406
|
+
const flags = [
|
|
1407
|
+
resumeAI ? "--continue" : "",
|
|
1408
|
+
skipPermissions ? "--dangerously-skip-permissions" : ""
|
|
1409
|
+
].filter(Boolean).join(" ");
|
|
1410
|
+
setTimeout(() => this.write(`claude${flags ? " " + flags : ""}\r`), 1200);
|
|
1362
1411
|
} else if (provider === "copilot") {
|
|
1363
|
-
|
|
1412
|
+
const flags = [
|
|
1413
|
+
"--output-format json",
|
|
1414
|
+
resumeAI ? "--continue" : "",
|
|
1415
|
+
skipPermissions ? "--yolo" : ""
|
|
1416
|
+
].filter(Boolean).join(" ");
|
|
1417
|
+
setTimeout(() => this.write(`copilot ${flags}\r`), 1200);
|
|
1364
1418
|
}
|
|
1365
1419
|
}
|
|
1366
1420
|
}
|
|
@@ -1373,7 +1427,11 @@ class AgentPtyAdapter {
|
|
|
1373
1427
|
dataCallback = null;
|
|
1374
1428
|
exitCallback = null;
|
|
1375
1429
|
messageHandler = null;
|
|
1430
|
+
dead = false;
|
|
1376
1431
|
async connect(cwd, cols, rows) {
|
|
1432
|
+
this.lastCwd = cwd;
|
|
1433
|
+
this.lastCols = cols;
|
|
1434
|
+
this.lastRows = rows;
|
|
1377
1435
|
this.ws = await AgentConnectionPool.getInstance().getConnection(this.tunnelId);
|
|
1378
1436
|
this.messageHandler = (raw) => {
|
|
1379
1437
|
try {
|
|
@@ -1389,13 +1447,8 @@ class AgentPtyAdapter {
|
|
|
1389
1447
|
};
|
|
1390
1448
|
this.ws.on("message", this.messageHandler);
|
|
1391
1449
|
this.ws.on("close", () => {
|
|
1392
|
-
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} WebSocket closed
|
|
1393
|
-
|
|
1394
|
-
this.dataCallback("\r\n\x1B[1;31m[Maestro] Connection to remote agent lost. Close and recreate the terminal.\x1B[0m\r\n");
|
|
1395
|
-
}
|
|
1396
|
-
if (this.exitCallback) {
|
|
1397
|
-
this.exitCallback(null);
|
|
1398
|
-
}
|
|
1450
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} WebSocket closed`);
|
|
1451
|
+
this.handleConnectionLost();
|
|
1399
1452
|
});
|
|
1400
1453
|
this.ws.on("error", (err) => {
|
|
1401
1454
|
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} WebSocket error: ${err.message}`);
|
|
@@ -1417,6 +1470,61 @@ class AgentPtyAdapter {
|
|
|
1417
1470
|
this.ws.on("message", ackHandler);
|
|
1418
1471
|
});
|
|
1419
1472
|
}
|
|
1473
|
+
reconnectAttempts = 0;
|
|
1474
|
+
lastCwd = "";
|
|
1475
|
+
lastCols = 120;
|
|
1476
|
+
lastRows = 30;
|
|
1477
|
+
async handleConnectionLost() {
|
|
1478
|
+
if (this.dead) return;
|
|
1479
|
+
if (this.reconnectAttempts < 3) {
|
|
1480
|
+
this.reconnectAttempts++;
|
|
1481
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} reconnecting (attempt ${this.reconnectAttempts}/3)...`);
|
|
1482
|
+
if (this.dataCallback) {
|
|
1483
|
+
this.dataCallback(`\r
|
|
1484
|
+
\x1B[1;33m[Maestro] Connection lost — reconnecting (${this.reconnectAttempts}/3)...\x1B[0m\r
|
|
1485
|
+
`);
|
|
1486
|
+
}
|
|
1487
|
+
AgentConnectionPool.getInstance().releaseOne(this.tunnelId);
|
|
1488
|
+
await new Promise((r) => setTimeout(r, 2e3 * this.reconnectAttempts));
|
|
1489
|
+
try {
|
|
1490
|
+
this.ws = await AgentConnectionPool.getInstance().getConnection(this.tunnelId);
|
|
1491
|
+
if (this.messageHandler) {
|
|
1492
|
+
this.ws.on("message", this.messageHandler);
|
|
1493
|
+
}
|
|
1494
|
+
this.ws.on("close", () => {
|
|
1495
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} WebSocket closed`);
|
|
1496
|
+
this.handleConnectionLost();
|
|
1497
|
+
});
|
|
1498
|
+
this.ws.on("error", (err) => {
|
|
1499
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} WebSocket error: ${err.message}`);
|
|
1500
|
+
});
|
|
1501
|
+
this.ws.send(JSON.stringify({
|
|
1502
|
+
type: "create",
|
|
1503
|
+
id: this.sessionId,
|
|
1504
|
+
shell: "powershell",
|
|
1505
|
+
cwd: this.lastCwd,
|
|
1506
|
+
cols: this.lastCols,
|
|
1507
|
+
rows: this.lastRows
|
|
1508
|
+
}));
|
|
1509
|
+
this.reconnectAttempts = 0;
|
|
1510
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} reconnected successfully`);
|
|
1511
|
+
if (this.dataCallback) {
|
|
1512
|
+
this.dataCallback("\r\n\x1B[1;32m[Maestro] Reconnected to remote agent.\x1B[0m\r\n");
|
|
1513
|
+
}
|
|
1514
|
+
return;
|
|
1515
|
+
} catch (err) {
|
|
1516
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
this.dead = true;
|
|
1520
|
+
console.log(`[AgentPty] ${this.sessionId.slice(0, 8)} all reconnect attempts failed`);
|
|
1521
|
+
if (this.dataCallback) {
|
|
1522
|
+
this.dataCallback("\r\n\x1B[1;31m[Maestro] Connection to remote agent lost. Close and recreate the terminal.\x1B[0m\r\n");
|
|
1523
|
+
}
|
|
1524
|
+
if (this.exitCallback) {
|
|
1525
|
+
this.exitCallback(null);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1420
1528
|
onData(cb) {
|
|
1421
1529
|
this.dataCallback = cb;
|
|
1422
1530
|
}
|
|
@@ -1429,21 +1537,35 @@ class AgentPtyAdapter {
|
|
|
1429
1537
|
}
|
|
1430
1538
|
}
|
|
1431
1539
|
resize(cols, rows) {
|
|
1540
|
+
this.lastCols = cols;
|
|
1541
|
+
this.lastRows = rows;
|
|
1432
1542
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
1433
1543
|
this.ws.send(JSON.stringify({ type: "resize", id: this.sessionId, cols, rows }));
|
|
1434
1544
|
}
|
|
1435
1545
|
}
|
|
1436
1546
|
kill() {
|
|
1547
|
+
this.dead = true;
|
|
1437
1548
|
if (this.ws && this.messageHandler) {
|
|
1438
1549
|
this.ws.removeListener("message", this.messageHandler);
|
|
1439
1550
|
}
|
|
1440
|
-
this.ws?.
|
|
1551
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
1552
|
+
this.ws.send(JSON.stringify({ type: "destroy", id: this.sessionId }));
|
|
1553
|
+
}
|
|
1441
1554
|
}
|
|
1442
|
-
autoLaunch(provider) {
|
|
1555
|
+
autoLaunch(provider, skipPermissions, resumeAI) {
|
|
1443
1556
|
if (provider === "claude") {
|
|
1444
|
-
|
|
1557
|
+
const flags = [
|
|
1558
|
+
resumeAI ? "--continue" : "",
|
|
1559
|
+
skipPermissions ? "--dangerously-skip-permissions" : ""
|
|
1560
|
+
].filter(Boolean).join(" ");
|
|
1561
|
+
setTimeout(() => this.write(`claude${flags ? " " + flags : ""}\r`), 600);
|
|
1445
1562
|
} else if (provider === "copilot") {
|
|
1446
|
-
|
|
1563
|
+
const flags = [
|
|
1564
|
+
"--output-format json",
|
|
1565
|
+
resumeAI ? "--continue" : "",
|
|
1566
|
+
skipPermissions ? "--yolo" : ""
|
|
1567
|
+
].filter(Boolean).join(" ");
|
|
1568
|
+
setTimeout(() => this.write(`copilot ${flags}\r`), 600);
|
|
1447
1569
|
}
|
|
1448
1570
|
}
|
|
1449
1571
|
}
|
|
@@ -1746,9 +1868,65 @@ class SessionManager {
|
|
|
1746
1868
|
entries = /* @__PURE__ */ new Map();
|
|
1747
1869
|
machineManager;
|
|
1748
1870
|
push;
|
|
1871
|
+
savedPath;
|
|
1872
|
+
savedSessions = [];
|
|
1749
1873
|
constructor(machineManager2, push) {
|
|
1750
1874
|
this.machineManager = machineManager2;
|
|
1751
1875
|
this.push = push;
|
|
1876
|
+
this.savedPath = path__namespace.join(electron.app.getPath("userData"), "saved-sessions.json");
|
|
1877
|
+
this.loadSaved();
|
|
1878
|
+
}
|
|
1879
|
+
loadSaved() {
|
|
1880
|
+
try {
|
|
1881
|
+
const data = fs__namespace.readFileSync(this.savedPath, "utf8");
|
|
1882
|
+
this.savedSessions = JSON.parse(data);
|
|
1883
|
+
console.log(`[SessionManager] Loaded ${this.savedSessions.length} saved sessions`);
|
|
1884
|
+
} catch {
|
|
1885
|
+
this.savedSessions = [];
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
saveToDisk() {
|
|
1889
|
+
try {
|
|
1890
|
+
fs__namespace.writeFileSync(this.savedPath, JSON.stringify(this.savedSessions, null, 2), "utf8");
|
|
1891
|
+
} catch (err) {
|
|
1892
|
+
console.error("[SessionManager] Failed to save sessions:", err);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
listSaved() {
|
|
1896
|
+
return this.savedSessions;
|
|
1897
|
+
}
|
|
1898
|
+
dismissSaved() {
|
|
1899
|
+
this.savedSessions = [];
|
|
1900
|
+
this.saveToDisk();
|
|
1901
|
+
}
|
|
1902
|
+
async resumeSaved(ids, resumeAI) {
|
|
1903
|
+
const resumed = [];
|
|
1904
|
+
const skipped = [];
|
|
1905
|
+
for (const saved of this.savedSessions.filter((s) => ids.includes(s.id))) {
|
|
1906
|
+
const machine = this.machineManager.getById(saved.machineId);
|
|
1907
|
+
if (saved.machineId !== "local" && (!machine || machine.status === "offline")) {
|
|
1908
|
+
skipped.push(saved.name);
|
|
1909
|
+
continue;
|
|
1910
|
+
}
|
|
1911
|
+
try {
|
|
1912
|
+
const session = await this.create({
|
|
1913
|
+
name: saved.name,
|
|
1914
|
+
machineId: saved.machineId,
|
|
1915
|
+
provider: saved.provider,
|
|
1916
|
+
workingDirectory: saved.workingDirectory,
|
|
1917
|
+
tags: saved.tags,
|
|
1918
|
+
skipPermissions: saved.skipPermissions,
|
|
1919
|
+
resumeAI: resumeAI && saved.provider !== "none"
|
|
1920
|
+
});
|
|
1921
|
+
resumed.push(session);
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
console.error(`[SessionManager] Failed to resume "${saved.name}":`, err);
|
|
1924
|
+
skipped.push(saved.name);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
this.savedSessions = [];
|
|
1928
|
+
this.saveToDisk();
|
|
1929
|
+
return { resumed, skipped };
|
|
1752
1930
|
}
|
|
1753
1931
|
list() {
|
|
1754
1932
|
return Array.from(this.entries.values()).map((e) => ({
|
|
@@ -1827,8 +2005,20 @@ class SessionManager {
|
|
|
1827
2005
|
});
|
|
1828
2006
|
monitor.start();
|
|
1829
2007
|
if (opts.provider !== "none") {
|
|
1830
|
-
adapter.autoLaunch(opts.provider);
|
|
1831
|
-
}
|
|
2008
|
+
adapter.autoLaunch(opts.provider, opts.skipPermissions, opts.resumeAI);
|
|
2009
|
+
}
|
|
2010
|
+
this.savedSessions = this.savedSessions.filter((s) => s.id !== session.id);
|
|
2011
|
+
this.savedSessions.push({
|
|
2012
|
+
id: session.id,
|
|
2013
|
+
name: session.name,
|
|
2014
|
+
machineId: session.machineId,
|
|
2015
|
+
provider: session.provider,
|
|
2016
|
+
workingDirectory: session.workingDirectory,
|
|
2017
|
+
tags: session.tags,
|
|
2018
|
+
skipPermissions: opts.skipPermissions,
|
|
2019
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2020
|
+
});
|
|
2021
|
+
this.saveToDisk();
|
|
1832
2022
|
return { ...session };
|
|
1833
2023
|
}
|
|
1834
2024
|
destroy(id) {
|
|
@@ -1837,6 +2027,8 @@ class SessionManager {
|
|
|
1837
2027
|
entry.monitor.stop();
|
|
1838
2028
|
entry.adapter.kill();
|
|
1839
2029
|
this.entries.delete(id);
|
|
2030
|
+
this.savedSessions = this.savedSessions.filter((s) => s.id !== id);
|
|
2031
|
+
this.saveToDisk();
|
|
1840
2032
|
}
|
|
1841
2033
|
rename(id, name) {
|
|
1842
2034
|
const entry = this.entries.get(id);
|
|
@@ -1884,26 +2076,32 @@ class SessionManager {
|
|
|
1884
2076
|
this.push("sessions:stateChanged", ev);
|
|
1885
2077
|
}
|
|
1886
2078
|
}
|
|
1887
|
-
|
|
1888
|
-
const logFile = path.join(logDir, "maestro-gui.log");
|
|
2079
|
+
let logStream = null;
|
|
1889
2080
|
try {
|
|
1890
|
-
|
|
1891
|
-
|
|
2081
|
+
const logDir = electron.app.getPath("userData");
|
|
2082
|
+
fs__namespace.mkdirSync(logDir, { recursive: true });
|
|
2083
|
+
const logFile = path.join(logDir, "maestro-gui.log");
|
|
2084
|
+
try {
|
|
2085
|
+
if (fs__namespace.existsSync(logFile)) fs__namespace.renameSync(logFile, logFile + ".prev");
|
|
2086
|
+
} catch {
|
|
2087
|
+
}
|
|
2088
|
+
logStream = fs__namespace.createWriteStream(logFile, { flags: "a" });
|
|
2089
|
+
const origLog = console.log;
|
|
2090
|
+
const origErr = console.error;
|
|
2091
|
+
console.log = (...args) => {
|
|
2092
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${args.map(String).join(" ")}`;
|
|
2093
|
+
logStream?.write(line + "\n");
|
|
2094
|
+
origLog(...args);
|
|
2095
|
+
};
|
|
2096
|
+
console.error = (...args) => {
|
|
2097
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ERROR ${args.map(String).join(" ")}`;
|
|
2098
|
+
logStream?.write(line + "\n");
|
|
2099
|
+
origErr(...args);
|
|
2100
|
+
};
|
|
2101
|
+
console.log(`[Maestro] Log file: ${logFile}`);
|
|
2102
|
+
} catch (err) {
|
|
2103
|
+
console.error("[Maestro] Failed to set up log file:", err);
|
|
1892
2104
|
}
|
|
1893
|
-
const logStream = fs__namespace.createWriteStream(logFile, { flags: "a" });
|
|
1894
|
-
const origLog = console.log;
|
|
1895
|
-
const origErr = console.error;
|
|
1896
|
-
console.log = (...args) => {
|
|
1897
|
-
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${args.map(String).join(" ")}`;
|
|
1898
|
-
logStream.write(line + "\n");
|
|
1899
|
-
origLog(...args);
|
|
1900
|
-
};
|
|
1901
|
-
console.error = (...args) => {
|
|
1902
|
-
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ERROR ${args.map(String).join(" ")}`;
|
|
1903
|
-
logStream.write(line + "\n");
|
|
1904
|
-
origErr(...args);
|
|
1905
|
-
};
|
|
1906
|
-
console.log(`[Maestro] Log file: ${logFile}`);
|
|
1907
2105
|
const startupArgs = process.argv.slice(2);
|
|
1908
2106
|
let startupPlanFile = null;
|
|
1909
2107
|
const pfIdx = startupArgs.indexOf("--plan-file");
|
|
@@ -1977,6 +2175,15 @@ electron.app.whenReady().then(() => {
|
|
|
1977
2175
|
electron.ipcMain.handle("app:getStartupArgs", () => ({
|
|
1978
2176
|
planFile: startupPlanFile
|
|
1979
2177
|
}));
|
|
2178
|
+
electron.ipcMain.handle("app:getVersion", () => {
|
|
2179
|
+
try {
|
|
2180
|
+
const pkgPath = path.join(__dirname, "..", "..", "..", "package.json");
|
|
2181
|
+
const pkg = JSON.parse(fs__namespace.readFileSync(pkgPath, "utf8"));
|
|
2182
|
+
return pkg.version ?? "unknown";
|
|
2183
|
+
} catch {
|
|
2184
|
+
return electron.app.getVersion();
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
1980
2187
|
createWindow();
|
|
1981
2188
|
electron.app.on("activate", () => {
|
|
1982
2189
|
if (electron.BrowserWindow.getAllWindows().length === 0) {
|
|
@@ -55,6 +55,7 @@ electron.contextBridge.exposeInMainWorld("maestro", {
|
|
|
55
55
|
},
|
|
56
56
|
// Read startup args passed from CLI
|
|
57
57
|
getStartupArgs: () => electron.ipcRenderer.invoke("app:getStartupArgs"),
|
|
58
|
+
getVersion: () => electron.ipcRenderer.invoke("app:getVersion"),
|
|
58
59
|
// Machine management
|
|
59
60
|
machines: {
|
|
60
61
|
list: () => electron.ipcRenderer.invoke("machines:list"),
|
|
@@ -84,6 +85,9 @@ electron.contextBridge.exposeInMainWorld("maestro", {
|
|
|
84
85
|
acknowledge: (id) => electron.ipcRenderer.invoke("sessions:acknowledge", id),
|
|
85
86
|
sendInput: (id, data) => electron.ipcRenderer.invoke("sessions:sendInput", id, data),
|
|
86
87
|
resize: (id, cols, rows) => electron.ipcRenderer.invoke("sessions:resize", id, cols, rows),
|
|
88
|
+
savedList: () => electron.ipcRenderer.invoke("sessions:savedList"),
|
|
89
|
+
savedResume: (ids, resumeAI) => electron.ipcRenderer.invoke("sessions:savedResume", ids, resumeAI),
|
|
90
|
+
savedDismiss: () => electron.ipcRenderer.invoke("sessions:savedDismiss"),
|
|
87
91
|
onData: (cb) => {
|
|
88
92
|
const handler = (_, ev) => cb(ev);
|
|
89
93
|
electron.ipcRenderer.on("sessions:data", handler);
|