@runtimescope/collector 0.8.0 → 0.9.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.
@@ -93,7 +93,8 @@ var EventStore = class {
93
93
  connectedAt: se.connectedAt,
94
94
  sdkVersion: se.sdkVersion,
95
95
  eventCount: 0,
96
- isConnected: true
96
+ isConnected: true,
97
+ projectId: se.projectId
97
98
  });
98
99
  }
99
100
  const session = this.sessions.get(event.sessionId);
@@ -113,6 +114,7 @@ var EventStore = class {
113
114
  return this.buffer.query((e) => {
114
115
  if (e.eventType !== "network") return false;
115
116
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
117
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
116
118
  const ne = e;
117
119
  if (ne.timestamp < since) return false;
118
120
  if (filter.urlPattern && !ne.url.includes(filter.urlPattern)) return false;
@@ -127,6 +129,7 @@ var EventStore = class {
127
129
  return this.buffer.query((e) => {
128
130
  if (e.eventType !== "console") return false;
129
131
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
132
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
130
133
  const ce = e;
131
134
  if (ce.timestamp < since) return false;
132
135
  if (filter.level && ce.level !== filter.level) return false;
@@ -148,26 +151,37 @@ var EventStore = class {
148
151
  return this.buffer.toArray().filter((e) => {
149
152
  if (e.timestamp < since) return false;
150
153
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
154
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
151
155
  if (typeSet && !typeSet.has(e.eventType)) return false;
152
156
  return true;
153
157
  });
154
158
  }
155
- getAllEvents(sinceSeconds, sessionId) {
159
+ getAllEvents(sinceSeconds, sessionId, projectId) {
156
160
  const since = sinceSeconds ? Date.now() - sinceSeconds * 1e3 : 0;
157
161
  return this.buffer.toArray().filter((e) => {
158
162
  if (e.timestamp < since) return false;
159
163
  if (sessionId && e.sessionId !== sessionId) return false;
164
+ if (projectId && !this.matchesProjectId(e.sessionId, projectId)) return false;
160
165
  return true;
161
166
  });
162
167
  }
163
168
  getSessionIdsForProject(appName) {
164
169
  return Array.from(this.sessions.values()).filter((s) => s.appName === appName).map((s) => s.sessionId);
165
170
  }
171
+ getSessionIdsForProjectId(projectId) {
172
+ return Array.from(this.sessions.values()).filter((s) => s.projectId === projectId).map((s) => s.sessionId);
173
+ }
174
+ /** Check if an event belongs to the given projectId (via its session). */
175
+ matchesProjectId(sessionId, projectId) {
176
+ const session = this.sessions.get(sessionId);
177
+ return session?.projectId === projectId;
178
+ }
166
179
  getStateEvents(filter = {}) {
167
180
  const since = filter.sinceSeconds ? Date.now() - filter.sinceSeconds * 1e3 : 0;
168
181
  return this.buffer.query((e) => {
169
182
  if (e.eventType !== "state") return false;
170
183
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
184
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
171
185
  const se = e;
172
186
  if (se.timestamp < since) return false;
173
187
  if (filter.storeId && se.storeId !== filter.storeId) return false;
@@ -179,6 +193,7 @@ var EventStore = class {
179
193
  return this.buffer.query((e) => {
180
194
  if (e.eventType !== "render") return false;
181
195
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
196
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
182
197
  const re = e;
183
198
  if (re.timestamp < since) return false;
184
199
  if (filter.componentName) {
@@ -195,6 +210,7 @@ var EventStore = class {
195
210
  return this.buffer.query((e) => {
196
211
  if (e.eventType !== "performance") return false;
197
212
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
213
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
198
214
  const pe = e;
199
215
  if (pe.timestamp < since) return false;
200
216
  if (filter.metricName && pe.metricName !== filter.metricName) return false;
@@ -206,6 +222,7 @@ var EventStore = class {
206
222
  return this.buffer.query((e) => {
207
223
  if (e.eventType !== "database") return false;
208
224
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
225
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
209
226
  const de = e;
210
227
  if (de.timestamp < since) return false;
211
228
  if (filter.table) {
@@ -227,6 +244,7 @@ var EventStore = class {
227
244
  return this.buffer.query((e) => {
228
245
  if (e.eventType !== "custom") return false;
229
246
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
247
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
230
248
  const ce = e;
231
249
  if (ce.timestamp < since) return false;
232
250
  if (filter.name && ce.name !== filter.name) return false;
@@ -238,6 +256,7 @@ var EventStore = class {
238
256
  return this.buffer.query((e) => {
239
257
  if (e.eventType !== "ui") return false;
240
258
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
259
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
241
260
  const ue = e;
242
261
  if (ue.timestamp < since) return false;
243
262
  if (filter.action && ue.action !== filter.action) return false;
@@ -252,6 +271,7 @@ var EventStore = class {
252
271
  const results = this.buffer.query((e) => {
253
272
  if (e.eventType !== eventType) return false;
254
273
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
274
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
255
275
  if (e.timestamp < since) return false;
256
276
  if (filter.url) {
257
277
  const re = e;
@@ -266,6 +286,7 @@ var EventStore = class {
266
286
  return this.buffer.query((e) => {
267
287
  if (e.eventType !== eventType) return false;
268
288
  if (filter.sessionId && e.sessionId !== filter.sessionId) return false;
289
+ if (filter.projectId && !this.matchesProjectId(e.sessionId, filter.projectId)) return false;
269
290
  if (e.timestamp < since) return false;
270
291
  if (filter.url) {
271
292
  const re = e;
@@ -306,6 +327,32 @@ var EventStore = class {
306
327
  }
307
328
  };
308
329
 
330
+ // src/project-id.ts
331
+ import { randomBytes } from "crypto";
332
+ var PROJECT_ID_PREFIX = "proj_";
333
+ var PROJECT_ID_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
334
+ var PROJECT_ID_LENGTH = 12;
335
+ var PROJECT_ID_REGEX = /^proj_[a-z0-9]{12}$/;
336
+ function generateProjectId() {
337
+ const bytes = randomBytes(PROJECT_ID_LENGTH);
338
+ let id = PROJECT_ID_PREFIX;
339
+ for (let i = 0; i < PROJECT_ID_LENGTH; i++) {
340
+ id += PROJECT_ID_CHARS[bytes[i] % PROJECT_ID_CHARS.length];
341
+ }
342
+ return id;
343
+ }
344
+ function isValidProjectId(id) {
345
+ return PROJECT_ID_REGEX.test(id);
346
+ }
347
+ function getOrCreateProjectId(projectManager, appName) {
348
+ const existing = projectManager.getProjectIdForApp(appName);
349
+ if (existing) return existing;
350
+ const projectId = generateProjectId();
351
+ projectManager.ensureProjectDir(appName);
352
+ projectManager.setProjectIdForApp(appName, projectId);
353
+ return projectId;
354
+ }
355
+
309
356
  // src/sqlite-store.ts
310
357
  import { renameSync, existsSync } from "fs";
311
358
  import { createRequire } from "module";
@@ -995,7 +1042,7 @@ var CollectorServer = class {
995
1042
  console.error(`[RuntimeScope] Session ${clientInfo.sessionId} disconnected`);
996
1043
  for (const cb of this.disconnectCallbacks) {
997
1044
  try {
998
- cb(clientInfo.sessionId, clientInfo.projectName);
1045
+ cb(clientInfo.sessionId, clientInfo.projectName, clientInfo.projectId);
999
1046
  } catch {
1000
1047
  }
1001
1048
  }
@@ -1027,9 +1074,11 @@ var CollectorServer = class {
1027
1074
  this.pendingHandshakes.delete(ws);
1028
1075
  }
1029
1076
  const projectName = payload.appName;
1077
+ const projectId = payload.projectId ?? (this.projectManager ? getOrCreateProjectId(this.projectManager, projectName) : void 0);
1030
1078
  this.clients.set(ws, {
1031
1079
  sessionId: payload.sessionId,
1032
- projectName
1080
+ projectName,
1081
+ projectId
1033
1082
  });
1034
1083
  const sqliteStore = this.ensureSqliteStore(projectName);
1035
1084
  if (sqliteStore) {
@@ -1050,6 +1099,7 @@ var CollectorServer = class {
1050
1099
  timestamp: msg.timestamp,
1051
1100
  eventType: "session",
1052
1101
  appName: payload.appName,
1102
+ projectId,
1053
1103
  connectedAt: msg.timestamp,
1054
1104
  sdkVersion: payload.sdkVersion
1055
1105
  });
@@ -1058,7 +1108,7 @@ var CollectorServer = class {
1058
1108
  );
1059
1109
  for (const cb of this.connectCallbacks) {
1060
1110
  try {
1061
- cb(payload.sessionId, projectName);
1111
+ cb(payload.sessionId, projectName, projectId);
1062
1112
  } catch {
1063
1113
  }
1064
1114
  }
@@ -1117,7 +1167,7 @@ var CollectorServer = class {
1117
1167
  getConnectedSessions() {
1118
1168
  const sessions = [];
1119
1169
  for (const [, info] of this.clients) {
1120
- sessions.push({ sessionId: info.sessionId, projectName: info.projectName });
1170
+ sessions.push({ sessionId: info.sessionId, projectName: info.projectName, projectId: info.projectId });
1121
1171
  }
1122
1172
  return sessions;
1123
1173
  }
@@ -1286,6 +1336,29 @@ var ProjectManager = class {
1286
1336
  projectExists(projectName) {
1287
1337
  return existsSync2(this.getProjectDir(projectName));
1288
1338
  }
1339
+ // --- Project ID helpers ---
1340
+ /** Look up the stored projectId for an appName. Returns null if none set. */
1341
+ getProjectIdForApp(appName) {
1342
+ const config = this.getProjectConfig(appName);
1343
+ return config?.projectId ?? null;
1344
+ }
1345
+ /** Persist a projectId for an appName in its project config. */
1346
+ setProjectIdForApp(appName, projectId) {
1347
+ this.ensureProjectDir(appName);
1348
+ const config = this.getProjectConfig(appName);
1349
+ if (config) {
1350
+ config.projectId = projectId;
1351
+ this.saveProjectConfig(appName, config);
1352
+ }
1353
+ }
1354
+ /** Resolve a projectId to an appName by scanning all project configs. Returns null if not found. */
1355
+ getAppForProjectId(projectId) {
1356
+ for (const name of this.listProjects()) {
1357
+ const config = this.getProjectConfig(name);
1358
+ if (config?.projectId === projectId) return name;
1359
+ }
1360
+ return null;
1361
+ }
1289
1362
  // --- Environment variable resolution ---
1290
1363
  resolveEnvVars(value) {
1291
1364
  return value.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
@@ -1340,7 +1413,7 @@ var ProjectManager = class {
1340
1413
  };
1341
1414
 
1342
1415
  // src/auth.ts
1343
- import { randomBytes, timingSafeEqual } from "crypto";
1416
+ import { randomBytes as randomBytes2, timingSafeEqual } from "crypto";
1344
1417
  var AuthManager = class {
1345
1418
  keys = /* @__PURE__ */ new Map();
1346
1419
  enabled;
@@ -1387,7 +1460,7 @@ var AuthManager = class {
1387
1460
  };
1388
1461
  function generateApiKey(label, project) {
1389
1462
  return {
1390
- key: randomBytes(32).toString("hex"),
1463
+ key: randomBytes2(32).toString("hex"),
1391
1464
  label,
1392
1465
  project,
1393
1466
  createdAt: Date.now()
@@ -1500,6 +1573,198 @@ var Redactor = class {
1500
1573
  }
1501
1574
  };
1502
1575
 
1576
+ // src/platform.ts
1577
+ import { execFileSync, execSync } from "child_process";
1578
+ import { readlinkSync, readdirSync as readdirSync2 } from "fs";
1579
+ import { join as join2 } from "path";
1580
+ var IS_WIN = process.platform === "win32";
1581
+ var IS_LINUX = process.platform === "linux";
1582
+ function runFile(cmd, args, timeoutMs = 5e3) {
1583
+ try {
1584
+ return execFileSync(cmd, args, { encoding: "utf-8", timeout: timeoutMs }).trim();
1585
+ } catch {
1586
+ return null;
1587
+ }
1588
+ }
1589
+ function runShell(cmd, timeoutMs = 5e3) {
1590
+ try {
1591
+ return execSync(cmd, { encoding: "utf-8", timeout: timeoutMs }).trim();
1592
+ } catch {
1593
+ return null;
1594
+ }
1595
+ }
1596
+ function getPidsOnPort_unix(port) {
1597
+ const lsof = runFile("lsof", ["-ti", `:${port}`], 5e3);
1598
+ if (lsof) {
1599
+ return lsof.split("\n").map(Number).filter((n) => n > 0);
1600
+ }
1601
+ if (IS_LINUX) {
1602
+ const ss = runShell(`ss -tlnp 2>/dev/null | grep ':${port} '`);
1603
+ if (ss) {
1604
+ const pids = [];
1605
+ for (const match of ss.matchAll(/pid=(\d+)/g)) {
1606
+ pids.push(parseInt(match[1], 10));
1607
+ }
1608
+ return [...new Set(pids)];
1609
+ }
1610
+ }
1611
+ return [];
1612
+ }
1613
+ function getPidsOnPort_win(port) {
1614
+ const out = runShell(`netstat -ano | findstr :${port} | findstr LISTENING`);
1615
+ if (!out) return [];
1616
+ const pids = [];
1617
+ for (const line of out.split("\n")) {
1618
+ const parts = line.trim().split(/\s+/);
1619
+ const pid = parseInt(parts[parts.length - 1], 10);
1620
+ if (pid > 0) pids.push(pid);
1621
+ }
1622
+ return [...new Set(pids)];
1623
+ }
1624
+ function getPidsOnPort(port) {
1625
+ return IS_WIN ? getPidsOnPort_win(port) : getPidsOnPort_unix(port);
1626
+ }
1627
+ function getListenPorts_unix(pid) {
1628
+ const lsof = runShell(`lsof -nP -p ${pid} 2>/dev/null | grep LISTEN`, 3e3);
1629
+ if (lsof) {
1630
+ const ports = [];
1631
+ for (const line of lsof.split("\n")) {
1632
+ const match = line.match(/:(\d+)\s+\(LISTEN\)/);
1633
+ if (match) ports.push(parseInt(match[1], 10));
1634
+ }
1635
+ return [...new Set(ports)];
1636
+ }
1637
+ if (IS_LINUX) {
1638
+ const ss = runShell(`ss -tlnp 2>/dev/null | grep 'pid=${pid},'`, 3e3);
1639
+ if (ss) {
1640
+ const ports = [];
1641
+ for (const match of ss.matchAll(/:(\d+)\s/g)) {
1642
+ ports.push(parseInt(match[1], 10));
1643
+ }
1644
+ return [...new Set(ports)];
1645
+ }
1646
+ }
1647
+ return [];
1648
+ }
1649
+ function getListenPorts_win(pid) {
1650
+ const out = runShell(`netstat -ano | findstr ${pid} | findstr LISTENING`);
1651
+ if (!out) return [];
1652
+ const ports = [];
1653
+ for (const line of out.split("\n")) {
1654
+ const match = line.match(/:(\d+)\s/);
1655
+ if (match) {
1656
+ const port = parseInt(match[1], 10);
1657
+ const linePid = parseInt(line.trim().split(/\s+/).pop(), 10);
1658
+ if (linePid === pid) ports.push(port);
1659
+ }
1660
+ }
1661
+ return [...new Set(ports)];
1662
+ }
1663
+ function getListenPorts(pid) {
1664
+ return IS_WIN ? getListenPorts_win(pid) : getListenPorts_unix(pid);
1665
+ }
1666
+ function getProcessCwd_mac(pid) {
1667
+ const out = runShell(`lsof -p ${pid} 2>/dev/null | grep cwd`, 3e3);
1668
+ if (!out) return void 0;
1669
+ const match = out.match(/cwd\s+\w+\s+\w+\s+\d+\w?\s+\d+\s+\d+\s+\d+\s+(.+)/);
1670
+ return match?.[1]?.trim();
1671
+ }
1672
+ function getProcessCwd_linux(pid) {
1673
+ try {
1674
+ return readlinkSync(`/proc/${pid}/cwd`);
1675
+ } catch {
1676
+ return getProcessCwd_mac(pid);
1677
+ }
1678
+ }
1679
+ function getProcessCwd(pid) {
1680
+ if (IS_WIN) return void 0;
1681
+ if (IS_LINUX) return getProcessCwd_linux(pid);
1682
+ return getProcessCwd_mac(pid);
1683
+ }
1684
+ function findPidsInDir_lsof(dir) {
1685
+ const out = runShell(`lsof -t +D "${dir}" 2>/dev/null | head -20`);
1686
+ if (!out) return [];
1687
+ return out.split("\n").map(Number).filter((n) => n > 0);
1688
+ }
1689
+ function findPidsInDir_linux(dir) {
1690
+ const lsofResult = findPidsInDir_lsof(dir);
1691
+ if (lsofResult.length > 0) return lsofResult;
1692
+ try {
1693
+ const pids = [];
1694
+ for (const entry of readdirSync2("/proc")) {
1695
+ const pid = parseInt(entry, 10);
1696
+ if (isNaN(pid) || pid <= 1) continue;
1697
+ try {
1698
+ const cwd = readlinkSync(join2("/proc", entry, "cwd"));
1699
+ if (cwd.startsWith(dir)) pids.push(pid);
1700
+ } catch {
1701
+ }
1702
+ }
1703
+ return pids;
1704
+ } catch {
1705
+ return [];
1706
+ }
1707
+ }
1708
+ function findPidsInDirectory(dir) {
1709
+ if (IS_WIN) return [];
1710
+ if (IS_LINUX) return findPidsInDir_linux(dir);
1711
+ return findPidsInDir_lsof(dir);
1712
+ }
1713
+ function parseProcessList_unix() {
1714
+ const output = runFile("ps", ["aux"]);
1715
+ if (!output) return [];
1716
+ const lines = output.split("\n").slice(1);
1717
+ const results = [];
1718
+ for (const line of lines) {
1719
+ const parts = line.trim().split(/\s+/);
1720
+ if (parts.length < 11) continue;
1721
+ const pid = parseInt(parts[1], 10);
1722
+ const cpu = parseFloat(parts[2]);
1723
+ const mem = parseFloat(parts[3]);
1724
+ const command = parts.slice(10).join(" ");
1725
+ if (isNaN(pid)) continue;
1726
+ results.push({ pid, cpu, mem, command });
1727
+ }
1728
+ return results;
1729
+ }
1730
+ function parseProcessList_win() {
1731
+ const output = runFile("tasklist", ["/FO", "CSV", "/NH"], 1e4);
1732
+ if (!output) return [];
1733
+ const results = [];
1734
+ for (const line of output.split("\n")) {
1735
+ const parts = line.match(/"([^"]*)"/g);
1736
+ if (!parts || parts.length < 5) continue;
1737
+ const pid = parseInt(parts[1].replace(/"/g, ""), 10);
1738
+ const command = parts[0].replace(/"/g, "");
1739
+ const memStr = parts[4].replace(/"/g, "").replace(/[, K]/gi, "");
1740
+ const mem = parseInt(memStr, 10) / 1024;
1741
+ if (isNaN(pid)) continue;
1742
+ results.push({ pid, cpu: 0, mem, command });
1743
+ }
1744
+ return results;
1745
+ }
1746
+ function parseProcessList() {
1747
+ return IS_WIN ? parseProcessList_win() : parseProcessList_unix();
1748
+ }
1749
+ function getProcessMemoryMB_unix(pid) {
1750
+ const out = runFile("ps", ["-o", "rss=", "-p", String(pid)], 2e3);
1751
+ if (!out) return 0;
1752
+ const rss = parseInt(out, 10);
1753
+ return isNaN(rss) ? 0 : rss / 1024;
1754
+ }
1755
+ function getProcessMemoryMB_win(pid) {
1756
+ const out = runFile("tasklist", ["/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], 3e3);
1757
+ if (!out) return 0;
1758
+ const parts = out.match(/"([^"]*)"/g);
1759
+ if (!parts || parts.length < 5) return 0;
1760
+ const memStr = parts[4].replace(/"/g, "").replace(/[, K]/gi, "");
1761
+ const kb = parseInt(memStr, 10);
1762
+ return isNaN(kb) ? 0 : kb / 1024;
1763
+ }
1764
+ function getProcessMemoryMB(pid) {
1765
+ return IS_WIN ? getProcessMemoryMB_win(pid) : getProcessMemoryMB_unix(pid);
1766
+ }
1767
+
1503
1768
  // src/session-manager.ts
1504
1769
  var SessionManager = class {
1505
1770
  projectManager;
@@ -1642,9 +1907,9 @@ import { WebSocketServer as WebSocketServer2 } from "ws";
1642
1907
  // src/pm/pm-routes.ts
1643
1908
  import { readdir, readFile, writeFile, unlink, mkdir } from "fs/promises";
1644
1909
  import { existsSync as existsSync3 } from "fs";
1645
- import { join as join2 } from "path";
1910
+ import { join as join3 } from "path";
1646
1911
  import { homedir as homedir2 } from "os";
1647
- import { spawn, execSync, execFileSync } from "child_process";
1912
+ import { spawn, execFileSync as execFileSync2 } from "child_process";
1648
1913
  var LOG_RING_SIZE = 500;
1649
1914
  var managedProcesses = /* @__PURE__ */ new Map();
1650
1915
  function pushLog(mp, stream, line) {
@@ -1954,13 +2219,13 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
1954
2219
  helpers.json(res, { data: [], count: 0 });
1955
2220
  return;
1956
2221
  }
1957
- const memoryDir = join2(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory");
2222
+ const memoryDir = join3(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory");
1958
2223
  try {
1959
2224
  const files = await readdir(memoryDir);
1960
2225
  const mdFiles = files.filter((f) => f.endsWith(".md"));
1961
2226
  const result = await Promise.all(
1962
2227
  mdFiles.map(async (filename) => {
1963
- const content = await readFile(join2(memoryDir, filename), "utf-8");
2228
+ const content = await readFile(join3(memoryDir, filename), "utf-8");
1964
2229
  return { filename, content, sizeBytes: Buffer.byteLength(content) };
1965
2230
  })
1966
2231
  );
@@ -1977,7 +2242,7 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
1977
2242
  helpers.json(res, { error: "Project not found" }, 404);
1978
2243
  return;
1979
2244
  }
1980
- const filePath = join2(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory", filename);
2245
+ const filePath = join3(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory", filename);
1981
2246
  try {
1982
2247
  const content = await readFile(filePath, "utf-8");
1983
2248
  helpers.json(res, { filename, content, sizeBytes: Buffer.byteLength(content) });
@@ -2000,9 +2265,9 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
2000
2265
  }
2001
2266
  try {
2002
2267
  const { content } = JSON.parse(body);
2003
- const memoryDir = join2(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory");
2268
+ const memoryDir = join3(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory");
2004
2269
  await mkdir(memoryDir, { recursive: true });
2005
- await writeFile(join2(memoryDir, filename), content, "utf-8");
2270
+ await writeFile(join3(memoryDir, filename), content, "utf-8");
2006
2271
  helpers.json(res, { ok: true });
2007
2272
  } catch (err) {
2008
2273
  helpers.json(res, { error: err.message }, 500);
@@ -2016,7 +2281,7 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
2016
2281
  helpers.json(res, { error: "Project not found" }, 404);
2017
2282
  return;
2018
2283
  }
2019
- const filePath = join2(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory", filename);
2284
+ const filePath = join3(homedir2(), ".claude", "projects", project.claudeProjectKey, "memory", filename);
2020
2285
  try {
2021
2286
  await unlink(filePath);
2022
2287
  helpers.json(res, { ok: true });
@@ -2076,7 +2341,7 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
2076
2341
  const { content } = JSON.parse(body);
2077
2342
  const paths = getRulesPaths(project.claudeProjectKey, project.path);
2078
2343
  const filePath = paths[scope];
2079
- const dir = join2(filePath, "..");
2344
+ const dir = join3(filePath, "..");
2080
2345
  await mkdir(dir, { recursive: true });
2081
2346
  await writeFile(filePath, content, "utf-8");
2082
2347
  helpers.json(res, { ok: true });
@@ -2096,7 +2361,7 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
2096
2361
  return;
2097
2362
  }
2098
2363
  try {
2099
- const pkgPath = join2(project.path, "package.json");
2364
+ const pkgPath = join3(project.path, "package.json");
2100
2365
  const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
2101
2366
  const scripts = pkg.scripts ?? {};
2102
2367
  const recommended = ["dev", "start", "serve"].find((s) => s in scripts) ?? null;
@@ -2266,15 +2531,8 @@ function createPmRouter(pmStore, discovery, helpers, broadcastDevServer) {
2266
2531
  }
2267
2532
  managedProcesses.delete(id);
2268
2533
  } else if (project.path) {
2269
- try {
2270
- const output = execSync(
2271
- `lsof -t +D "${project.path}" 2>/dev/null | head -5`,
2272
- { encoding: "utf-8", timeout: 5e3 }
2273
- ).trim();
2274
- const pids = output.split("\n").filter(Boolean).map(Number).filter((n) => n > 1 && n !== process.pid);
2275
- if (pids.length > 0) pid = pids[0];
2276
- } catch {
2277
- }
2534
+ const pids = findPidsInDirectory(project.path).filter((n) => n > 1 && n !== process.pid);
2535
+ if (pids.length > 0) pid = pids[0];
2278
2536
  }
2279
2537
  if (!pid) {
2280
2538
  helpers.json(res, { error: "No running dev server found for this project" }, 404);
@@ -2529,17 +2787,17 @@ function sanitizeFilename(name) {
2529
2787
  function getRulesPaths(claudeProjectKey, projectPath) {
2530
2788
  const home = homedir2();
2531
2789
  return {
2532
- global: join2(home, ".claude", "CLAUDE.md"),
2533
- project: claudeProjectKey ? join2(home, ".claude", "projects", claudeProjectKey, "CLAUDE.md") : join2(projectPath ?? "", ".claude", "CLAUDE.md"),
2534
- local: projectPath ? join2(projectPath, "CLAUDE.md") : join2(home, "CLAUDE.md")
2790
+ global: join3(home, ".claude", "CLAUDE.md"),
2791
+ project: claudeProjectKey ? join3(home, ".claude", "projects", claudeProjectKey, "CLAUDE.md") : join3(projectPath ?? "", ".claude", "CLAUDE.md"),
2792
+ local: projectPath ? join3(projectPath, "CLAUDE.md") : join3(home, "CLAUDE.md")
2535
2793
  };
2536
2794
  }
2537
2795
  function execGit(args, cwd) {
2538
- return execFileSync("git", args, { cwd, encoding: "utf-8", timeout: 1e4, maxBuffer: 10 * 1024 * 1024 });
2796
+ return execFileSync2("git", args, { cwd, encoding: "utf-8", timeout: 1e4, maxBuffer: 10 * 1024 * 1024 });
2539
2797
  }
2540
2798
  function isGitRepo(path) {
2541
2799
  try {
2542
- execFileSync("git", ["rev-parse", "--git-dir"], { cwd: path, encoding: "utf-8", timeout: 3e3 });
2800
+ execFileSync2("git", ["rev-parse", "--git-dir"], { cwd: path, encoding: "utf-8", timeout: 3e3 });
2543
2801
  return true;
2544
2802
  } catch {
2545
2803
  return false;
@@ -2602,6 +2860,8 @@ var HttpServer = class {
2602
2860
  activePort = 9091;
2603
2861
  startedAt = Date.now();
2604
2862
  connectedSessionsGetter = null;
2863
+ pmStore = null;
2864
+ projectManager = null;
2605
2865
  constructor(store, processMonitor, options) {
2606
2866
  this.store = store;
2607
2867
  this.processMonitor = processMonitor ?? null;
@@ -2609,6 +2869,8 @@ var HttpServer = class {
2609
2869
  this.allowedOrigins = options?.allowedOrigins ?? null;
2610
2870
  this.rateLimiter = options?.rateLimiter ?? null;
2611
2871
  this.connectedSessionsGetter = options?.getConnectedSessions ?? null;
2872
+ this.pmStore = options?.pmStore ?? null;
2873
+ this.projectManager = options?.projectManager ?? null;
2612
2874
  this.registerRoutes();
2613
2875
  if (options?.pmStore && options?.discovery) {
2614
2876
  this.pmRouter = createPmRouter(options.pmStore, options.discovery, {
@@ -2640,12 +2902,14 @@ var HttpServer = class {
2640
2902
  existing.sessions.push(s.sessionId);
2641
2903
  existing.eventCount += s.eventCount;
2642
2904
  if (s.isConnected) existing.isConnected = true;
2905
+ if (!existing.projectId && s.projectId) existing.projectId = s.projectId;
2643
2906
  } else {
2644
2907
  projectMap.set(s.appName, {
2645
2908
  appName: s.appName,
2646
2909
  sessions: [s.sessionId],
2647
2910
  isConnected: s.isConnected,
2648
- eventCount: s.eventCount
2911
+ eventCount: s.eventCount,
2912
+ projectId: s.projectId
2649
2913
  });
2650
2914
  }
2651
2915
  }
@@ -2715,7 +2979,8 @@ var HttpServer = class {
2715
2979
  sinceSeconds: numParam(params, "since_seconds"),
2716
2980
  urlPattern: params.get("url_pattern") ?? void 0,
2717
2981
  method: params.get("method") ?? void 0,
2718
- sessionId: params.get("session_id") ?? void 0
2982
+ sessionId: params.get("session_id") ?? void 0,
2983
+ projectId: params.get("project_id") ?? void 0
2719
2984
  });
2720
2985
  this.json(res, { data: events, count: events.length });
2721
2986
  });
@@ -2724,7 +2989,8 @@ var HttpServer = class {
2724
2989
  sinceSeconds: numParam(params, "since_seconds"),
2725
2990
  level: params.get("level") ?? void 0,
2726
2991
  search: params.get("search") ?? void 0,
2727
- sessionId: params.get("session_id") ?? void 0
2992
+ sessionId: params.get("session_id") ?? void 0,
2993
+ projectId: params.get("project_id") ?? void 0
2728
2994
  });
2729
2995
  this.json(res, { data: events, count: events.length });
2730
2996
  });
@@ -2732,7 +2998,8 @@ var HttpServer = class {
2732
2998
  const events = this.store.getStateEvents({
2733
2999
  sinceSeconds: numParam(params, "since_seconds"),
2734
3000
  storeId: params.get("store_id") ?? void 0,
2735
- sessionId: params.get("session_id") ?? void 0
3001
+ sessionId: params.get("session_id") ?? void 0,
3002
+ projectId: params.get("project_id") ?? void 0
2736
3003
  });
2737
3004
  this.json(res, { data: events, count: events.length });
2738
3005
  });
@@ -2740,7 +3007,8 @@ var HttpServer = class {
2740
3007
  const events = this.store.getRenderEvents({
2741
3008
  sinceSeconds: numParam(params, "since_seconds"),
2742
3009
  componentName: params.get("component") ?? void 0,
2743
- sessionId: params.get("session_id") ?? void 0
3010
+ sessionId: params.get("session_id") ?? void 0,
3011
+ projectId: params.get("project_id") ?? void 0
2744
3012
  });
2745
3013
  this.json(res, { data: events, count: events.length });
2746
3014
  });
@@ -2748,7 +3016,8 @@ var HttpServer = class {
2748
3016
  const events = this.store.getPerformanceMetrics({
2749
3017
  sinceSeconds: numParam(params, "since_seconds"),
2750
3018
  metricName: params.get("metric") ?? void 0,
2751
- sessionId: params.get("session_id") ?? void 0
3019
+ sessionId: params.get("session_id") ?? void 0,
3020
+ projectId: params.get("project_id") ?? void 0
2752
3021
  });
2753
3022
  this.json(res, { data: events, count: events.length });
2754
3023
  });
@@ -2758,7 +3027,8 @@ var HttpServer = class {
2758
3027
  table: params.get("table") ?? void 0,
2759
3028
  minDurationMs: numParam(params, "min_duration_ms"),
2760
3029
  search: params.get("search") ?? void 0,
2761
- sessionId: params.get("session_id") ?? void 0
3030
+ sessionId: params.get("session_id") ?? void 0,
3031
+ projectId: params.get("project_id") ?? void 0
2762
3032
  });
2763
3033
  this.json(res, { data: events, count: events.length });
2764
3034
  });
@@ -2767,7 +3037,8 @@ var HttpServer = class {
2767
3037
  const events = this.store.getEventTimeline({
2768
3038
  sinceSeconds: numParam(params, "since_seconds"),
2769
3039
  eventTypes,
2770
- sessionId: params.get("session_id") ?? void 0
3040
+ sessionId: params.get("session_id") ?? void 0,
3041
+ projectId: params.get("project_id") ?? void 0
2771
3042
  });
2772
3043
  this.json(res, { data: events, count: events.length });
2773
3044
  });
@@ -2775,7 +3046,8 @@ var HttpServer = class {
2775
3046
  const events = this.store.getCustomEvents({
2776
3047
  name: params.get("name") ?? void 0,
2777
3048
  sinceSeconds: numParam(params, "since_seconds"),
2778
- sessionId: params.get("session_id") ?? void 0
3049
+ sessionId: params.get("session_id") ?? void 0,
3050
+ projectId: params.get("project_id") ?? void 0
2779
3051
  });
2780
3052
  this.json(res, { data: events, count: events.length });
2781
3053
  });
@@ -2784,7 +3056,8 @@ var HttpServer = class {
2784
3056
  const events = this.store.getUIInteractions({
2785
3057
  action: action ?? void 0,
2786
3058
  sinceSeconds: numParam(params, "since_seconds"),
2787
- sessionId: params.get("session_id") ?? void 0
3059
+ sessionId: params.get("session_id") ?? void 0,
3060
+ projectId: params.get("project_id") ?? void 0
2788
3061
  });
2789
3062
  this.json(res, { data: events, count: events.length });
2790
3063
  });
@@ -2806,6 +3079,7 @@ var HttpServer = class {
2806
3079
  return;
2807
3080
  }
2808
3081
  const payload = parsed;
3082
+ const projectId = typeof payload.projectId === "string" ? payload.projectId : payload.appName && this.projectManager ? getOrCreateProjectId(this.projectManager, payload.appName) : void 0;
2809
3083
  if (!payload.sessionId || !Array.isArray(payload.events) || payload.events.length === 0) {
2810
3084
  this.json(res, {
2811
3085
  error: "Required: sessionId (string), events (non-empty array)",
@@ -2822,9 +3096,16 @@ var HttpServer = class {
2822
3096
  timestamp: Date.now(),
2823
3097
  eventType: "session",
2824
3098
  appName: payload.appName,
3099
+ projectId,
2825
3100
  connectedAt: Date.now(),
2826
3101
  sdkVersion: payload.sdkVersion ?? "http"
2827
3102
  });
3103
+ if (this.pmStore) {
3104
+ try {
3105
+ this.pmStore.autoLinkApp(payload.appName, projectId);
3106
+ } catch {
3107
+ }
3108
+ }
2828
3109
  }
2829
3110
  const VALID_EVENT_TYPES = /* @__PURE__ */ new Set([
2830
3111
  "network",
@@ -3297,6 +3578,10 @@ var PmStore = class {
3297
3578
  this.db.exec("ALTER TABLE pm_projects ADD COLUMN runtime_apps TEXT DEFAULT NULL");
3298
3579
  } catch {
3299
3580
  }
3581
+ try {
3582
+ this.db.exec("ALTER TABLE pm_projects ADD COLUMN runtime_project_id TEXT DEFAULT NULL");
3583
+ } catch {
3584
+ }
3300
3585
  }
3301
3586
  // ============================================================
3302
3587
  // Projects
@@ -3382,6 +3667,10 @@ var PmStore = class {
3382
3667
  sets.push("runtimescope_project = ?");
3383
3668
  params.push(updates.runtimescopeProject ?? null);
3384
3669
  }
3670
+ if (updates.runtimeProjectId !== void 0) {
3671
+ sets.push("runtime_project_id = ?");
3672
+ params.push(updates.runtimeProjectId ?? null);
3673
+ }
3385
3674
  if (updates.metadata !== void 0) {
3386
3675
  sets.push("metadata = ?");
3387
3676
  params.push(JSON.stringify(updates.metadata));
@@ -3392,6 +3681,55 @@ var PmStore = class {
3392
3681
  params.push(id);
3393
3682
  this.db.prepare(`UPDATE pm_projects SET ${sets.join(", ")} WHERE id = ?`).run(...params);
3394
3683
  }
3684
+ /**
3685
+ * Auto-link an SDK appName to a PM project.
3686
+ * Matches by: exact name, directory basename, runtimescopeProject, or existing runtimeApps.
3687
+ * Returns the project ID if linked, null if no match found.
3688
+ */
3689
+ autoLinkApp(appName, projectId) {
3690
+ const projects = this.listProjects();
3691
+ const appLower = appName.toLowerCase();
3692
+ if (projectId) {
3693
+ const byProjectId = projects.find((p) => p.runtimeProjectId === projectId);
3694
+ if (byProjectId) {
3695
+ const apps2 = byProjectId.runtimeApps ?? [];
3696
+ if (!apps2.some((a) => a.toLowerCase() === appLower)) {
3697
+ apps2.push(appName);
3698
+ this.updateProject(byProjectId.id, { runtimeApps: apps2 });
3699
+ }
3700
+ return byProjectId.id;
3701
+ }
3702
+ }
3703
+ const alreadyLinked = projects.find(
3704
+ (p) => p.runtimeApps?.some((a) => a.toLowerCase() === appLower)
3705
+ );
3706
+ if (alreadyLinked) {
3707
+ if (projectId && !alreadyLinked.runtimeProjectId) {
3708
+ this.updateProject(alreadyLinked.id, { runtimeProjectId: projectId });
3709
+ }
3710
+ return alreadyLinked.id;
3711
+ }
3712
+ const match = projects.find((p) => {
3713
+ if (p.name.toLowerCase() === appLower) return true;
3714
+ if (p.path) {
3715
+ const basename3 = p.path.replace(/\/+$/, "").split("/").pop()?.toLowerCase();
3716
+ if (basename3 === appLower) return true;
3717
+ }
3718
+ if (p.runtimescopeProject?.toLowerCase() === appLower) return true;
3719
+ return false;
3720
+ });
3721
+ if (!match) return null;
3722
+ const apps = match.runtimeApps ?? [];
3723
+ if (!apps.some((a) => a.toLowerCase() === appLower)) {
3724
+ apps.push(appName);
3725
+ }
3726
+ const updates = { runtimeApps: apps };
3727
+ if (projectId && !match.runtimeProjectId) {
3728
+ updates.runtimeProjectId = projectId;
3729
+ }
3730
+ this.updateProject(match.id, updates);
3731
+ return match.id;
3732
+ }
3395
3733
  listCategories() {
3396
3734
  const rows = this.db.prepare("SELECT DISTINCT category FROM pm_projects WHERE category IS NOT NULL ORDER BY category ASC").all();
3397
3735
  return rows.map((r) => r.category);
@@ -3403,6 +3741,7 @@ var PmStore = class {
3403
3741
  path: row.path ?? void 0,
3404
3742
  claudeProjectKey: row.claude_project_key ?? void 0,
3405
3743
  runtimescopeProject: row.runtimescope_project ?? void 0,
3744
+ runtimeProjectId: row.runtime_project_id ?? void 0,
3406
3745
  runtimeApps: row.runtime_apps ? JSON.parse(row.runtime_apps) : void 0,
3407
3746
  phase: row.phase,
3408
3747
  managementAuthorized: row.management_authorized === 1,
@@ -3700,6 +4039,7 @@ var PmStore = class {
3700
4039
  p.category,
3701
4040
  p.sdk_installed,
3702
4041
  p.runtimescope_project,
4042
+ p.runtime_apps,
3703
4043
  COUNT(s.id) as session_count,
3704
4044
  COALESCE(SUM(s.cost_microdollars), 0) as total_cost,
3705
4045
  COALESCE(SUM(s.active_minutes), 0) as total_active_minutes,
@@ -4238,13 +4578,13 @@ async function parseSessionJsonl(jsonlPath, sessionId, projectId) {
4238
4578
 
4239
4579
  // src/pm/project-discovery.ts
4240
4580
  import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
4241
- import { join as join3, basename as basename2 } from "path";
4581
+ import { join as join4, basename as basename2 } from "path";
4242
4582
  import { existsSync as existsSync5 } from "fs";
4243
4583
  import { homedir as homedir3 } from "os";
4244
4584
  var LOG_PREFIX = "[RuntimeScope PM]";
4245
4585
  async function detectSdkInstalled(projectPath) {
4246
4586
  try {
4247
- const pkgPath = join3(projectPath, "package.json");
4587
+ const pkgPath = join4(projectPath, "package.json");
4248
4588
  const pkg = JSON.parse(await readFile2(pkgPath, "utf-8"));
4249
4589
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
4250
4590
  if ("@runtimescope/sdk" in allDeps || "@runtimescope/server-sdk" in allDeps) {
@@ -4254,13 +4594,13 @@ async function detectSdkInstalled(projectPath) {
4254
4594
  const workspaces = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages ?? [];
4255
4595
  for (const ws of workspaces) {
4256
4596
  const wsBase = ws.replace(/\/?\*$/, "");
4257
- const wsDir = join3(projectPath, wsBase);
4597
+ const wsDir = join4(projectPath, wsBase);
4258
4598
  try {
4259
4599
  const entries = await readdir2(wsDir, { withFileTypes: true });
4260
4600
  for (const entry of entries) {
4261
4601
  if (!entry.isDirectory()) continue;
4262
4602
  try {
4263
- const wsPkg = JSON.parse(await readFile2(join3(wsDir, entry.name, "package.json"), "utf-8"));
4603
+ const wsPkg = JSON.parse(await readFile2(join4(wsDir, entry.name, "package.json"), "utf-8"));
4264
4604
  const wsDeps = { ...wsPkg.dependencies, ...wsPkg.devDependencies };
4265
4605
  if ("@runtimescope/sdk" in wsDeps || "@runtimescope/server-sdk" in wsDeps) {
4266
4606
  return true;
@@ -4275,7 +4615,7 @@ async function detectSdkInstalled(projectPath) {
4275
4615
  } catch {
4276
4616
  }
4277
4617
  try {
4278
- await stat2(join3(projectPath, "node_modules", "@runtimescope"));
4618
+ await stat2(join4(projectPath, "node_modules", "@runtimescope"));
4279
4619
  return true;
4280
4620
  } catch {
4281
4621
  return false;
@@ -4318,7 +4658,7 @@ function resolvePathSegments(parts) {
4318
4658
  }
4319
4659
  for (let count = remaining.length; count >= 1; count--) {
4320
4660
  const segment = remaining.slice(0, count).join("-");
4321
- const candidate = join3(prefix, segment);
4661
+ const candidate = join4(prefix, segment);
4322
4662
  if (count === remaining.length) {
4323
4663
  if (existsSync5(candidate)) return candidate;
4324
4664
  } else {
@@ -4345,7 +4685,7 @@ var ProjectDiscovery = class {
4345
4685
  constructor(pmStore, projectManager, claudeBaseDir) {
4346
4686
  this.pmStore = pmStore;
4347
4687
  this.projectManager = projectManager;
4348
- this.claudeBaseDir = claudeBaseDir ?? join3(homedir3(), ".claude");
4688
+ this.claudeBaseDir = claudeBaseDir ?? join4(homedir3(), ".claude");
4349
4689
  }
4350
4690
  claudeBaseDir;
4351
4691
  /**
@@ -4378,7 +4718,7 @@ var ProjectDiscovery = class {
4378
4718
  sessionsUpdated: 0,
4379
4719
  errors: []
4380
4720
  };
4381
- const projectsDir = join3(this.claudeBaseDir, "projects");
4721
+ const projectsDir = join4(this.claudeBaseDir, "projects");
4382
4722
  try {
4383
4723
  await stat2(projectsDir);
4384
4724
  } catch {
@@ -4431,9 +4771,14 @@ var ProjectDiscovery = class {
4431
4771
  const sourcePath = existing?.path ?? projectDir;
4432
4772
  const sdkInstalled = await detectSdkInstalled(sourcePath);
4433
4773
  if (existing) {
4774
+ const apps = existing.runtimeApps ?? [];
4775
+ if (!apps.some((a) => a.toLowerCase() === projectName.toLowerCase())) {
4776
+ apps.push(projectName);
4777
+ }
4434
4778
  const updated = {
4435
4779
  ...existing,
4436
4780
  runtimescopeProject: projectName,
4781
+ runtimeApps: apps,
4437
4782
  sdkInstalled: sdkInstalled || existing.sdkInstalled,
4438
4783
  updatedAt: now
4439
4784
  };
@@ -4446,6 +4791,7 @@ var ProjectDiscovery = class {
4446
4791
  name: projectName,
4447
4792
  path: fsPath,
4448
4793
  runtimescopeProject: projectName,
4794
+ runtimeApps: [projectName],
4449
4795
  phase: "application_development",
4450
4796
  managementAuthorized: false,
4451
4797
  probableToComplete: true,
@@ -4484,10 +4830,10 @@ var ProjectDiscovery = class {
4484
4830
  if (!project.claudeProjectKey) {
4485
4831
  return 0;
4486
4832
  }
4487
- const projectDir = join3(this.claudeBaseDir, "projects", project.claudeProjectKey);
4833
+ const projectDir = join4(this.claudeBaseDir, "projects", project.claudeProjectKey);
4488
4834
  let sessionsIndexed = 0;
4489
4835
  try {
4490
- const indexPath = join3(projectDir, "sessions-index.json");
4836
+ const indexPath = join4(projectDir, "sessions-index.json");
4491
4837
  let indexEntries = null;
4492
4838
  try {
4493
4839
  const indexContent = await readFile2(indexPath, "utf-8");
@@ -4500,7 +4846,7 @@ var ProjectDiscovery = class {
4500
4846
  for (const jsonlFile of jsonlFiles) {
4501
4847
  try {
4502
4848
  const sessionId = jsonlFile.replace(".jsonl", "");
4503
- const jsonlPath = join3(projectDir, jsonlFile);
4849
+ const jsonlPath = join4(projectDir, jsonlFile);
4504
4850
  const fileStat = await stat2(jsonlPath);
4505
4851
  const fileSize = fileStat.size;
4506
4852
  const existingSession = await this.pmStore.getSession(sessionId);
@@ -4535,7 +4881,7 @@ var ProjectDiscovery = class {
4535
4881
  * Process a single Claude project directory key.
4536
4882
  */
4537
4883
  async processClaudeProject(key, result) {
4538
- const projectDir = join3(this.claudeBaseDir, "projects", key);
4884
+ const projectDir = join4(this.claudeBaseDir, "projects", key);
4539
4885
  let fsPath = decodeClaudeKey(key);
4540
4886
  if (!fsPath) {
4541
4887
  fsPath = await this.resolvePathFromIndex(projectDir);
@@ -4587,7 +4933,7 @@ var ProjectDiscovery = class {
4587
4933
  */
4588
4934
  async resolvePathFromIndex(projectDir) {
4589
4935
  try {
4590
- const indexPath = join3(projectDir, "sessions-index.json");
4936
+ const indexPath = join4(projectDir, "sessions-index.json");
4591
4937
  const content = await readFile2(indexPath, "utf-8");
4592
4938
  const index = JSON.parse(content);
4593
4939
  const entry = index.entries?.find((e) => e.projectPath);
@@ -4602,11 +4948,11 @@ var ProjectDiscovery = class {
4602
4948
  */
4603
4949
  async indexSessionsForClaudeProject(projectId, claudeKey) {
4604
4950
  const counts = { discovered: 0, updated: 0 };
4605
- const projectDir = join3(this.claudeBaseDir, "projects", claudeKey);
4951
+ const projectDir = join4(this.claudeBaseDir, "projects", claudeKey);
4606
4952
  try {
4607
4953
  let indexEntries = null;
4608
4954
  try {
4609
- const indexPath = join3(projectDir, "sessions-index.json");
4955
+ const indexPath = join4(projectDir, "sessions-index.json");
4610
4956
  const indexContent = await readFile2(indexPath, "utf-8");
4611
4957
  const index = JSON.parse(indexContent);
4612
4958
  indexEntries = index.entries ?? [];
@@ -4617,7 +4963,7 @@ var ProjectDiscovery = class {
4617
4963
  for (const jsonlFile of jsonlFiles) {
4618
4964
  try {
4619
4965
  const sessionId = jsonlFile.replace(".jsonl", "");
4620
- const jsonlPath = join3(projectDir, jsonlFile);
4966
+ const jsonlPath = join4(projectDir, jsonlFile);
4621
4967
  const fileStat = await stat2(jsonlPath);
4622
4968
  const fileSize = fileStat.size;
4623
4969
  const existingSession = await this.pmStore.getSession(sessionId);
@@ -4798,6 +5144,9 @@ export {
4798
5144
  __require,
4799
5145
  RingBuffer,
4800
5146
  EventStore,
5147
+ generateProjectId,
5148
+ isValidProjectId,
5149
+ getOrCreateProjectId,
4801
5150
  SqliteStore,
4802
5151
  isSqliteAvailable,
4803
5152
  SessionRateLimiter,
@@ -4809,6 +5158,12 @@ export {
4809
5158
  generateApiKey,
4810
5159
  BUILT_IN_RULES,
4811
5160
  Redactor,
5161
+ getPidsOnPort,
5162
+ getListenPorts,
5163
+ getProcessCwd,
5164
+ findPidsInDirectory,
5165
+ parseProcessList,
5166
+ getProcessMemoryMB,
4812
5167
  SessionManager,
4813
5168
  HttpServer,
4814
5169
  PmStore,
@@ -4817,4 +5172,4 @@ export {
4817
5172
  parseSessionJsonl,
4818
5173
  ProjectDiscovery
4819
5174
  };
4820
- //# sourceMappingURL=chunk-BKRGXAJB.js.map
5175
+ //# sourceMappingURL=chunk-HZWALDZM.js.map