@circuitwall/jarela 1.3.0 → 1.4.1
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js +10 -1
- package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/dashboard/currency/route.js +10 -5
- package/.next/standalone/.next/server/app/api/v1/dashboard/currency/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/page-capture/route.js +37 -3
- package/.next/standalone/.next/server/app/api/v1/page-capture/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js +9 -1
- package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +33 -8
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js.map +1 -1
- package/.next/standalone/.next/server/app/page.js +73 -204
- package/.next/standalone/.next/server/app/page.js.map +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup/page.js +1 -1
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/1718.js +159 -0
- package/.next/standalone/.next/server/chunks/1718.js.map +1 -0
- package/.next/standalone/.next/server/chunks/2082.js +6 -3
- package/.next/standalone/.next/server/chunks/2082.js.map +1 -1
- package/.next/standalone/.next/server/chunks/210.js +28 -0
- package/.next/standalone/.next/server/chunks/210.js.map +1 -1
- package/.next/standalone/.next/server/chunks/423.js +6 -3
- package/.next/standalone/.next/server/chunks/423.js.map +1 -1
- package/.next/standalone/.next/server/chunks/4631.js +37 -5
- package/.next/standalone/.next/server/chunks/4631.js.map +1 -1
- package/.next/standalone/.next/server/chunks/8167.js +255 -204
- package/.next/standalone/.next/server/chunks/8167.js.map +1 -1
- package/.next/standalone/.next/server/chunks/8866.js +38 -5
- package/.next/standalone/.next/server/chunks/8866.js.map +1 -1
- package/.next/standalone/.next/server/chunks/9032.js +8 -0
- package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
- package/.next/standalone/.next/server/chunks/{7883.js → 9557.js} +15 -3
- package/.next/standalone/.next/server/chunks/9557.js.map +1 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +6 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/proxy.js.map +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/{2351-68d8987bbe17ba2d.js → 2351-1ab119fb3b48f4c9.js} +258 -205
- package/.next/standalone/.next/static/chunks/2351-1ab119fb3b48f4c9.js.map +1 -0
- package/.next/standalone/.next/static/chunks/{9209-0d46118e502f8bf5.js → 4097-64691f9110cf167c.js} +14 -2
- package/.next/standalone/.next/static/chunks/4097-64691f9110cf167c.js.map +1 -0
- package/.next/standalone/.next/static/chunks/app/{page-2ab710949b62a638.js → page-145150e0468544e7.js} +74 -205
- package/.next/standalone/.next/static/chunks/app/page-145150e0468544e7.js.map +1 -0
- package/.next/standalone/.next/static/chunks/app/setup/{page-9a465b5fa755b3c3.js → page-a1463a9ace439ff7.js} +2 -2
- package/.next/standalone/.next/static/chunks/app/setup/{page-9a465b5fa755b3c3.js.map → page-a1463a9ace439ff7.js.map} +1 -1
- package/.next/standalone/.next/static/chunks/{webpack-ff5627013a5e3842.js → webpack-f4ac5c5f92cfd1c1.js} +13 -1
- package/.next/standalone/.next/static/chunks/webpack-f4ac5c5f92cfd1c1.js.map +1 -0
- package/.next/standalone/package.json +2 -1
- package/CHANGELOG.md +84 -0
- package/README.md +51 -26
- package/api/client.ts +10 -9
- package/app/api/v1/dashboard/currency/route.ts +7 -2
- package/app/api/v1/providers/[provider]/probe/route.ts +12 -1
- package/app/api/v1/threads/[thread_id]/run/route.ts +22 -8
- package/components/chat/InputBar.tsx +10 -1
- package/components/layout/AppShell.tsx +53 -17
- package/components/setup/PinKeypad.tsx +238 -0
- package/components/setup/ScreenLock.tsx +8 -173
- package/components/setup/UnlockScreen.tsx +25 -192
- package/lib/api/page-capture.test.ts +58 -0
- package/lib/api/page-capture.ts +31 -1
- package/lib/documents/remote/github.ts +16 -2
- package/lib/documents/remote/mail.ts +11 -2
- package/lib/lifecycle/shutdown.ts +9 -0
- package/lib/providers/github-copilot-auth.ts +2 -0
- package/lib/providers/github-copilot.ts +1 -0
- package/lib/tools/async-results.ts +11 -0
- package/package.json +2 -1
- package/scripts/install-to-system.ps1 +2 -2
- package/scripts/installed-launcher.ps1 +81 -17
- package/.next/standalone/.next/server/chunks/7883.js.map +0 -1
- package/.next/standalone/.next/static/chunks/2351-68d8987bbe17ba2d.js.map +0 -1
- package/.next/standalone/.next/static/chunks/9209-0d46118e502f8bf5.js.map +0 -1
- package/.next/standalone/.next/static/chunks/app/page-2ab710949b62a638.js.map +0 -1
- package/.next/standalone/.next/static/chunks/webpack-ff5627013a5e3842.js.map +0 -1
- /package/.next/standalone/.next/static/{ZKy7LJ3KXj2TIyKOg_fBH → WQdcnm9NyqpeNc0Z8_woo}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZKy7LJ3KXj2TIyKOg_fBH → WQdcnm9NyqpeNc0Z8_woo}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"node":{},"edge":{},"encryptionKey":"
|
|
1
|
+
{"node":{},"edge":{},"encryptionKey":"3ec6+G/tKi1SBOGgPaFEga0lY1bQVWG8P8Wez8h2IBE="}
|
package/.next/standalone/.next/static/chunks/{2351-68d8987bbe17ba2d.js → 2351-1ab119fb3b48f4c9.js}
RENAMED
|
@@ -292,6 +292,239 @@ function MarkdownTextarea({ value, onChange, className = "", placeholder, rows,
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
|
|
295
|
+
/***/ }),
|
|
296
|
+
|
|
297
|
+
/***/ 772:
|
|
298
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
299
|
+
|
|
300
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
301
|
+
/* harmony export */ y: () => (/* binding */ PinKeypad)
|
|
302
|
+
/* harmony export */ });
|
|
303
|
+
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5155);
|
|
304
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2115);
|
|
305
|
+
/* harmony import */ var _components_ui_Logo__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8439);
|
|
306
|
+
/* __next_internal_client_entry_do_not_use__ PinKeypad auto */
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
// Shared 6-digit PIN keypad used by both the decrypt splash (master key
|
|
310
|
+
// locked at boot) and the screen-lock overlay (idle timer fired). The
|
|
311
|
+
// only differences between the two states are (a) the endpoint hit
|
|
312
|
+
// (b) the copy on screen and (c) what happens after success. Everything
|
|
313
|
+
// else — keypad layout, dot strip, rate-limit handling, keyboard
|
|
314
|
+
// support, error mapping — is identical, so the two were collapsed into
|
|
315
|
+
// one component to avoid drift.
|
|
316
|
+
const PIN_LENGTH = 6;
|
|
317
|
+
const MODES = {
|
|
318
|
+
decrypt: {
|
|
319
|
+
endpoint: "/api/v1/security/unlock",
|
|
320
|
+
title: "Decrypt Jarela",
|
|
321
|
+
subtitle: "Enter your 6-digit PIN to decrypt your data.",
|
|
322
|
+
busyLabel: "Decrypting\u2026"
|
|
323
|
+
},
|
|
324
|
+
unlock: {
|
|
325
|
+
endpoint: "/api/v1/security/verify-pin",
|
|
326
|
+
title: "Welcome back",
|
|
327
|
+
subtitle: "Enter your 6-digit PIN to unlock.",
|
|
328
|
+
busyLabel: "Unlocking\u2026"
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
function PinKeypad({ mode, onSuccess }) {
|
|
332
|
+
const cfg = MODES[mode];
|
|
333
|
+
const [digits, setDigits] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
|
|
334
|
+
const [error, setError] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
|
|
335
|
+
const [submitting, setSubmitting] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
|
|
336
|
+
const [retryAfterSec, setRetryAfterSec] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(0);
|
|
337
|
+
// Hard guard against parallel submits. setState updaters can run more
|
|
338
|
+
// than once (dev StrictMode, concurrent rendering), so if `submit`
|
|
339
|
+
// were called from inside `setDigits` we'd POST twice and the second
|
|
340
|
+
// request would race the first into `unlockMasterKey()` after state
|
|
341
|
+
// already flipped to unlocked — the route would 500.
|
|
342
|
+
const submittingRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(false);
|
|
343
|
+
const submit = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(async (pin)=>{
|
|
344
|
+
if (submittingRef.current) return;
|
|
345
|
+
submittingRef.current = true;
|
|
346
|
+
setSubmitting(true);
|
|
347
|
+
setError(null);
|
|
348
|
+
try {
|
|
349
|
+
const res = await fetch(cfg.endpoint, {
|
|
350
|
+
method: "POST",
|
|
351
|
+
headers: {
|
|
352
|
+
"content-type": "application/json"
|
|
353
|
+
},
|
|
354
|
+
body: JSON.stringify({
|
|
355
|
+
pin
|
|
356
|
+
})
|
|
357
|
+
});
|
|
358
|
+
if (res.ok) {
|
|
359
|
+
onSuccess();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const body = await res.json().catch(()=>({}));
|
|
363
|
+
// Goal-state convergence: both endpoints can return 409 when the
|
|
364
|
+
// server already reached the target state (decrypt → master key
|
|
365
|
+
// already unlocked; unlock → screen not locked). Treat as success
|
|
366
|
+
// rather than surfacing a confusing error.
|
|
367
|
+
if (res.status === 409 && (body.error === "not-locked" || body.error === "no-pin")) {
|
|
368
|
+
onSuccess();
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (res.status === 429 && typeof body.retry_after_ms === "number") {
|
|
372
|
+
setRetryAfterSec(Math.ceil(body.retry_after_ms / 1000));
|
|
373
|
+
setError("Too many attempts. Try again later.");
|
|
374
|
+
} else if (res.status === 401) {
|
|
375
|
+
setError("Wrong PIN. Try again.");
|
|
376
|
+
} else if (res.status === 400) {
|
|
377
|
+
setError("Invalid PIN format.");
|
|
378
|
+
} else {
|
|
379
|
+
setError(body.error ?? `Error (${res.status})`);
|
|
380
|
+
}
|
|
381
|
+
setDigits("");
|
|
382
|
+
} catch (err) {
|
|
383
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
384
|
+
setDigits("");
|
|
385
|
+
} finally{
|
|
386
|
+
submittingRef.current = false;
|
|
387
|
+
setSubmitting(false);
|
|
388
|
+
}
|
|
389
|
+
}, [
|
|
390
|
+
cfg.endpoint,
|
|
391
|
+
onSuccess
|
|
392
|
+
]);
|
|
393
|
+
const append = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)((d)=>{
|
|
394
|
+
if (submitting || retryAfterSec > 0) return;
|
|
395
|
+
setError(null);
|
|
396
|
+
setDigits((cur)=>cur.length >= PIN_LENGTH ? cur : cur + d);
|
|
397
|
+
}, [
|
|
398
|
+
submitting,
|
|
399
|
+
retryAfterSec
|
|
400
|
+
]);
|
|
401
|
+
// Auto-submit once the buffer hits 6 digits.
|
|
402
|
+
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
403
|
+
if (digits.length === PIN_LENGTH && !submittingRef.current) {
|
|
404
|
+
void submit(digits);
|
|
405
|
+
}
|
|
406
|
+
}, [
|
|
407
|
+
digits,
|
|
408
|
+
submit
|
|
409
|
+
]);
|
|
410
|
+
const backspace = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{
|
|
411
|
+
if (submitting) return;
|
|
412
|
+
setError(null);
|
|
413
|
+
setDigits((cur)=>cur.slice(0, -1));
|
|
414
|
+
}, [
|
|
415
|
+
submitting
|
|
416
|
+
]);
|
|
417
|
+
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
418
|
+
function onKey(e) {
|
|
419
|
+
if (/^[0-9]$/.test(e.key)) {
|
|
420
|
+
e.preventDefault();
|
|
421
|
+
append(e.key);
|
|
422
|
+
} else if (e.key === "Backspace") {
|
|
423
|
+
e.preventDefault();
|
|
424
|
+
backspace();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
window.addEventListener("keydown", onKey);
|
|
428
|
+
return ()=>window.removeEventListener("keydown", onKey);
|
|
429
|
+
}, [
|
|
430
|
+
append,
|
|
431
|
+
backspace
|
|
432
|
+
]);
|
|
433
|
+
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
434
|
+
if (retryAfterSec <= 0) return;
|
|
435
|
+
const t = setInterval(()=>{
|
|
436
|
+
setRetryAfterSec((s)=>s > 0 ? s - 1 : 0);
|
|
437
|
+
}, 1000);
|
|
438
|
+
return ()=>clearInterval(t);
|
|
439
|
+
}, [
|
|
440
|
+
retryAfterSec
|
|
441
|
+
]);
|
|
442
|
+
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
443
|
+
className: "fixed inset-0 z-[1000] flex flex-col items-center justify-center gap-6 bg-surface text-fg animate-in fade-in duration-200",
|
|
444
|
+
style: {
|
|
445
|
+
paddingTop: "env(safe-area-inset-top)",
|
|
446
|
+
paddingBottom: "env(safe-area-inset-bottom)"
|
|
447
|
+
},
|
|
448
|
+
"data-pin-mode": mode,
|
|
449
|
+
children: [
|
|
450
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_ui_Logo__WEBPACK_IMPORTED_MODULE_2__/* .Logo */ .g, {
|
|
451
|
+
className: "h-16 w-auto"
|
|
452
|
+
}),
|
|
453
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
454
|
+
className: "w-full max-w-xs p-6",
|
|
455
|
+
children: [
|
|
456
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
|
|
457
|
+
className: "mb-1 text-center text-lg font-semibold text-fg",
|
|
458
|
+
children: cfg.title
|
|
459
|
+
}),
|
|
460
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
461
|
+
className: "mb-6 text-center text-xs text-fg-faint",
|
|
462
|
+
children: cfg.subtitle
|
|
463
|
+
}),
|
|
464
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
|
|
465
|
+
className: "mb-6 flex justify-center gap-3",
|
|
466
|
+
"aria-label": "PIN entry progress",
|
|
467
|
+
children: Array.from({
|
|
468
|
+
length: PIN_LENGTH
|
|
469
|
+
}).map((_, i)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
|
|
470
|
+
className: `h-3 w-3 rounded-full transition-colors ${i < digits.length ? error ? "bg-red-500" : "bg-fg" : "bg-surface-3"}`
|
|
471
|
+
}, i))
|
|
472
|
+
}),
|
|
473
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
474
|
+
className: "grid grid-cols-3 gap-2",
|
|
475
|
+
children: [
|
|
476
|
+
[
|
|
477
|
+
"1",
|
|
478
|
+
"2",
|
|
479
|
+
"3",
|
|
480
|
+
"4",
|
|
481
|
+
"5",
|
|
482
|
+
"6",
|
|
483
|
+
"7",
|
|
484
|
+
"8",
|
|
485
|
+
"9"
|
|
486
|
+
].map((d)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
487
|
+
digit: d,
|
|
488
|
+
onPress: ()=>append(d),
|
|
489
|
+
disabled: submitting || retryAfterSec > 0
|
|
490
|
+
}, d)),
|
|
491
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {}),
|
|
492
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
493
|
+
digit: "0",
|
|
494
|
+
onPress: ()=>append("0"),
|
|
495
|
+
disabled: submitting || retryAfterSec > 0
|
|
496
|
+
}),
|
|
497
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
498
|
+
digit: "\\u2190",
|
|
499
|
+
onPress: backspace,
|
|
500
|
+
disabled: submitting || digits.length === 0,
|
|
501
|
+
ariaLabel: "Backspace"
|
|
502
|
+
})
|
|
503
|
+
]
|
|
504
|
+
}),
|
|
505
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
506
|
+
className: `mt-4 min-h-[1.5rem] text-center text-xs ${error ? "text-red-400" : "text-fg-faint"}`,
|
|
507
|
+
role: "status",
|
|
508
|
+
"aria-live": "polite",
|
|
509
|
+
children: retryAfterSec > 0 ? `Try again in ${retryAfterSec}s` : error ?? (submitting ? cfg.busyLabel : "\u00A0")
|
|
510
|
+
})
|
|
511
|
+
]
|
|
512
|
+
})
|
|
513
|
+
]
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function PinKey({ digit, onPress, disabled, ariaLabel }) {
|
|
517
|
+
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
|
|
518
|
+
type: "button",
|
|
519
|
+
onClick: onPress,
|
|
520
|
+
disabled: disabled,
|
|
521
|
+
"aria-label": ariaLabel ?? digit,
|
|
522
|
+
className: "h-14 rounded-xl bg-surface-3 text-xl font-medium text-fg transition-colors hover:bg-surface-3/70 active:bg-surface-3/50 disabled:cursor-not-allowed disabled:opacity-50",
|
|
523
|
+
children: digit
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
|
|
295
528
|
/***/ }),
|
|
296
529
|
|
|
297
530
|
/***/ 1490:
|
|
@@ -395,15 +628,19 @@ async function request(path, init) {
|
|
|
395
628
|
signal: timeoutCtrl.signal
|
|
396
629
|
});
|
|
397
630
|
if (!res.ok) {
|
|
398
|
-
// 423
|
|
399
|
-
//
|
|
400
|
-
// sees the failure
|
|
401
|
-
// going to clear on its own.
|
|
631
|
+
// 423 lock states: distinct events so AppShell can mount the
|
|
632
|
+
// right overlay (decrypt vs presence-check). Both throw so the
|
|
633
|
+
// caller still sees the failure — no point retrying, the lock
|
|
634
|
+
// isn't going to clear on its own.
|
|
402
635
|
if (res.status === 423) {
|
|
403
636
|
const cloned = res.clone();
|
|
404
637
|
const body = await cloned.json().catch(()=>null);
|
|
405
|
-
if (
|
|
406
|
-
|
|
638
|
+
if (true) {
|
|
639
|
+
if (body?.error === "screen-locked") {
|
|
640
|
+
window.dispatchEvent(new CustomEvent("jarela:screen-locked"));
|
|
641
|
+
} else if (body?.error === "locked") {
|
|
642
|
+
window.dispatchEvent(new CustomEvent("jarela:master-key-locked"));
|
|
643
|
+
}
|
|
407
644
|
}
|
|
408
645
|
throw new Error(`423 ${body?.error ?? "locked"}`);
|
|
409
646
|
}
|
|
@@ -1270,209 +1507,25 @@ function CapBadges({ caps, provider, modelId, size = "xs" }) {
|
|
|
1270
1507
|
/* harmony export */ UnlockScreen: () => (/* binding */ UnlockScreen)
|
|
1271
1508
|
/* harmony export */ });
|
|
1272
1509
|
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5155);
|
|
1273
|
-
/* harmony import */ var
|
|
1274
|
-
/* harmony import */ var
|
|
1510
|
+
/* harmony import */ var next_navigation__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3321);
|
|
1511
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2115);
|
|
1512
|
+
/* harmony import */ var _PinKeypad__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(772);
|
|
1275
1513
|
/* __next_internal_client_entry_do_not_use__ UnlockScreen auto */
|
|
1276
1514
|
|
|
1277
1515
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const [digits, setDigits] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
|
|
1285
|
-
const [error, setError] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
|
|
1286
|
-
const [submitting, setSubmitting] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
|
|
1287
|
-
const [retryAfterSec, setRetryAfterSec] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(0);
|
|
1288
|
-
const containerRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
|
|
1289
|
-
// Hard guard against parallel submits. setState updaters can run more
|
|
1290
|
-
// than once (dev StrictMode, concurrent rendering), so if `submit`
|
|
1291
|
-
// were called from inside `setDigits` we'd POST twice and the second
|
|
1292
|
-
// request would race the first into `unlockMasterKey()` after state
|
|
1293
|
-
// already flipped to unlocked - the route would 500.
|
|
1294
|
-
const submittingRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(false);
|
|
1295
|
-
const submit = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(async (pin)=>{
|
|
1296
|
-
if (submittingRef.current) return;
|
|
1297
|
-
submittingRef.current = true;
|
|
1298
|
-
setSubmitting(true);
|
|
1299
|
-
setError(null);
|
|
1300
|
-
try {
|
|
1301
|
-
const res = await fetch("/api/v1/security/unlock", {
|
|
1302
|
-
method: "POST",
|
|
1303
|
-
headers: {
|
|
1304
|
-
"content-type": "application/json"
|
|
1305
|
-
},
|
|
1306
|
-
body: JSON.stringify({
|
|
1307
|
-
pin
|
|
1308
|
-
})
|
|
1309
|
-
});
|
|
1310
|
-
if (res.ok) {
|
|
1311
|
-
window.location.reload();
|
|
1312
|
-
return;
|
|
1313
|
-
}
|
|
1314
|
-
const body = await res.json().catch(()=>({}));
|
|
1315
|
-
if (res.status === 409 && body.error === "not-locked") {
|
|
1316
|
-
// Master key was already unlocked (host typed the PIN, or
|
|
1317
|
-
// another tab beat us to it). The goal state is reached —
|
|
1318
|
-
// reload into the app shell.
|
|
1319
|
-
window.location.reload();
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
if (res.status === 429 && typeof body.retry_after_ms === "number") {
|
|
1323
|
-
setRetryAfterSec(Math.ceil(body.retry_after_ms / 1000));
|
|
1324
|
-
setError("Too many attempts. Try again later.");
|
|
1325
|
-
} else if (res.status === 401) {
|
|
1326
|
-
setError("Wrong PIN. Try again.");
|
|
1327
|
-
} else if (res.status === 400) {
|
|
1328
|
-
setError("Invalid PIN format.");
|
|
1329
|
-
} else {
|
|
1330
|
-
setError(body.error ?? `Error (${res.status})`);
|
|
1331
|
-
}
|
|
1332
|
-
setDigits("");
|
|
1333
|
-
} catch (err) {
|
|
1334
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
1335
|
-
setDigits("");
|
|
1336
|
-
} finally{
|
|
1337
|
-
submittingRef.current = false;
|
|
1338
|
-
setSubmitting(false);
|
|
1339
|
-
}
|
|
1340
|
-
}, []);
|
|
1341
|
-
const append = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)((d)=>{
|
|
1342
|
-
if (submitting || retryAfterSec > 0) return;
|
|
1343
|
-
setError(null);
|
|
1344
|
-
setDigits((cur)=>cur.length >= PIN_LENGTH ? cur : cur + d);
|
|
1345
|
-
}, [
|
|
1346
|
-
submitting,
|
|
1347
|
-
retryAfterSec
|
|
1348
|
-
]);
|
|
1349
|
-
// Auto-submit once the buffer hits 6 digits. Effect runs once per
|
|
1350
|
-
// state transition (not per updater invocation), so we POST exactly
|
|
1351
|
-
// one time even under StrictMode double-render.
|
|
1352
|
-
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1353
|
-
if (digits.length === PIN_LENGTH && !submittingRef.current) {
|
|
1354
|
-
void submit(digits);
|
|
1355
|
-
}
|
|
1356
|
-
}, [
|
|
1357
|
-
digits,
|
|
1358
|
-
submit
|
|
1359
|
-
]);
|
|
1360
|
-
const backspace = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{
|
|
1361
|
-
if (submitting) return;
|
|
1362
|
-
setError(null);
|
|
1363
|
-
setDigits((cur)=>cur.slice(0, -1));
|
|
1364
|
-
}, [
|
|
1365
|
-
submitting
|
|
1366
|
-
]);
|
|
1367
|
-
// Physical keyboard support.
|
|
1368
|
-
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1369
|
-
function onKey(e) {
|
|
1370
|
-
if (/^[0-9]$/.test(e.key)) {
|
|
1371
|
-
e.preventDefault();
|
|
1372
|
-
append(e.key);
|
|
1373
|
-
} else if (e.key === "Backspace") {
|
|
1374
|
-
e.preventDefault();
|
|
1375
|
-
backspace();
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
window.addEventListener("keydown", onKey);
|
|
1379
|
-
return ()=>window.removeEventListener("keydown", onKey);
|
|
1380
|
-
}, [
|
|
1381
|
-
append,
|
|
1382
|
-
backspace
|
|
1383
|
-
]);
|
|
1384
|
-
// Tick down the rate-limit countdown.
|
|
1385
|
-
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1386
|
-
if (retryAfterSec <= 0) return;
|
|
1387
|
-
const t = setInterval(()=>{
|
|
1388
|
-
setRetryAfterSec((s)=>s > 0 ? s - 1 : 0);
|
|
1389
|
-
}, 1000);
|
|
1390
|
-
return ()=>clearInterval(t);
|
|
1516
|
+
|
|
1517
|
+
function UnlockScreen({ onUnlock } = {}) {
|
|
1518
|
+
const router = (0,next_navigation__WEBPACK_IMPORTED_MODULE_1__.useRouter)();
|
|
1519
|
+
const onSuccess = (0,react__WEBPACK_IMPORTED_MODULE_2__.useCallback)(()=>{
|
|
1520
|
+
if (onUnlock) onUnlock();
|
|
1521
|
+
else router.refresh();
|
|
1391
1522
|
}, [
|
|
1392
|
-
|
|
1523
|
+
onUnlock,
|
|
1524
|
+
router
|
|
1393
1525
|
]);
|
|
1394
|
-
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
style: {
|
|
1398
|
-
paddingTop: "env(safe-area-inset-top)",
|
|
1399
|
-
paddingBottom: "env(safe-area-inset-bottom)"
|
|
1400
|
-
},
|
|
1401
|
-
children: [
|
|
1402
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_ui_Logo__WEBPACK_IMPORTED_MODULE_2__/* .Logo */ .g, {
|
|
1403
|
-
className: "h-16 w-auto"
|
|
1404
|
-
}),
|
|
1405
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1406
|
-
className: "w-full max-w-xs p-6",
|
|
1407
|
-
children: [
|
|
1408
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
|
|
1409
|
-
className: "mb-1 text-center text-lg font-semibold text-fg",
|
|
1410
|
-
children: "Unlock Jarela"
|
|
1411
|
-
}),
|
|
1412
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
1413
|
-
className: "mb-6 text-center text-xs text-fg-faint",
|
|
1414
|
-
children: "Enter your 6-digit PIN to decrypt your data."
|
|
1415
|
-
}),
|
|
1416
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
|
|
1417
|
-
className: "mb-6 flex justify-center gap-3",
|
|
1418
|
-
"aria-label": "PIN entry progress",
|
|
1419
|
-
children: Array.from({
|
|
1420
|
-
length: PIN_LENGTH
|
|
1421
|
-
}).map((_, i)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
|
|
1422
|
-
className: `h-3 w-3 rounded-full transition-colors ${i < digits.length ? error ? "bg-red-500" : "bg-fg" : "bg-surface-3"}`
|
|
1423
|
-
}, i))
|
|
1424
|
-
}),
|
|
1425
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1426
|
-
className: "grid grid-cols-3 gap-2",
|
|
1427
|
-
children: [
|
|
1428
|
-
[
|
|
1429
|
-
"1",
|
|
1430
|
-
"2",
|
|
1431
|
-
"3",
|
|
1432
|
-
"4",
|
|
1433
|
-
"5",
|
|
1434
|
-
"6",
|
|
1435
|
-
"7",
|
|
1436
|
-
"8",
|
|
1437
|
-
"9"
|
|
1438
|
-
].map((d)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1439
|
-
digit: d,
|
|
1440
|
-
onPress: ()=>append(d),
|
|
1441
|
-
disabled: submitting || retryAfterSec > 0
|
|
1442
|
-
}, d)),
|
|
1443
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {}),
|
|
1444
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1445
|
-
digit: "0",
|
|
1446
|
-
onPress: ()=>append("0"),
|
|
1447
|
-
disabled: submitting || retryAfterSec > 0
|
|
1448
|
-
}),
|
|
1449
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1450
|
-
digit: "←",
|
|
1451
|
-
onPress: backspace,
|
|
1452
|
-
disabled: submitting || digits.length === 0,
|
|
1453
|
-
ariaLabel: "Backspace"
|
|
1454
|
-
})
|
|
1455
|
-
]
|
|
1456
|
-
}),
|
|
1457
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
1458
|
-
className: `mt-4 min-h-[1.5rem] text-center text-xs ${error ? "text-red-400" : "text-fg-faint"}`,
|
|
1459
|
-
role: "status",
|
|
1460
|
-
"aria-live": "polite",
|
|
1461
|
-
children: retryAfterSec > 0 ? `Try again in ${retryAfterSec}s` : error ?? (submitting ? "Unlocking…" : "\u00A0")
|
|
1462
|
-
})
|
|
1463
|
-
]
|
|
1464
|
-
})
|
|
1465
|
-
]
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
function PinKey({ digit, onPress, disabled, ariaLabel }) {
|
|
1469
|
-
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
|
|
1470
|
-
type: "button",
|
|
1471
|
-
onClick: onPress,
|
|
1472
|
-
disabled: disabled,
|
|
1473
|
-
"aria-label": ariaLabel ?? digit,
|
|
1474
|
-
className: "h-14 rounded-xl bg-surface-3 text-xl font-medium text-fg transition-colors hover:bg-surface-3/70 active:bg-surface-3/50 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1475
|
-
children: digit
|
|
1526
|
+
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_PinKeypad__WEBPACK_IMPORTED_MODULE_3__/* .PinKeypad */ .y, {
|
|
1527
|
+
mode: "decrypt",
|
|
1528
|
+
onSuccess: onSuccess
|
|
1476
1529
|
});
|
|
1477
1530
|
}
|
|
1478
1531
|
|
|
@@ -2865,4 +2918,4 @@ function computeFeatureReadiness({ models, integrations, selectedProvider, selec
|
|
|
2865
2918
|
/***/ })
|
|
2866
2919
|
|
|
2867
2920
|
}]);
|
|
2868
|
-
//# sourceMappingURL=2351-
|
|
2921
|
+
//# sourceMappingURL=2351-1ab119fb3b48f4c9.js.map
|