@echomem/mcp 1.3.2 → 1.4.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.
- package/dist/city/_live.html +37 -0
- package/dist/city/_serve.mjs +45 -0
- package/dist/city/card-data.json +15 -0
- package/dist/city/city-data.json +248 -0
- package/dist/city/echo-ai-city-only.html +1254 -0
- package/dist/city/echo-ai-city-only.template.html +1254 -0
- package/dist/city/echo-extraction-plate.html +330 -0
- package/dist/city/generate-echo-city-only.mjs +112 -0
- package/dist/city/vendor/OrbitControls.js +1417 -0
- package/dist/city/vendor/RoundedBoxGeometry.js +155 -0
- package/dist/city/vendor/echo_general-file-21.riv +0 -0
- package/dist/city/vendor/rive.js +8139 -0
- package/dist/city/vendor/rive.wasm +0 -0
- package/dist/city/vendor/three.module.min.js +6 -0
- package/dist/forensics.js +41 -5
- package/dist/migrate.js +46 -4
- package/dist/setup-page.js +768 -266
- package/dist/setup.js +64 -4
- package/package.json +2 -2
package/dist/setup.js
CHANGED
|
@@ -20,13 +20,15 @@ import fs from "node:fs";
|
|
|
20
20
|
import os from "node:os";
|
|
21
21
|
import path from "node:path";
|
|
22
22
|
import readline from "node:readline";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
23
24
|
import axios from "axios";
|
|
24
25
|
import { KeyStore } from "./keystore.js";
|
|
25
26
|
import { fetchEncryptionConfig, deriveAndVerifyKey, verifyKeyB64 } from "./encryption.js";
|
|
26
27
|
import { collect, runReport, buildStatsPayload } from "./report.js";
|
|
27
|
-
import { cmdMigrate, applyAccountImportStatus, applyFastAccountImportStatus, discoverMigratableFastDiscovery, discoverMigratableSessions, estimateMigrationEta, fetchProcessedImportKeys, isImportStatusUnsupported, markAccountImportStatusFailed, markAccountImportStatusUnavailable, markFastAccountImportStatusUnavailable, startMigration, summarizeFastMigratableDiscovery, MIGRATE_CONCURRENCY, } from "./migrate.js";
|
|
28
|
+
import { cmdMigrate, applyAccountImportStatus, applyFastAccountImportStatus, discoverMigratableFastDiscovery, discoverMigratableSessions, discoverPendingSessionsTargeted, estimateMigrationEta, fetchProcessedImportKeys, isImportStatusUnsupported, markAccountImportStatusFailed, markAccountImportStatusUnavailable, markFastAccountImportStatusUnavailable, startMigration, summarizeFastMigratableDiscovery, MIGRATE_CONCURRENCY, } from "./migrate.js";
|
|
28
29
|
import { syncCodexUsage } from "./codex-sync.js";
|
|
29
30
|
import { renderSetupPage } from "./setup-page.js";
|
|
31
|
+
import { repoLabel } from "./forensics.js";
|
|
30
32
|
// The hosted connect-device page is now only the account-auth/token courier. The dashboard itself is
|
|
31
33
|
// served by this localhost bridge, where local logs and processed/unprocessed counts never leave the
|
|
32
34
|
// device unless the user explicitly starts migration. Override the hosted auth origin with ECHO_WEB_URL.
|
|
@@ -262,6 +264,43 @@ function withTimeout(promise, ms, code, onTimeout) {
|
|
|
262
264
|
function delay(ms) {
|
|
263
265
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
264
266
|
}
|
|
267
|
+
const CITY_ASSET_TYPES = {
|
|
268
|
+
".html": "text/html; charset=utf-8",
|
|
269
|
+
".js": "text/javascript; charset=utf-8",
|
|
270
|
+
".json": "application/json; charset=utf-8",
|
|
271
|
+
".wasm": "application/wasm",
|
|
272
|
+
".riv": "application/octet-stream",
|
|
273
|
+
".png": "image/png",
|
|
274
|
+
".svg": "image/svg+xml",
|
|
275
|
+
};
|
|
276
|
+
function repoCityArtifactsRoot() {
|
|
277
|
+
// Monorepo/dev: read the city assets live from repo-root /artifacts.
|
|
278
|
+
const repo = fileURLToPath(new URL("../../../artifacts/", import.meta.url));
|
|
279
|
+
if (fs.existsSync(repo))
|
|
280
|
+
return repo;
|
|
281
|
+
// Published install: fall back to the copy bundled into dist/city by prepack (bundle-city.mjs).
|
|
282
|
+
return fileURLToPath(new URL("./city/", import.meta.url));
|
|
283
|
+
}
|
|
284
|
+
function serveRepoCityAsset(reqPath, res) {
|
|
285
|
+
const root = repoCityArtifactsRoot();
|
|
286
|
+
const rel = reqPath === "/city" || reqPath === "/city/" ? "echo-ai-city-only.html" : decodeURIComponent(reqPath.slice("/city/".length));
|
|
287
|
+
const filePath = path.resolve(root, rel);
|
|
288
|
+
const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;
|
|
289
|
+
if (!filePath.startsWith(rootWithSep)) {
|
|
290
|
+
res.writeHead(403).end("forbidden");
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
294
|
+
res.writeHead(404).end("not found");
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
res.writeHead(200, {
|
|
298
|
+
"Content-Type": CITY_ASSET_TYPES[path.extname(filePath)] || "application/octet-stream",
|
|
299
|
+
"Cache-Control": "no-store",
|
|
300
|
+
});
|
|
301
|
+
fs.createReadStream(filePath).pipe(res);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
265
304
|
function discoverMigratableSessionsOffThread() {
|
|
266
305
|
const migrateUrl = new URL("./migrate.js", import.meta.url).href;
|
|
267
306
|
const code = `
|
|
@@ -440,6 +479,10 @@ export function startCallbackServer(opts = {}) {
|
|
|
440
479
|
const url = new URL(req.url || "/", "http://127.0.0.1");
|
|
441
480
|
const route = url.pathname;
|
|
442
481
|
const run = async () => {
|
|
482
|
+
if ((route === "/city" || route.startsWith("/city/")) && req.method === "GET") {
|
|
483
|
+
serveRepoCityAsset(route, res);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
443
486
|
if (route === "/setup" && req.method === "GET") {
|
|
444
487
|
res.writeHead(200, {
|
|
445
488
|
"Content-Type": "text/html; charset=utf-8",
|
|
@@ -817,12 +860,15 @@ async function cmdLogin(flags) {
|
|
|
817
860
|
let exactDiscovery = Promise.resolve(null);
|
|
818
861
|
let refreshGeneration = 0;
|
|
819
862
|
let latestPendingEstimate = 0;
|
|
863
|
+
// The account's already-imported keys, shared so the /migrate sizing can assemble ONLY pending sessions.
|
|
864
|
+
let lastProcessedImportKeys = null;
|
|
820
865
|
const resetLocalLoginState = () => {
|
|
821
866
|
refreshGeneration++;
|
|
822
867
|
stats = null;
|
|
823
868
|
disc = null;
|
|
824
869
|
exactDiscovery = Promise.resolve(null);
|
|
825
870
|
latestPendingEstimate = 0;
|
|
871
|
+
lastProcessedImportKeys = null;
|
|
826
872
|
srv.setStats(null);
|
|
827
873
|
srv.setProgress({
|
|
828
874
|
status: "idle",
|
|
@@ -836,7 +882,7 @@ async function cmdLogin(flags) {
|
|
|
836
882
|
};
|
|
837
883
|
const refreshLocalStatsForToken = async (activeToken) => {
|
|
838
884
|
const generation = ++refreshGeneration;
|
|
839
|
-
|
|
885
|
+
lastProcessedImportKeys = null; // shared with /migrate so it can assemble only the pending sessions
|
|
840
886
|
let importStatusUnavailable = false;
|
|
841
887
|
const quickDiscovery = discoverMigratableFastDiscovery();
|
|
842
888
|
const quick = summarizeFastMigratableDiscovery(quickDiscovery);
|
|
@@ -1103,8 +1149,19 @@ async function cmdLogin(flags) {
|
|
|
1103
1149
|
}
|
|
1104
1150
|
let exact = disc;
|
|
1105
1151
|
if (!exact) {
|
|
1106
|
-
//
|
|
1107
|
-
// a
|
|
1152
|
+
// The full account-reconciled scan (disc) is not ready yet. Do NOT read the whole local history to
|
|
1153
|
+
// size a few new jobs — assemble ONLY the sessions the fast scan already flagged as pending (using
|
|
1154
|
+
// the account's processed keys when we have them). This is the fast path for "start extraction".
|
|
1155
|
+
try {
|
|
1156
|
+
exact = discoverPendingSessionsTargeted(lastProcessedImportKeys);
|
|
1157
|
+
}
|
|
1158
|
+
catch (e) {
|
|
1159
|
+
console.error(`Targeted local sizing failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
if (!exact) {
|
|
1163
|
+
// Last resort only: the off-thread full scan, then an in-process full scan. Capped so a stalled
|
|
1164
|
+
// worker can never leave extraction stuck at "finishing local job sizing".
|
|
1108
1165
|
exact = await withTimeout(exactDiscovery, 40_000, "SIZING_TIMEOUT").catch(() => null);
|
|
1109
1166
|
if (!exact) {
|
|
1110
1167
|
srv.setProgress({ status: "starting", total: activeJobCount, completed: 0, running: 0, queued: activeJobCount, failed: 0, extracted: 0, latest: "Sizing your sessions…" });
|
|
@@ -1170,15 +1227,18 @@ async function cmdLogin(flags) {
|
|
|
1170
1227
|
pending: exact.pending,
|
|
1171
1228
|
signal: controller.signal,
|
|
1172
1229
|
onProgress: (ev) => {
|
|
1230
|
+
let latestRepo;
|
|
1173
1231
|
if (ev.error) {
|
|
1174
1232
|
progressFailed += 1;
|
|
1175
1233
|
}
|
|
1176
1234
|
else {
|
|
1177
1235
|
progressDone += 1;
|
|
1178
1236
|
progressExtracted += ev.memories ?? 0;
|
|
1237
|
+
latestRepo = repoLabel(ev.session.cwd); // building this completed conversation belongs to
|
|
1179
1238
|
}
|
|
1180
1239
|
updateProgress({
|
|
1181
1240
|
latest: `${ev.session.source} ${(ev.session.firstTs || "").slice(0, 10)}${ev.error ? ` failed: ${ev.error}` : ""}`,
|
|
1241
|
+
...(latestRepo ? { latestRepo } : {}),
|
|
1182
1242
|
});
|
|
1183
1243
|
},
|
|
1184
1244
|
}), 30_000, "IMPORT_START_TIMEOUT", () => controller.abort());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@echomem/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "EchoMem Cloud-First MCP Server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dev": "tsx src/index.ts",
|
|
19
19
|
"smoke": "node smoke.mjs",
|
|
20
20
|
"test": "npm run build && node test/crypto.test.mjs && node test/integration.test.mjs && node test/no-restart.test.mjs && node test/report.test.mjs && node test/tools.test.mjs && node test/delete.test.mjs && node test/migrate.test.mjs",
|
|
21
|
-
"prepack": "npm run build"
|
|
21
|
+
"prepack": "npm run build && node scripts/bundle-city.mjs"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@modelcontextprotocol/sdk": "^1.0.1",
|