@echomem/mcp 1.4.43 → 1.4.45
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/local-data-paths.js +38 -0
- package/dist/migrate.js +136 -64
- package/dist/setup-page/client-extraction.js +19 -7
- package/dist/setup.js +37 -9
- package/dist/source-session.js +3 -9
- package/package.json +1 -1
package/dist/local-data-paths.js
CHANGED
|
@@ -85,3 +85,41 @@ export function resolveClaudeProjectsDir(opts = {}) {
|
|
|
85
85
|
const root = configuredRoot("CLAUDE_CONFIG_DIR", ".claude", opts);
|
|
86
86
|
return resolveReadableDirectory(root ? path.join(root, "projects") : null);
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve Claude Desktop's local Cowork sandbox root.
|
|
90
|
+
*
|
|
91
|
+
* Cowork does not write into the normal `~/.claude/projects` profile. Claude Desktop creates an
|
|
92
|
+
* OS-specific support directory and stores each local-agent session below
|
|
93
|
+
* `local-agent-mode-sessions/<org>/<conversation>/local_<id>/`. The actual transcript is nested
|
|
94
|
+
* again under that sandbox's `.claude/projects` directory.
|
|
95
|
+
*/
|
|
96
|
+
export function resolveClaudeCoworkSessionsDir(opts = {}) {
|
|
97
|
+
const homeDir = normalizedHomeDir(opts.homeDir ?? os.homedir());
|
|
98
|
+
if (!homeDir)
|
|
99
|
+
return null;
|
|
100
|
+
const env = opts.env ?? process.env;
|
|
101
|
+
const configured = env.CLAUDE_DESKTOP_SUPPORT_DIR;
|
|
102
|
+
let supportRoot = null;
|
|
103
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
104
|
+
supportRoot = expandCurrentUserHome(configured, homeDir);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const platform = opts.platform ?? process.platform;
|
|
108
|
+
if (platform === "darwin") {
|
|
109
|
+
supportRoot = path.join(homeDir, "Library", "Application Support", "Claude");
|
|
110
|
+
}
|
|
111
|
+
else if (platform === "win32") {
|
|
112
|
+
const appData = typeof env.APPDATA === "string" && env.APPDATA.trim()
|
|
113
|
+
? expandCurrentUserHome(env.APPDATA, homeDir)
|
|
114
|
+
: path.join(homeDir, "AppData", "Roaming");
|
|
115
|
+
supportRoot = appData ? path.join(appData, "Claude") : null;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const configHome = typeof env.XDG_CONFIG_HOME === "string" && env.XDG_CONFIG_HOME.trim()
|
|
119
|
+
? expandCurrentUserHome(env.XDG_CONFIG_HOME, homeDir)
|
|
120
|
+
: path.join(homeDir, ".config");
|
|
121
|
+
supportRoot = configHome ? path.join(configHome, "Claude") : null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return resolveReadableDirectory(supportRoot ? path.join(supportRoot, "local-agent-mode-sessions") : null);
|
|
125
|
+
}
|
package/dist/migrate.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `echomem-mcp migrate` — one-shot bulk back-fill of the user's EXISTING local coding-agent history
|
|
3
|
-
* (Codex + Claude Code) into their Echo cloud. The onboarding
|
|
3
|
+
* (Codex + Claude Code + local Claude Desktop Cowork) into their Echo cloud. The onboarding
|
|
4
|
+
* "see it instantly know my work" moment.
|
|
4
5
|
*
|
|
5
6
|
* Why this lives in the bridge (client-side): the session logs exist ONLY on the user's machine
|
|
6
|
-
* (~/.codex/sessions, ~/.claude/projects). The bridge
|
|
7
|
+
* (~/.codex/sessions, ~/.claude/projects, Claude Desktop local-agent-mode-sessions). The bridge
|
|
8
|
+
* discovers each session, assembles it into a
|
|
7
9
|
* text-turn-only `## ` transcript, and feeds it to the shared durable import queue
|
|
8
10
|
* through MCP-owned policy entrypoints. POST /api/extension/mcp/historical-import-sessions
|
|
9
11
|
* creates one import_jobs row per session, then the bridge POSTs each transcript to
|
|
@@ -27,7 +29,7 @@ import axios from "axios";
|
|
|
27
29
|
import { KeyStore, echoConfigDir } from "./keystore.js";
|
|
28
30
|
import { fetchEncryptionConfig } from "./encryption.js";
|
|
29
31
|
import { discoverCodexSessionFiles } from "./codex-session-files.js";
|
|
30
|
-
import { resolveClaudeProjectsDir } from "./local-data-paths.js";
|
|
32
|
+
import { resolveClaudeCoworkSessionsDir, resolveClaudeProjectsDir } from "./local-data-paths.js";
|
|
31
33
|
import { walk, eachLine } from "./report.js";
|
|
32
34
|
const API_BASE = (process.env.ECHO_API_BASE_URL || "https://echo-mem-chrome.vercel.app").replace(/\/$/, "");
|
|
33
35
|
const RATE_MAX = 28; // stay under the import-jobs /run limit of 30 / 60s
|
|
@@ -102,7 +104,7 @@ export function assembleCodex(file) {
|
|
|
102
104
|
};
|
|
103
105
|
}
|
|
104
106
|
/** Claude Code <uuid>.jsonl → one session. User/assistant text turns are sent; tool calls/results/thinking stay local. */
|
|
105
|
-
export function assembleClaude(file) {
|
|
107
|
+
export function assembleClaude(file, opts = {}) {
|
|
106
108
|
let sessionId = null;
|
|
107
109
|
let cwd = null;
|
|
108
110
|
let firstTs = null;
|
|
@@ -157,10 +159,12 @@ export function assembleClaude(file) {
|
|
|
157
159
|
if (!turns.length)
|
|
158
160
|
return null;
|
|
159
161
|
const stat = statSafe(file);
|
|
162
|
+
const source = opts.source ?? "claude-code";
|
|
163
|
+
const conversationId = opts.conversationId ?? sessionId ?? sha16(file);
|
|
160
164
|
return {
|
|
161
165
|
filePath: file,
|
|
162
|
-
source
|
|
163
|
-
conversationKey:
|
|
166
|
+
source,
|
|
167
|
+
conversationKey: `${source}:${conversationId}`,
|
|
164
168
|
cwd: normalizeCwd(cwd),
|
|
165
169
|
firstTs,
|
|
166
170
|
title: title || "Claude text turns",
|
|
@@ -201,6 +205,45 @@ function userCreatedCodexFiles(codexRoot) {
|
|
|
201
205
|
function userCreatedClaudeFiles(claudeRoot) {
|
|
202
206
|
return walk(claudeRoot, (candidate) => candidate.endsWith(".jsonl"), (name) => name === "subagents" || name === "workflows").filter(isUserCreatedClaudeSessionFile);
|
|
203
207
|
}
|
|
208
|
+
function coworkConversationId(filePath, root) {
|
|
209
|
+
const relative = path.relative(root, filePath);
|
|
210
|
+
if (!relative || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
211
|
+
return null;
|
|
212
|
+
const parts = relative.split(path.sep);
|
|
213
|
+
for (let index = 0; index + 2 < parts.length; index += 1) {
|
|
214
|
+
if (/^local_[A-Za-z0-9_-]{1,194}$/.test(parts[index] || "")
|
|
215
|
+
&& parts[index + 1] === ".claude"
|
|
216
|
+
&& parts[index + 2] === "projects") {
|
|
217
|
+
return parts[index] || null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function claudeSessionCandidates(opts = {}) {
|
|
223
|
+
const candidates = [];
|
|
224
|
+
const claudeRoot = opts.claudeRoot ?? resolveClaudeProjectsDir();
|
|
225
|
+
if (claudeRoot) {
|
|
226
|
+
for (const filePath of userCreatedClaudeFiles(claudeRoot)) {
|
|
227
|
+
candidates.push({ filePath, source: "claude-code" });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Supplying an explicit Claude root is an isolated discovery request (tests, diagnostics, or a
|
|
231
|
+
// moved profile). Callers can still add Cowork roots explicitly. Normal onboarding supplies no
|
|
232
|
+
// roots, so the platform-default Cowork directory is included automatically.
|
|
233
|
+
const defaultCoworkRoot = opts.claudeRoot === undefined
|
|
234
|
+
? resolveClaudeCoworkSessionsDir()
|
|
235
|
+
: null;
|
|
236
|
+
const coworkRoots = opts.coworkRoots ?? (defaultCoworkRoot ? [defaultCoworkRoot] : []);
|
|
237
|
+
for (const root of coworkRoots) {
|
|
238
|
+
for (const filePath of userCreatedClaudeFiles(root)) {
|
|
239
|
+
const conversationId = coworkConversationId(filePath, root);
|
|
240
|
+
if (!conversationId)
|
|
241
|
+
continue;
|
|
242
|
+
candidates.push({ filePath, source: "claude-desktop", conversationId });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return candidates;
|
|
246
|
+
}
|
|
204
247
|
/** Discover every user-created local session, newest first (by first-turn timestamp). */
|
|
205
248
|
export function discoverSessions(opts = {}) {
|
|
206
249
|
const out = [];
|
|
@@ -209,17 +252,31 @@ export function discoverSessions(opts = {}) {
|
|
|
209
252
|
if (s)
|
|
210
253
|
out.push(s);
|
|
211
254
|
}
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (s)
|
|
217
|
-
out.push(s);
|
|
218
|
-
}
|
|
255
|
+
for (const candidate of claudeSessionCandidates(opts)) {
|
|
256
|
+
const s = assembleClaude(candidate.filePath, candidate);
|
|
257
|
+
if (s)
|
|
258
|
+
out.push(s);
|
|
219
259
|
}
|
|
220
260
|
out.sort((a, b) => String(b.firstTs || "").localeCompare(String(a.firstTs || "")));
|
|
221
261
|
return out;
|
|
222
262
|
}
|
|
263
|
+
function sourceCounts(sessions, pending) {
|
|
264
|
+
const codexCount = sessions.filter((session) => session.source === "codex").length;
|
|
265
|
+
const claudeCodeCount = sessions.filter((session) => session.source === "claude-code").length;
|
|
266
|
+
const coworkCount = sessions.filter((session) => session.source === "claude-desktop").length;
|
|
267
|
+
const pendingCodex = pending.filter((session) => session.source === "codex").length;
|
|
268
|
+
const pendingClaudeCode = pending.filter((session) => session.source === "claude-code").length;
|
|
269
|
+
const pendingCowork = pending.filter((session) => session.source === "claude-desktop").length;
|
|
270
|
+
return {
|
|
271
|
+
codexCount,
|
|
272
|
+
claudeCount: claudeCodeCount + coworkCount,
|
|
273
|
+
claudeCodeCount,
|
|
274
|
+
coworkCount,
|
|
275
|
+
pendingCodex,
|
|
276
|
+
pendingClaudeCode,
|
|
277
|
+
pendingCowork,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
223
280
|
const IMPORT_STATUS_CHUNK_SIZE = 1000;
|
|
224
281
|
function initialJsonObjects(file, maxBytes = 1024 * 1024) {
|
|
225
282
|
let fd;
|
|
@@ -279,7 +336,7 @@ function isUserCreatedClaudeSessionFile(file) {
|
|
|
279
336
|
});
|
|
280
337
|
}
|
|
281
338
|
function fastSessionInfo(file, source) {
|
|
282
|
-
let conversationKey = `${source
|
|
339
|
+
let conversationKey = `${source}:${sha16(file)}`;
|
|
283
340
|
let hasTextTurn = false;
|
|
284
341
|
let hasRealKey = false;
|
|
285
342
|
for (const obj of initialJsonObjects(file)) {
|
|
@@ -301,11 +358,11 @@ function fastSessionInfo(file, source) {
|
|
|
301
358
|
hasTextTurn = true;
|
|
302
359
|
continue;
|
|
303
360
|
}
|
|
304
|
-
if (
|
|
305
|
-
conversationKey =
|
|
361
|
+
if (typeof obj.sessionId === "string" && obj.sessionId) {
|
|
362
|
+
conversationKey = `${source}:${obj.sessionId}`;
|
|
306
363
|
hasRealKey = true;
|
|
307
364
|
}
|
|
308
|
-
if (
|
|
365
|
+
if (obj.type === "user" || obj.type === "assistant") {
|
|
309
366
|
const message = isRecord(obj.message) ? obj.message : {};
|
|
310
367
|
if (hasClaudeText(message.content))
|
|
311
368
|
hasTextTurn = true;
|
|
@@ -330,19 +387,25 @@ function fastSessionEntries(opts = {}) {
|
|
|
330
387
|
mtimeMs: Math.round(file.mtimeMs),
|
|
331
388
|
});
|
|
332
389
|
}
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
390
|
+
for (const candidate of claudeSessionCandidates(opts)) {
|
|
391
|
+
const stat = statSafe(candidate.filePath);
|
|
392
|
+
const filenameId = path.basename(candidate.filePath, path.extname(candidate.filePath));
|
|
393
|
+
const filenameHasStableId = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(filenameId);
|
|
394
|
+
const stableId = candidate.conversationId
|
|
395
|
+
?? (filenameHasStableId ? filenameId.toLowerCase() : null);
|
|
396
|
+
const info = stableId
|
|
397
|
+
? { conversationKey: `${candidate.source}:${stableId}`, hasTextTurn: true, hasRealKey: true }
|
|
398
|
+
: fastSessionInfo(candidate.filePath, candidate.source);
|
|
399
|
+
// Same rule as codex: include on text OR a real session id so large sessions aren't undercounted,
|
|
400
|
+
// while keeping the key stable. Cowork keys use the outer local_<id>, not the nested CLI UUID.
|
|
401
|
+
if (info.hasTextTurn || info.hasRealKey) {
|
|
402
|
+
out.push({
|
|
403
|
+
filePath: candidate.filePath,
|
|
404
|
+
source: candidate.source,
|
|
405
|
+
conversationKey: info.conversationKey,
|
|
406
|
+
size: stat.size,
|
|
407
|
+
mtimeMs: stat.mtimeMs,
|
|
408
|
+
});
|
|
346
409
|
}
|
|
347
410
|
}
|
|
348
411
|
return out;
|
|
@@ -506,7 +569,7 @@ function printMigrationEstimate(e, useJson) {
|
|
|
506
569
|
console.log("");
|
|
507
570
|
console.log("Migration estimate (local metadata only; no transcripts uploaded)");
|
|
508
571
|
console.log(` Sessions: ${humanNum(e.sessions)} total · ${humanNum(e.pending)} pending · ${humanNum(e.alreadyMigrated)} already processed`);
|
|
509
|
-
console.log(` Sources: ${humanNum(e.pendingCodex)} Codex + ${humanNum(e.pendingClaudeCode)} Claude Code pending`);
|
|
572
|
+
console.log(` Sources: ${humanNum(e.pendingCodex)} Codex + ${humanNum(e.pendingClaudeCode)} Claude Code + ${humanNum(e.pendingCowork)} Cowork pending`);
|
|
510
573
|
console.log(` Assembled transcript size: ${humanNum(e.chars.total)} chars ≈ ${humanNum(e.approxInputTokens.total)} input tokens`);
|
|
511
574
|
console.log(` Size percentiles: p50 ${humanNum(e.chars.p50)} chars · p90 ${humanNum(e.chars.p90)} · p95 ${humanNum(e.chars.p95)} · max ${humanNum(e.chars.max)}`);
|
|
512
575
|
console.log(` Turns: ${humanNum(e.turns.total)} total · p50 ${humanNum(e.turns.p50)} · p90 ${humanNum(e.turns.p90)} · max ${humanNum(e.turns.max)}`);
|
|
@@ -615,7 +678,7 @@ function percentile(nums, p) {
|
|
|
615
678
|
export function estimateMigration(sessions, pending) {
|
|
616
679
|
const chars = pending.map((s) => s.rawData.length);
|
|
617
680
|
const turns = pending.map((s) => s.turnCount);
|
|
618
|
-
const
|
|
681
|
+
const counts = sourceCounts(sessions, pending);
|
|
619
682
|
const bucketDefs = [
|
|
620
683
|
{ label: "small <=10k chars", min: 0, max: 10_000 },
|
|
621
684
|
{ label: "routine 10k-30k", min: 10_000, max: 30_000 },
|
|
@@ -640,8 +703,10 @@ export function estimateMigration(sessions, pending) {
|
|
|
640
703
|
alreadyMigrated: sessions.length - pending.length,
|
|
641
704
|
codex: sessions.filter((s) => s.source === "codex").length,
|
|
642
705
|
claudeCode: sessions.filter((s) => s.source === "claude-code").length,
|
|
643
|
-
|
|
644
|
-
|
|
706
|
+
cowork: counts.coworkCount,
|
|
707
|
+
pendingCodex: counts.pendingCodex,
|
|
708
|
+
pendingClaudeCode: counts.pendingClaudeCode,
|
|
709
|
+
pendingCowork: counts.pendingCowork,
|
|
645
710
|
chars: {
|
|
646
711
|
total: sum(chars),
|
|
647
712
|
p50: percentile(chars, 0.5),
|
|
@@ -723,7 +788,7 @@ export function estimateMigrationEtaFromLengths(lengths, skippedActive = 0, opts
|
|
|
723
788
|
};
|
|
724
789
|
}
|
|
725
790
|
export function summarizeFastMigratableDiscovery(discovery) {
|
|
726
|
-
const
|
|
791
|
+
const counts = sourceCounts(discovery.sessions, discovery.pending);
|
|
727
792
|
return {
|
|
728
793
|
sessions: discovery.sessions.length,
|
|
729
794
|
pending: discovery.pending.length,
|
|
@@ -732,8 +797,11 @@ export function summarizeFastMigratableDiscovery(discovery) {
|
|
|
732
797
|
skippedActive: discovery.skippedActive,
|
|
733
798
|
codexCount: discovery.codexCount,
|
|
734
799
|
claudeCount: discovery.claudeCount,
|
|
735
|
-
|
|
736
|
-
|
|
800
|
+
claudeCodeCount: discovery.claudeCodeCount ?? counts.claudeCodeCount,
|
|
801
|
+
coworkCount: discovery.coworkCount ?? counts.coworkCount,
|
|
802
|
+
pendingCodex: discovery.pendingCodex ?? counts.pendingCodex,
|
|
803
|
+
pendingClaudeCode: discovery.pendingClaudeCode ?? counts.pendingClaudeCode,
|
|
804
|
+
pendingCowork: discovery.pendingCowork ?? counts.pendingCowork,
|
|
737
805
|
eta: estimateMigrationEtaFromLengths(discovery.pending.map((s) => s.size), discovery.skippedActive, {
|
|
738
806
|
secondsPerSession: measuredSecondsPerSession() ?? undefined,
|
|
739
807
|
}),
|
|
@@ -771,8 +839,7 @@ export function discoverMigratableFastDiscovery(opts = {}) {
|
|
|
771
839
|
return !e || e.size !== s.size;
|
|
772
840
|
});
|
|
773
841
|
const pending = opts.limit && opts.limit > 0 ? pendingAll.slice(0, opts.limit) : pendingAll;
|
|
774
|
-
const
|
|
775
|
-
const pendingCodex = pending.filter((s) => s.source === "codex").length;
|
|
842
|
+
const counts = sourceCounts(sessions, pending);
|
|
776
843
|
return {
|
|
777
844
|
sessions,
|
|
778
845
|
pending,
|
|
@@ -780,10 +847,7 @@ export function discoverMigratableFastDiscovery(opts = {}) {
|
|
|
780
847
|
alreadyMigrated: selectable.length - pendingAll.length,
|
|
781
848
|
skippedActive,
|
|
782
849
|
limited: pending.length < pendingAll.length,
|
|
783
|
-
|
|
784
|
-
claudeCount: sessions.length - codexCount,
|
|
785
|
-
pendingCodex,
|
|
786
|
-
pendingClaudeCode: pending.length - pendingCodex,
|
|
850
|
+
...counts,
|
|
787
851
|
};
|
|
788
852
|
}
|
|
789
853
|
export function discoverMigratableSummaryFast(opts = {}) {
|
|
@@ -841,8 +905,7 @@ export function discoverMigratableSessions(opts = {}) {
|
|
|
841
905
|
return !e || e.size !== s.size;
|
|
842
906
|
});
|
|
843
907
|
const pending = opts.limit && opts.limit > 0 ? pendingAll.slice(0, opts.limit) : pendingAll;
|
|
844
|
-
const
|
|
845
|
-
const pendingCodex = pending.filter((s) => s.source === "codex").length;
|
|
908
|
+
const counts = sourceCounts(sessions, pending);
|
|
846
909
|
return {
|
|
847
910
|
sessions,
|
|
848
911
|
pending,
|
|
@@ -850,10 +913,7 @@ export function discoverMigratableSessions(opts = {}) {
|
|
|
850
913
|
alreadyMigrated: selectableSessions.length - pendingAll.length,
|
|
851
914
|
skippedActive,
|
|
852
915
|
limited: pending.length < pendingAll.length,
|
|
853
|
-
|
|
854
|
-
claudeCount: sessions.length - codexCount,
|
|
855
|
-
pendingCodex,
|
|
856
|
-
pendingClaudeCode: pending.length - pendingCodex,
|
|
916
|
+
...counts,
|
|
857
917
|
};
|
|
858
918
|
}
|
|
859
919
|
/**
|
|
@@ -871,11 +931,16 @@ export function discoverPendingSessionsTargeted(processedKeys, opts = {}) {
|
|
|
871
931
|
const filtered = processedKeys ? applyFastAccountImportStatus(fast, processedKeys, opts) : fast;
|
|
872
932
|
const pending = [];
|
|
873
933
|
for (const entry of filtered.pending) {
|
|
874
|
-
const s = entry.source === "codex"
|
|
934
|
+
const s = entry.source === "codex"
|
|
935
|
+
? assembleCodex(entry.filePath)
|
|
936
|
+
: assembleClaude(entry.filePath, {
|
|
937
|
+
source: entry.source,
|
|
938
|
+
conversationId: bareId(entry),
|
|
939
|
+
});
|
|
875
940
|
if (s)
|
|
876
941
|
pending.push(s);
|
|
877
942
|
}
|
|
878
|
-
const
|
|
943
|
+
const pendingCounts = sourceCounts(pending, pending);
|
|
879
944
|
return {
|
|
880
945
|
sessions: pending,
|
|
881
946
|
pending,
|
|
@@ -885,8 +950,11 @@ export function discoverPendingSessionsTargeted(processedKeys, opts = {}) {
|
|
|
885
950
|
limited: filtered.limited,
|
|
886
951
|
codexCount: filtered.codexCount,
|
|
887
952
|
claudeCount: filtered.claudeCount,
|
|
888
|
-
|
|
889
|
-
|
|
953
|
+
claudeCodeCount: filtered.claudeCodeCount,
|
|
954
|
+
coworkCount: filtered.coworkCount,
|
|
955
|
+
pendingCodex: pendingCounts.pendingCodex,
|
|
956
|
+
pendingClaudeCode: pendingCounts.pendingClaudeCode,
|
|
957
|
+
pendingCowork: pendingCounts.pendingCowork,
|
|
890
958
|
accountChecked: filtered.accountChecked,
|
|
891
959
|
accountCheckFailed: filtered.accountCheckFailed,
|
|
892
960
|
accountCheckUnavailable: filtered.accountCheckUnavailable,
|
|
@@ -899,7 +967,7 @@ export function applyAccountImportStatus(discovery, processedKeys, opts = {}) {
|
|
|
899
967
|
const skippedActive = discovery.sessions.length - selectableSessions.length;
|
|
900
968
|
const pendingAll = selectableSessions.filter((s) => !processedKeys.has(s.conversationKey));
|
|
901
969
|
const pending = opts.limit && opts.limit > 0 ? pendingAll.slice(0, opts.limit) : pendingAll;
|
|
902
|
-
const
|
|
970
|
+
const counts = sourceCounts(discovery.sessions, pending);
|
|
903
971
|
return {
|
|
904
972
|
...discovery,
|
|
905
973
|
pending,
|
|
@@ -907,8 +975,9 @@ export function applyAccountImportStatus(discovery, processedKeys, opts = {}) {
|
|
|
907
975
|
alreadyMigrated: selectableSessions.length - pendingAll.length,
|
|
908
976
|
skippedActive,
|
|
909
977
|
limited: pending.length < pendingAll.length,
|
|
910
|
-
pendingCodex,
|
|
911
|
-
pendingClaudeCode:
|
|
978
|
+
pendingCodex: counts.pendingCodex,
|
|
979
|
+
pendingClaudeCode: counts.pendingClaudeCode,
|
|
980
|
+
pendingCowork: counts.pendingCowork,
|
|
912
981
|
accountChecked: true,
|
|
913
982
|
accountCheckFailed: false,
|
|
914
983
|
accountCheckUnavailable: false,
|
|
@@ -921,7 +990,7 @@ export function applyFastAccountImportStatus(discovery, processedKeys, opts = {}
|
|
|
921
990
|
const skippedActive = discovery.sessions.length - selectableSessions.length;
|
|
922
991
|
const pendingAll = selectableSessions.filter((s) => !processedKeys.has(s.conversationKey));
|
|
923
992
|
const pending = opts.limit && opts.limit > 0 ? pendingAll.slice(0, opts.limit) : pendingAll;
|
|
924
|
-
const
|
|
993
|
+
const counts = sourceCounts(discovery.sessions, pending);
|
|
925
994
|
return {
|
|
926
995
|
...discovery,
|
|
927
996
|
pending,
|
|
@@ -929,8 +998,9 @@ export function applyFastAccountImportStatus(discovery, processedKeys, opts = {}
|
|
|
929
998
|
alreadyMigrated: selectableSessions.length - pendingAll.length,
|
|
930
999
|
skippedActive,
|
|
931
1000
|
limited: pending.length < pendingAll.length,
|
|
932
|
-
pendingCodex,
|
|
933
|
-
pendingClaudeCode:
|
|
1001
|
+
pendingCodex: counts.pendingCodex,
|
|
1002
|
+
pendingClaudeCode: counts.pendingClaudeCode,
|
|
1003
|
+
pendingCowork: counts.pendingCowork,
|
|
934
1004
|
accountChecked: true,
|
|
935
1005
|
accountCheckFailed: false,
|
|
936
1006
|
accountCheckUnavailable: false,
|
|
@@ -1236,10 +1306,10 @@ export async function cmdMigrate(flags) {
|
|
|
1236
1306
|
}
|
|
1237
1307
|
}
|
|
1238
1308
|
}
|
|
1239
|
-
const { sessions, pending, pendingTotal, alreadyMigrated, skippedActive, limited, codexCount,
|
|
1309
|
+
const { sessions, pending, pendingTotal, alreadyMigrated, skippedActive, limited, codexCount, claudeCodeCount, coworkCount, accountChecked, accountCheckFailed, accountCheckUnavailable } = discovery;
|
|
1240
1310
|
const estimateSessions = includeActive ? sessions : sessions.filter((s) => !isActiveMigrationSession(s));
|
|
1241
1311
|
if (!sessions.length) {
|
|
1242
|
-
console.log("No local Codex
|
|
1312
|
+
console.log("No local Codex, Claude Code, or Cowork sessions found to migrate.");
|
|
1243
1313
|
return;
|
|
1244
1314
|
}
|
|
1245
1315
|
if (flags.estimate === true && flags.json === true) {
|
|
@@ -1252,7 +1322,7 @@ export async function cmdMigrate(flags) {
|
|
|
1252
1322
|
const pendingText = limited
|
|
1253
1323
|
? `${c.bold(String(pending.length))} of ${c.bold(String(pendingTotal))} ${pendingName} selected`
|
|
1254
1324
|
: `${c.bold(String(pending.length))} ${pendingName} to import`;
|
|
1255
|
-
console.log(`Found ${c.bold(String(sessions.length))} sessions (${codexCount} Codex + ${
|
|
1325
|
+
console.log(`Found ${c.bold(String(sessions.length))} sessions (${codexCount} Codex + ${claudeCodeCount} Claude Code + ${coworkCount} Cowork) · ${pendingText}` +
|
|
1256
1326
|
(alreadyMigrated ? c.dim(`, ${alreadyMigrated} already migrated`) : "") +
|
|
1257
1327
|
(skippedActive ? c.dim(`, ${skippedActive} active skipped`) : ""));
|
|
1258
1328
|
if (accountChecked)
|
|
@@ -1277,7 +1347,8 @@ export async function cmdMigrate(flags) {
|
|
|
1277
1347
|
if (flags["dry-run"] === true) {
|
|
1278
1348
|
console.log(c.dim("\n--dry-run: discovered + assembled only, nothing sent.\n"));
|
|
1279
1349
|
for (const s of pending.slice(0, 50)) {
|
|
1280
|
-
|
|
1350
|
+
const sourceLabel = s.source === "codex" ? "codex " : s.source === "claude-desktop" ? "cowork" : "claude";
|
|
1351
|
+
console.log(` ${sourceLabel} ${(s.firstTs || "").slice(0, 10)} ${humanNum(s.rawData.length)} chars ${s.turnCount} text turns`);
|
|
1281
1352
|
}
|
|
1282
1353
|
if (pending.length > 50)
|
|
1283
1354
|
console.log(c.dim(` … and ${pending.length - 50} more`));
|
|
@@ -1302,7 +1373,8 @@ export async function cmdMigrate(flags) {
|
|
|
1302
1373
|
metricsFile,
|
|
1303
1374
|
selection,
|
|
1304
1375
|
onProgress: (ev) => {
|
|
1305
|
-
const
|
|
1376
|
+
const sourceLabel = ev.session.source === "codex" ? "codex " : ev.session.source === "claude-desktop" ? "cowork" : "claude";
|
|
1377
|
+
const tag = `${c.dim(`[${ev.index}/${ev.total}]`)} ${sourceLabel} ${(ev.session.firstTs || "").slice(0, 10)}`;
|
|
1306
1378
|
if (ev.error) {
|
|
1307
1379
|
console.log(`${tag} ${c.red("✗ failed")} ${c.dim(truncate(ev.error, 60))}` + (ev.durationMs ? c.dim(` ${humanDuration(ev.durationMs)}`) : ""));
|
|
1308
1380
|
return;
|
|
@@ -231,6 +231,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
231
231
|
var pendN = hasPendingCount ? pending : 0;
|
|
232
232
|
var pendingCodex = typeof migratable.pendingCodex === "number" ? migratable.pendingCodex : null;
|
|
233
233
|
var pendingClaudeCode = typeof migratable.pendingClaudeCode === "number" ? migratable.pendingClaudeCode : null;
|
|
234
|
+
var pendingCowork = typeof migratable.pendingCowork === "number" ? migratable.pendingCowork : null;
|
|
234
235
|
if (connected && !billingStatusLoading && !setupPlanConfirmed()) {
|
|
235
236
|
renderPlanGate();
|
|
236
237
|
return;
|
|
@@ -312,6 +313,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
312
313
|
document.getElementById("exSub").textContent = sub;
|
|
313
314
|
var codexN = pendingTrusted && pendingCodex !== null ? pendingCodex : (sessions.codex || 0);
|
|
314
315
|
var claudeN = pendingTrusted && pendingClaudeCode !== null ? pendingClaudeCode : (sessions.claudeCode || 0);
|
|
316
|
+
var coworkN = pendingTrusted && pendingCowork !== null ? pendingCowork : (sessions.cowork || 0);
|
|
315
317
|
// Proof strip: one quiet line of evidence that the scan is real. Hidden when there is nothing to show.
|
|
316
318
|
var srcIcon = function (id, fallback) {
|
|
317
319
|
var asset = id === "claude-desktop" ? "claude" : "codex";
|
|
@@ -325,7 +327,9 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
325
327
|
? '<span class="pf">' + srcIcon("codex", "CX") + '<strong>' + esc(number(codexN)) + '</strong> Codex</span>' +
|
|
326
328
|
'<span class="pfDot">·</span>' +
|
|
327
329
|
'<span class="pf">' + srcIcon("claude-desktop", "CL") + '<strong>' + esc(number(claudeN)) + '</strong> Claude Code</span>' +
|
|
328
|
-
'<span class="
|
|
330
|
+
'<span class="pfDot">·</span>' +
|
|
331
|
+
'<span class="pf">' + srcIcon("claude-desktop", "CW") + '<strong>' + esc(number(coworkN)) + '</strong> Cowork</span>' +
|
|
332
|
+
'<span class="pfNote">found on this computer</span>'
|
|
329
333
|
: "");
|
|
330
334
|
// Reassurances live at the moment of commitment — right under the button.
|
|
331
335
|
document.getElementById("exEta").innerHTML = degradedWithoutCounts
|
|
@@ -776,7 +780,9 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
776
780
|
}
|
|
777
781
|
}
|
|
778
782
|
function sessionSourceLabel(source) {
|
|
779
|
-
|
|
783
|
+
if (source === "claude-code") return "Claude Code";
|
|
784
|
+
if (source === "claude-desktop") return "Cowork";
|
|
785
|
+
return "Codex";
|
|
780
786
|
}
|
|
781
787
|
function sessionDateLabel(value) {
|
|
782
788
|
if (!value) return "Date unavailable";
|
|
@@ -931,11 +937,12 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
931
937
|
var key = String(session.key || "");
|
|
932
938
|
var encodedKey = encodeURIComponent(key);
|
|
933
939
|
var checked = !!selectedSessionKeys[key];
|
|
934
|
-
var
|
|
935
|
-
var
|
|
940
|
+
var isClaude = session.source === "claude-code" || session.source === "claude-desktop";
|
|
941
|
+
var sourceIcon = isClaude ? "claude" : "codex";
|
|
942
|
+
var sourceFallback = session.source === "claude-desktop" ? "CW" : (session.source === "claude-code" ? "CL" : "CX");
|
|
936
943
|
return '<label class="sessionPickerRow' + (checked ? " is-selected" : "") + '">' +
|
|
937
944
|
'<input type="checkbox" data-session-key="' + esc(encodedKey) + '"' + (checked ? " checked" : "") + ' />' +
|
|
938
|
-
'<span class="sessionPickerSource ' + (
|
|
945
|
+
'<span class="sessionPickerSource ' + (isClaude ? "is-claude" : "is-codex") + '"><img src="/hud-assets/' + sourceIcon + '.svg" alt="" onerror="this.style.display="none";this.nextElementSibling.style.display="grid";" /><span class="sessionPickerSourceFallback">' + sourceFallback + '</span></span>' +
|
|
939
946
|
'<span class="sessionPickerMain"><strong>' + esc(session.title || "Untitled coding session") + '</strong><small>' + esc(session.project || "No project detected") + ' · ' + esc(sessionDateLabel(session.date)) + '</small></span>' +
|
|
940
947
|
'<span class="sessionPickerMeta">' + esc(compact(Number(session.approxInputTokens) || 0)) + ' tok</span>' +
|
|
941
948
|
'</label>';
|
|
@@ -947,7 +954,7 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
947
954
|
'<div class="sessionPickerShell">' +
|
|
948
955
|
'<header class="sessionPickerHead"><div><h2 id="sessionPickerTitle">Choose conversations</h2><p class="sessionPickerSummary"><span class="sessionPickerQuota">Up to <strong>' + esc(number(limit)) + '</strong></span>' + inlineUpgrade + '<span class="sessionPickerOrder">· newest selected first</span></p></div><button type="button" class="sessionPickerClose" id="closeSessionPicker" aria-label="Close">×</button></header>' +
|
|
949
956
|
'<div class="sessionPickerToolbar"><label class="sessionSearch"><span>Search</span><input type="search" id="sessionPickerSearch" placeholder="Title or project" value="' + esc(sessionPickerQuery) + '" /></label>' +
|
|
950
|
-
'<label class="sessionSourceFilter"><span>Source</span><select id="sessionPickerSource"><option value="all"' + (sessionPickerSource === "all" ? " selected" : "") + '>All sources</option><option value="codex"' + (sessionPickerSource === "codex" ? " selected" : "") + '>Codex</option><option value="claude-code"' + (sessionPickerSource === "claude-code" ? " selected" : "") + '>Claude Code</option></select></label>' +
|
|
957
|
+
'<label class="sessionSourceFilter"><span>Source</span><select id="sessionPickerSource"><option value="all"' + (sessionPickerSource === "all" ? " selected" : "") + '>All sources</option><option value="codex"' + (sessionPickerSource === "codex" ? " selected" : "") + '>Codex</option><option value="claude-code"' + (sessionPickerSource === "claude-code" ? " selected" : "") + '>Claude Code</option><option value="claude-desktop"' + (sessionPickerSource === "claude-desktop" ? " selected" : "") + '>Cowork</option></select></label>' +
|
|
951
958
|
'</div>' +
|
|
952
959
|
'<div class="sessionPickerActions"><strong>' + esc(number(selected.length)) + ' / ' + esc(number(limit)) + ' selected</strong><span id="sessionPickerStatus" class="sessionPickerStatus" role="status" aria-live="polite">' + (limit === 0 ? "Plan limit reached. Upgrade to import more." : "") + '</span><button type="button" class="textButton" id="selectNewestSessions">Select newest</button><button type="button" class="textButton" id="clearSessions">Clear</button></div>' +
|
|
953
960
|
'<div class="sessionPickerListWrap' + (limit === 0 ? " is-limit-blocked" : "") + '" id="sessionPickerListWrap"><div class="sessionPickerList"' + (limit === 0 ? " inert" : "") + '>' + (rows || '<div class="sessionPickerEmpty">No conversations match these filters.</div>') + overflow + '</div><div class="sessionPickerLimitOverlay" id="sessionPickerLimitOverlay" role="alert"' + (limit === 0 ? "" : " hidden") + '>' + (limit === 0 ? sessionPickerLimitNotice(false) : "") + '</div></div>' +
|
|
@@ -1207,13 +1214,18 @@ export const SETUP_PAGE_CLIENT_EXTRACTION = String.raw ` /* ---------- dash
|
|
|
1207
1214
|
function setupPlanDefinition(plan) {
|
|
1208
1215
|
var trialAvailable = !billingStatus || billingStatus.trialAvailable !== false;
|
|
1209
1216
|
var limits = quotaLimitsForPlan(plan);
|
|
1217
|
+
var fallbackFeatures = plan === "power"
|
|
1218
|
+
? ["2,000 past chats", "agent-heavy memory", "2,000 memory recalls / week"]
|
|
1219
|
+
: plan === "pro"
|
|
1220
|
+
? ["500 past chats", "daily memory updates", "500 memory recalls / week"]
|
|
1221
|
+
: ["100 coding sessions", "a few new sessions / week", "100 memory recalls / week"];
|
|
1210
1222
|
var features = limits
|
|
1211
1223
|
? [
|
|
1212
1224
|
quotaNumber(limits.historicalConversationLimit) + " past chats",
|
|
1213
1225
|
quotaTokens(limits.memoryProcessingInputTokensWeeklyLimit) + " new-chat tokens / week",
|
|
1214
1226
|
quotaNumber(limits.memorySearchWeeklyLimit) + " memory recalls / week"
|
|
1215
1227
|
]
|
|
1216
|
-
:
|
|
1228
|
+
: fallbackFeatures;
|
|
1217
1229
|
if (plan === "power") return {
|
|
1218
1230
|
id: "power",
|
|
1219
1231
|
name: "Power",
|
package/dist/setup.js
CHANGED
|
@@ -876,6 +876,7 @@ function openClaudeDesktop() {
|
|
|
876
876
|
};
|
|
877
877
|
}
|
|
878
878
|
const LOCAL_SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
879
|
+
const LOCAL_COWORK_SESSION_ID_RE = /^local_[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
879
880
|
// `claude --resume <id>` only finds sessions that belong to the current project directory, so
|
|
880
881
|
// recover the session's original cwd from its transcript before resuming.
|
|
881
882
|
function claudeSessionCwd(sessionId) {
|
|
@@ -906,10 +907,14 @@ function shellQuote(value) {
|
|
|
906
907
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
907
908
|
}
|
|
908
909
|
function openExistingAgentSession(source, sessionId) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
910
|
+
const validSessionId = source === "claude-desktop"
|
|
911
|
+
? LOCAL_COWORK_SESSION_ID_RE.test(sessionId)
|
|
912
|
+
: LOCAL_SESSION_ID_RE.test(sessionId);
|
|
913
|
+
if (!validSessionId)
|
|
912
914
|
return { ok: false, message: "The local session identifier is invalid." };
|
|
915
|
+
if (source !== "claude-desktop" && process.platform !== "darwin") {
|
|
916
|
+
return { ok: false, message: "Opening local Codex and Claude Code sessions is currently available on macOS." };
|
|
917
|
+
}
|
|
913
918
|
try {
|
|
914
919
|
if (source === "codex") {
|
|
915
920
|
execFileSync("open", [`codex://threads/${sessionId}`], { stdio: "pipe" });
|
|
@@ -926,6 +931,23 @@ function openExistingAgentSession(source, sessionId) {
|
|
|
926
931
|
], { stdio: "pipe" });
|
|
927
932
|
return { ok: true, message: "Opened the original Claude Code session in Terminal." };
|
|
928
933
|
}
|
|
934
|
+
if (source === "claude-desktop") {
|
|
935
|
+
const url = `claude://claude.ai/claude-code-desktop/${sessionId}`;
|
|
936
|
+
if (process.platform === "win32") {
|
|
937
|
+
spawn(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", `start "" "${url}"`], {
|
|
938
|
+
detached: true,
|
|
939
|
+
stdio: "ignore",
|
|
940
|
+
windowsHide: true,
|
|
941
|
+
}).unref();
|
|
942
|
+
}
|
|
943
|
+
else if (process.platform === "darwin") {
|
|
944
|
+
execFileSync("open", [url], { stdio: "pipe" });
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
return { ok: false, message: "Opening local Cowork sessions is not supported on this system yet." };
|
|
948
|
+
}
|
|
949
|
+
return { ok: true, message: "Opened the original Cowork session in Claude Desktop." };
|
|
950
|
+
}
|
|
929
951
|
return { ok: false, message: "Unsupported agent session source." };
|
|
930
952
|
}
|
|
931
953
|
catch (error) {
|
|
@@ -966,7 +988,8 @@ function migratableFromDiscovery(disc) {
|
|
|
966
988
|
pending: disc.pending.length,
|
|
967
989
|
pendingTotal: disc.pendingTotal,
|
|
968
990
|
pendingCodex,
|
|
969
|
-
pendingClaudeCode: disc.pendingClaudeCode ?? disc.pending.
|
|
991
|
+
pendingClaudeCode: disc.pendingClaudeCode ?? disc.pending.filter((s) => s.source === "claude-code").length,
|
|
992
|
+
pendingCowork: disc.pendingCowork ?? disc.pending.filter((s) => s.source === "claude-desktop").length,
|
|
970
993
|
alreadyMigrated: disc.alreadyMigrated,
|
|
971
994
|
skippedActive: disc.skippedActive,
|
|
972
995
|
limited: disc.limited,
|
|
@@ -985,7 +1008,8 @@ function sessionsFromDiscovery(disc) {
|
|
|
985
1008
|
return {
|
|
986
1009
|
total: disc.sessions.length,
|
|
987
1010
|
codex: disc.codexCount,
|
|
988
|
-
claudeCode: disc.
|
|
1011
|
+
claudeCode: disc.claudeCodeCount,
|
|
1012
|
+
cowork: disc.coworkCount,
|
|
989
1013
|
};
|
|
990
1014
|
}
|
|
991
1015
|
function migratableFromFastSummary(summary) {
|
|
@@ -994,6 +1018,7 @@ function migratableFromFastSummary(summary) {
|
|
|
994
1018
|
pendingTotal: summary.pendingTotal,
|
|
995
1019
|
pendingCodex: summary.pendingCodex,
|
|
996
1020
|
pendingClaudeCode: summary.pendingClaudeCode,
|
|
1021
|
+
pendingCowork: summary.pendingCowork,
|
|
997
1022
|
alreadyMigrated: summary.alreadyMigrated,
|
|
998
1023
|
skippedActive: summary.skippedActive,
|
|
999
1024
|
eta: summary.eta,
|
|
@@ -1482,7 +1507,7 @@ export function completeOptionalStatsPayload(payload, reason, countsTrusted) {
|
|
|
1482
1507
|
generatedFrom: ["~/.codex/sessions", "~/.claude/projects"],
|
|
1483
1508
|
llmCallsUsed: 0,
|
|
1484
1509
|
transcriptsUploaded: false,
|
|
1485
|
-
sessions: { total: 0, codex: 0, claudeCode: 0 },
|
|
1510
|
+
sessions: { total: 0, codex: 0, claudeCode: 0, cowork: 0 },
|
|
1486
1511
|
migratable: { pending: 0, alreadyMigrated: 0 },
|
|
1487
1512
|
memoriesCaptured: null,
|
|
1488
1513
|
};
|
|
@@ -3290,7 +3315,8 @@ async function cmdOnboarding(flags) {
|
|
|
3290
3315
|
let sessionSummary = {
|
|
3291
3316
|
total: quick.sessions,
|
|
3292
3317
|
codex: quick.codexCount,
|
|
3293
|
-
claudeCode: quick.
|
|
3318
|
+
claudeCode: quick.claudeCodeCount,
|
|
3319
|
+
cowork: quick.coworkCount,
|
|
3294
3320
|
};
|
|
3295
3321
|
stats = await buildStatsPayload([], {
|
|
3296
3322
|
partial: true,
|
|
@@ -3326,7 +3352,8 @@ async function cmdOnboarding(flags) {
|
|
|
3326
3352
|
sessionSummary = {
|
|
3327
3353
|
total: cloudSummary.sessions,
|
|
3328
3354
|
codex: cloudSummary.codexCount,
|
|
3329
|
-
claudeCode: cloudSummary.
|
|
3355
|
+
claudeCode: cloudSummary.claudeCodeCount,
|
|
3356
|
+
cowork: cloudSummary.coworkCount,
|
|
3330
3357
|
};
|
|
3331
3358
|
const cloudPayload = await buildStatsPayload([], {
|
|
3332
3359
|
partial: true,
|
|
@@ -3363,7 +3390,8 @@ async function cmdOnboarding(flags) {
|
|
|
3363
3390
|
sessionSummary = {
|
|
3364
3391
|
total: unavailableSummary.sessions,
|
|
3365
3392
|
codex: unavailableSummary.codexCount,
|
|
3366
|
-
claudeCode: unavailableSummary.
|
|
3393
|
+
claudeCode: unavailableSummary.claudeCodeCount,
|
|
3394
|
+
cowork: unavailableSummary.coworkCount,
|
|
3367
3395
|
};
|
|
3368
3396
|
const unavailablePayload = await buildStatsPayload([], {
|
|
3369
3397
|
partial: true,
|
package/dist/source-session.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import { resolveClaudeProjectsDir, resolveCodexSessionRoots } from "./local-data-paths.js";
|
|
3
|
+
import { resolveClaudeCoworkSessionsDir, resolveClaudeProjectsDir, resolveCodexSessionRoots, } from "./local-data-paths.js";
|
|
5
4
|
export const SOURCE_SESSION_BINDING_EVIDENCE = "local_jsonl_tool_call";
|
|
6
5
|
export const SOURCE_SESSION_HOOK_EVIDENCE = "local_session_start_hook";
|
|
7
6
|
export const SOURCE_SESSION_MCP_METADATA_EVIDENCE = "local_mcp_session_metadata";
|
|
@@ -81,18 +80,13 @@ function readableDirectory(candidate) {
|
|
|
81
80
|
return false;
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
|
-
function coworkRoot() {
|
|
85
|
-
const supportRoot = process.env.CLAUDE_DESKTOP_SUPPORT_DIR?.trim()
|
|
86
|
-
|| path.join(os.homedir(), "Library", "Application Support", "Claude");
|
|
87
|
-
return path.join(supportRoot, "local-agent-mode-sessions");
|
|
88
|
-
}
|
|
89
83
|
function defaultRoots() {
|
|
90
84
|
const claudeProjects = resolveClaudeProjectsDir();
|
|
91
|
-
const desktopRoot =
|
|
85
|
+
const desktopRoot = resolveClaudeCoworkSessionsDir();
|
|
92
86
|
return {
|
|
93
87
|
codex: resolveCodexSessionRoots().map((root) => root.path),
|
|
94
88
|
claudeCode: claudeProjects ? [claudeProjects] : [],
|
|
95
|
-
cowork:
|
|
89
|
+
cowork: desktopRoot ? [desktopRoot] : [],
|
|
96
90
|
};
|
|
97
91
|
}
|
|
98
92
|
function walkJsonlFiles(roots) {
|
package/package.json
CHANGED