@mobvibe/cli 0.1.4 → 0.1.6
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 +188 -201
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -379,12 +379,13 @@ var getCliConfig = async () => {
|
|
|
379
379
|
|
|
380
380
|
// src/daemon/daemon.ts
|
|
381
381
|
import { spawn as spawn2 } from "child_process";
|
|
382
|
-
import
|
|
382
|
+
import fs5 from "fs/promises";
|
|
383
383
|
import path5 from "path";
|
|
384
384
|
|
|
385
385
|
// src/acp/session-manager.ts
|
|
386
386
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
387
387
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
388
|
+
import fs3 from "fs/promises";
|
|
388
389
|
|
|
389
390
|
// ../../packages/shared/dist/types/errors.js
|
|
390
391
|
var createErrorDetail = (input) => ({
|
|
@@ -559,8 +560,7 @@ var AcpConnection = class {
|
|
|
559
560
|
getSessionCapabilities() {
|
|
560
561
|
return {
|
|
561
562
|
list: this.agentCapabilities?.sessionCapabilities?.list != null,
|
|
562
|
-
load: this.agentCapabilities?.loadSession === true
|
|
563
|
-
resume: this.agentCapabilities?.sessionCapabilities?.resume != null
|
|
563
|
+
load: this.agentCapabilities?.loadSession === true
|
|
564
564
|
};
|
|
565
565
|
}
|
|
566
566
|
/**
|
|
@@ -575,12 +575,6 @@ var AcpConnection = class {
|
|
|
575
575
|
supportsSessionLoad() {
|
|
576
576
|
return this.agentCapabilities?.loadSession === true;
|
|
577
577
|
}
|
|
578
|
-
/**
|
|
579
|
-
* Check if the agent supports session/resume.
|
|
580
|
-
*/
|
|
581
|
-
supportsSessionResume() {
|
|
582
|
-
return this.agentCapabilities?.sessionCapabilities?.resume != null;
|
|
583
|
-
}
|
|
584
578
|
/**
|
|
585
579
|
* List sessions from the agent (session/list).
|
|
586
580
|
* @param params Optional filter parameters
|
|
@@ -588,14 +582,17 @@ var AcpConnection = class {
|
|
|
588
582
|
*/
|
|
589
583
|
async listSessions(params) {
|
|
590
584
|
if (!this.supportsSessionList()) {
|
|
591
|
-
return [];
|
|
585
|
+
return { sessions: [] };
|
|
592
586
|
}
|
|
593
587
|
const connection = await this.ensureReady();
|
|
594
588
|
const response = await connection.unstable_listSessions({
|
|
595
589
|
cursor: params?.cursor ?? void 0,
|
|
596
590
|
cwd: params?.cwd ?? void 0
|
|
597
591
|
});
|
|
598
|
-
return
|
|
592
|
+
return {
|
|
593
|
+
sessions: response.sessions,
|
|
594
|
+
nextCursor: response.nextCursor ?? void 0
|
|
595
|
+
};
|
|
599
596
|
}
|
|
600
597
|
/**
|
|
601
598
|
* Load a historical session with message history replay (session/load).
|
|
@@ -616,25 +613,6 @@ var AcpConnection = class {
|
|
|
616
613
|
this.sessionId = sessionId;
|
|
617
614
|
return response;
|
|
618
615
|
}
|
|
619
|
-
/**
|
|
620
|
-
* Resume an active session without message history replay (session/resume).
|
|
621
|
-
* @param sessionId The session ID to resume
|
|
622
|
-
* @param cwd The working directory
|
|
623
|
-
* @returns Resume session response
|
|
624
|
-
*/
|
|
625
|
-
async resumeSession(sessionId, cwd) {
|
|
626
|
-
if (!this.supportsSessionResume()) {
|
|
627
|
-
throw new Error("Agent does not support session/resume capability");
|
|
628
|
-
}
|
|
629
|
-
const connection = await this.ensureReady();
|
|
630
|
-
const response = await connection.unstable_resumeSession({
|
|
631
|
-
sessionId,
|
|
632
|
-
cwd,
|
|
633
|
-
mcpServers: []
|
|
634
|
-
});
|
|
635
|
-
this.sessionId = sessionId;
|
|
636
|
-
return response;
|
|
637
|
-
}
|
|
638
616
|
setPermissionHandler(handler) {
|
|
639
617
|
this.permissionHandler = handler;
|
|
640
618
|
}
|
|
@@ -979,6 +957,14 @@ var createCapabilityNotSupportedError = (message) => new AppError(
|
|
|
979
957
|
}),
|
|
980
958
|
409
|
|
981
959
|
);
|
|
960
|
+
var isValidWorkspacePath = async (cwd) => {
|
|
961
|
+
try {
|
|
962
|
+
const stats = await fs3.stat(cwd);
|
|
963
|
+
return stats.isDirectory();
|
|
964
|
+
} catch {
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
};
|
|
982
968
|
var SessionManager = class {
|
|
983
969
|
constructor(config) {
|
|
984
970
|
this.config = config;
|
|
@@ -988,6 +974,7 @@ var SessionManager = class {
|
|
|
988
974
|
this.defaultBackendId = config.defaultAcpBackendId;
|
|
989
975
|
}
|
|
990
976
|
sessions = /* @__PURE__ */ new Map();
|
|
977
|
+
discoveredSessions = /* @__PURE__ */ new Map();
|
|
991
978
|
backendById;
|
|
992
979
|
defaultBackendId;
|
|
993
980
|
permissionRequests = /* @__PURE__ */ new Map();
|
|
@@ -997,6 +984,8 @@ var SessionManager = class {
|
|
|
997
984
|
permissionResultEmitter = new EventEmitter2();
|
|
998
985
|
terminalOutputEmitter = new EventEmitter2();
|
|
999
986
|
sessionsChangedEmitter = new EventEmitter2();
|
|
987
|
+
sessionAttachedEmitter = new EventEmitter2();
|
|
988
|
+
sessionDetachedEmitter = new EventEmitter2();
|
|
1000
989
|
listSessions() {
|
|
1001
990
|
return Array.from(this.sessions.values()).map(
|
|
1002
991
|
(record) => this.buildSummary(record)
|
|
@@ -1041,9 +1030,54 @@ var SessionManager = class {
|
|
|
1041
1030
|
this.sessionsChangedEmitter.off("changed", listener);
|
|
1042
1031
|
};
|
|
1043
1032
|
}
|
|
1033
|
+
onSessionAttached(listener) {
|
|
1034
|
+
this.sessionAttachedEmitter.on("attached", listener);
|
|
1035
|
+
return () => {
|
|
1036
|
+
this.sessionAttachedEmitter.off("attached", listener);
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
onSessionDetached(listener) {
|
|
1040
|
+
this.sessionDetachedEmitter.on("detached", listener);
|
|
1041
|
+
return () => {
|
|
1042
|
+
this.sessionDetachedEmitter.off("detached", listener);
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1044
1045
|
emitSessionsChanged(payload) {
|
|
1045
1046
|
this.sessionsChangedEmitter.emit("changed", payload);
|
|
1046
1047
|
}
|
|
1048
|
+
emitSessionAttached(sessionId) {
|
|
1049
|
+
const record = this.sessions.get(sessionId);
|
|
1050
|
+
if (!record) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
if (record.isAttached) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
const attachedAt = /* @__PURE__ */ new Date();
|
|
1057
|
+
record.isAttached = true;
|
|
1058
|
+
record.attachedAt = attachedAt;
|
|
1059
|
+
this.sessionAttachedEmitter.emit("attached", {
|
|
1060
|
+
sessionId,
|
|
1061
|
+
machineId: this.config.machineId,
|
|
1062
|
+
attachedAt: attachedAt.toISOString()
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
emitSessionDetached(sessionId, reason) {
|
|
1066
|
+
const record = this.sessions.get(sessionId);
|
|
1067
|
+
if (!record) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
if (!record.isAttached) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
record.isAttached = false;
|
|
1074
|
+
this.sessionDetachedEmitter.emit("detached", {
|
|
1075
|
+
sessionId,
|
|
1076
|
+
machineId: this.config.machineId,
|
|
1077
|
+
detachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1078
|
+
reason
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1047
1081
|
listPendingPermissions(sessionId) {
|
|
1048
1082
|
return Array.from(this.permissionRequests.values()).filter((record) => record.sessionId === sessionId).map((record) => this.buildPermissionRequestPayload(record));
|
|
1049
1083
|
}
|
|
@@ -1132,29 +1166,9 @@ var SessionManager = class {
|
|
|
1132
1166
|
};
|
|
1133
1167
|
record.unsubscribe = connection.onSessionUpdate(
|
|
1134
1168
|
(notification) => {
|
|
1135
|
-
|
|
1169
|
+
record.updatedAt = /* @__PURE__ */ new Date();
|
|
1136
1170
|
this.sessionUpdateEmitter.emit("update", notification);
|
|
1137
|
-
|
|
1138
|
-
if (update.sessionUpdate === "current_mode_update") {
|
|
1139
|
-
record.modeId = update.currentModeId;
|
|
1140
|
-
record.modeName = record.availableModes?.find(
|
|
1141
|
-
(mode) => mode.id === update.currentModeId
|
|
1142
|
-
)?.name ?? record.modeName;
|
|
1143
|
-
return;
|
|
1144
|
-
}
|
|
1145
|
-
if (update.sessionUpdate === "session_info_update") {
|
|
1146
|
-
if (typeof update.title === "string") {
|
|
1147
|
-
record.title = update.title;
|
|
1148
|
-
}
|
|
1149
|
-
if (typeof update.updatedAt === "string") {
|
|
1150
|
-
record.updatedAt = new Date(update.updatedAt);
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
if (update.sessionUpdate === "available_commands_update") {
|
|
1154
|
-
if (update.availableCommands) {
|
|
1155
|
-
record.availableCommands = update.availableCommands;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1171
|
+
this.applySessionUpdateToRecord(record, notification);
|
|
1158
1172
|
}
|
|
1159
1173
|
);
|
|
1160
1174
|
record.unsubscribeTerminal = connection.onTerminalOutput((event) => {
|
|
@@ -1166,6 +1180,7 @@ var SessionManager = class {
|
|
|
1166
1180
|
sessionId: session.sessionId,
|
|
1167
1181
|
error: status.error
|
|
1168
1182
|
});
|
|
1183
|
+
this.emitSessionDetached(session.sessionId, "agent_exit");
|
|
1169
1184
|
}
|
|
1170
1185
|
});
|
|
1171
1186
|
this.sessions.set(session.sessionId, record);
|
|
@@ -1175,6 +1190,7 @@ var SessionManager = class {
|
|
|
1175
1190
|
updated: [],
|
|
1176
1191
|
removed: []
|
|
1177
1192
|
});
|
|
1193
|
+
this.emitSessionAttached(session.sessionId);
|
|
1178
1194
|
return summary;
|
|
1179
1195
|
} catch (error) {
|
|
1180
1196
|
const status = connection.getStatus();
|
|
@@ -1391,6 +1407,7 @@ var SessionManager = class {
|
|
|
1391
1407
|
} catch (error) {
|
|
1392
1408
|
logger.error({ err: error, sessionId }, "session_disconnect_failed");
|
|
1393
1409
|
}
|
|
1410
|
+
this.emitSessionDetached(sessionId, "unknown");
|
|
1394
1411
|
this.sessions.delete(sessionId);
|
|
1395
1412
|
this.emitSessionsChanged({
|
|
1396
1413
|
added: [],
|
|
@@ -1424,11 +1441,30 @@ var SessionManager = class {
|
|
|
1424
1441
|
await connection.connect();
|
|
1425
1442
|
const capabilities = connection.getSessionCapabilities();
|
|
1426
1443
|
const sessions = [];
|
|
1444
|
+
let nextCursor;
|
|
1427
1445
|
if (capabilities.list) {
|
|
1428
|
-
const
|
|
1429
|
-
cwd: options?.cwd
|
|
1446
|
+
const response = await connection.listSessions({
|
|
1447
|
+
cwd: options?.cwd,
|
|
1448
|
+
cursor: options?.cursor
|
|
1430
1449
|
});
|
|
1431
|
-
|
|
1450
|
+
nextCursor = response.nextCursor;
|
|
1451
|
+
const validity = await Promise.all(
|
|
1452
|
+
response.sessions.map(async (session) => ({
|
|
1453
|
+
session,
|
|
1454
|
+
isValid: session.cwd ? await isValidWorkspacePath(session.cwd) : false
|
|
1455
|
+
}))
|
|
1456
|
+
);
|
|
1457
|
+
for (const { session, isValid } of validity) {
|
|
1458
|
+
if (!isValid) {
|
|
1459
|
+
this.discoveredSessions.delete(session.sessionId);
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
this.discoveredSessions.set(session.sessionId, {
|
|
1463
|
+
sessionId: session.sessionId,
|
|
1464
|
+
cwd: session.cwd,
|
|
1465
|
+
title: session.title ?? void 0,
|
|
1466
|
+
updatedAt: session.updatedAt ?? void 0
|
|
1467
|
+
});
|
|
1432
1468
|
sessions.push({
|
|
1433
1469
|
sessionId: session.sessionId,
|
|
1434
1470
|
cwd: session.cwd,
|
|
@@ -1445,7 +1481,7 @@ var SessionManager = class {
|
|
|
1445
1481
|
},
|
|
1446
1482
|
"sessions_discovered"
|
|
1447
1483
|
);
|
|
1448
|
-
return { sessions, capabilities };
|
|
1484
|
+
return { sessions, capabilities, nextCursor };
|
|
1449
1485
|
} finally {
|
|
1450
1486
|
await connection.disconnect();
|
|
1451
1487
|
}
|
|
@@ -1461,6 +1497,7 @@ var SessionManager = class {
|
|
|
1461
1497
|
async loadSession(sessionId, cwd, backendId) {
|
|
1462
1498
|
const existing = this.sessions.get(sessionId);
|
|
1463
1499
|
if (existing) {
|
|
1500
|
+
this.emitSessionAttached(sessionId);
|
|
1464
1501
|
return this.buildSummary(existing);
|
|
1465
1502
|
}
|
|
1466
1503
|
const backend = this.resolveBackend(backendId);
|
|
@@ -1478,6 +1515,19 @@ var SessionManager = class {
|
|
|
1478
1515
|
"Agent does not support session loading"
|
|
1479
1516
|
);
|
|
1480
1517
|
}
|
|
1518
|
+
const bufferedUpdates = [];
|
|
1519
|
+
let recordRef;
|
|
1520
|
+
const unsubscribe = connection.onSessionUpdate(
|
|
1521
|
+
(notification) => {
|
|
1522
|
+
this.sessionUpdateEmitter.emit("update", notification);
|
|
1523
|
+
if (recordRef) {
|
|
1524
|
+
recordRef.updatedAt = /* @__PURE__ */ new Date();
|
|
1525
|
+
this.applySessionUpdateToRecord(recordRef, notification);
|
|
1526
|
+
} else {
|
|
1527
|
+
bufferedUpdates.push(notification);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
);
|
|
1481
1531
|
const response = await connection.loadSession(sessionId, cwd);
|
|
1482
1532
|
connection.setPermissionHandler(
|
|
1483
1533
|
(params) => this.handlePermissionRequest(sessionId, params)
|
|
@@ -1490,9 +1540,10 @@ var SessionManager = class {
|
|
|
1490
1540
|
const { modeId, modeName, availableModes } = resolveModeState(
|
|
1491
1541
|
response.modes
|
|
1492
1542
|
);
|
|
1543
|
+
const discovered = this.discoveredSessions.get(sessionId);
|
|
1493
1544
|
const record = {
|
|
1494
1545
|
sessionId,
|
|
1495
|
-
title:
|
|
1546
|
+
title: discovered?.title ?? sessionId,
|
|
1496
1547
|
backendId: backend.id,
|
|
1497
1548
|
backendLabel: backend.label,
|
|
1498
1549
|
connection,
|
|
@@ -1508,7 +1559,12 @@ var SessionManager = class {
|
|
|
1508
1559
|
availableModels,
|
|
1509
1560
|
availableCommands: void 0
|
|
1510
1561
|
};
|
|
1511
|
-
|
|
1562
|
+
recordRef = record;
|
|
1563
|
+
record.unsubscribe = unsubscribe;
|
|
1564
|
+
for (const notification of bufferedUpdates) {
|
|
1565
|
+
this.applySessionUpdateToRecord(record, notification);
|
|
1566
|
+
}
|
|
1567
|
+
this.setupSessionSubscriptions(record, { skipSessionUpdates: true });
|
|
1512
1568
|
this.sessions.set(sessionId, record);
|
|
1513
1569
|
const summary = this.buildSummary(record);
|
|
1514
1570
|
this.emitSessionsChanged({
|
|
@@ -1516,6 +1572,7 @@ var SessionManager = class {
|
|
|
1516
1572
|
updated: [],
|
|
1517
1573
|
removed: []
|
|
1518
1574
|
});
|
|
1575
|
+
this.emitSessionAttached(sessionId);
|
|
1519
1576
|
logger.info({ sessionId, backendId: backend.id }, "session_loaded");
|
|
1520
1577
|
return summary;
|
|
1521
1578
|
} catch (error) {
|
|
@@ -1523,111 +1580,41 @@ var SessionManager = class {
|
|
|
1523
1580
|
throw error;
|
|
1524
1581
|
}
|
|
1525
1582
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
* @returns The resumed session summary
|
|
1533
|
-
*/
|
|
1534
|
-
async resumeSession(sessionId, cwd, backendId) {
|
|
1535
|
-
const existing = this.sessions.get(sessionId);
|
|
1536
|
-
if (existing) {
|
|
1537
|
-
return this.buildSummary(existing);
|
|
1583
|
+
applySessionUpdateToRecord(record, notification) {
|
|
1584
|
+
const update = notification.update;
|
|
1585
|
+
if (update.sessionUpdate === "current_mode_update") {
|
|
1586
|
+
record.modeId = update.currentModeId;
|
|
1587
|
+
record.modeName = record.availableModes?.find((mode) => mode.id === update.currentModeId)?.name ?? record.modeName;
|
|
1588
|
+
return;
|
|
1538
1589
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
client: {
|
|
1543
|
-
name: this.config.clientName,
|
|
1544
|
-
version: this.config.clientVersion
|
|
1590
|
+
if (update.sessionUpdate === "session_info_update") {
|
|
1591
|
+
if (typeof update.title === "string") {
|
|
1592
|
+
record.title = update.title;
|
|
1545
1593
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1594
|
+
if (typeof update.updatedAt === "string") {
|
|
1595
|
+
record.updatedAt = new Date(update.updatedAt);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
if (update.sessionUpdate === "available_commands_update") {
|
|
1599
|
+
if (update.availableCommands) {
|
|
1600
|
+
record.availableCommands = update.availableCommands;
|
|
1553
1601
|
}
|
|
1554
|
-
const response = await connection.resumeSession(sessionId, cwd);
|
|
1555
|
-
connection.setPermissionHandler(
|
|
1556
|
-
(params) => this.handlePermissionRequest(sessionId, params)
|
|
1557
|
-
);
|
|
1558
|
-
const now = /* @__PURE__ */ new Date();
|
|
1559
|
-
const agentInfo = connection.getAgentInfo();
|
|
1560
|
-
const { modelId, modelName, availableModels } = resolveModelState(
|
|
1561
|
-
response.models
|
|
1562
|
-
);
|
|
1563
|
-
const { modeId, modeName, availableModes } = resolveModeState(
|
|
1564
|
-
response.modes
|
|
1565
|
-
);
|
|
1566
|
-
const record = {
|
|
1567
|
-
sessionId,
|
|
1568
|
-
title: `Resumed Session`,
|
|
1569
|
-
backendId: backend.id,
|
|
1570
|
-
backendLabel: backend.label,
|
|
1571
|
-
connection,
|
|
1572
|
-
createdAt: now,
|
|
1573
|
-
updatedAt: now,
|
|
1574
|
-
cwd,
|
|
1575
|
-
agentName: agentInfo?.title ?? agentInfo?.name,
|
|
1576
|
-
modelId,
|
|
1577
|
-
modelName,
|
|
1578
|
-
modeId,
|
|
1579
|
-
modeName,
|
|
1580
|
-
availableModes,
|
|
1581
|
-
availableModels,
|
|
1582
|
-
availableCommands: void 0
|
|
1583
|
-
};
|
|
1584
|
-
this.setupSessionSubscriptions(record);
|
|
1585
|
-
this.sessions.set(sessionId, record);
|
|
1586
|
-
const summary = this.buildSummary(record);
|
|
1587
|
-
this.emitSessionsChanged({
|
|
1588
|
-
added: [summary],
|
|
1589
|
-
updated: [],
|
|
1590
|
-
removed: []
|
|
1591
|
-
});
|
|
1592
|
-
logger.info({ sessionId, backendId: backend.id }, "session_resumed");
|
|
1593
|
-
return summary;
|
|
1594
|
-
} catch (error) {
|
|
1595
|
-
await connection.disconnect();
|
|
1596
|
-
throw error;
|
|
1597
1602
|
}
|
|
1598
1603
|
}
|
|
1599
1604
|
/**
|
|
1600
1605
|
* Set up event subscriptions for a session record.
|
|
1601
1606
|
*/
|
|
1602
|
-
setupSessionSubscriptions(record) {
|
|
1607
|
+
setupSessionSubscriptions(record, options) {
|
|
1603
1608
|
const { sessionId, connection } = record;
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
record.modeId = update.currentModeId;
|
|
1611
|
-
record.modeName = record.availableModes?.find(
|
|
1612
|
-
(mode) => mode.id === update.currentModeId
|
|
1613
|
-
)?.name ?? record.modeName;
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
if (update.sessionUpdate === "session_info_update") {
|
|
1617
|
-
if (typeof update.title === "string") {
|
|
1618
|
-
record.title = update.title;
|
|
1619
|
-
}
|
|
1620
|
-
if (typeof update.updatedAt === "string") {
|
|
1621
|
-
record.updatedAt = new Date(update.updatedAt);
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
if (update.sessionUpdate === "available_commands_update") {
|
|
1625
|
-
if (update.availableCommands) {
|
|
1626
|
-
record.availableCommands = update.availableCommands;
|
|
1627
|
-
}
|
|
1609
|
+
if (!options?.skipSessionUpdates) {
|
|
1610
|
+
record.unsubscribe = connection.onSessionUpdate(
|
|
1611
|
+
(notification) => {
|
|
1612
|
+
record.updatedAt = /* @__PURE__ */ new Date();
|
|
1613
|
+
this.sessionUpdateEmitter.emit("update", notification);
|
|
1614
|
+
this.applySessionUpdateToRecord(record, notification);
|
|
1628
1615
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1631
1618
|
record.unsubscribeTerminal = connection.onTerminalOutput((event) => {
|
|
1632
1619
|
this.terminalOutputEmitter.emit("output", event);
|
|
1633
1620
|
});
|
|
@@ -1637,6 +1624,7 @@ var SessionManager = class {
|
|
|
1637
1624
|
sessionId,
|
|
1638
1625
|
error: status.error
|
|
1639
1626
|
});
|
|
1627
|
+
this.emitSessionDetached(sessionId, "agent_exit");
|
|
1640
1628
|
}
|
|
1641
1629
|
});
|
|
1642
1630
|
}
|
|
@@ -1647,7 +1635,6 @@ var SessionManager = class {
|
|
|
1647
1635
|
title: record.title,
|
|
1648
1636
|
backendId: record.backendId,
|
|
1649
1637
|
backendLabel: record.backendLabel,
|
|
1650
|
-
state: status.state,
|
|
1651
1638
|
error: status.error,
|
|
1652
1639
|
pid: status.pid,
|
|
1653
1640
|
createdAt: record.createdAt.toISOString(),
|
|
@@ -1667,7 +1654,7 @@ var SessionManager = class {
|
|
|
1667
1654
|
|
|
1668
1655
|
// src/daemon/socket-client.ts
|
|
1669
1656
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
1670
|
-
import
|
|
1657
|
+
import fs4 from "fs/promises";
|
|
1671
1658
|
import { homedir } from "os";
|
|
1672
1659
|
import path4 from "path";
|
|
1673
1660
|
import ignore from "ignore";
|
|
@@ -1692,7 +1679,7 @@ var loadGitignore = async (rootPath) => {
|
|
|
1692
1679
|
const ig = ignore().add(DEFAULT_IGNORES);
|
|
1693
1680
|
try {
|
|
1694
1681
|
const gitignorePath = path4.join(rootPath, ".gitignore");
|
|
1695
|
-
const content = await
|
|
1682
|
+
const content = await fs4.readFile(gitignorePath, "utf8");
|
|
1696
1683
|
ig.add(content);
|
|
1697
1684
|
} catch {
|
|
1698
1685
|
}
|
|
@@ -1722,14 +1709,14 @@ var resolveImageMimeType = (filePath) => {
|
|
|
1722
1709
|
}
|
|
1723
1710
|
};
|
|
1724
1711
|
var readDirectoryEntries = async (dirPath) => {
|
|
1725
|
-
const entries = await
|
|
1712
|
+
const entries = await fs4.readdir(dirPath, { withFileTypes: true });
|
|
1726
1713
|
const resolvedEntries = await Promise.all(
|
|
1727
1714
|
entries.map(async (entry) => {
|
|
1728
1715
|
const entryPath = path4.join(dirPath, entry.name);
|
|
1729
1716
|
let isDirectory = entry.isDirectory();
|
|
1730
1717
|
if (!isDirectory && entry.isSymbolicLink()) {
|
|
1731
1718
|
try {
|
|
1732
|
-
const stats = await
|
|
1719
|
+
const stats = await fs4.stat(entryPath);
|
|
1733
1720
|
isDirectory = stats.isDirectory();
|
|
1734
1721
|
} catch {
|
|
1735
1722
|
}
|
|
@@ -1813,14 +1800,24 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
1813
1800
|
this.socket.on("cli:registered", async (info) => {
|
|
1814
1801
|
logger.info({ machineId: info.machineId }, "gateway_registered");
|
|
1815
1802
|
try {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1803
|
+
let cursor;
|
|
1804
|
+
let page = 0;
|
|
1805
|
+
do {
|
|
1806
|
+
const { sessions, capabilities, nextCursor } = await this.options.sessionManager.discoverSessions({ cursor });
|
|
1807
|
+
cursor = nextCursor;
|
|
1808
|
+
if (sessions.length > 0) {
|
|
1809
|
+
this.socket.emit("sessions:discovered", {
|
|
1810
|
+
sessions,
|
|
1811
|
+
capabilities,
|
|
1812
|
+
nextCursor
|
|
1813
|
+
});
|
|
1814
|
+
logger.info(
|
|
1815
|
+
{ count: sessions.length, capabilities, page },
|
|
1816
|
+
"historical_sessions_discovered"
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1819
|
+
page += 1;
|
|
1820
|
+
} while (cursor);
|
|
1824
1821
|
} catch (error) {
|
|
1825
1822
|
logger.warn({ err: error }, "session_discovery_failed");
|
|
1826
1823
|
}
|
|
@@ -2144,7 +2141,7 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
2144
2141
|
const resolved = path4.isAbsolute(requestPath) ? requestPath : path4.join(record.cwd, requestPath);
|
|
2145
2142
|
const mimeType = resolveImageMimeType(resolved);
|
|
2146
2143
|
if (mimeType) {
|
|
2147
|
-
const buffer = await
|
|
2144
|
+
const buffer = await fs4.readFile(resolved);
|
|
2148
2145
|
const preview2 = {
|
|
2149
2146
|
path: resolved,
|
|
2150
2147
|
previewType: "image",
|
|
@@ -2154,7 +2151,7 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
2154
2151
|
this.sendRpcResponse(request.requestId, preview2);
|
|
2155
2152
|
return;
|
|
2156
2153
|
}
|
|
2157
|
-
const content = await
|
|
2154
|
+
const content = await fs4.readFile(resolved, "utf8");
|
|
2158
2155
|
const preview = {
|
|
2159
2156
|
path: resolved,
|
|
2160
2157
|
previewType: "code",
|
|
@@ -2203,14 +2200,15 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
2203
2200
|
});
|
|
2204
2201
|
this.socket.on("rpc:sessions:discover", async (request) => {
|
|
2205
2202
|
try {
|
|
2206
|
-
const { cwd, backendId } = request.params;
|
|
2203
|
+
const { cwd, backendId, cursor } = request.params;
|
|
2207
2204
|
logger.info(
|
|
2208
|
-
{ requestId: request.requestId, cwd, backendId },
|
|
2205
|
+
{ requestId: request.requestId, cwd, backendId, cursor },
|
|
2209
2206
|
"rpc_sessions_discover"
|
|
2210
2207
|
);
|
|
2211
2208
|
const result = await sessionManager.discoverSessions({
|
|
2212
2209
|
cwd,
|
|
2213
|
-
backendId
|
|
2210
|
+
backendId,
|
|
2211
|
+
cursor
|
|
2214
2212
|
});
|
|
2215
2213
|
this.sendRpcResponse(request.requestId, result);
|
|
2216
2214
|
} catch (error) {
|
|
@@ -2245,27 +2243,6 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
2245
2243
|
this.sendRpcError(request.requestId, error);
|
|
2246
2244
|
}
|
|
2247
2245
|
});
|
|
2248
|
-
this.socket.on("rpc:session:resume", async (request) => {
|
|
2249
|
-
try {
|
|
2250
|
-
const { sessionId, cwd } = request.params;
|
|
2251
|
-
logger.info(
|
|
2252
|
-
{ requestId: request.requestId, sessionId, cwd },
|
|
2253
|
-
"rpc_session_resume"
|
|
2254
|
-
);
|
|
2255
|
-
const session = await sessionManager.resumeSession(sessionId, cwd);
|
|
2256
|
-
this.sendRpcResponse(request.requestId, session);
|
|
2257
|
-
} catch (error) {
|
|
2258
|
-
logger.error(
|
|
2259
|
-
{
|
|
2260
|
-
err: error,
|
|
2261
|
-
requestId: request.requestId,
|
|
2262
|
-
sessionId: request.params.sessionId
|
|
2263
|
-
},
|
|
2264
|
-
"rpc_session_resume_error"
|
|
2265
|
-
);
|
|
2266
|
-
this.sendRpcError(request.requestId, error);
|
|
2267
|
-
}
|
|
2268
|
-
});
|
|
2269
2246
|
}
|
|
2270
2247
|
setupSessionManagerListeners() {
|
|
2271
2248
|
const { sessionManager } = this.options;
|
|
@@ -2310,6 +2287,16 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
2310
2287
|
this.socket.emit("sessions:changed", payload);
|
|
2311
2288
|
}
|
|
2312
2289
|
});
|
|
2290
|
+
sessionManager.onSessionAttached((payload) => {
|
|
2291
|
+
if (this.connected) {
|
|
2292
|
+
this.socket.emit("session:attached", payload);
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
sessionManager.onSessionDetached((payload) => {
|
|
2296
|
+
if (this.connected) {
|
|
2297
|
+
this.socket.emit("session:detached", payload);
|
|
2298
|
+
}
|
|
2299
|
+
});
|
|
2313
2300
|
}
|
|
2314
2301
|
async listSessionResources(rootPath) {
|
|
2315
2302
|
const ig = await loadGitignore(rootPath);
|
|
@@ -2324,7 +2311,7 @@ var SocketClient = class extends EventEmitter3 {
|
|
|
2324
2311
|
if (collected.length >= MAX_RESOURCE_FILES) {
|
|
2325
2312
|
return collected;
|
|
2326
2313
|
}
|
|
2327
|
-
const entries = await
|
|
2314
|
+
const entries = await fs4.readdir(rootPath, { withFileTypes: true });
|
|
2328
2315
|
for (const entry of entries) {
|
|
2329
2316
|
if (collected.length >= MAX_RESOURCE_FILES) {
|
|
2330
2317
|
break;
|
|
@@ -2424,12 +2411,12 @@ var DaemonManager = class {
|
|
|
2424
2411
|
this.config = config;
|
|
2425
2412
|
}
|
|
2426
2413
|
async ensureHomeDirectory() {
|
|
2427
|
-
await
|
|
2428
|
-
await
|
|
2414
|
+
await fs5.mkdir(this.config.homePath, { recursive: true });
|
|
2415
|
+
await fs5.mkdir(this.config.logPath, { recursive: true });
|
|
2429
2416
|
}
|
|
2430
2417
|
async getPid() {
|
|
2431
2418
|
try {
|
|
2432
|
-
const content = await
|
|
2419
|
+
const content = await fs5.readFile(this.config.pidFile, "utf8");
|
|
2433
2420
|
const pid = Number.parseInt(content.trim(), 10);
|
|
2434
2421
|
if (Number.isNaN(pid)) {
|
|
2435
2422
|
return null;
|
|
@@ -2446,11 +2433,11 @@ var DaemonManager = class {
|
|
|
2446
2433
|
}
|
|
2447
2434
|
}
|
|
2448
2435
|
async writePidFile(pid) {
|
|
2449
|
-
await
|
|
2436
|
+
await fs5.writeFile(this.config.pidFile, String(pid), "utf8");
|
|
2450
2437
|
}
|
|
2451
2438
|
async removePidFile() {
|
|
2452
2439
|
try {
|
|
2453
|
-
await
|
|
2440
|
+
await fs5.unlink(this.config.pidFile);
|
|
2454
2441
|
} catch {
|
|
2455
2442
|
}
|
|
2456
2443
|
}
|
|
@@ -2535,7 +2522,7 @@ var DaemonManager = class {
|
|
|
2535
2522
|
logger.error("daemon_spawn_failed");
|
|
2536
2523
|
throw new Error("Failed to spawn daemon process");
|
|
2537
2524
|
}
|
|
2538
|
-
const logStream = await
|
|
2525
|
+
const logStream = await fs5.open(logFile, "a");
|
|
2539
2526
|
const fileHandle = logStream;
|
|
2540
2527
|
child.stdout?.on("data", (data) => {
|
|
2541
2528
|
fileHandle.write(`[stdout] ${data.toString()}`).catch(() => {
|
|
@@ -2611,7 +2598,7 @@ var DaemonManager = class {
|
|
|
2611
2598
|
});
|
|
2612
2599
|
}
|
|
2613
2600
|
async logs(options) {
|
|
2614
|
-
const files = await
|
|
2601
|
+
const files = await fs5.readdir(this.config.logPath);
|
|
2615
2602
|
const logFiles = files.filter((f) => f.endsWith("-daemon.log")).sort().reverse();
|
|
2616
2603
|
if (logFiles.length === 0) {
|
|
2617
2604
|
logger.warn("daemon_logs_empty");
|
|
@@ -2630,7 +2617,7 @@ var DaemonManager = class {
|
|
|
2630
2617
|
tail.on("close", () => resolve());
|
|
2631
2618
|
});
|
|
2632
2619
|
} else {
|
|
2633
|
-
const content = await
|
|
2620
|
+
const content = await fs5.readFile(latestLog, "utf8");
|
|
2634
2621
|
const lines = content.split("\n");
|
|
2635
2622
|
const count = options?.lines ?? 50;
|
|
2636
2623
|
console.log(lines.slice(-count).join("\n"));
|