@echomem/mcp 1.4.43 → 1.4.44
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/README.md +25 -28
- package/dist/config-files.js +63 -0
- package/dist/context-analysis/claude-native-canonical.js +2 -2
- package/dist/context-analysis/vendored-canonical.js +2 -2
- package/dist/context-analysis/workspace-report.js +3 -3
- package/dist/hud/hooks.js +43 -31
- package/dist/index.js +15 -79
- package/dist/local-jsonl.js +87 -0
- package/dist/migrate.js +6 -4
- package/dist/onboarding-stats.js +16 -0
- package/dist/save-checkpoint-hook.js +1 -1
- package/dist/setup-page/client-core.js +18 -372
- package/dist/setup-page/client-extraction.js +33 -188
- package/dist/setup-page/client-lifecycle.js +31 -101
- package/dist/setup-page/client.js +0 -2
- package/dist/setup-page/styles-extraction.js +1 -31
- package/dist/setup-page/styles-foundation.js +0 -89
- package/dist/setup-page/styles-mvp.js +10 -155
- package/dist/setup-page/styles-website-alignment.js +0 -204
- package/dist/setup-page/styles.js +0 -4
- package/dist/setup-page.js +4 -4
- package/dist/setup-preview.js +4 -212
- package/dist/setup.js +314 -667
- package/dist/v1-contract.js +0 -8
- package/package.json +9 -7
- package/dist/city/README.md +0 -9
- package/dist/city/echo-ai-city-only.html +0 -2232
- package/dist/city/echo-extraction-plate.html +0 -330
- package/dist/city/echo-face-cutout.png +0 -0
- package/dist/city/personality_stickers/bossy.png +0 -0
- package/dist/city/personality_stickers/ghosty.png +0 -0
- package/dist/city/personality_stickers/loopy.png +0 -0
- package/dist/city/personality_stickers/lusty.png +0 -0
- package/dist/city/personality_stickers/maxxy.png +0 -0
- package/dist/city/personality_stickers/tabby.png +0 -0
- package/dist/city/vendor/OrbitControls.js +0 -1417
- package/dist/city/vendor/RoundedBoxGeometry.js +0 -155
- package/dist/city/vendor/echo_general-file-21.riv +0 -0
- package/dist/city/vendor/rive.js +0 -8139
- package/dist/city/vendor/rive.wasm +0 -0
- package/dist/city/vendor/three.module.min.js +0 -6
- package/dist/forensics.js +0 -1531
- package/dist/report.js +0 -721
- package/dist/setup-page/client-report-audit.js +0 -819
- package/dist/setup-page/client-report-city.js +0 -356
- package/dist/setup-page/client-report.js +0 -6
- package/dist/setup-page/styles-city-report.js +0 -880
- package/dist/setup-page/styles-context-audit.js +0 -470
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { StringDecoder } from "node:string_decoder";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Stream a JSONL file without loading large agent transcripts into one string.
|
|
6
|
+
* Malformed lines and unreadable files are skipped so local discovery remains recoverable.
|
|
7
|
+
*/
|
|
8
|
+
export function eachJsonLine(file, visit) {
|
|
9
|
+
let descriptor;
|
|
10
|
+
try {
|
|
11
|
+
descriptor = fs.openSync(file, "r");
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const decoder = new StringDecoder("utf8");
|
|
18
|
+
const buffer = Buffer.allocUnsafe(1 << 20);
|
|
19
|
+
let remainder = "";
|
|
20
|
+
let bytesRead;
|
|
21
|
+
const consume = (chunk) => {
|
|
22
|
+
const lines = (remainder + chunk).split("\n");
|
|
23
|
+
remainder = lines.pop() ?? "";
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
const value = line.trim();
|
|
26
|
+
if (!value)
|
|
27
|
+
continue;
|
|
28
|
+
try {
|
|
29
|
+
visit(JSON.parse(value));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* Ignore malformed or partially-written rows. */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
while ((bytesRead = fs.readSync(descriptor, buffer, 0, buffer.length, null)) > 0) {
|
|
37
|
+
consume(decoder.write(buffer.subarray(0, bytesRead)));
|
|
38
|
+
}
|
|
39
|
+
const finalValue = (remainder + decoder.end()).trim();
|
|
40
|
+
if (finalValue) {
|
|
41
|
+
try {
|
|
42
|
+
visit(JSON.parse(finalValue));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* Ignore a malformed or partially-written final row. */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
try {
|
|
51
|
+
fs.closeSync(descriptor);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
/* Best effort. */
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Recursively list matching regular files without following child symlinks. */
|
|
59
|
+
export function walkLocalFiles(directory, matches, skipDirectory, output = []) {
|
|
60
|
+
let entries;
|
|
61
|
+
try {
|
|
62
|
+
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const absolute = path.join(directory, entry.name);
|
|
69
|
+
let stat;
|
|
70
|
+
try {
|
|
71
|
+
stat = fs.lstatSync(absolute);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (stat.isSymbolicLink())
|
|
77
|
+
continue;
|
|
78
|
+
if (stat.isDirectory()) {
|
|
79
|
+
if (!skipDirectory(entry.name))
|
|
80
|
+
walkLocalFiles(absolute, matches, skipDirectory, output);
|
|
81
|
+
}
|
|
82
|
+
else if (stat.isFile() && matches(absolute)) {
|
|
83
|
+
output.push(absolute);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return output;
|
|
87
|
+
}
|
package/dist/migrate.js
CHANGED
|
@@ -28,7 +28,7 @@ import { KeyStore, echoConfigDir } from "./keystore.js";
|
|
|
28
28
|
import { fetchEncryptionConfig } from "./encryption.js";
|
|
29
29
|
import { discoverCodexSessionFiles } from "./codex-session-files.js";
|
|
30
30
|
import { resolveClaudeProjectsDir } from "./local-data-paths.js";
|
|
31
|
-
import {
|
|
31
|
+
import { eachJsonLine, walkLocalFiles } from "./local-jsonl.js";
|
|
32
32
|
const API_BASE = (process.env.ECHO_API_BASE_URL || "https://echo-mem-chrome.vercel.app").replace(/\/$/, "");
|
|
33
33
|
const RATE_MAX = 28; // stay under the import-jobs /run limit of 30 / 60s
|
|
34
34
|
const RATE_WINDOW_MS = 60_000;
|
|
@@ -56,7 +56,8 @@ export function assembleCodex(file) {
|
|
|
56
56
|
let firstTs = null;
|
|
57
57
|
let title = "";
|
|
58
58
|
const turns = [];
|
|
59
|
-
|
|
59
|
+
eachJsonLine(file, (value) => {
|
|
60
|
+
const o = value;
|
|
60
61
|
if (o && o.type === "session_meta") {
|
|
61
62
|
sessionId = (o.payload && typeof o.payload.id === "string" && o.payload.id) || sessionId;
|
|
62
63
|
cwd = (o.payload && typeof o.payload.cwd === "string" && o.payload.cwd) || cwd;
|
|
@@ -108,7 +109,8 @@ export function assembleClaude(file) {
|
|
|
108
109
|
let firstTs = null;
|
|
109
110
|
let title = "";
|
|
110
111
|
const turns = [];
|
|
111
|
-
|
|
112
|
+
eachJsonLine(file, (value) => {
|
|
113
|
+
const o = value;
|
|
112
114
|
if (!cwd && typeof o.cwd === "string")
|
|
113
115
|
cwd = o.cwd;
|
|
114
116
|
if (!sessionId && typeof o.sessionId === "string")
|
|
@@ -199,7 +201,7 @@ function userCreatedCodexFiles(codexRoot) {
|
|
|
199
201
|
return userCreatedCodexSessionFiles(codexRoot).map((file) => file.path);
|
|
200
202
|
}
|
|
201
203
|
function userCreatedClaudeFiles(claudeRoot) {
|
|
202
|
-
return
|
|
204
|
+
return walkLocalFiles(claudeRoot, (candidate) => candidate.endsWith(".jsonl"), (name) => name === "subagents" || name === "workflows").filter(isUserCreatedClaudeSessionFile);
|
|
203
205
|
}
|
|
204
206
|
/** Discover every user-created local session, newest first (by first-turn timestamp). */
|
|
205
207
|
export function discoverSessions(opts = {}) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The onboarding page only needs session discovery and import state. Usage-audit calculations
|
|
3
|
+
* (tokens, cost, context reads, recall ratios, and memory totals) are intentionally excluded.
|
|
4
|
+
*/
|
|
5
|
+
export function buildOnboardingStatsPayload(input) {
|
|
6
|
+
return {
|
|
7
|
+
schemaVersion: 1,
|
|
8
|
+
...(input.partial ? { partial: true } : {}),
|
|
9
|
+
discovery: input.discovery,
|
|
10
|
+
generatedFrom: ["~/.codex/sessions", "~/.claude/projects"],
|
|
11
|
+
llmCallsUsed: 0,
|
|
12
|
+
transcriptsUploaded: false,
|
|
13
|
+
sessions: input.sessions,
|
|
14
|
+
migratable: input.migratable,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -10,7 +10,7 @@ const CHECKPOINT_REASON = [
|
|
|
10
10
|
"Private persistence happens first. For a user with a company group, call `request_group_session_sharing` near conversation start or after a qualifying private save.",
|
|
11
11
|
"On the first request_group_session_sharing call in a conversation, omit groupSharingScopeId so EchoMem mints a fresh opaque scope. Reuse that exact scope only in this conversation for later get/request/set/save calls, and never persist it as memory. If multiple groups are returned, reuse the same scope and call once per groupId. Relay the tool's exact text question and call set_group_session_sharing only after an explicit Yes/No. Silence leaves that group's state unset. A Share decision syncs eligible memories from later saves carrying the same scope to that group; saves automatically sync to every approved group. Keep private keeps them private for that group.",
|
|
12
12
|
"Flagged memories are withheld from automatic conversation sync and remain private.",
|
|
13
|
-
"If EchoMem reports that the encrypted vault is locked,
|
|
13
|
+
"If EchoMem reports that the encrypted vault is locked, relay its local unlock instruction; for a standalone installation, use `echomem-mcp unlock`. Never silently skip a qualifying checkpoint.",
|
|
14
14
|
].join(" ");
|
|
15
15
|
function currentTurnSlice(transcript) {
|
|
16
16
|
const lines = transcript.split(/\r?\n/);
|