@circuitwall/jarela 1.4.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/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 +63 -202
- 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-74846c864241b96d.js → page-145150e0468544e7.js} +64 -203
- 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 +1 -1
- package/CHANGELOG.md +60 -0
- package/README.md +1 -1
- 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/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/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 +1 -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-74846c864241b96d.js.map +0 -1
- package/.next/standalone/.next/static/chunks/webpack-ff5627013a5e3842.js.map +0 -1
- /package/.next/standalone/.next/static/{AV5AO0yTRABo-NgwxhDe7 → WQdcnm9NyqpeNc0Z8_woo}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{AV5AO0yTRABo-NgwxhDe7 → WQdcnm9NyqpeNc0Z8_woo}/_ssgManifest.js +0 -0
|
@@ -13,9 +13,9 @@ Promise.resolve(/* import() eager */).then(__webpack_require__.bind(__webpack_re
|
|
|
13
13
|
},
|
|
14
14
|
/******/ __webpack_require__ => { // webpackRuntimeModules
|
|
15
15
|
/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId))
|
|
16
|
-
/******/ __webpack_require__.O(0, [
|
|
16
|
+
/******/ __webpack_require__.O(0, [4097,2351,8441,3457,7358], () => (__webpack_exec__(7339)));
|
|
17
17
|
/******/ var __webpack_exports__ = __webpack_require__.O();
|
|
18
18
|
/******/ _N_E = __webpack_exports__;
|
|
19
19
|
/******/ }
|
|
20
20
|
]);
|
|
21
|
-
//# sourceMappingURL=page-
|
|
21
|
+
//# sourceMappingURL=page-a1463a9ace439ff7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static/chunks/app/setup/page-
|
|
1
|
+
{"version":3,"file":"static/chunks/app/setup/page-a1463a9ace439ff7.js","mappings":";;;;;AAAA,+FAAgJ;AAChJ;AACA,+FAAwI","sources":["webpack://_N_E/?e6bd"],"sourcesContent":["import(/* webpackMode: \"eager\", webpackExports: [\"OnboardingWizard\"] */ \"/home/runner/work/jarela/jarela/components/setup/OnboardingWizard.tsx\");\n;\nimport(/* webpackMode: \"eager\", webpackExports: [\"UnlockScreen\"] */ \"/home/runner/work/jarela/jarela/components/setup/UnlockScreen.tsx\");\n"],"names":[],"sourceRoot":"","ignoreList":[]}
|
|
@@ -68,6 +68,18 @@
|
|
|
68
68
|
/******/ };
|
|
69
69
|
/******/ })();
|
|
70
70
|
/******/
|
|
71
|
+
/******/ /* webpack/runtime/compat get default export */
|
|
72
|
+
/******/ (() => {
|
|
73
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
74
|
+
/******/ __webpack_require__.n = (module) => {
|
|
75
|
+
/******/ var getter = module && module.__esModule ?
|
|
76
|
+
/******/ () => (module['default']) :
|
|
77
|
+
/******/ () => (module);
|
|
78
|
+
/******/ __webpack_require__.d(getter, { a: getter });
|
|
79
|
+
/******/ return getter;
|
|
80
|
+
/******/ };
|
|
81
|
+
/******/ })();
|
|
82
|
+
/******/
|
|
71
83
|
/******/ /* webpack/runtime/create fake namespace object */
|
|
72
84
|
/******/ (() => {
|
|
73
85
|
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
|
|
@@ -327,4 +339,4 @@
|
|
|
327
339
|
/******/
|
|
328
340
|
/******/ })()
|
|
329
341
|
;
|
|
330
|
-
//# sourceMappingURL=webpack-
|
|
342
|
+
//# sourceMappingURL=webpack-f4ac5c5f92cfd1c1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static/chunks/webpack-f4ac5c5f92cfd1c1.js","mappings":";;;;UAAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA,GAAG;UACH;UACA;;UAEA;UACA;UACA;;UAEA;UACA;;;;;WC/BA;WACA;WACA;WACA;WACA,+BAA+B,wCAAwC;WACvE;WACA;WACA;WACA;WACA,iBAAiB,qBAAqB;WACtC;WACA;WACA,kBAAkB,qBAAqB;WACvC;WACA;WACA,KAAK;WACL;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;WC3BA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,sDAAsD;WACtD,sCAAsC,iEAAiE;WACvG;WACA;WACA;WACA;WACA;WACA;;;;;WCzBA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,EAAE;WACF;;;;;WCRA;WACA;WACA;WACA;WACA;;;;;WCJA;WACA;WACA;WACA;WACA;;;;;WCJA;;;;;WCAA;WACA;WACA;WACA;WACA,uBAAuB,4BAA4B;WACnD;WACA;WACA;WACA,iBAAiB,oBAAoB;WACrC;WACA,mGAAmG,YAAY;WAC/G;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mEAAmE,iCAAiC;WACpG;WACA;WACA;WACA;;;;;WCzCA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;WCNA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;WCZA;;;;;WCAA;;;;;WCAA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA,iCAAiC;;WAEjC;WACA;WACA;WACA,KAAK;WACL;WACA;WACA;WACA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,MAAM;WACN;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,MAAM,qBAAqB;WAC3B;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;WACA;WACA","sources":["webpack://_N_E/webpack/bootstrap","webpack://_N_E/webpack/runtime/chunk loaded","webpack://_N_E/webpack/runtime/compat get default export","webpack://_N_E/webpack/runtime/create fake namespace object","webpack://_N_E/webpack/runtime/define property getters","webpack://_N_E/webpack/runtime/ensure chunk","webpack://_N_E/webpack/runtime/get javascript chunk filename","webpack://_N_E/webpack/runtime/get mini-css chunk filename","webpack://_N_E/webpack/runtime/hasOwnProperty shorthand","webpack://_N_E/webpack/runtime/load script","webpack://_N_E/webpack/runtime/make namespace object","webpack://_N_E/webpack/runtime/trusted types policy","webpack://_N_E/webpack/runtime/trusted types script url","webpack://_N_E/webpack/runtime/publicPath","webpack://_N_E/webpack/runtime/jsonp chunk loading","webpack://_N_E/webpack/before-startup","webpack://_N_E/webpack/startup","webpack://_N_E/webpack/after-startup"],"sourcesContent":["// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\tvar threw = true;\n\ttry {\n\t\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\t\tthrew = false;\n\t} finally {\n\t\tif(threw) delete __webpack_module_cache__[moduleId];\n\t}\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","var deferred = [];\n__webpack_require__.O = (result, chunkIds, fn, priority) => {\n\tif(chunkIds) {\n\t\tpriority = priority || 0;\n\t\tfor(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar [chunkIds, fn, priority] = deferred[i];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));\n\t}\n\tdef['default'] = () => (value);\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn undefined;\n};","// This function allow to reference async chunks\n__webpack_require__.miniCssF = (chunkId) => {\n\t// return url for filenames based on template\n\treturn undefined;\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","var inProgress = {};\nvar dataWebpackPrefix = \"_N_E:\";\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url || s.getAttribute(\"data-webpack\") == dataWebpackPrefix + key) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\n\t\tscript.charset = 'utf-8';\n\t\tscript.timeout = 120;\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\t\tscript.setAttribute(\"data-webpack\", dataWebpackPrefix + key);\n\n\t\tscript.src = __webpack_require__.tu(url);\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var policy;\n__webpack_require__.tt = () => {\n\t// Create Trusted Type policy if Trusted Types are available and the policy doesn't exist yet.\n\tif (policy === undefined) {\n\t\tpolicy = {\n\t\t\tcreateScriptURL: (url) => (url)\n\t\t};\n\t\tif (typeof trustedTypes !== \"undefined\" && trustedTypes.createPolicy) {\n\t\t\tpolicy = trustedTypes.createPolicy(\"nextjs#bundler\", policy);\n\t\t}\n\t}\n\treturn policy;\n};","__webpack_require__.tu = (url) => (__webpack_require__.tt().createScriptURL(url));","__webpack_require__.p = \"/_next/\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t8068: 0,\n\t7513: 0,\n\t1973: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(!/^(1973|7513|8068)$/.test(chunkId)) {\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t} else installedChunks[chunkId] = 0;\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunk_N_E\"] = self[\"webpackChunk_N_E\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","","",""],"names":[],"sourceRoot":"","ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]}
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,66 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.4.1] - 2026-06-09
|
|
11
|
+
|
|
12
|
+
A reliability + UX polish patch on top of 1.4.0. The PIN keypad used at
|
|
13
|
+
boot-time decrypt and screen-unlock has been collapsed into a single
|
|
14
|
+
component with two modes, the API client treats both 423 lock states
|
|
15
|
+
symmetrically (master-key-locked is no longer surfaced as a toast
|
|
16
|
+
error), and several silent failure modes uncovered during last
|
|
17
|
+
night's launcher diagnosis are now bounded and observable.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Unified PIN keypad**
|
|
22
|
+
([#226](https://github.com/CircuitWall/jarela/pull/226)).
|
|
23
|
+
`UnlockScreen` and `ScreenLock` were near-duplicate 6-digit keypads;
|
|
24
|
+
collapsed into a single `PinKeypad` with `mode: "decrypt" | "unlock"`.
|
|
25
|
+
Both unlock paths converge on a shared `landOnAgentPicker()` so the
|
|
26
|
+
user always lands on the agent selector. Boot-time decrypt now uses
|
|
27
|
+
`router.refresh()` instead of a full `window.location.reload()`, so
|
|
28
|
+
the transition to the AppShell is seamless. The API client handles
|
|
29
|
+
423 `locked` symmetrically with `screen-locked` — neither lock state
|
|
30
|
+
is surfaced as a toast error any more; the matching overlay mounts
|
|
31
|
+
instead.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **Bounded resource leaks and last-resort error handlers**
|
|
36
|
+
([#227](https://github.com/CircuitWall/jarela/pull/227)). Installs
|
|
37
|
+
`process.on("uncaughtException")` and `process.on("unhandledRejection")`
|
|
38
|
+
in `instrumentation-node.ts` so stray async errors land in the in-memory
|
|
39
|
+
log ring instead of going to raw stderr or killing the server. The
|
|
40
|
+
thread-run SSE route's `cancel()` now tears down its 500 ms poll and
|
|
41
|
+
event subscriber on client disconnect; the provider-probe route clears
|
|
42
|
+
its timeout on the win-path; `lib/tools/async-results` exposes
|
|
43
|
+
`stopAsyncResults()` and the shutdown drain calls it. Gmail and GitHub
|
|
44
|
+
page-walkers switched from `Promise.all` to `Promise.allSettled` so one
|
|
45
|
+
failed item no longer bins the whole page. `AbortSignal.timeout()`
|
|
46
|
+
added to the dashboard currency lookups (Nominatim, restcountries,
|
|
47
|
+
open.er-api) and the GitHub-Copilot provider fetches.
|
|
48
|
+
- **Installer captures node stdio and bounds task retries**
|
|
49
|
+
([#225](https://github.com/CircuitWall/jarela/pull/225)). Replaces
|
|
50
|
+
`Start-Process -RedirectStandardOutput/Error` (which silently dropped
|
|
51
|
+
both streams under `wscript -> powershell`) with a raw
|
|
52
|
+
`[System.Diagnostics.Process]` plus async `BeginOutputReadLine` /
|
|
53
|
+
`BeginErrorReadLine` writing through autoflushed `StreamWriter` s, so
|
|
54
|
+
`server.out.log` and `server.err.log` actually contain output. Logs
|
|
55
|
+
node's exit code on every cycle. Tightens the in-launcher rate limiter
|
|
56
|
+
from 5 to 3 restarts in 60 s. Reduces the scheduled-task retry policy
|
|
57
|
+
from `RestartCount=999, RestartInterval=1m` to `RestartCount=3,
|
|
58
|
+
RestartInterval=5m` — with an encrypted master key, every retry needs
|
|
59
|
+
manual PIN re-entry, so 999 silent retries was strictly worse than
|
|
60
|
+
failing loudly.
|
|
61
|
+
- **README promo video renders**
|
|
62
|
+
([#223](https://github.com/CircuitWall/jarela/pull/223)). GitHub raw
|
|
63
|
+
was serving the audio-less `.webm` as `audio/webm` (mime-sniffer
|
|
64
|
+
mis-classifies); browsers refused to render it in a `<video>`.
|
|
65
|
+
Re-encoded to h264 mp4 (faststart, video-only) which raw.githubusercontent.com
|
|
66
|
+
tags as `video/mp4`, and switched the README to absolute raw URLs for
|
|
67
|
+
`src` and `poster` since the markdown sanitizer doesn't rewrite
|
|
68
|
+
relative paths inside `<video>`.
|
|
69
|
+
|
|
10
70
|
## [1.4.0] - 2026-06-08
|
|
11
71
|
|
|
12
72
|
### Added
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
</p>
|
|
55
55
|
|
|
56
56
|
<p align="center">
|
|
57
|
-
<video src="
|
|
57
|
+
<video src="https://raw.githubusercontent.com/CircuitWall/jarela/main/docs/assets/jarela-promo.webm" poster="https://raw.githubusercontent.com/CircuitWall/jarela/main/docs/assets/jarela-promo-poster.jpg" autoplay loop muted playsinline controls width="320">
|
|
58
58
|
<img src="./docs/assets/jarela-promo-poster.jpg" alt="Jarela promo — PIN unlock, agent picker, chat, panel tour" width="320" />
|
|
59
59
|
</video>
|
|
60
60
|
<br/>
|
package/api/client.ts
CHANGED
|
@@ -157,20 +157,21 @@ async function request<T>(path: string, init?: RequestInit & { timeoutMs?: numbe
|
|
|
157
157
|
signal: timeoutCtrl.signal,
|
|
158
158
|
});
|
|
159
159
|
if (!res.ok) {
|
|
160
|
-
// 423
|
|
161
|
-
//
|
|
162
|
-
// sees the failure
|
|
163
|
-
// going to clear on its own.
|
|
160
|
+
// 423 lock states: distinct events so AppShell can mount the
|
|
161
|
+
// right overlay (decrypt vs presence-check). Both throw so the
|
|
162
|
+
// caller still sees the failure — no point retrying, the lock
|
|
163
|
+
// isn't going to clear on its own.
|
|
164
164
|
if (res.status === 423) {
|
|
165
165
|
const cloned = res.clone();
|
|
166
166
|
const body = (await cloned.json().catch(() => null)) as
|
|
167
167
|
| { error?: string }
|
|
168
168
|
| null;
|
|
169
|
-
if (
|
|
170
|
-
body?.error === "screen-locked"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
if (typeof window !== "undefined") {
|
|
170
|
+
if (body?.error === "screen-locked") {
|
|
171
|
+
window.dispatchEvent(new CustomEvent("jarela:screen-locked"));
|
|
172
|
+
} else if (body?.error === "locked") {
|
|
173
|
+
window.dispatchEvent(new CustomEvent("jarela:master-key-locked"));
|
|
174
|
+
}
|
|
174
175
|
}
|
|
175
176
|
throw new Error(`423 ${body?.error ?? "locked"}`);
|
|
176
177
|
}
|
|
@@ -53,7 +53,8 @@ export async function GET(req: NextRequest) {
|
|
|
53
53
|
source: "manual",
|
|
54
54
|
updated_at: new Date().toISOString(),
|
|
55
55
|
}, 3600);
|
|
56
|
-
} catch {
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.warn("[currency] manual lookup failed, falling back to USD:", err);
|
|
57
58
|
return cachedJson<CurrencyResponse>({
|
|
58
59
|
currency: "USD",
|
|
59
60
|
rate_from_usd: 1,
|
|
@@ -116,7 +117,8 @@ export async function GET(req: NextRequest) {
|
|
|
116
117
|
source: "location",
|
|
117
118
|
updated_at: new Date().toISOString(),
|
|
118
119
|
}, 3600);
|
|
119
|
-
} catch {
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.warn("[currency] location lookup failed, falling back to USD:", err);
|
|
120
122
|
return cachedJson<CurrencyResponse>({
|
|
121
123
|
currency: "USD",
|
|
122
124
|
rate_from_usd: 1,
|
|
@@ -140,6 +142,7 @@ async function reverseCountryCode(lat: number, lng: number): Promise<string | nu
|
|
|
140
142
|
"user-agent": "jarela-dashboard-currency/1.0",
|
|
141
143
|
accept: "application/json",
|
|
142
144
|
},
|
|
145
|
+
signal: AbortSignal.timeout(8_000),
|
|
143
146
|
});
|
|
144
147
|
if (!res.ok) return null;
|
|
145
148
|
const body = await res.json() as {
|
|
@@ -158,6 +161,7 @@ async function resolveCurrencyForCountry(countryCode: string): Promise<string |
|
|
|
158
161
|
|
|
159
162
|
const res = await fetch(`https://restcountries.com/v3.1/alpha/${countryCode}?fields=currencies`, {
|
|
160
163
|
headers: { accept: "application/json" },
|
|
164
|
+
signal: AbortSignal.timeout(8_000),
|
|
161
165
|
});
|
|
162
166
|
if (!res.ok) return null;
|
|
163
167
|
const body = await res.json() as Array<{ currencies?: Record<string, { name?: string }> }>;
|
|
@@ -173,6 +177,7 @@ async function fetchFxRates(): Promise<Record<string, number>> {
|
|
|
173
177
|
|
|
174
178
|
const res = await fetch("https://open.er-api.com/v6/latest/USD", {
|
|
175
179
|
headers: { accept: "application/json" },
|
|
180
|
+
signal: AbortSignal.timeout(8_000),
|
|
176
181
|
});
|
|
177
182
|
if (!res.ok) throw new Error(`fx status ${res.status}`);
|
|
178
183
|
|
|
@@ -66,14 +66,25 @@ export async function POST(req: NextRequest, { params }: Params) {
|
|
|
66
66
|
try { await (iter as { return?: () => Promise<unknown> }).return?.(); } catch { /* best-effort */ }
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
69
70
|
try {
|
|
70
71
|
await Promise.race([
|
|
71
72
|
probe(),
|
|
72
|
-
new Promise((_, reject) =>
|
|
73
|
+
new Promise<never>((_, reject) => {
|
|
74
|
+
timer = setTimeout(
|
|
75
|
+
() => reject(new Error(`probe timed out after ${PROBE_TIMEOUT_MS}ms`)),
|
|
76
|
+
PROBE_TIMEOUT_MS,
|
|
77
|
+
);
|
|
78
|
+
}),
|
|
73
79
|
]);
|
|
74
80
|
return NextResponse.json({ ok: true });
|
|
75
81
|
} catch (e) {
|
|
76
82
|
const msg = e instanceof Error ? e.message : String(e);
|
|
77
83
|
return NextResponse.json({ ok: false, error: msg });
|
|
84
|
+
} finally {
|
|
85
|
+
// Without this, the 15s timer keeps firing after probe() wins the
|
|
86
|
+
// race and the route returns — leaking an unhandled rejection per
|
|
87
|
+
// probe (which then trips the global handler).
|
|
88
|
+
if (timer) clearTimeout(timer);
|
|
78
89
|
}
|
|
79
90
|
}
|
|
@@ -233,9 +233,17 @@ function attachStream(
|
|
|
233
233
|
thread_id: string,
|
|
234
234
|
stream_options?: StreamOptions,
|
|
235
235
|
): Response {
|
|
236
|
+
// Captured by both start() and cancel() so the cancel branch can tear
|
|
237
|
+
// down the poll timer + subscription when the client disconnects.
|
|
238
|
+
// Without this, navigating away from a long-running thread leaks an
|
|
239
|
+
// event subscriber + 2Hz setInterval per visit until the run finishes
|
|
240
|
+
// (or forever if it doesn't).
|
|
241
|
+
let poll: ReturnType<typeof setInterval> | null = null;
|
|
242
|
+
let unsubscribe: (() => void) | null = null;
|
|
243
|
+
let clientGone = false;
|
|
244
|
+
|
|
236
245
|
const stream = new ReadableStream({
|
|
237
246
|
start(controller) {
|
|
238
|
-
let clientGone = false;
|
|
239
247
|
const safeEnqueue = (chunk: Uint8Array): void => {
|
|
240
248
|
if (clientGone) return;
|
|
241
249
|
try { controller.enqueue(chunk); } catch { clientGone = true; }
|
|
@@ -246,14 +254,15 @@ function attachStream(
|
|
|
246
254
|
safeEnqueue(sse({ type: ev.type, ...ev.data }));
|
|
247
255
|
};
|
|
248
256
|
|
|
249
|
-
const
|
|
250
|
-
|
|
257
|
+
const sub = subscribe(thread_id, onEvent);
|
|
258
|
+
unsubscribe = sub.unsubscribe;
|
|
259
|
+
if (!sub.run) {
|
|
251
260
|
controller.close();
|
|
252
261
|
return;
|
|
253
262
|
}
|
|
254
263
|
|
|
255
264
|
// If run already terminal, close after replay.
|
|
256
|
-
if (run.status !== "running") {
|
|
265
|
+
if (sub.run.status !== "running") {
|
|
257
266
|
try { controller.close(); } catch { /* */ }
|
|
258
267
|
return;
|
|
259
268
|
}
|
|
@@ -261,11 +270,11 @@ function attachStream(
|
|
|
261
270
|
// When the run finishes (status changes), close our response. We
|
|
262
271
|
// poll lightly because the run might finish due to other subscribers'
|
|
263
272
|
// signals; simpler than wiring a second listener channel.
|
|
264
|
-
|
|
273
|
+
poll = setInterval(() => {
|
|
265
274
|
const r = getRun(thread_id);
|
|
266
275
|
if (!r || r.status !== "running") {
|
|
267
|
-
clearInterval(poll);
|
|
268
|
-
unsubscribe();
|
|
276
|
+
if (poll) { clearInterval(poll); poll = null; }
|
|
277
|
+
if (unsubscribe) { unsubscribe(); unsubscribe = null; }
|
|
269
278
|
if (!clientGone) {
|
|
270
279
|
try { controller.close(); } catch { /* */ }
|
|
271
280
|
}
|
|
@@ -274,7 +283,12 @@ function attachStream(
|
|
|
274
283
|
poll.unref?.();
|
|
275
284
|
},
|
|
276
285
|
cancel() {
|
|
277
|
-
// Client navigated away — agent run keeps going in registry
|
|
286
|
+
// Client navigated away — agent run keeps going in registry, but
|
|
287
|
+
// tear down OUR poll + subscription so we don't leak a timer +
|
|
288
|
+
// subscriber per disconnect.
|
|
289
|
+
clientGone = true;
|
|
290
|
+
if (poll) { clearInterval(poll); poll = null; }
|
|
291
|
+
if (unsubscribe) { unsubscribe(); unsubscribe = null; }
|
|
278
292
|
},
|
|
279
293
|
});
|
|
280
294
|
|
|
@@ -31,6 +31,7 @@ import { Toaster } from "@/components/ui/Toaster";
|
|
|
31
31
|
import { Logo } from "@/components/ui/Logo";
|
|
32
32
|
import { BootScreen } from "@/components/ui/BootScreen";
|
|
33
33
|
import { ScreenLock } from "@/components/setup/ScreenLock";
|
|
34
|
+
import { UnlockScreen } from "@/components/setup/UnlockScreen";
|
|
34
35
|
import { clearUnreadForAgent, useUnreadCount } from "@/lib/ui/toasts";
|
|
35
36
|
import { getAppName } from "@/lib/env/app-config";
|
|
36
37
|
import { MenuPanel } from "./MenuPanel";
|
|
@@ -198,25 +199,31 @@ export function AppShell() {
|
|
|
198
199
|
? agents.find((a) => a.id === state.activeAgentId) ?? null
|
|
199
200
|
: null;
|
|
200
201
|
|
|
201
|
-
// Screen-lock overlay
|
|
202
|
-
// (
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
202
|
+
// Screen-lock overlay (presence check) AND master-key-locked overlay
|
|
203
|
+
// (decrypt). Distinct from the boot-time gate in `app/page.tsx`:
|
|
204
|
+
// those mounts are triggered mid-session — either by an idle timer
|
|
205
|
+
// (screen-lock) or by the master key being re-locked by an external
|
|
206
|
+
// process (decrypt). Both are signalled by the API client when it
|
|
207
|
+
// sees the matching 423 response.
|
|
207
208
|
const [screenLocked, setScreenLocked] = useState(false);
|
|
209
|
+
const [masterKeyLocked, setMasterKeyLocked] = useState(false);
|
|
208
210
|
// Bumped after each unlock so BootScreen remounts with fresh state
|
|
209
211
|
// (its `done` / `pickedId` / `prefetchStartedRef` would otherwise
|
|
210
|
-
// suppress the picker on the second appearance).
|
|
212
|
+
// suppress the picker on the second appearance). Both unlock paths
|
|
213
|
+
// bump this — the agent selector is always the post-unlock landing.
|
|
211
214
|
const [bootSeq, setBootSeq] = useState(0);
|
|
212
215
|
useEffect(() => {
|
|
213
216
|
let cancelled = false;
|
|
214
217
|
let timer: ReturnType<typeof setInterval> | null = null;
|
|
215
218
|
|
|
216
|
-
function
|
|
219
|
+
function onScreenLocked() {
|
|
217
220
|
if (!cancelled) setScreenLocked(true);
|
|
218
221
|
}
|
|
219
|
-
|
|
222
|
+
function onMasterKeyLocked() {
|
|
223
|
+
if (!cancelled) setMasterKeyLocked(true);
|
|
224
|
+
}
|
|
225
|
+
window.addEventListener("jarela:screen-locked", onScreenLocked);
|
|
226
|
+
window.addEventListener("jarela:master-key-locked", onMasterKeyLocked);
|
|
220
227
|
|
|
221
228
|
// Soft poll every 30s so the overlay still appears if no user
|
|
222
229
|
// action triggered a request after the idle timer elapsed.
|
|
@@ -224,12 +231,19 @@ export function AppShell() {
|
|
|
224
231
|
try {
|
|
225
232
|
const res = await fetch("/api/v1/security/state");
|
|
226
233
|
if (!res.ok) return;
|
|
227
|
-
const body = (await res.json()) as {
|
|
228
|
-
|
|
229
|
-
|
|
234
|
+
const body = (await res.json()) as {
|
|
235
|
+
screen_locked?: boolean;
|
|
236
|
+
state?: string;
|
|
237
|
+
};
|
|
238
|
+
if (cancelled) return;
|
|
239
|
+
if (body.state === "locked") setMasterKeyLocked(true);
|
|
240
|
+
if (body.screen_locked === true) setScreenLocked(true);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
// Network blip; try again next tick. Logged at debug-level so
|
|
243
|
+
// a sustained outage is at least findable in devtools.
|
|
244
|
+
if (process.env.NODE_ENV !== "production") {
|
|
245
|
+
console.debug("[security/state] probe failed:", err);
|
|
230
246
|
}
|
|
231
|
-
} catch {
|
|
232
|
-
// Network blip; try again next tick.
|
|
233
247
|
}
|
|
234
248
|
}
|
|
235
249
|
void probe();
|
|
@@ -238,10 +252,20 @@ export function AppShell() {
|
|
|
238
252
|
return () => {
|
|
239
253
|
cancelled = true;
|
|
240
254
|
if (timer) clearInterval(timer);
|
|
241
|
-
window.removeEventListener("jarela:screen-locked",
|
|
255
|
+
window.removeEventListener("jarela:screen-locked", onScreenLocked);
|
|
256
|
+
window.removeEventListener("jarela:master-key-locked", onMasterKeyLocked);
|
|
242
257
|
};
|
|
243
258
|
}, []);
|
|
244
259
|
|
|
260
|
+
// Shared post-unlock landing: clear the current chat and force the
|
|
261
|
+
// BootScreen to remount so the user lands on the agent picker. Used
|
|
262
|
+
// by BOTH the screen-unlock and the master-key decrypt paths so the
|
|
263
|
+
// two transitions feel identical from the user's side.
|
|
264
|
+
const landOnAgentPicker = useCallback(() => {
|
|
265
|
+
dispatch({ type: "NEW_CHAT" });
|
|
266
|
+
setBootSeq((n) => n + 1);
|
|
267
|
+
}, [dispatch]);
|
|
268
|
+
|
|
245
269
|
return (
|
|
246
270
|
// `dvh` natively tracks the visible viewport on iOS 16.4+ / modern
|
|
247
271
|
// Chromium, including the on-screen keyboard. The `--actual-vh`
|
|
@@ -270,12 +294,24 @@ export function AppShell() {
|
|
|
270
294
|
onUnlock={() => {
|
|
271
295
|
// Drop the user back on the picker so they consciously
|
|
272
296
|
// re-enter their workspace rather than landing mid-chat.
|
|
273
|
-
|
|
274
|
-
setBootSeq((n) => n + 1);
|
|
297
|
+
landOnAgentPicker();
|
|
275
298
|
setScreenLocked(false);
|
|
276
299
|
}}
|
|
277
300
|
/>
|
|
278
301
|
)}
|
|
302
|
+
{masterKeyLocked && !screenLocked && (
|
|
303
|
+
// Master key got re-locked mid-session (e.g. external lock
|
|
304
|
+
// command). Mount the decrypt splash — same shared keypad as
|
|
305
|
+
// boot — and on success drop back to the agent picker. The
|
|
306
|
+
// existing components below stay mounted underneath; once
|
|
307
|
+
// unlocked they resume against the now-unlocked DB.
|
|
308
|
+
<UnlockScreen
|
|
309
|
+
onUnlock={() => {
|
|
310
|
+
landOnAgentPicker();
|
|
311
|
+
setMasterKeyLocked(false);
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
)}
|
|
279
315
|
<NotificationStatus />
|
|
280
316
|
<Toaster />
|
|
281
317
|
<ServerStatus />
|