@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.
- package/README.md +1 -1
- package/assets/hud/echo-pricing-free-sticker.png +0 -0
- package/assets/hud/echo-pricing-power-sticker.png +0 -0
- package/assets/hud/echo-pricing-pro-sticker.png +0 -0
- package/assets/hud/scan-loop/frame-01-ready.png +0 -0
- package/assets/hud/scan-loop/frame-02-open.png +0 -0
- package/assets/hud/scan-loop/frame-03-pull.png +0 -0
- package/assets/hud/scan-loop/frame-04-read.png +0 -0
- package/assets/hud/scan-loop/frame-05-file.png +0 -0
- package/assets/hud/scan-loop/frame-06-close.png +0 -0
- package/dist/crypto.js +4 -0
- package/dist/encryption.js +14 -2
- package/dist/index.js +52 -117
- package/dist/migrate.js +38 -1
- package/dist/setup-page/client-core.js +286 -26
- package/dist/setup-page/client-extraction.js +322 -101
- package/dist/setup-page/client-lifecycle.js +46 -44
- package/dist/setup-page/client-report-audit.js +1 -1
- package/dist/setup-page/client-report-city.js +35 -22
- package/dist/setup-page/document.js +1 -1
- package/dist/setup-page/styles-extraction.js +455 -0
- package/dist/setup-page/styles-mvp.js +976 -0
- package/dist/setup-page/styles-website-alignment.js +237 -5
- package/dist/setup-page/styles.js +2 -0
- package/dist/setup-page.js +1 -0
- package/dist/setup-preview.js +142 -37
- package/dist/setup.js +527 -61
- package/dist/v1-contract.js +1 -11
- package/package.json +6 -2
|
@@ -8,6 +8,9 @@ export const SETUP_PAGE_CLIENT_CORE = String.raw ` var params = new URLSear
|
|
|
8
8
|
var status = document.getElementById("status");
|
|
9
9
|
var report = null;
|
|
10
10
|
var reportEnvelope = null;
|
|
11
|
+
// The local bridge is authoritative. Login never gets access to scan/import routes;
|
|
12
|
+
// onboarding may request the separate local-history permission.
|
|
13
|
+
var setupFlow = "onboarding";
|
|
11
14
|
var activeScanId = "";
|
|
12
15
|
var stats = null;
|
|
13
16
|
var statsSlow = false;
|
|
@@ -23,28 +26,46 @@ export const SETUP_PAGE_CLIENT_CORE = String.raw ` var params = new URLSear
|
|
|
23
26
|
var progressPollRunId = 0; // invalidates older progress loops when extraction resumes
|
|
24
27
|
var dashMounted = false; // dashboard shell (incl. plate iframe) mounted once; persists across /stats polls
|
|
25
28
|
var reportMounted = false; // report shell (city iframe) mounted once; loading is an overlay on it, not a separate page
|
|
26
|
-
var authUrl = "";
|
|
27
|
-
var switchAccountUrl = "";
|
|
28
29
|
var workspacePath = "";
|
|
29
30
|
var billingStatus = null;
|
|
30
31
|
var billingStatusLoading = false;
|
|
31
32
|
var setupPlanChoice = "";
|
|
33
|
+
var setupPlanPreview = "free";
|
|
34
|
+
var billingActivationPendingPlan = "";
|
|
32
35
|
var selectedSessionKeys = Object.create(null);
|
|
33
36
|
var sessionSelectionTouched = false;
|
|
34
37
|
var sessionPickerQuery = "";
|
|
35
38
|
var sessionPickerSource = "all";
|
|
36
39
|
var billingPollTimer = null;
|
|
37
|
-
var
|
|
38
|
-
var
|
|
40
|
+
var localAuthEmail = "";
|
|
41
|
+
var localAuthTermsAccepted = false;
|
|
42
|
+
var localAuthAgeConfirmed = false;
|
|
39
43
|
var statsPollStarted = false;
|
|
40
44
|
var reportPollStarted = false;
|
|
45
|
+
var reportEstimateDeadline = 0;
|
|
46
|
+
var reportEstimateTimer = null;
|
|
47
|
+
var readyStage = "";
|
|
41
48
|
var NIGHT_HOURS = { 22: 1, 23: 1, 0: 1, 1: 1, 2: 1, 3: 1 };
|
|
42
49
|
|
|
50
|
+
try {
|
|
51
|
+
var storedSetupPlanChoice = sessionStorage.getItem("echomem:setup-plan:" + nonce) || "";
|
|
52
|
+
if (storedSetupPlanChoice === "free" || storedSetupPlanChoice === "pro" || storedSetupPlanChoice === "power") {
|
|
53
|
+
setupPlanChoice = storedSetupPlanChoice;
|
|
54
|
+
}
|
|
55
|
+
} catch (_) {}
|
|
56
|
+
|
|
43
57
|
function esc(value) {
|
|
44
58
|
return String(value == null ? "" : value).replace(/[&<>"']/g, function (ch) {
|
|
45
59
|
return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch];
|
|
46
60
|
});
|
|
47
61
|
}
|
|
62
|
+
function rememberSetupPlanChoice(choice) {
|
|
63
|
+
setupPlanChoice = choice === "free" || choice === "pro" || choice === "power" ? choice : "";
|
|
64
|
+
try {
|
|
65
|
+
if (setupPlanChoice) sessionStorage.setItem("echomem:setup-plan:" + nonce, setupPlanChoice);
|
|
66
|
+
else sessionStorage.removeItem("echomem:setup-plan:" + nonce);
|
|
67
|
+
} catch (_) {}
|
|
68
|
+
}
|
|
48
69
|
function setupIcon(name) {
|
|
49
70
|
var paths = {
|
|
50
71
|
"shield-check": '<path d="M12 3 5 6v5c0 4.6 2.9 8.1 7 10 4.1-1.9 7-5.4 7-10V6l-7-3Z"/><path d="m8.8 12.1 2 2 4.4-4.5"/>',
|
|
@@ -142,10 +163,6 @@ export const SETUP_PAGE_CLIENT_CORE = String.raw ` var params = new URLSear
|
|
|
142
163
|
if (code === "NOT_LOGGED_IN") return "Echo on this machine is not signed in yet. Sign in again and retry.";
|
|
143
164
|
return code || "Something went wrong. Retry in a moment.";
|
|
144
165
|
}
|
|
145
|
-
function closeAuthWindow() {
|
|
146
|
-
try { if (authWindow && !authWindow.closed) authWindow.close(); } catch (_) {}
|
|
147
|
-
authWindow = null;
|
|
148
|
-
}
|
|
149
166
|
function notifyOpenerConnected() {
|
|
150
167
|
try {
|
|
151
168
|
if (window.opener && window.opener !== window && !window.opener.closed) {
|
|
@@ -156,25 +173,268 @@ export const SETUP_PAGE_CLIENT_CORE = String.raw ` var params = new URLSear
|
|
|
156
173
|
} catch (_) {}
|
|
157
174
|
return false;
|
|
158
175
|
}
|
|
159
|
-
function
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
function localAuthLegalHtml() {
|
|
177
|
+
return '<label class="localAuthConsent">' +
|
|
178
|
+
'<input type="checkbox" id="localAuthConsent" ' + (localAuthAgeConfirmed && localAuthTermsAccepted ? "checked" : "") + ' />' +
|
|
179
|
+
'<span>I confirm that I am at least 18 years old and that I have read and agree to Echo\'s <a href="https://echoknows.com/terms-of-use" target="_blank" rel="noopener noreferrer">Terms of Use</a>, <a href="https://echoknows.com/memory-terms" target="_blank" rel="noopener noreferrer">Memory Usage Terms</a>, and <a href="https://echoknows.com/privacy-policy" target="_blank" rel="noopener noreferrer">Privacy Policy</a>. See our <a href="https://echoknows.com/subprocessor-list" target="_blank" rel="noopener noreferrer">Subprocessor List</a>.</span>' +
|
|
180
|
+
'</label>';
|
|
181
|
+
}
|
|
182
|
+
function captureLocalAuthConsents() {
|
|
183
|
+
var consent = document.getElementById("localAuthConsent");
|
|
184
|
+
if (consent) {
|
|
185
|
+
localAuthTermsAccepted = !!consent.checked;
|
|
186
|
+
localAuthAgeConfirmed = !!consent.checked;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
var terms = document.getElementById("localAuthTerms");
|
|
190
|
+
var age = document.getElementById("localAuthAge");
|
|
191
|
+
localAuthTermsAccepted = !!(terms && terms.checked);
|
|
192
|
+
localAuthAgeConfirmed = !!(age && age.checked);
|
|
193
|
+
}
|
|
194
|
+
function setLocalAuthStatus(message, tone) {
|
|
195
|
+
var node = document.getElementById("localAuthStatus");
|
|
196
|
+
if (!node) return;
|
|
197
|
+
node.textContent = message || "";
|
|
198
|
+
node.classList.toggle("is-error", tone === "error");
|
|
199
|
+
}
|
|
200
|
+
function updateLocalAuthSendButton() {
|
|
201
|
+
captureLocalAuthConsents();
|
|
202
|
+
var email = document.getElementById("localAuthEmail");
|
|
203
|
+
var button = document.getElementById("localAuthSend");
|
|
204
|
+
if (!button) return;
|
|
205
|
+
var value = email && email.value ? String(email.value).trim() : "";
|
|
206
|
+
button.disabled = !value || !localAuthTermsAccepted || !localAuthAgeConfirmed;
|
|
207
|
+
}
|
|
208
|
+
function renderLocalLogin(message) {
|
|
209
|
+
setCityMode(false);
|
|
210
|
+
setScanMode(false);
|
|
211
|
+
setExtractMode(false);
|
|
212
|
+
setReadyMode(true);
|
|
213
|
+
setHead("Connect EchoMem", "Local login");
|
|
214
|
+
app.className = "localAuthStage localAuthWelcomeStage";
|
|
215
|
+
app.innerHTML =
|
|
216
|
+
'<section class="localAuthWelcome" aria-labelledby="localAuthWelcomeTitle">' +
|
|
217
|
+
'<div class="localAuthOrbScene" aria-hidden="true"><span class="localAuthOrb"></span><span class="localAuthOrbShadow"></span></div>' +
|
|
218
|
+
'<div class="localAuthWelcomeCopy">' +
|
|
219
|
+
'<h2 id="localAuthWelcomeTitle">Welcome to Echo</h2>' +
|
|
220
|
+
'<p>Your memories, sovereign and shared.</p>' +
|
|
221
|
+
'</div>' +
|
|
222
|
+
'<form class="localAuthWelcomeForm" id="localAuthEmailForm">' +
|
|
223
|
+
'<div class="localAuthEmailCapsule">' +
|
|
224
|
+
'<label class="srOnly" for="localAuthEmail">Email</label>' +
|
|
225
|
+
'<input id="localAuthEmail" type="email" autocomplete="email" placeholder="Enter your email" value="' + esc(localAuthEmail) + '" />' +
|
|
226
|
+
'<button id="localAuthSend" type="submit" aria-label="Send one-time code"><span aria-hidden="true">→</span></button>' +
|
|
227
|
+
'</div>' +
|
|
228
|
+
localAuthLegalHtml() +
|
|
229
|
+
'<p class="localAuthStatus' + (message ? " is-error" : "") + '" id="localAuthStatus" role="status" aria-live="polite">' + esc(message || "") + '</p>' +
|
|
230
|
+
'</form>' +
|
|
231
|
+
'</section>';
|
|
232
|
+
var form = document.getElementById("localAuthEmailForm");
|
|
233
|
+
if (form) form.onsubmit = function (event) { event.preventDefault(); void sendLocalOtp(); };
|
|
234
|
+
var email = document.getElementById("localAuthEmail");
|
|
235
|
+
if (email) {
|
|
236
|
+
email.oninput = function () { localAuthEmail = String(email.value || ""); updateLocalAuthSendButton(); };
|
|
237
|
+
email.focus();
|
|
238
|
+
}
|
|
239
|
+
var consent = document.getElementById("localAuthConsent");
|
|
240
|
+
if (consent) consent.onchange = updateLocalAuthSendButton;
|
|
241
|
+
updateLocalAuthSendButton();
|
|
242
|
+
}
|
|
243
|
+
function renderLocalLoginComplete() {
|
|
244
|
+
setCityMode(false);
|
|
245
|
+
setScanMode(false);
|
|
246
|
+
setExtractMode(false);
|
|
247
|
+
setReadyMode(true);
|
|
248
|
+
setHead("EchoMem connected", "Done");
|
|
249
|
+
app.className = "notice";
|
|
250
|
+
app.innerHTML =
|
|
251
|
+
'<section class="localAuthCard localAuthComplete">' +
|
|
252
|
+
'<div class="localAuthLead">' +
|
|
253
|
+
'<img src="/hud-assets/echo-face-cutout.png" alt="" />' +
|
|
254
|
+
'<p class="consentEyebrow">This device is connected</p>' +
|
|
255
|
+
'<h2 class="siteHeadline">You\'re signed in.</h2>' +
|
|
256
|
+
'<p>Your local device token and encryption key are ready. Return to Terminal, or run <code>echomem-mcp init</code> when you want to start local-history onboarding.</p>' +
|
|
257
|
+
'</div>' +
|
|
258
|
+
'</section>';
|
|
259
|
+
}
|
|
260
|
+
async function finishLocalLogin() {
|
|
261
|
+
connected = true;
|
|
262
|
+
if (setupFlow === "login") {
|
|
263
|
+
renderLocalLoginComplete();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
rememberSetupPlanChoice("");
|
|
267
|
+
try { await refreshBillingStatus(); } catch (_) {}
|
|
268
|
+
await waitForStats();
|
|
269
|
+
}
|
|
270
|
+
async function sendLocalOtp() {
|
|
271
|
+
var email = document.getElementById("localAuthEmail");
|
|
272
|
+
localAuthEmail = email && email.value ? String(email.value).trim().toLowerCase() : "";
|
|
273
|
+
captureLocalAuthConsents();
|
|
274
|
+
if (!localAuthEmail) { setLocalAuthStatus("Enter your email.", "error"); return; }
|
|
275
|
+
if (!localAuthAgeConfirmed || !localAuthTermsAccepted) {
|
|
276
|
+
setLocalAuthStatus("Confirm your age and accept Echo's terms before continuing.", "error");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
var button = document.getElementById("localAuthSend");
|
|
280
|
+
if (button) { button.disabled = true; button.innerHTML = '<span class="localAuthSpinner" aria-hidden="true"></span>'; }
|
|
281
|
+
setLocalAuthStatus("Sending verification code...");
|
|
162
282
|
try {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
283
|
+
var data = await postJson("/local-auth/send-otp", {
|
|
284
|
+
email: localAuthEmail,
|
|
285
|
+
acceptedTerms: localAuthTermsAccepted,
|
|
286
|
+
ageConfirmed: localAuthAgeConfirmed
|
|
287
|
+
}, 12000);
|
|
288
|
+
localAuthEmail = data.email || localAuthEmail;
|
|
289
|
+
renderLocalOtp("Code sent. Check your inbox.");
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (button) { button.disabled = false; button.innerHTML = '<span aria-hidden="true">→</span>'; }
|
|
292
|
+
setLocalAuthStatus(error && error.message ? error.message : "Could not send the code.", "error");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function renderLocalOtp(message) {
|
|
296
|
+
setCityMode(false);
|
|
297
|
+
setScanMode(false);
|
|
298
|
+
setExtractMode(false);
|
|
299
|
+
setReadyMode(true);
|
|
300
|
+
setHead("Check your email", "Local login");
|
|
301
|
+
app.className = "localAuthStage localAuthWelcomeStage localAuthOtpStage";
|
|
302
|
+
app.innerHTML =
|
|
303
|
+
'<section class="localAuthWelcome localAuthOtpWelcome" aria-labelledby="localAuthOtpTitle">' +
|
|
304
|
+
'<div class="localAuthOrbScene" aria-hidden="true"><span class="localAuthOrb"></span><span class="localAuthOrbShadow"></span></div>' +
|
|
305
|
+
'<div class="localAuthWelcomeCopy">' +
|
|
306
|
+
'<h2 id="localAuthOtpTitle">Check your inbox</h2>' +
|
|
307
|
+
'<p>We sent a 6-digit code to <strong>' + esc(localAuthEmail) + '</strong></p>' +
|
|
308
|
+
'</div>' +
|
|
309
|
+
'<form class="localAuthWelcomeForm localAuthOtpForm" id="localAuthOtpForm">' +
|
|
310
|
+
'<label class="srOnly" for="localAuthOtp0">6-digit code</label>' +
|
|
311
|
+
'<div class="localAuthOtpCells" aria-label="6-digit verification code">' +
|
|
312
|
+
'<input id="localAuthOtp0" data-otp-cell type="text" inputmode="numeric" autocomplete="one-time-code" maxlength="1" />' +
|
|
313
|
+
'<input data-otp-cell type="text" inputmode="numeric" maxlength="1" />' +
|
|
314
|
+
'<input data-otp-cell type="text" inputmode="numeric" maxlength="1" />' +
|
|
315
|
+
'<input data-otp-cell type="text" inputmode="numeric" maxlength="1" />' +
|
|
316
|
+
'<input data-otp-cell type="text" inputmode="numeric" maxlength="1" />' +
|
|
317
|
+
'<input data-otp-cell type="text" inputmode="numeric" maxlength="1" />' +
|
|
318
|
+
'</div>' +
|
|
319
|
+
'<button class="primary localAuthVerifyWide" id="localAuthVerify" type="submit">Verify</button>' +
|
|
320
|
+
'<button class="secondary localAuthBackWide" id="localAuthBack" type="button">Use another email</button>' +
|
|
321
|
+
'<p class="localAuthStatus" id="localAuthStatus" role="status" aria-live="polite">' + esc(message || "") + '</p>' +
|
|
322
|
+
'</form>' +
|
|
323
|
+
'</section>';
|
|
324
|
+
var form = document.getElementById("localAuthOtpForm");
|
|
325
|
+
if (form) form.onsubmit = function (event) { event.preventDefault(); void verifyLocalOtp(); };
|
|
326
|
+
var back = document.getElementById("localAuthBack");
|
|
327
|
+
if (back) back.onclick = function () { renderLocalLogin(""); };
|
|
328
|
+
bindLocalOtpCells();
|
|
329
|
+
var otp = document.querySelector("[data-otp-cell]");
|
|
330
|
+
if (otp) otp.focus();
|
|
331
|
+
}
|
|
332
|
+
function localOtpCellValues() {
|
|
333
|
+
return Array.prototype.map.call(document.querySelectorAll("[data-otp-cell]"), function (node) {
|
|
334
|
+
return String(node.value || "").replace(/\\D/g, "").slice(0, 1);
|
|
335
|
+
}).join("");
|
|
336
|
+
}
|
|
337
|
+
function fillLocalOtpCells(value) {
|
|
338
|
+
var digits = String(value || "").replace(/\\D/g, "").slice(0, 6);
|
|
339
|
+
var cells = Array.prototype.slice.call(document.querySelectorAll("[data-otp-cell]"));
|
|
340
|
+
cells.forEach(function (cell, index) { cell.value = digits.charAt(index) || ""; });
|
|
341
|
+
var focusIndex = Math.min(digits.length, cells.length - 1);
|
|
342
|
+
if (cells[focusIndex]) cells[focusIndex].focus();
|
|
343
|
+
}
|
|
344
|
+
function bindLocalOtpCells() {
|
|
345
|
+
var cells = Array.prototype.slice.call(document.querySelectorAll("[data-otp-cell]"));
|
|
346
|
+
cells.forEach(function (cell, index) {
|
|
347
|
+
cell.oninput = function () {
|
|
348
|
+
var value = String(cell.value || "").replace(/\\D/g, "");
|
|
349
|
+
if (value.length > 1) { fillLocalOtpCells(value); return; }
|
|
350
|
+
cell.value = value;
|
|
351
|
+
if (value && cells[index + 1]) cells[index + 1].focus();
|
|
352
|
+
};
|
|
353
|
+
cell.onkeydown = function (event) {
|
|
354
|
+
if (event.key === "Backspace" && !cell.value && cells[index - 1]) cells[index - 1].focus();
|
|
355
|
+
};
|
|
356
|
+
cell.onpaste = function (event) {
|
|
357
|
+
var text = event.clipboardData && event.clipboardData.getData ? event.clipboardData.getData("text") : "";
|
|
358
|
+
if (text) {
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
fillLocalOtpCells(text);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
176
363
|
});
|
|
177
364
|
}
|
|
365
|
+
async function verifyLocalOtp() {
|
|
366
|
+
var otp = localOtpCellValues();
|
|
367
|
+
if (otp.length !== 6) { setLocalAuthStatus("Enter the 6-digit code.", "error"); return; }
|
|
368
|
+
var button = document.getElementById("localAuthVerify");
|
|
369
|
+
if (button) { button.disabled = true; button.textContent = "Verifying..."; }
|
|
370
|
+
setLocalAuthStatus("Verifying code...");
|
|
371
|
+
try {
|
|
372
|
+
var data = await postJson("/local-auth/verify-otp", {
|
|
373
|
+
email: localAuthEmail,
|
|
374
|
+
otp: otp,
|
|
375
|
+
acceptedTerms: localAuthTermsAccepted,
|
|
376
|
+
ageConfirmed: localAuthAgeConfirmed
|
|
377
|
+
}, 18000);
|
|
378
|
+
if (data && data.stage === "passphrase") {
|
|
379
|
+
renderLocalPassphrase(data.mode || "unlock", data.email || localAuthEmail);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
await finishLocalLogin();
|
|
383
|
+
} catch (error) {
|
|
384
|
+
if (button) { button.disabled = false; button.textContent = "Verify code"; }
|
|
385
|
+
setLocalAuthStatus(error && error.message ? error.message : "Could not verify the code.", "error");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function renderLocalPassphrase(mode, email, message) {
|
|
389
|
+
localAuthEmail = email || localAuthEmail;
|
|
390
|
+
var setupMode = mode === "setup";
|
|
391
|
+
setCityMode(false);
|
|
392
|
+
setScanMode(false);
|
|
393
|
+
setExtractMode(false);
|
|
394
|
+
setReadyMode(true);
|
|
395
|
+
setHead(setupMode ? "Create encrypted vault" : "Unlock encrypted vault", "Local passphrase");
|
|
396
|
+
app.className = "localAuthStage localAuthWelcomeStage localAuthPassphraseStage";
|
|
397
|
+
app.innerHTML =
|
|
398
|
+
'<section class="localAuthWelcome localAuthPassphraseWelcome">' +
|
|
399
|
+
'<div class="localAuthOrbScene" aria-hidden="true"><span class="localAuthOrb"></span><span class="localAuthOrbShadow"></span></div>' +
|
|
400
|
+
'<div class="localAuthWelcomeCopy">' +
|
|
401
|
+
'<h2>' + (setupMode ? 'Create your local vault passphrase.' : 'Enter your vault passphrase.') + '</h2>' +
|
|
402
|
+
'<p>' + (setupMode ? 'Echo derives your encryption key on this device. The passphrase and key are never sent to EchoMem. If you forget it, encrypted memories cannot be recovered.' : 'Echo verifies this passphrase locally against your account encryption token. The passphrase is never sent to EchoMem.') + '</p>' +
|
|
403
|
+
'</div>' +
|
|
404
|
+
'<form class="localAuthWelcomeForm localAuthPassphraseForm" id="localAuthPassphraseForm">' +
|
|
405
|
+
'<label class="localAuthField"><span>Passphrase</span><input id="localAuthPassphrase" type="password" autocomplete="' + (setupMode ? "new-password" : "current-password") + '" placeholder="Encryption passphrase" /></label>' +
|
|
406
|
+
(setupMode ? '<label class="localAuthField"><span>Confirm passphrase</span><input id="localAuthPassphraseConfirm" type="password" autocomplete="new-password" placeholder="Confirm passphrase" /></label>' : '') +
|
|
407
|
+
'<button class="primary localAuthPassphraseSubmit" id="localAuthUnlock" type="submit">' + (setupMode ? "Create vault" : "Unlock") + '</button>' +
|
|
408
|
+
'<button class="secondary localAuthBackWide" id="localAuthRestart" type="button">Use a different email</button>' +
|
|
409
|
+
'<p class="localAuthStatus' + (message ? " is-error" : "") + '" id="localAuthStatus" role="status" aria-live="polite">' + esc(message || "") + '</p>' +
|
|
410
|
+
'</form>' +
|
|
411
|
+
'</section>';
|
|
412
|
+
var form = document.getElementById("localAuthPassphraseForm");
|
|
413
|
+
if (form) form.onsubmit = function (event) { event.preventDefault(); void submitLocalPassphrase(setupMode); };
|
|
414
|
+
var restart = document.getElementById("localAuthRestart");
|
|
415
|
+
if (restart) restart.onclick = function () { renderLocalLogin(""); };
|
|
416
|
+
var pass = document.getElementById("localAuthPassphrase");
|
|
417
|
+
if (pass) pass.focus();
|
|
418
|
+
}
|
|
419
|
+
async function submitLocalPassphrase(setupMode) {
|
|
420
|
+
var pass = document.getElementById("localAuthPassphrase");
|
|
421
|
+
var confirm = document.getElementById("localAuthPassphraseConfirm");
|
|
422
|
+
var passphrase = pass && typeof pass.value === "string" ? pass.value : "";
|
|
423
|
+
var confirmValue = confirm && typeof confirm.value === "string" ? confirm.value : "";
|
|
424
|
+
if (passphrase.length < 4) { setLocalAuthStatus("Use at least 4 characters.", "error"); return; }
|
|
425
|
+
if (setupMode && passphrase !== confirmValue) { setLocalAuthStatus("Passphrases do not match.", "error"); return; }
|
|
426
|
+
var button = document.getElementById("localAuthUnlock");
|
|
427
|
+
if (button) { button.disabled = true; button.textContent = setupMode ? "Creating..." : "Unlocking..."; }
|
|
428
|
+
setLocalAuthStatus(setupMode ? "Creating encrypted vault..." : "Unlocking vault...");
|
|
429
|
+
try {
|
|
430
|
+
await postJson("/local-auth/passphrase", { passphrase: passphrase }, 30000);
|
|
431
|
+
await finishLocalLogin();
|
|
432
|
+
} catch (error) {
|
|
433
|
+
if (button) { button.disabled = false; button.textContent = setupMode ? "Create vault" : "Unlock"; }
|
|
434
|
+
var msg = error && error.message ? error.message : "Could not unlock encrypted memory.";
|
|
435
|
+
renderLocalPassphrase(setupMode ? "setup" : "unlock", localAuthEmail, msg);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
178
438
|
function onConnectClick() {
|
|
179
439
|
if (!localHistoryConsentGranted) {
|
|
180
440
|
renderLocalScanConsent();
|
|
@@ -182,8 +442,8 @@ export const SETUP_PAGE_CLIENT_CORE = String.raw ` var params = new URLSear
|
|
|
182
442
|
return;
|
|
183
443
|
}
|
|
184
444
|
if (connected) { void waitForStats(); return; }
|
|
185
|
-
|
|
186
|
-
|
|
445
|
+
rememberSetupPlanChoice("");
|
|
446
|
+
renderLocalLogin("");
|
|
187
447
|
}
|
|
188
448
|
function bindConnect() {
|
|
189
449
|
Array.prototype.forEach.call(document.querySelectorAll("[data-connect-echo]"), function (signin) {
|