codesesh 1.0.2 → 1.0.4

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.
Files changed (64) hide show
  1. package/README.md +18 -10
  2. package/dist/chunk-3SXCTDD6.js +326 -0
  3. package/dist/chunk-3SXCTDD6.js.map +1 -0
  4. package/dist/chunk-BYB77UAJ.js +6054 -0
  5. package/dist/chunk-BYB77UAJ.js.map +1 -0
  6. package/dist/{chunk-PROMURPM.js → chunk-G4HRD3AR.js} +13 -8
  7. package/dist/chunk-G4HRD3AR.js.map +1 -0
  8. package/dist/chunk-MBZ4VV4C.js +670 -0
  9. package/dist/chunk-MBZ4VV4C.js.map +1 -0
  10. package/dist/chunk-QH2HOXJH.js +2 -0
  11. package/dist/chunk-RF3EEWPH.js +17 -0
  12. package/dist/chunk-RF3EEWPH.js.map +1 -0
  13. package/dist/chunk-W4PZHL2C.js +2035 -0
  14. package/dist/chunk-W4PZHL2C.js.map +1 -0
  15. package/dist/chunk-YCUN52HA.js +8422 -0
  16. package/dist/chunk-YCUN52HA.js.map +1 -0
  17. package/dist/discovery-K3GLISI6.js +102 -0
  18. package/dist/discovery-K3GLISI6.js.map +1 -0
  19. package/dist/index.js +3016 -1392
  20. package/dist/index.js.map +1 -1
  21. package/dist/project-identity-worker.js +29 -0
  22. package/dist/project-identity-worker.js.map +1 -0
  23. package/dist/scan-refresh-worker.js +129 -139
  24. package/dist/scan-refresh-worker.js.map +1 -1
  25. package/dist/search-index-worker.js +39 -13
  26. package/dist/search-index-worker.js.map +1 -1
  27. package/dist/smart-tag-worker.js +8 -5
  28. package/dist/smart-tag-worker.js.map +1 -1
  29. package/dist/web/assets/InteractiveReceipt-Dk50TBsH.js +1 -0
  30. package/dist/web/assets/OverviewScreen-5hvQVp4H.js +1 -0
  31. package/dist/web/assets/{PrismHighlighter-BZkkX0AU.js → PrismHighlighter-B1a2dPJO.js} +2 -2
  32. package/dist/web/assets/Projects-Cy_v2yep.js +1 -0
  33. package/dist/web/assets/SearchResultsPanel-DfheEwpz.js +1 -0
  34. package/dist/web/assets/SessionDetail-DRI8R3st.js +53 -0
  35. package/dist/web/assets/api-GP9UCedC.js +1 -0
  36. package/dist/web/assets/contract-xJyW5u7a.js +2 -0
  37. package/dist/web/assets/index-Bd-YkWkZ.css +1 -0
  38. package/dist/web/assets/index-DEjvzumm.js +1410 -0
  39. package/dist/web/assets/panel-ND0J63d3.js +1 -0
  40. package/dist/web/assets/{session-indexes-0oT5HSS5.js → session-indexes-BsgjUoc1.js} +2 -2
  41. package/dist/web/assets/utils-C7BqyxxV.js +1 -0
  42. package/dist/web/index.html +7 -11
  43. package/package.json +4 -10
  44. package/dist/chunk-PROMURPM.js.map +0 -1
  45. package/dist/chunk-VAUC2W7I.js +0 -16119
  46. package/dist/chunk-VAUC2W7I.js.map +0 -1
  47. package/dist/dist-6QTGTYZT.js +0 -175
  48. package/dist/web/assets/ErrorBoundary-D11tWzfn.js +0 -1
  49. package/dist/web/assets/InteractiveReceipt-DYGyHeZd.js +0 -1
  50. package/dist/web/assets/OverviewScreen-DriPmCam.js +0 -1
  51. package/dist/web/assets/Projects-BPDIcUBK.js +0 -1
  52. package/dist/web/assets/SearchFilterBar-Bx73B5e9.js +0 -1
  53. package/dist/web/assets/SearchResultsPanel-A6_3TPhW.js +0 -1
  54. package/dist/web/assets/SessionDetail-BnpvDwtH.js +0 -54
  55. package/dist/web/assets/SmartTagChips-ux6idUGJ.js +0 -1
  56. package/dist/web/assets/agents-BkRP_ww3.js +0 -1
  57. package/dist/web/assets/code-metrics-UYFsCC7E.js +0 -1
  58. package/dist/web/assets/contract-V6QuD0UY.js +0 -1
  59. package/dist/web/assets/index-99ZxpV2y.js +0 -1410
  60. package/dist/web/assets/index-B2RqUgZ5.css +0 -1
  61. package/dist/web/assets/panel-C-gc79Gz.js +0 -1
  62. package/dist/web/assets/session-title-DdtlxtlR.js +0 -1
  63. package/dist/web/assets/utils-DgUgu15E.js +0 -1
  64. /package/dist/{dist-6QTGTYZT.js.map → chunk-QH2HOXJH.js.map} +0 -0
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center"><strong>One place to see every AI coding session you've ever had.</strong></p>
8
8
 
9
- CodeSesh scans your local machine, finds every AI agent session (Claude Code, Cursor, Kimi, Kimi-Code, Codex, Grok, Pi, OpenCode, ZCode, DSH), and surfaces them in a unified, beautiful Web UI.
9
+ CodeSesh scans your local machine, finds sessions from supported AI coding agents, and surfaces them in a unified Web UI.
10
10
 
11
11
  ## Quick Start
12
12
 
@@ -41,11 +41,13 @@ Your browser will open at `http://localhost:4521` with all your sessions ready t
41
41
 
42
42
  ## Supported Agents
43
43
 
44
+ <!-- repo-fact:agents:start -->
45
+
44
46
  | Agent | Status |
45
47
  | ----------- | ------------ |
46
48
  | Claude Code | ✅ Supported |
47
49
  | Cursor | ✅ Supported |
48
- | Kimi | ✅ Supported |
50
+ | Kimi-Cli | ✅ Supported |
49
51
  | Kimi-Code | ✅ Supported |
50
52
  | Codex | ✅ Supported |
51
53
  | Grok | ✅ Supported |
@@ -54,6 +56,8 @@ Your browser will open at `http://localhost:4521` with all your sessions ready t
54
56
  | ZCode | ✅ Supported |
55
57
  | DSH | ✅ Supported |
56
58
 
59
+ <!-- repo-fact:agents:end -->
60
+
57
61
  ## Usage
58
62
 
59
63
  ```bash
@@ -88,10 +92,11 @@ npx codesesh --trace
88
92
  | ----------- | ----- | ------- | ----------------------------------------------------------- |
89
93
  | `--port` | `-p` | `4521` | HTTP server starting port; falls back to the next available port if busy |
90
94
  | `--host` | — | `127.0.0.1` | HTTP server bind address; non-loopback values require `--remote-access` |
91
- | `--remote-access` | — | `false` | Enable token-protected access for any remotely exposed transport |
95
+ | `--remote-access` | — | `false` | Allow network or reverse-proxy exposure; every API mode uses a token |
92
96
  | `--tls-cert` | — | — | Path to a TLS certificate; serves remote access over HTTPS |
93
97
  | `--tls-key` | — | — | Path to the private key matching `--tls-cert` |
94
98
  | `--trust-proxy` | — | `false` | A reverse proxy in front of CodeSesh terminates TLS |
99
+ | `--public-url` | — | — | Public HTTPS origin used with `--trust-proxy` for startup links |
95
100
  | `--days` | `-d` | `7` | Only include sessions active in the last N days (`0` = all time) |
96
101
  | `--cwd` | — | — | Filter to sessions from a project directory |
97
102
  | `--agent` | `-a` | all | Filter to specific agent(s), comma-separated |
@@ -105,10 +110,11 @@ npx codesesh --trace
105
110
  | `--clear-cache` | — | `false` | Clear scan cache before starting |
106
111
  | `-v` | — | — | Print version number |
107
112
 
108
- Non-loopback binding and `--trust-proxy` both require `--remote-access`. This includes the common
109
- setup where CodeSesh listens on `127.0.0.1` and a same-machine reverse proxy exposes it publicly.
110
- CodeSesh prints a startup URL containing a fresh access token. Anyone with that URL can read the
111
- indexed AI session history, so treat it as a password and do not share or persist it.
113
+ Every CodeSesh server process protects its API with a fresh access token, including the default
114
+ loopback listener, and prints that token in the startup URL. Non-loopback binding requires
115
+ `--remote-access`. A trusted proxy also requires `--remote-access`, a loopback `--host`, and an
116
+ HTTPS `--public-url`. Anyone with the startup URL can read the indexed AI session history, so treat
117
+ it as a password and do not share or persist it.
112
118
 
113
119
  A token authenticates the requester but does not encrypt traffic. Without TLS, the token and full
114
120
  session content travel over the network in plaintext, and URL tokens may be recorded in proxy logs.
@@ -119,11 +125,13 @@ Use one of these protected transports:
119
125
  npx codesesh --host 0.0.0.0 --remote-access --tls-cert ./cert.pem --tls-key ./key.pem
120
126
 
121
127
  # A reverse proxy terminates TLS; CodeSesh stays bound to loopback
122
- npx codesesh --host 127.0.0.1 --remote-access --trust-proxy
128
+ npx codesesh --host 127.0.0.1 --remote-access --trust-proxy \
129
+ --public-url https://codesesh.example.com
123
130
  ```
124
131
 
125
- `--trust-proxy` requires `X-Forwarded-Proto: https` on every API request. The listener must remain
126
- unreachable except through that trusted proxy.
132
+ `--trust-proxy` requires `X-Forwarded-Proto: https` on every API request. That header validates the
133
+ proxy configuration but cannot identify its sender, so CodeSesh enforces a loopback backend rather
134
+ than trusting the header as a network boundary. Startup links use the configured `--public-url`.
127
135
 
128
136
  ## Requirements
129
137
 
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ buildSessionTree,
4
+ getProjectIdentityKey,
5
+ getSessionAgentKey,
6
+ matchesProjectIdentity
7
+ } from "./chunk-W4PZHL2C.js";
8
+
9
+ // ../core/dist/chunk-G47S2ACS.mjs
10
+ import * as os from "os";
11
+ import * as path from "path";
12
+ import { createHash } from "crypto";
13
+ import { existsSync, readFileSync } from "fs";
14
+ import { spawnSync } from "child_process";
15
+ function fallbackDisplayName(input) {
16
+ if (input === "/") return "(root)";
17
+ const trimmed = input.replace(/[/\\]+$/, "");
18
+ const parts = trimmed.split(/[/\\]+/).filter(Boolean);
19
+ return parts.at(-1) ?? trimmed;
20
+ }
21
+ var realFs = {
22
+ exists(path2) {
23
+ return existsSync(path2);
24
+ },
25
+ readText(path2) {
26
+ try {
27
+ return readFileSync(path2, "utf8");
28
+ } catch {
29
+ return null;
30
+ }
31
+ },
32
+ spawn(cmd, args, opts) {
33
+ const result = spawnSync(cmd, args, { cwd: opts.cwd, encoding: "utf8", timeout: 1e3 });
34
+ return {
35
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
36
+ exitCode: result.status ?? 1
37
+ };
38
+ }
39
+ };
40
+ var PROJECT_IDENTITY_RESOLVER_REVISION = "project-identity-v2";
41
+ var MANIFESTS = [
42
+ "package.json",
43
+ "Cargo.toml",
44
+ "pyproject.toml",
45
+ "go.mod",
46
+ "Gemfile",
47
+ "pom.xml",
48
+ "build.gradle"
49
+ ];
50
+ var PARSEABLE_MANIFESTS = ["package.json", "Cargo.toml", "pyproject.toml"];
51
+ var LOOSE_DIRS = /* @__PURE__ */ new Set(["/tmp", "/private/tmp"]);
52
+ var LOOSE_HOME_DIRS = ["Desktop", "Downloads", "Documents"];
53
+ function normalizeGitRemote(url) {
54
+ if (!url) return null;
55
+ let value = url.trim();
56
+ if (/^[a-z][a-z\d+.-]*:\/\//i.test(value)) {
57
+ try {
58
+ const parsed = new URL(value);
59
+ value = `${parsed.hostname}${parsed.pathname}`;
60
+ } catch {
61
+ return null;
62
+ }
63
+ } else {
64
+ const sshMatch = value.match(/^[^@]+@([^:]+):(.+)$/);
65
+ if (sshMatch) value = `${sshMatch[1]}/${sshMatch[2]}`;
66
+ }
67
+ value = value.replace(/\.git$/, "");
68
+ if (!value.includes("/")) return null;
69
+ return value.toLowerCase();
70
+ }
71
+ var IDENTITY_CACHE_TTL_MS = 10 * 60 * 1e3;
72
+ var IDENTITY_CACHE_MAX_ENTRIES = 512;
73
+ var identityCache = /* @__PURE__ */ new Map();
74
+ function computeIdentity(cwd, fs) {
75
+ return computeIdentityProjection(cwd, fs).identity;
76
+ }
77
+ function normalizeProjectDirectory(cwd) {
78
+ if (!cwd) return "";
79
+ return getPathOps(cwd).resolve(cwd);
80
+ }
81
+ function computeIdentityProjection(cwd, fs = realFs, resolverRevision = PROJECT_IDENTITY_RESOLVER_REVISION) {
82
+ if (fs !== realFs) return resolveIdentityProjection(cwd, fs, resolverRevision);
83
+ const key = normalizeProjectDirectory(cwd);
84
+ const cached = identityCache.get(key);
85
+ if (cached) {
86
+ if (cached.projection.resolverRevision === resolverRevision && Date.now() - cached.resolvedAt < IDENTITY_CACHE_TTL_MS) {
87
+ identityCache.delete(key);
88
+ identityCache.set(key, cached);
89
+ return cached.projection;
90
+ }
91
+ identityCache.delete(key);
92
+ }
93
+ const projection = resolveIdentityProjection(cwd, fs, resolverRevision);
94
+ identityCache.set(key, { projection, resolvedAt: Date.now() });
95
+ while (identityCache.size > IDENTITY_CACHE_MAX_ENTRIES) {
96
+ const oldestKey = identityCache.keys().next().value;
97
+ if (oldestKey == null) break;
98
+ identityCache.delete(oldestKey);
99
+ }
100
+ return projection;
101
+ }
102
+ function resolveIdentityProjection(cwd, fs, resolverRevision) {
103
+ if (!cwd) return projectIdentityProjection(loose(), resolverRevision, ["loose", "missing"]);
104
+ const pathOps = getPathOps(cwd);
105
+ const absoluteCwd = pathOps.resolve(cwd);
106
+ const homeDir = os.homedir();
107
+ const homePathOps = getPathOps(homeDir);
108
+ const home = homePathOps === pathOps ? pathOps.resolve(homeDir) : homeDir;
109
+ if (absoluteCwd === home || LOOSE_DIRS.has(absoluteCwd)) {
110
+ return projectIdentityProjection(loose(), resolverRevision, ["loose", absoluteCwd]);
111
+ }
112
+ if (homePathOps === pathOps && LOOSE_HOME_DIRS.some((dir) => absoluteCwd === pathOps.join(home, dir))) {
113
+ return projectIdentityProjection(loose(), resolverRevision, ["loose", absoluteCwd]);
114
+ }
115
+ const gitRoot = findGitRoot(absoluteCwd, fs, pathOps);
116
+ if (gitRoot) {
117
+ const remote = fs.spawn("git", ["config", "--get", "remote.origin.url"], { cwd: gitRoot });
118
+ if (remote.exitCode === 0) {
119
+ const normalized = normalizeGitRemote(remote.stdout.trim());
120
+ if (normalized) {
121
+ const identity2 = {
122
+ kind: "git_remote",
123
+ key: normalized,
124
+ displayName: deriveDisplayName({ kind: "git_remote", key: normalized, gitRoot, fs })
125
+ };
126
+ return projectIdentityProjection(identity2, resolverRevision, [
127
+ "git_remote",
128
+ gitRoot,
129
+ normalized,
130
+ identity2.displayName
131
+ ]);
132
+ }
133
+ }
134
+ const common = fs.spawn("git", ["rev-parse", "--git-common-dir"], { cwd: gitRoot });
135
+ if (common.exitCode === 0) {
136
+ const raw = common.stdout.trim();
137
+ if (raw) {
138
+ const key = pathOps.isAbsolute(raw) ? raw : pathOps.resolve(gitRoot, raw);
139
+ const identity2 = {
140
+ kind: "git_common_dir",
141
+ key,
142
+ displayName: deriveDisplayName({ kind: "git_common_dir", key, gitRoot, fs })
143
+ };
144
+ return projectIdentityProjection(identity2, resolverRevision, [
145
+ "git_common_dir",
146
+ gitRoot,
147
+ key,
148
+ identity2.displayName
149
+ ]);
150
+ }
151
+ }
152
+ }
153
+ const manifestDir = findManifestDir(absoluteCwd, fs, pathOps);
154
+ if (manifestDir) {
155
+ const identity2 = {
156
+ kind: "manifest_path",
157
+ key: manifestDir,
158
+ displayName: deriveDisplayName({ kind: "manifest_path", key: manifestDir, fs })
159
+ };
160
+ return projectIdentityProjection(identity2, resolverRevision, [
161
+ "manifest_path",
162
+ manifestDir,
163
+ identity2.displayName
164
+ ]);
165
+ }
166
+ if (homePathOps === pathOps) {
167
+ const synthetic = synthesizeCodexScratchIdentity(absoluteCwd, home, pathOps);
168
+ if (synthetic) {
169
+ return projectIdentityProjection(synthetic, resolverRevision, ["synthetic", synthetic.key]);
170
+ }
171
+ }
172
+ const identity = {
173
+ kind: "path",
174
+ key: absoluteCwd,
175
+ displayName: fallbackDisplayName(absoluteCwd)
176
+ };
177
+ return projectIdentityProjection(identity, resolverRevision, ["path", absoluteCwd]);
178
+ }
179
+ function projectIdentityProjection(identity, resolverRevision, inputs) {
180
+ return {
181
+ identity,
182
+ resolverRevision,
183
+ inputSignature: createHash("sha256").update(JSON.stringify(inputs)).digest("hex")
184
+ };
185
+ }
186
+ function loose() {
187
+ return { kind: "loose", key: "loose", displayName: "Loose" };
188
+ }
189
+ function synthesizeCodexScratchIdentity(absoluteCwd, home, pathOps) {
190
+ const root = pathOps.resolve(pathOps.join(home, "Documents", "Codex"));
191
+ const child = pathOps.relative(root, absoluteCwd);
192
+ if (!child || child === ".." || child.startsWith(`..${pathOps.sep}`) || pathOps.isAbsolute(child)) {
193
+ return null;
194
+ }
195
+ return { kind: "synthetic", key: "codex:scratch", displayName: "Chats" };
196
+ }
197
+ function getPathOps(input) {
198
+ if (/^[a-zA-Z]:[\\/]/.test(input) || input.startsWith("\\\\")) {
199
+ return path.win32;
200
+ }
201
+ if (input.startsWith("/")) return path.posix;
202
+ return path;
203
+ }
204
+ function findGitRoot(start, fs, pathOps) {
205
+ let current = start;
206
+ while (current) {
207
+ if (fs.exists(pathOps.join(current, ".git"))) return current;
208
+ const parent = pathOps.dirname(current);
209
+ if (parent === current) break;
210
+ current = parent;
211
+ }
212
+ return null;
213
+ }
214
+ function findManifestDir(start, fs, pathOps) {
215
+ let current = start;
216
+ while (current) {
217
+ for (const manifest of MANIFESTS) {
218
+ if (fs.exists(pathOps.join(current, manifest))) return current;
219
+ }
220
+ const parent = pathOps.dirname(current);
221
+ if (parent === current) break;
222
+ current = parent;
223
+ }
224
+ return null;
225
+ }
226
+ function deriveDisplayName(input) {
227
+ const pathOps = getPathOps(input.gitRoot ?? input.key);
228
+ const dir = input.gitRoot ?? (input.kind === "manifest_path" ? input.key : null);
229
+ if (dir) {
230
+ for (const manifest of PARSEABLE_MANIFESTS) {
231
+ const manifestPath = pathOps.join(dir, manifest);
232
+ if (input.fs.exists(manifestPath)) {
233
+ const name = parseManifestName(manifest, input.fs.readText(manifestPath) ?? "");
234
+ if (name) return name;
235
+ }
236
+ }
237
+ }
238
+ if (input.kind === "git_remote") {
239
+ return input.key.split("/").at(-1) || input.key;
240
+ }
241
+ if (input.gitRoot) return fallbackDisplayName(input.gitRoot);
242
+ return fallbackDisplayName(input.key);
243
+ }
244
+ function parseManifestName(file, text) {
245
+ if (!text) return null;
246
+ if (file === "package.json" || file === "Cargo.toml" || file === "pyproject.toml") {
247
+ const match = text.match(/"name"\s*:\s*"([^"]+)"/) || text.match(/^\s*name\s*=\s*"([^"]+)"/m);
248
+ if (match?.[1]) return match[1];
249
+ }
250
+ return null;
251
+ }
252
+ function createProjectScopeMatcher(queryPath, fs = realFs) {
253
+ const identity = computeIdentity(queryPath, fs);
254
+ return createProjectScopeMatcherFromIdentity(queryPath, identity);
255
+ }
256
+ function createProjectScopeMatcherFromIdentity(queryPath, identity) {
257
+ return {
258
+ identity: { kind: identity.kind, key: identity.key },
259
+ path: normalizeProjectScopePath(queryPath)
260
+ };
261
+ }
262
+ function matchesProjectScope(session, scope) {
263
+ if (matchesProjectIdentity(session.project_identity, scope.identity)) return true;
264
+ return session.directory ? isPathScopeMatch(scope.path, session.directory) : false;
265
+ }
266
+ function filterSessionsByProjectScope(sessions, queryPath, fs) {
267
+ const scope = createProjectScopeMatcher(queryPath, fs);
268
+ return sessions.filter((session) => matchesProjectScope(session, scope));
269
+ }
270
+ function isPathScopeMatch(queryPath, sessionPath) {
271
+ const session = normalizeProjectScopePath(sessionPath);
272
+ return session === queryPath || session.startsWith(queryPath + "/") || queryPath.startsWith(session + "/");
273
+ }
274
+ function normalizeProjectScopePath(path2) {
275
+ return normalizeProjectDirectory(path2).replaceAll("\\", "/");
276
+ }
277
+ function buildProjectGroups(sessions) {
278
+ const groups = /* @__PURE__ */ new Map();
279
+ const tree = buildSessionTree(sessions);
280
+ for (const session of sessions) {
281
+ if (tree.mountStateOf(session) === "mounted-child") continue;
282
+ const identity = session.project_identity;
283
+ if (!identity) continue;
284
+ const activity = session.time_updated ?? session.time_created;
285
+ const groupKey = getProjectIdentityKey(identity);
286
+ const current = groups.get(groupKey);
287
+ if (current) {
288
+ current.sources.add(getSessionAgentKey(session));
289
+ current.sessionCount += 1;
290
+ current.lastActivity = Math.max(current.lastActivity, activity);
291
+ } else {
292
+ groups.set(groupKey, {
293
+ identity,
294
+ sources: /* @__PURE__ */ new Set([getSessionAgentKey(session)]),
295
+ sessionCount: 1,
296
+ lastActivity: activity
297
+ });
298
+ }
299
+ }
300
+ return [...groups.values()].map((group) => ({
301
+ identityKind: group.identity.kind,
302
+ identityKey: group.identity.key,
303
+ displayName: group.identity.displayName,
304
+ sources: [...group.sources].sort(),
305
+ sessionCount: group.sessionCount,
306
+ lastActivity: group.lastActivity || null
307
+ })).sort((a, b) => {
308
+ if (a.identityKind === "loose" && b.identityKind !== "loose") return 1;
309
+ if (b.identityKind === "loose" && a.identityKind !== "loose") return -1;
310
+ return (b.lastActivity ?? 0) - (a.lastActivity ?? 0);
311
+ });
312
+ }
313
+
314
+ export {
315
+ realFs,
316
+ PROJECT_IDENTITY_RESOLVER_REVISION,
317
+ computeIdentity,
318
+ normalizeProjectDirectory,
319
+ computeIdentityProjection,
320
+ createProjectScopeMatcherFromIdentity,
321
+ matchesProjectScope,
322
+ filterSessionsByProjectScope,
323
+ normalizeProjectScopePath,
324
+ buildProjectGroups
325
+ };
326
+ //# sourceMappingURL=chunk-3SXCTDD6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/projects/identity.ts","../../core/src/projects/display-name.ts","../../core/src/projects/fs.ts","../../core/src/projects/scope.ts","../../core/src/projects/groups.ts"],"sourcesContent":["import * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport type { ProjectIdentity, ProjectIdentityKind } from \"../types/index.js\";\nimport { fallbackDisplayName } from \"./display-name.js\";\nimport { realFs, type IdentityFs } from \"./fs.js\";\n\nexport type { IdentityFs } from \"./fs.js\";\n\nexport const PROJECT_IDENTITY_RESOLVER_REVISION = \"project-identity-v2\";\n\nexport interface ProjectIdentityProjection {\n identity: ProjectIdentity;\n resolverRevision: string;\n inputSignature: string;\n}\n\nconst MANIFESTS = [\n \"package.json\",\n \"Cargo.toml\",\n \"pyproject.toml\",\n \"go.mod\",\n \"Gemfile\",\n \"pom.xml\",\n \"build.gradle\",\n] as const;\n\nconst PARSEABLE_MANIFESTS = [\"package.json\", \"Cargo.toml\", \"pyproject.toml\"] as const;\n\nconst LOOSE_DIRS = new Set([\"/tmp\", \"/private/tmp\"]);\nconst LOOSE_HOME_DIRS = [\"Desktop\", \"Downloads\", \"Documents\"];\ntype PathOps = Pick<\n typeof path.posix,\n \"dirname\" | \"isAbsolute\" | \"join\" | \"relative\" | \"resolve\" | \"sep\"\n>;\n\n// Identity semantics live in the contract; this module only discovers them\n// from the filesystem and git.\nexport {\n getProjectIdentityKey,\n isProjectIdentityKind,\n matchesProjectIdentity,\n} from \"../contract/project-identity.js\";\n\nexport function normalizeGitRemote(url: string): string | null {\n if (!url) return null;\n let value = url.trim();\n if (/^[a-z][a-z\\d+.-]*:\\/\\//i.test(value)) {\n try {\n const parsed = new URL(value);\n value = `${parsed.hostname}${parsed.pathname}`;\n } catch {\n return null;\n }\n } else {\n const sshMatch = value.match(/^[^@]+@([^:]+):(.+)$/);\n if (sshMatch) value = `${sshMatch[1]}/${sshMatch[2]}`;\n }\n value = value.replace(/\\.git$/, \"\");\n if (!value.includes(\"/\")) return null;\n return value.toLowerCase();\n}\n\n// Process-lifetime identity cache, keyed by directory. Only used for the real\n// filesystem (fs === realFs); tests inject fake IdentityFs implementations and\n// must bypass the cache to stay isolated from each other. git remotes change\n// rarely, so a coarse TTL is enough — no file-watch invalidation needed.\nconst IDENTITY_CACHE_TTL_MS = 10 * 60 * 1000;\nexport const IDENTITY_CACHE_MAX_ENTRIES = 512;\n\ninterface IdentityCacheEntry {\n projection: ProjectIdentityProjection;\n resolvedAt: number;\n}\n\nconst identityCache = new Map<string, IdentityCacheEntry>();\n\n/** Clears the process-lifetime identity cache. For tests and explicit invalidation. */\nexport function clearIdentityCache(): void {\n identityCache.clear();\n}\n\nexport function computeIdentity(cwd: string | null | undefined, fs: IdentityFs): ProjectIdentity {\n return computeIdentityProjection(cwd, fs).identity;\n}\n\nexport function normalizeProjectDirectory(cwd: string | null | undefined): string {\n if (!cwd) return \"\";\n return getPathOps(cwd).resolve(cwd);\n}\n\nexport function computeIdentityProjection(\n cwd: string | null | undefined,\n fs: IdentityFs = realFs,\n resolverRevision = PROJECT_IDENTITY_RESOLVER_REVISION,\n): ProjectIdentityProjection {\n if (fs !== realFs) return resolveIdentityProjection(cwd, fs, resolverRevision);\n\n const key = normalizeProjectDirectory(cwd);\n const cached = identityCache.get(key);\n if (cached) {\n if (\n cached.projection.resolverRevision === resolverRevision &&\n Date.now() - cached.resolvedAt < IDENTITY_CACHE_TTL_MS\n ) {\n identityCache.delete(key);\n identityCache.set(key, cached);\n return cached.projection;\n }\n identityCache.delete(key);\n }\n const projection = resolveIdentityProjection(cwd, fs, resolverRevision);\n identityCache.set(key, { projection, resolvedAt: Date.now() });\n while (identityCache.size > IDENTITY_CACHE_MAX_ENTRIES) {\n const oldestKey = identityCache.keys().next().value;\n if (oldestKey == null) break;\n identityCache.delete(oldestKey);\n }\n return projection;\n}\n\nfunction resolveIdentityProjection(\n cwd: string | null | undefined,\n fs: IdentityFs,\n resolverRevision: string,\n): ProjectIdentityProjection {\n if (!cwd) return projectIdentityProjection(loose(), resolverRevision, [\"loose\", \"missing\"]);\n\n const pathOps = getPathOps(cwd);\n const absoluteCwd = pathOps.resolve(cwd);\n const homeDir = os.homedir();\n const homePathOps = getPathOps(homeDir);\n const home = homePathOps === pathOps ? pathOps.resolve(homeDir) : homeDir;\n if (absoluteCwd === home || LOOSE_DIRS.has(absoluteCwd)) {\n return projectIdentityProjection(loose(), resolverRevision, [\"loose\", absoluteCwd]);\n }\n if (\n homePathOps === pathOps &&\n LOOSE_HOME_DIRS.some((dir) => absoluteCwd === pathOps.join(home, dir))\n ) {\n return projectIdentityProjection(loose(), resolverRevision, [\"loose\", absoluteCwd]);\n }\n\n const gitRoot = findGitRoot(absoluteCwd, fs, pathOps);\n if (gitRoot) {\n const remote = fs.spawn(\"git\", [\"config\", \"--get\", \"remote.origin.url\"], { cwd: gitRoot });\n if (remote.exitCode === 0) {\n const normalized = normalizeGitRemote(remote.stdout.trim());\n if (normalized) {\n const identity: ProjectIdentity = {\n kind: \"git_remote\",\n key: normalized,\n displayName: deriveDisplayName({ kind: \"git_remote\", key: normalized, gitRoot, fs }),\n };\n return projectIdentityProjection(identity, resolverRevision, [\n \"git_remote\",\n gitRoot,\n normalized,\n identity.displayName,\n ]);\n }\n }\n\n const common = fs.spawn(\"git\", [\"rev-parse\", \"--git-common-dir\"], { cwd: gitRoot });\n if (common.exitCode === 0) {\n const raw = common.stdout.trim();\n if (raw) {\n const key = pathOps.isAbsolute(raw) ? raw : pathOps.resolve(gitRoot, raw);\n const identity: ProjectIdentity = {\n kind: \"git_common_dir\",\n key,\n displayName: deriveDisplayName({ kind: \"git_common_dir\", key, gitRoot, fs }),\n };\n return projectIdentityProjection(identity, resolverRevision, [\n \"git_common_dir\",\n gitRoot,\n key,\n identity.displayName,\n ]);\n }\n }\n }\n\n const manifestDir = findManifestDir(absoluteCwd, fs, pathOps);\n if (manifestDir) {\n const identity: ProjectIdentity = {\n kind: \"manifest_path\",\n key: manifestDir,\n displayName: deriveDisplayName({ kind: \"manifest_path\", key: manifestDir, fs }),\n };\n return projectIdentityProjection(identity, resolverRevision, [\n \"manifest_path\",\n manifestDir,\n identity.displayName,\n ]);\n }\n\n if (homePathOps === pathOps) {\n const synthetic = synthesizeCodexScratchIdentity(absoluteCwd, home, pathOps);\n if (synthetic) {\n return projectIdentityProjection(synthetic, resolverRevision, [\"synthetic\", synthetic.key]);\n }\n }\n\n const identity: ProjectIdentity = {\n kind: \"path\",\n key: absoluteCwd,\n displayName: fallbackDisplayName(absoluteCwd),\n };\n return projectIdentityProjection(identity, resolverRevision, [\"path\", absoluteCwd]);\n}\n\nfunction projectIdentityProjection(\n identity: ProjectIdentity,\n resolverRevision: string,\n inputs: readonly string[],\n): ProjectIdentityProjection {\n return {\n identity,\n resolverRevision,\n inputSignature: createHash(\"sha256\").update(JSON.stringify(inputs)).digest(\"hex\"),\n };\n}\n\nfunction loose(): ProjectIdentity {\n return { kind: \"loose\", key: \"loose\", displayName: \"Loose\" };\n}\n\nfunction synthesizeCodexScratchIdentity(\n absoluteCwd: string,\n home: string,\n pathOps: PathOps,\n): ProjectIdentity | null {\n const root = pathOps.resolve(pathOps.join(home, \"Documents\", \"Codex\"));\n const child = pathOps.relative(root, absoluteCwd);\n if (\n !child ||\n child === \"..\" ||\n child.startsWith(`..${pathOps.sep}`) ||\n pathOps.isAbsolute(child)\n ) {\n return null;\n }\n return { kind: \"synthetic\", key: \"codex:scratch\", displayName: \"Chats\" };\n}\n\nfunction getPathOps(input: string): PathOps {\n if (/^[a-zA-Z]:[\\\\/]/.test(input) || input.startsWith(\"\\\\\\\\\")) {\n return path.win32;\n }\n if (input.startsWith(\"/\")) return path.posix;\n return path;\n}\n\nfunction findGitRoot(start: string, fs: IdentityFs, pathOps: PathOps): string | null {\n let current = start;\n while (current) {\n if (fs.exists(pathOps.join(current, \".git\"))) return current;\n const parent = pathOps.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return null;\n}\n\nfunction findManifestDir(start: string, fs: IdentityFs, pathOps: PathOps): string | null {\n let current = start;\n while (current) {\n for (const manifest of MANIFESTS) {\n if (fs.exists(pathOps.join(current, manifest))) return current;\n }\n const parent = pathOps.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return null;\n}\n\ninterface DisplayNameInput {\n kind: ProjectIdentityKind;\n key: string;\n gitRoot?: string;\n fs: IdentityFs;\n}\n\nfunction deriveDisplayName(input: DisplayNameInput): string {\n const pathOps = getPathOps(input.gitRoot ?? input.key);\n const dir = input.gitRoot ?? (input.kind === \"manifest_path\" ? input.key : null);\n if (dir) {\n for (const manifest of PARSEABLE_MANIFESTS) {\n const manifestPath = pathOps.join(dir, manifest);\n if (input.fs.exists(manifestPath)) {\n const name = parseManifestName(manifest, input.fs.readText(manifestPath) ?? \"\");\n if (name) return name;\n }\n }\n }\n\n if (input.kind === \"git_remote\") {\n return input.key.split(\"/\").at(-1) || input.key;\n }\n if (input.gitRoot) return fallbackDisplayName(input.gitRoot);\n return fallbackDisplayName(input.key);\n}\n\nfunction parseManifestName(file: string, text: string): string | null {\n if (!text) return null;\n if (file === \"package.json\" || file === \"Cargo.toml\" || file === \"pyproject.toml\") {\n const match = text.match(/\"name\"\\s*:\\s*\"([^\"]+)\"/) || text.match(/^\\s*name\\s*=\\s*\"([^\"]+)\"/m);\n if (match?.[1]) return match[1];\n }\n return null;\n}\n","export function fallbackDisplayName(input: string): string {\n if (input === \"/\") return \"(root)\";\n const trimmed = input.replace(/[/\\\\]+$/, \"\");\n const parts = trimmed.split(/[/\\\\]+/).filter(Boolean);\n return parts.at(-1) ?? trimmed;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\n\nexport interface IdentityFs {\n exists(path: string): boolean;\n readText(path: string): string | null;\n spawn(cmd: string, args: string[], opts: { cwd: string }): { stdout: string; exitCode: number };\n}\n\nexport const realFs: IdentityFs = {\n exists(path) {\n return existsSync(path);\n },\n readText(path) {\n try {\n return readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n },\n spawn(cmd, args, opts) {\n const result = spawnSync(cmd, args, { cwd: opts.cwd, encoding: \"utf8\", timeout: 1000 });\n return {\n stdout: typeof result.stdout === \"string\" ? result.stdout : \"\",\n exitCode: result.status ?? 1,\n };\n },\n};\n","import type { ProjectIdentityRef, SessionHead } from \"../types/index.js\";\nimport {\n computeIdentity,\n matchesProjectIdentity,\n normalizeProjectDirectory,\n type IdentityFs,\n} from \"./identity.js\";\nimport { realFs } from \"./fs.js\";\n\nexport interface ProjectScopeMatcher {\n identity: ProjectIdentityRef;\n path: string;\n}\n\nexport function createProjectScopeMatcher(\n queryPath: string,\n fs: IdentityFs = realFs,\n): ProjectScopeMatcher {\n const identity = computeIdentity(queryPath, fs);\n return createProjectScopeMatcherFromIdentity(queryPath, identity);\n}\n\nexport function createProjectScopeMatcherFromIdentity(\n queryPath: string,\n identity: ProjectIdentityRef,\n): ProjectScopeMatcher {\n return {\n identity: { kind: identity.kind, key: identity.key },\n path: normalizeProjectScopePath(queryPath),\n };\n}\n\nexport function matchesProjectScope(session: SessionHead, scope: ProjectScopeMatcher): boolean {\n if (matchesProjectIdentity(session.project_identity, scope.identity)) return true;\n return session.directory ? isPathScopeMatch(scope.path, session.directory) : false;\n}\n\nexport function filterSessionsByProjectScope<T extends SessionHead>(\n sessions: T[],\n queryPath: string,\n fs?: IdentityFs,\n): T[] {\n const scope = createProjectScopeMatcher(queryPath, fs);\n return sessions.filter((session) => matchesProjectScope(session, scope));\n}\n\nfunction isPathScopeMatch(queryPath: string, sessionPath: string): boolean {\n const session = normalizeProjectScopePath(sessionPath);\n return (\n session === queryPath ||\n session.startsWith(queryPath + \"/\") ||\n queryPath.startsWith(session + \"/\")\n );\n}\n\nexport function normalizeProjectScopePath(path: string): string {\n return normalizeProjectDirectory(path).replaceAll(\"\\\\\", \"/\");\n}\n","import type { ProjectGroup, ProjectIdentity, SessionHead } from \"../types/index.js\";\nimport { buildSessionTree, getSessionAgentKey } from \"../contract/index.js\";\nimport { getProjectIdentityKey } from \"./identity.js\";\n\nexport function buildProjectGroups(sessions: SessionHead[]): ProjectGroup[] {\n const groups = new Map<\n string,\n { identity: ProjectIdentity; sources: Set<string>; sessionCount: number; lastActivity: number }\n >();\n\n const tree = buildSessionTree(sessions);\n for (const session of sessions) {\n if (tree.mountStateOf(session) === \"mounted-child\") continue;\n const identity = session.project_identity;\n if (!identity) continue;\n const activity = session.time_updated ?? session.time_created;\n const groupKey = getProjectIdentityKey(identity);\n const current = groups.get(groupKey);\n if (current) {\n current.sources.add(getSessionAgentKey(session));\n current.sessionCount += 1;\n current.lastActivity = Math.max(current.lastActivity, activity);\n } else {\n groups.set(groupKey, {\n identity,\n sources: new Set([getSessionAgentKey(session)]),\n sessionCount: 1,\n lastActivity: activity,\n });\n }\n }\n\n return [...groups.values()]\n .map((group) => ({\n identityKind: group.identity.kind,\n identityKey: group.identity.key,\n displayName: group.identity.displayName,\n sources: [...group.sources].sort(),\n sessionCount: group.sessionCount,\n lastActivity: group.lastActivity || null,\n }))\n .sort((a, b) => {\n if (a.identityKind === \"loose\" && b.identityKind !== \"loose\") return 1;\n if (b.identityKind === \"loose\" && a.identityKind !== \"loose\") return -1;\n return (b.lastActivity ?? 0) - (a.lastActivity ?? 0);\n });\n}\n"],"mappings":"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","names":["path","identity"]}