@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 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 fs4 from "fs/promises";
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 response.sessions;
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
- this.touchSession(session.sessionId);
1169
+ record.updatedAt = /* @__PURE__ */ new Date();
1136
1170
  this.sessionUpdateEmitter.emit("update", notification);
1137
- const update = notification.update;
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 agentSessions = await connection.listSessions({
1429
- cwd: options?.cwd
1446
+ const response = await connection.listSessions({
1447
+ cwd: options?.cwd,
1448
+ cursor: options?.cursor
1430
1449
  });
1431
- for (const session of agentSessions) {
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: `Loaded Session`,
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
- this.setupSessionSubscriptions(record);
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
- * Resume an active session from the ACP agent.
1528
- * This does not replay message history.
1529
- * @param sessionId The session ID to resume
1530
- * @param cwd The working directory
1531
- * @param backendId Optional backend ID
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
- const backend = this.resolveBackend(backendId);
1540
- const connection = new AcpConnection({
1541
- backend,
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
- try {
1548
- await connection.connect();
1549
- if (!connection.supportsSessionResume()) {
1550
- throw createCapabilityNotSupportedError(
1551
- "Agent does not support session resuming"
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
- record.unsubscribe = connection.onSessionUpdate(
1605
- (notification) => {
1606
- this.touchSession(sessionId);
1607
- this.sessionUpdateEmitter.emit("update", notification);
1608
- const update = notification.update;
1609
- if (update.sessionUpdate === "current_mode_update") {
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 fs3 from "fs/promises";
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 fs3.readFile(gitignorePath, "utf8");
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 fs3.readdir(dirPath, { withFileTypes: true });
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 fs3.stat(entryPath);
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
- const { sessions, capabilities } = await this.options.sessionManager.discoverSessions();
1817
- if (sessions.length > 0) {
1818
- this.socket.emit("sessions:discovered", { sessions, capabilities });
1819
- logger.info(
1820
- { count: sessions.length, capabilities },
1821
- "historical_sessions_discovered"
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 fs3.readFile(resolved);
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 fs3.readFile(resolved, "utf8");
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 fs3.readdir(rootPath, { withFileTypes: true });
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 fs4.mkdir(this.config.homePath, { recursive: true });
2428
- await fs4.mkdir(this.config.logPath, { recursive: true });
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 fs4.readFile(this.config.pidFile, "utf8");
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 fs4.writeFile(this.config.pidFile, String(pid), "utf8");
2436
+ await fs5.writeFile(this.config.pidFile, String(pid), "utf8");
2450
2437
  }
2451
2438
  async removePidFile() {
2452
2439
  try {
2453
- await fs4.unlink(this.config.pidFile);
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 fs4.open(logFile, "a");
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 fs4.readdir(this.config.logPath);
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 fs4.readFile(latestLog, "utf8");
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"));