@echomem/mcp 1.4.20 → 1.4.22

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.
@@ -6,39 +6,24 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
6
6
  setExtractMode(false);
7
7
  setReadyMode(true);
8
8
  setHead("Local file access", "Consent");
9
- app.className = "consentStage";
9
+ app.className = "consentStage mvpConsentStage";
10
10
  app.innerHTML =
11
- '<section class="localConsent localConsentAccess">' +
12
- '<div class="consentLead">' +
13
- '<p class="consentEyebrow">Local history access / required</p>' +
14
- '<h2 class="siteHeadline">Let Echo read your <strong>local coding history?</strong></h2>' +
15
- '<p class="consentIntro">Echo needs this access to find your local Codex and Claude Code sessions, build your private usage report, and prepare conversations for memory extraction.</p>' +
16
- '<div class="consentLocalMark"><img src="/hud-assets/echo-face-cutout.png" alt="" /><span><strong>Processed on this Mac</strong><small>This scan does not send transcripts to EchoMem.</small></span></div>' +
17
- '</div>' +
18
- '<div class="consentDecision">' +
19
- '<div class="consentScopeHead"><strong>What this access does</strong><span>Local scan</span></div>' +
20
- '<div class="consentScope" aria-label="What this permission allows">' +
21
- '<div><span class="consentScopeNumber" aria-hidden="true">01</span><p><strong>Reads supported session logs</strong><small>Only local Codex and Claude Code history on this device.</small></p></div>' +
22
- '<div><span class="consentScopeNumber" aria-hidden="true">02</span><p><strong>Builds the Agent Doctor report</strong><small>Usage patterns and visualizations are computed locally.</small></p></div>' +
23
- '<div><span class="consentScopeNumber" aria-hidden="true">03</span><p><strong>Keeps transcripts on this device</strong><small>Nothing is uploaded unless you later select conversations for memory extraction.</small></p></div>' +
24
- '</div>' +
25
- '<label class="consentCheck"><input type="checkbox" id="localScanConsentCheck" /><span><strong>I understand and allow this local scan</strong><small>Required for Agent Doctor and to choose local conversations for extraction.</small></span></label>' +
26
- '<div class="consentActions">' +
27
- '<button type="button" class="primary" id="allowLocalScan" disabled>Allow local scan</button>' +
28
- '<button type="button" class="textButton" id="declineLocalScan">Not now</button>' +
29
- '</div>' +
30
- '<p class="consentFine" id="consentStatus" role="status" aria-live="polite"></p>' +
31
- '</div>' +
11
+ '<section class="mvpConsent" aria-labelledby="mvpConsentTitle">' +
12
+ '<img class="mvpConsentEcho" src="/hud-assets/echo-face-cutout.png" alt="" />' +
13
+ '<p class="mvpKicker">Before we begin</p>' +
14
+ '<h2 id="mvpConsentTitle">Echo has something for you.</h2>' +
15
+ '<p class="mvpConsentIntro">Let Echo read your local Codex and Claude Code history to build a private report. You’ll see what’s filling your context window, what keeps getting repeated, and where memory could help.</p>' +
16
+ '<p class="mvpPrivacy">The scan runs on this Mac. Nothing leaves it unless you later choose memories to extract.</p>' +
17
+ '<button type="button" class="primary mvpPrimary" id="allowLocalScan">Allow scan &amp; build my report <span aria-hidden="true">→</span></button>' +
18
+ '<button type="button" class="mvpQuiet" id="declineLocalScan">Not now</button>' +
19
+ '<p class="consentFine" id="consentStatus" role="status" aria-live="polite"></p>' +
32
20
  '</section>';
33
21
  var allow = document.getElementById("allowLocalScan");
34
22
  if (allow) allow.onclick = allowLocalScan;
35
- var consentCheck = document.getElementById("localScanConsentCheck");
36
- if (consentCheck && allow) consentCheck.onchange = function () {
37
- allow.disabled = !consentCheck.checked;
38
- if (consentCheck.checked) setConsentStatus("");
39
- };
40
23
  var decline = document.getElementById("declineLocalScan");
41
- if (decline) decline.onclick = declineLocalScan;
24
+ if (decline) decline.onclick = function () {
25
+ setConsentStatus("Echo can't continue setup without permission to scan your local history.", "required");
26
+ };
42
27
  }
43
28
  function setConsentStatus(message, tone) {
44
29
  var node = document.getElementById("consentStatus");
@@ -48,22 +33,33 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
48
33
  }
49
34
  }
50
35
  async function allowLocalScan() {
36
+ var allow = document.getElementById("allowLocalScan");
37
+ if (allow) allow.disabled = true;
51
38
  setConsentStatus("Starting local scan...");
52
39
  try {
53
- await postJson("/report-consent", { allowed: true });
40
+ await postJson("/report-consent", { allowed: true }, 5000);
54
41
  localHistoryConsentGranted = true;
55
42
  await showReport();
56
43
  } catch (error) {
44
+ // The localhost bridge may have accepted consent even if the browser loses the POST
45
+ // response while it swaps connections. Recover from the server's authoritative state
46
+ // instead of leaving the user forever on "Starting local scan...".
47
+ try {
48
+ var configResponse = await fetchWithTimeout(
49
+ "/config?nonce=" + encodeURIComponent(nonce),
50
+ { credentials: "omit", cache: "no-store" },
51
+ 3000
52
+ );
53
+ var config = configResponse.ok ? await configResponse.json() : null;
54
+ if (config && config.consentGranted === true) {
55
+ localHistoryConsentGranted = true;
56
+ await showReport();
57
+ return;
58
+ }
59
+ } catch (_) {}
57
60
  renderReportIssue("CONSENT_SAVE_FAILED", error && error.message ? error.message : "Could not start the local scan.");
58
61
  }
59
62
  }
60
- async function declineLocalScan() {
61
- localHistoryConsentGranted = false;
62
- try {
63
- await postJson("/report-consent", { allowed: false });
64
- } catch (_) {}
65
- setConsentStatus("Local history access is required to continue setup. Nothing will be scanned or uploaded unless you allow it.", "required");
66
- }
67
63
  async function showReport() {
68
64
  if (reportPollStarted) return;
69
65
  reportPollStarted = true;
@@ -136,14 +132,14 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
136
132
  if (event.origin !== location.origin) return;
137
133
  var data = event.data || {};
138
134
  if (data.type === "echomem:connected" && data.nonce === nonce) {
139
- if (!localHistoryConsentGranted) {
140
- closeAuthWindow();
141
- renderLocalScanConsent();
142
- setConsentStatus("Local history access is required before you can connect EchoMem or continue setup.", "required");
135
+ if (setupFlow === "login") {
136
+ connected = true;
137
+ renderLocalLoginComplete();
143
138
  return;
144
139
  }
145
140
  connected = true;
146
- closeAuthWindow();
141
+ rememberSetupPlanChoice("");
142
+ try { void refreshBillingStatus(); } catch (_) {}
147
143
  void waitForStats();
148
144
  } else if (data.type === "echomem:city-ready" && data.nonce === nonce) {
149
145
  scheduleReportToCity();
@@ -159,18 +155,23 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
159
155
  var configLoaded = false;
160
156
  var consentRequired = true;
161
157
  var consentGranted = false;
162
- // Learn the auth URL, connection state, and server-side consent gate before routing.
158
+ // Learn the connection state and server-side consent gate before routing.
163
159
  try {
164
160
  var cfg = await getJson("/config");
165
161
  configLoaded = true;
166
- authUrl = cfg.authUrl || authUrl;
167
- switchAccountUrl = cfg.switchAccountUrl || switchAccountUrl || authUrl;
168
162
  connected = !!cfg.connected;
163
+ setupFlow = cfg.flow === "login" ? "login" : "onboarding";
169
164
  workspacePath = cfg.workspacePath || workspacePath;
170
165
  consentRequired = cfg.consentRequired !== false;
171
166
  consentGranted = cfg.consentGranted === true;
172
167
  localHistoryConsentGranted = consentGranted;
173
168
  } catch (_) {}
169
+ if (setupFlow === "login") {
170
+ if (connected) renderLocalLoginComplete();
171
+ else renderLocalLogin("");
172
+ return;
173
+ }
174
+ if (!connected) rememberSetupPlanChoice("");
174
175
  if (params.get("connected") === "1") {
175
176
  if (!configLoaded || (consentRequired && !consentGranted)) {
176
177
  renderLocalScanConsent();
@@ -189,6 +190,7 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
189
190
  setTimeout(function () { if (!window.closed) void waitForStats(); }, 1500);
190
191
  } else {
191
192
  // Same-tab fallback (popup was blocked): go straight to the extraction dashboard.
193
+ try { void refreshBillingStatus(); } catch (_) {}
192
194
  void waitForStats();
193
195
  }
194
196
  return;
@@ -717,7 +717,7 @@ export const SETUP_PAGE_CLIENT_REPORT_AUDIT = String.raw ` /* ---------- fo
717
717
  '<div class="audit-list">' + groups.map(function (group, index) { return auditRowHtml(group, index, totalWaste, slices[index]); }).join('') + '</div>' +
718
718
  '<footer class="audit-end">' +
719
719
  '<h3>Stop burning tokens on these mistakes.</h3>' +
720
- '<button class="rdo-cta" data-connect-echo>' + (connected ? 'Continue' : 'Add') +
720
+ '<button class="rdo-cta" data-connect-echo>' + (connected ? 'Continue' : 'Connect Echo') +
721
721
  '<img class="cta-face" src="/hud-assets/echo-face-cutout.png" alt="Echo" />' +
722
722
  '<svg class="cta-arrow" viewBox="0 0 64 26" aria-hidden="true">' +
723
723
  '<defs><filter id="ctaPencil" x="-20%" y="-40%" width="140%" height="180%"><feTurbulence type="fractalNoise" baseFrequency="0.05 0.12" numOctaves="3" seed="11"/><feDisplacementMap in="SourceGraphic" scale="2.6"/></filter></defs>' +
@@ -173,12 +173,23 @@ export const SETUP_PAGE_CLIENT_REPORT_CITY = String.raw ` /* ---------- ful
173
173
  var scanned = Math.max(0, Number(progress.scanned || 0));
174
174
  var total = Math.max(0, Number(progress.total || 0));
175
175
  var pct = total > 0 ? Math.min(100, Math.round((scanned / total) * 100)) : 0;
176
- var elapsed = Number(progress.stageElapsedMs || 0);
177
- var unit = progress.stage === "reading-transcripts" ? "transcript files" : "sessions";
178
- var stage = progress.label || "Starting local scan";
179
- var caption = total > 0
180
- ? stage + " " + number(scanned) + " / " + number(total) + " " + unit + (elapsed > 0 ? " · " + Math.round(elapsed / 1000) + "s" : "")
181
- : stage + "…";
176
+ if (!reportEstimateDeadline) reportEstimateDeadline = Date.now() + 30000;
177
+ function estimateCaption() {
178
+ var seconds = Math.ceil((reportEstimateDeadline - Date.now()) / 1000);
179
+ return seconds > 0
180
+ ? "About " + seconds + " seconds remaining"
181
+ : "Still working — your report will be ready soon.";
182
+ }
183
+ var caption = estimateCaption();
184
+ function updateEstimateCaption() {
185
+ var node = document.getElementById("scan-load-caption");
186
+ if (!node) return;
187
+ node.textContent = estimateCaption();
188
+ if (Date.now() >= reportEstimateDeadline && reportEstimateTimer) {
189
+ clearInterval(reportEstimateTimer);
190
+ reportEstimateTimer = null;
191
+ }
192
+ }
182
193
  var fill = document.getElementById("scan-load-fill");
183
194
  if (app.className === "scanLoading" && fill) {
184
195
  fill.style.width = pct + "%";
@@ -191,23 +202,25 @@ export const SETUP_PAGE_CLIENT_REPORT_CITY = String.raw ` /* ---------- ful
191
202
  setHead("Reading your local coding history", "Local scan");
192
203
  app.className = "scanLoading";
193
204
  app.innerHTML =
194
- '<div class="scanLoader" aria-live="polite">' +
195
- '<div class="scanLoaderLead">' +
196
- '<span class="scanMascot" aria-hidden="true"><img src="/hud-assets/echo-face-cutout.png" alt="" /></span>' +
197
- '<div class="scanLoaderCopy">' +
198
- '<p class="scanEyebrow">Local scan</p>' +
199
- '<h2 class="siteHeadline">Reading your <strong>local coding history</strong></h2>' +
200
- '<p class="scanIntro">Echo is organizing your Codex and Claude Code sessions on this Mac.</p>' +
201
- '</div>' +
202
- '</div>' +
203
- '<div class="scanProgressCard">' +
204
- '<div class="scanProgress" role="progressbar" aria-label="Local scan progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="' + pct + '">' +
205
- '<i id="scan-load-fill" class="scanProgressFill" style="width:' + pct + '%"></i>' +
206
- '</div>' +
207
- '<p id="scan-load-caption" class="scanCaption">' + esc(caption) + '</p>' +
205
+ '<section class="mvpReading" aria-live="polite">' +
206
+ '<span class="scanMascot mvpReadingAnimation" aria-hidden="true">' +
207
+ '<span class="scanMascotFrames">' +
208
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-01-ready.png" alt="" />' +
209
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-02-open.png" alt="" />' +
210
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-03-pull.png" alt="" />' +
211
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-04-read.png" alt="" />' +
212
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-04-read.png" alt="" />' +
213
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-05-file.png" alt="" />' +
214
+ '<img class="scanMascotFrame" src="/hud-assets/scan-loop/frame-06-close.png" alt="" />' +
215
+ '</span>' +
216
+ '</span>' +
217
+ '<h2>Reading your coding history…</h2>' +
218
+ '<p id="scan-load-caption" class="scanCaption">' + esc(caption) + '</p>' +
219
+ '<div class="scanProgress mvpReadingProgress" role="progressbar" aria-label="Local scan progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="' + pct + '">' +
220
+ '<i id="scan-load-fill" class="scanProgressFill" style="width:' + pct + '%"></i>' +
208
221
  '</div>' +
209
- '<p class="scanPrivacy"><span aria-hidden="true"></span>Transcripts stay on this device during the scan.</p>' +
210
- '</div>';
222
+ '</section>';
223
+ if (!reportEstimateTimer) reportEstimateTimer = setInterval(updateEstimateCaption, 1000);
211
224
  }
212
225
 
213
226
  `;
@@ -11,7 +11,7 @@ const SETUP_PAGE_BODY = String.raw `
11
11
  </div>
12
12
  <div class="pill" id="status">Local</div>
13
13
  </header>
14
- <section id="app" class="state">Preparing setup...</section>
14
+ <section id="app" class="state" aria-busy="true"></section>
15
15
  </main>
16
16
  <dialog id="echo-note" class="echo-note-dlg">
17
17
  <button class="nclose" aria-label="Close">&times;</button>