bloby-bot 0.17.2

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.
Files changed (1128) hide show
  1. package/README.md +593 -0
  2. package/bin/cli.js +1788 -0
  3. package/cli/commands/daemon.ts +31 -0
  4. package/cli/commands/init.ts +40 -0
  5. package/cli/commands/start.ts +91 -0
  6. package/cli/commands/tunnel.ts +175 -0
  7. package/cli/commands/update.ts +170 -0
  8. package/cli/core/base-adapter.ts +99 -0
  9. package/cli/core/cloudflared.ts +71 -0
  10. package/cli/core/config.ts +58 -0
  11. package/cli/core/os-detector.ts +31 -0
  12. package/cli/core/server.ts +87 -0
  13. package/cli/core/types.ts +15 -0
  14. package/cli/index.ts +72 -0
  15. package/cli/platforms/darwin.ts +110 -0
  16. package/cli/platforms/index.ts +20 -0
  17. package/cli/platforms/linux.ts +116 -0
  18. package/cli/platforms/win32.ts +20 -0
  19. package/cli/utils/ui.ts +24 -0
  20. package/components.json +20 -0
  21. package/dist-bloby/assets/bloby-CMybOOCK.js +37 -0
  22. package/dist-bloby/assets/globals-CCAaaNDA.css +2 -0
  23. package/dist-bloby/assets/globals-Eq8C57hc.js +18 -0
  24. package/dist-bloby/assets/onboard-J0aqq4XH.js +1 -0
  25. package/dist-bloby/bloby.html +28 -0
  26. package/dist-bloby/onboard.html +14 -0
  27. package/package.json +103 -0
  28. package/postcss.config.js +5 -0
  29. package/scripts/install +316 -0
  30. package/scripts/install.ps1 +351 -0
  31. package/scripts/install.sh +322 -0
  32. package/scripts/postinstall.js +137 -0
  33. package/shared/ai.ts +141 -0
  34. package/shared/config.ts +68 -0
  35. package/shared/logger.ts +13 -0
  36. package/shared/paths.ts +20 -0
  37. package/shared/relay.ts +148 -0
  38. package/supervisor/app-ws.js +200 -0
  39. package/supervisor/backend.ts +143 -0
  40. package/supervisor/bloby-agent.ts +299 -0
  41. package/supervisor/channels/manager.ts +618 -0
  42. package/supervisor/channels/types.ts +59 -0
  43. package/supervisor/channels/whatsapp.ts +436 -0
  44. package/supervisor/chat/ARCHITECTURE.md +89 -0
  45. package/supervisor/chat/OnboardWizard.tsx +2353 -0
  46. package/supervisor/chat/bloby-main.tsx +567 -0
  47. package/supervisor/chat/bloby.html +26 -0
  48. package/supervisor/chat/onboard-main.tsx +19 -0
  49. package/supervisor/chat/onboard.html +12 -0
  50. package/supervisor/chat/src/components/Chat/AudioBubble.tsx +117 -0
  51. package/supervisor/chat/src/components/Chat/ChatView.tsx +25 -0
  52. package/supervisor/chat/src/components/Chat/ImageLightbox.tsx +86 -0
  53. package/supervisor/chat/src/components/Chat/InputBar.tsx +508 -0
  54. package/supervisor/chat/src/components/Chat/MessageBubble.tsx +226 -0
  55. package/supervisor/chat/src/components/Chat/MessageList.tsx +126 -0
  56. package/supervisor/chat/src/components/Chat/TypingIndicator.tsx +47 -0
  57. package/supervisor/chat/src/components/LoginScreen.tsx +280 -0
  58. package/supervisor/chat/src/hooks/useBlobyChat.ts +266 -0
  59. package/supervisor/chat/src/hooks/useChat.ts +237 -0
  60. package/supervisor/chat/src/hooks/useSpeechRecognition.ts +137 -0
  61. package/supervisor/chat/src/lib/auth.ts +38 -0
  62. package/supervisor/chat/src/lib/ws-client.ts +126 -0
  63. package/supervisor/chat/src/styles/globals.css +116 -0
  64. package/supervisor/file-saver.ts +50 -0
  65. package/supervisor/index.ts +1537 -0
  66. package/supervisor/scheduler.ts +309 -0
  67. package/supervisor/tunnel.ts +156 -0
  68. package/supervisor/vite-dev.ts +72 -0
  69. package/supervisor/widget.js +441 -0
  70. package/tsconfig.json +20 -0
  71. package/vite.bloby.config.ts +43 -0
  72. package/vite.config.ts +68 -0
  73. package/worker/claude-auth.ts +282 -0
  74. package/worker/codex-auth.ts +199 -0
  75. package/worker/db.ts +198 -0
  76. package/worker/index.ts +886 -0
  77. package/worker/prompts/bloby-system-prompt.txt +739 -0
  78. package/workspace/.backend.log +1 -0
  79. package/workspace/.env +3 -0
  80. package/workspace/CRONS.json +1 -0
  81. package/workspace/MEMORY.md +0 -0
  82. package/workspace/MYHUMAN.md +0 -0
  83. package/workspace/MYSELF.md +20 -0
  84. package/workspace/PULSE.json +8 -0
  85. package/workspace/app.db +0 -0
  86. package/workspace/backend/index.ts +52 -0
  87. package/workspace/client/index.html +75 -0
  88. package/workspace/client/public/.gitkeep +0 -0
  89. package/workspace/client/public/arrow.png +0 -0
  90. package/workspace/client/public/bloby-badge.png +0 -0
  91. package/workspace/client/public/bloby-icon-192.png +0 -0
  92. package/workspace/client/public/bloby-icon-512.png +0 -0
  93. package/workspace/client/public/bloby.png +0 -0
  94. package/workspace/client/public/bloby_frame1.png +0 -0
  95. package/workspace/client/public/bloby_happy.mov +0 -0
  96. package/workspace/client/public/bloby_happy.webm +0 -0
  97. package/workspace/client/public/bloby_happy_reappearing.mov +0 -0
  98. package/workspace/client/public/bloby_happy_reappearing.webm +0 -0
  99. package/workspace/client/public/bloby_say_hi.mov +0 -0
  100. package/workspace/client/public/bloby_say_hi.webm +0 -0
  101. package/workspace/client/public/bloby_tilts.webm +0 -0
  102. package/workspace/client/public/icons/claude.png +0 -0
  103. package/workspace/client/public/icons/codex.png +0 -0
  104. package/workspace/client/public/icons/openai.svg +15 -0
  105. package/workspace/client/public/manifest.json +23 -0
  106. package/workspace/client/public/spritesheet.webp +0 -0
  107. package/workspace/client/public/sw.js +136 -0
  108. package/workspace/client/src/App.tsx +191 -0
  109. package/workspace/client/src/components/Dashboard/DashboardPage.tsx +134 -0
  110. package/workspace/client/src/components/ErrorBoundary.tsx +23 -0
  111. package/workspace/client/src/components/Layout/DashboardLayout.tsx +56 -0
  112. package/workspace/client/src/components/Layout/Footer.tsx +14 -0
  113. package/workspace/client/src/components/Layout/MobileNav.tsx +30 -0
  114. package/workspace/client/src/components/Layout/Sidebar.tsx +110 -0
  115. package/workspace/client/src/components/deleteme_onboarding/WorkspaceTour.tsx +104 -0
  116. package/workspace/client/src/components/deleteme_onboarding/tour-theme.css +75 -0
  117. package/workspace/client/src/components/ui/avatar.tsx +109 -0
  118. package/workspace/client/src/components/ui/badge.tsx +48 -0
  119. package/workspace/client/src/components/ui/button.tsx +64 -0
  120. package/workspace/client/src/components/ui/card.tsx +92 -0
  121. package/workspace/client/src/components/ui/dialog.tsx +156 -0
  122. package/workspace/client/src/components/ui/dropdown-menu.tsx +257 -0
  123. package/workspace/client/src/components/ui/input.tsx +21 -0
  124. package/workspace/client/src/components/ui/scroll-area.tsx +58 -0
  125. package/workspace/client/src/components/ui/select.tsx +190 -0
  126. package/workspace/client/src/components/ui/separator.tsx +28 -0
  127. package/workspace/client/src/components/ui/sheet.tsx +142 -0
  128. package/workspace/client/src/components/ui/skeleton.tsx +13 -0
  129. package/workspace/client/src/components/ui/switch.tsx +33 -0
  130. package/workspace/client/src/components/ui/tabs.tsx +89 -0
  131. package/workspace/client/src/components/ui/textarea.tsx +18 -0
  132. package/workspace/client/src/components/ui/tooltip.tsx +55 -0
  133. package/workspace/client/src/lib/utils.ts +6 -0
  134. package/workspace/client/src/main.tsx +20 -0
  135. package/workspace/client/src/styles/globals.css +119 -0
  136. package/workspace/node_modules/.package-lock.json +1248 -0
  137. package/workspace/node_modules/.vite/deps/_metadata.json +116 -0
  138. package/workspace/node_modules/.vite/deps/clsx.js +18 -0
  139. package/workspace/node_modules/.vite/deps/clsx.js.map +1 -0
  140. package/workspace/node_modules/.vite/deps/driver__js.js +581 -0
  141. package/workspace/node_modules/.vite/deps/driver__js.js.map +1 -0
  142. package/workspace/node_modules/.vite/deps/framer-motion.js +13761 -0
  143. package/workspace/node_modules/.vite/deps/framer-motion.js.map +1 -0
  144. package/workspace/node_modules/.vite/deps/lucide-react.js +34357 -0
  145. package/workspace/node_modules/.vite/deps/lucide-react.js.map +1 -0
  146. package/workspace/node_modules/.vite/deps/package.json +3 -0
  147. package/workspace/node_modules/.vite/deps/radix-ui.js +13835 -0
  148. package/workspace/node_modules/.vite/deps/radix-ui.js.map +1 -0
  149. package/workspace/node_modules/.vite/deps/react-3_O8oni9.js +799 -0
  150. package/workspace/node_modules/.vite/deps/react-3_O8oni9.js.map +1 -0
  151. package/workspace/node_modules/.vite/deps/react-dom.js +185 -0
  152. package/workspace/node_modules/.vite/deps/react-dom.js.map +1 -0
  153. package/workspace/node_modules/.vite/deps/react-dom_client.js +14384 -0
  154. package/workspace/node_modules/.vite/deps/react-dom_client.js.map +1 -0
  155. package/workspace/node_modules/.vite/deps/react-router.js +9785 -0
  156. package/workspace/node_modules/.vite/deps/react-router.js.map +1 -0
  157. package/workspace/node_modules/.vite/deps/react.js +2 -0
  158. package/workspace/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
  159. package/workspace/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
  160. package/workspace/node_modules/.vite/deps/react_jsx-runtime.js +209 -0
  161. package/workspace/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
  162. package/workspace/node_modules/.vite/deps/recharts.js +34142 -0
  163. package/workspace/node_modules/.vite/deps/recharts.js.map +1 -0
  164. package/workspace/node_modules/.vite/deps/sonner.js +943 -0
  165. package/workspace/node_modules/.vite/deps/sonner.js.map +1 -0
  166. package/workspace/node_modules/.vite/deps/tailwind-merge.js +2025 -0
  167. package/workspace/node_modules/.vite/deps/tailwind-merge.js.map +1 -0
  168. package/workspace/node_modules/.vite/deps/use-sync-external-store.js +27 -0
  169. package/workspace/node_modules/.vite/deps/use-sync-external-store.js.map +1 -0
  170. package/workspace/node_modules/.vite/deps/use-sync-external-store_shim.js +75 -0
  171. package/workspace/node_modules/.vite/deps/use-sync-external-store_shim.js.map +1 -0
  172. package/workspace/node_modules/.vite/deps/zustand.js +49 -0
  173. package/workspace/node_modules/.vite/deps/zustand.js.map +1 -0
  174. package/workspace/node_modules/accepts/HISTORY.md +250 -0
  175. package/workspace/node_modules/accepts/LICENSE +23 -0
  176. package/workspace/node_modules/accepts/README.md +140 -0
  177. package/workspace/node_modules/accepts/index.js +238 -0
  178. package/workspace/node_modules/accepts/package.json +47 -0
  179. package/workspace/node_modules/base64-js/LICENSE +21 -0
  180. package/workspace/node_modules/base64-js/README.md +34 -0
  181. package/workspace/node_modules/base64-js/base64js.min.js +1 -0
  182. package/workspace/node_modules/base64-js/index.d.ts +3 -0
  183. package/workspace/node_modules/base64-js/index.js +150 -0
  184. package/workspace/node_modules/base64-js/package.json +47 -0
  185. package/workspace/node_modules/better-sqlite3/LICENSE +21 -0
  186. package/workspace/node_modules/better-sqlite3/README.md +99 -0
  187. package/workspace/node_modules/better-sqlite3/binding.gyp +38 -0
  188. package/workspace/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
  189. package/workspace/node_modules/better-sqlite3/deps/common.gypi +68 -0
  190. package/workspace/node_modules/better-sqlite3/deps/copy.js +31 -0
  191. package/workspace/node_modules/better-sqlite3/deps/defines.gypi +41 -0
  192. package/workspace/node_modules/better-sqlite3/deps/download.sh +122 -0
  193. package/workspace/node_modules/better-sqlite3/deps/patches/1208.patch +15 -0
  194. package/workspace/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +265994 -0
  195. package/workspace/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +13968 -0
  196. package/workspace/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +730 -0
  197. package/workspace/node_modules/better-sqlite3/deps/sqlite3.gyp +80 -0
  198. package/workspace/node_modules/better-sqlite3/deps/test_extension.c +21 -0
  199. package/workspace/node_modules/better-sqlite3/lib/database.js +90 -0
  200. package/workspace/node_modules/better-sqlite3/lib/index.js +3 -0
  201. package/workspace/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
  202. package/workspace/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
  203. package/workspace/node_modules/better-sqlite3/lib/methods/function.js +31 -0
  204. package/workspace/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
  205. package/workspace/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
  206. package/workspace/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
  207. package/workspace/node_modules/better-sqlite3/lib/methods/table.js +189 -0
  208. package/workspace/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
  209. package/workspace/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
  210. package/workspace/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
  211. package/workspace/node_modules/better-sqlite3/lib/util.js +12 -0
  212. package/workspace/node_modules/better-sqlite3/package.json +59 -0
  213. package/workspace/node_modules/better-sqlite3/src/addon.cpp +47 -0
  214. package/workspace/node_modules/better-sqlite3/src/better_sqlite3.cpp +74 -0
  215. package/workspace/node_modules/better-sqlite3/src/objects/backup.cpp +120 -0
  216. package/workspace/node_modules/better-sqlite3/src/objects/backup.hpp +36 -0
  217. package/workspace/node_modules/better-sqlite3/src/objects/database.cpp +417 -0
  218. package/workspace/node_modules/better-sqlite3/src/objects/database.hpp +103 -0
  219. package/workspace/node_modules/better-sqlite3/src/objects/statement-iterator.cpp +113 -0
  220. package/workspace/node_modules/better-sqlite3/src/objects/statement-iterator.hpp +50 -0
  221. package/workspace/node_modules/better-sqlite3/src/objects/statement.cpp +383 -0
  222. package/workspace/node_modules/better-sqlite3/src/objects/statement.hpp +58 -0
  223. package/workspace/node_modules/better-sqlite3/src/util/bind-map.cpp +73 -0
  224. package/workspace/node_modules/better-sqlite3/src/util/binder.cpp +193 -0
  225. package/workspace/node_modules/better-sqlite3/src/util/constants.cpp +172 -0
  226. package/workspace/node_modules/better-sqlite3/src/util/custom-aggregate.cpp +121 -0
  227. package/workspace/node_modules/better-sqlite3/src/util/custom-function.cpp +59 -0
  228. package/workspace/node_modules/better-sqlite3/src/util/custom-table.cpp +409 -0
  229. package/workspace/node_modules/better-sqlite3/src/util/data-converter.cpp +17 -0
  230. package/workspace/node_modules/better-sqlite3/src/util/data.cpp +194 -0
  231. package/workspace/node_modules/better-sqlite3/src/util/helpers.cpp +109 -0
  232. package/workspace/node_modules/better-sqlite3/src/util/macros.cpp +83 -0
  233. package/workspace/node_modules/better-sqlite3/src/util/query-macros.cpp +71 -0
  234. package/workspace/node_modules/better-sqlite3/src/util/row-builder.cpp +49 -0
  235. package/workspace/node_modules/bindings/LICENSE.md +22 -0
  236. package/workspace/node_modules/bindings/README.md +98 -0
  237. package/workspace/node_modules/bindings/bindings.js +221 -0
  238. package/workspace/node_modules/bindings/package.json +28 -0
  239. package/workspace/node_modules/bl/.travis.yml +17 -0
  240. package/workspace/node_modules/bl/BufferList.js +396 -0
  241. package/workspace/node_modules/bl/LICENSE.md +13 -0
  242. package/workspace/node_modules/bl/README.md +247 -0
  243. package/workspace/node_modules/bl/bl.js +84 -0
  244. package/workspace/node_modules/bl/package.json +37 -0
  245. package/workspace/node_modules/bl/test/convert.js +21 -0
  246. package/workspace/node_modules/bl/test/indexOf.js +492 -0
  247. package/workspace/node_modules/bl/test/isBufferList.js +32 -0
  248. package/workspace/node_modules/bl/test/test.js +869 -0
  249. package/workspace/node_modules/body-parser/LICENSE +23 -0
  250. package/workspace/node_modules/body-parser/README.md +494 -0
  251. package/workspace/node_modules/body-parser/index.js +71 -0
  252. package/workspace/node_modules/body-parser/lib/read.js +247 -0
  253. package/workspace/node_modules/body-parser/lib/types/json.js +158 -0
  254. package/workspace/node_modules/body-parser/lib/types/raw.js +42 -0
  255. package/workspace/node_modules/body-parser/lib/types/text.js +36 -0
  256. package/workspace/node_modules/body-parser/lib/types/urlencoded.js +142 -0
  257. package/workspace/node_modules/body-parser/lib/utils.js +98 -0
  258. package/workspace/node_modules/body-parser/package.json +52 -0
  259. package/workspace/node_modules/buffer/AUTHORS.md +70 -0
  260. package/workspace/node_modules/buffer/LICENSE +21 -0
  261. package/workspace/node_modules/buffer/README.md +410 -0
  262. package/workspace/node_modules/buffer/index.d.ts +186 -0
  263. package/workspace/node_modules/buffer/index.js +1817 -0
  264. package/workspace/node_modules/buffer/package.json +96 -0
  265. package/workspace/node_modules/bytes/History.md +97 -0
  266. package/workspace/node_modules/bytes/LICENSE +23 -0
  267. package/workspace/node_modules/bytes/Readme.md +152 -0
  268. package/workspace/node_modules/bytes/index.js +170 -0
  269. package/workspace/node_modules/bytes/package.json +42 -0
  270. package/workspace/node_modules/call-bind-apply-helpers/.eslintrc +17 -0
  271. package/workspace/node_modules/call-bind-apply-helpers/.github/FUNDING.yml +12 -0
  272. package/workspace/node_modules/call-bind-apply-helpers/.nycrc +9 -0
  273. package/workspace/node_modules/call-bind-apply-helpers/CHANGELOG.md +30 -0
  274. package/workspace/node_modules/call-bind-apply-helpers/LICENSE +21 -0
  275. package/workspace/node_modules/call-bind-apply-helpers/README.md +62 -0
  276. package/workspace/node_modules/call-bind-apply-helpers/actualApply.d.ts +1 -0
  277. package/workspace/node_modules/call-bind-apply-helpers/actualApply.js +10 -0
  278. package/workspace/node_modules/call-bind-apply-helpers/applyBind.d.ts +19 -0
  279. package/workspace/node_modules/call-bind-apply-helpers/applyBind.js +10 -0
  280. package/workspace/node_modules/call-bind-apply-helpers/functionApply.d.ts +1 -0
  281. package/workspace/node_modules/call-bind-apply-helpers/functionApply.js +4 -0
  282. package/workspace/node_modules/call-bind-apply-helpers/functionCall.d.ts +1 -0
  283. package/workspace/node_modules/call-bind-apply-helpers/functionCall.js +4 -0
  284. package/workspace/node_modules/call-bind-apply-helpers/index.d.ts +64 -0
  285. package/workspace/node_modules/call-bind-apply-helpers/index.js +15 -0
  286. package/workspace/node_modules/call-bind-apply-helpers/package.json +85 -0
  287. package/workspace/node_modules/call-bind-apply-helpers/reflectApply.d.ts +3 -0
  288. package/workspace/node_modules/call-bind-apply-helpers/reflectApply.js +4 -0
  289. package/workspace/node_modules/call-bind-apply-helpers/test/index.js +63 -0
  290. package/workspace/node_modules/call-bind-apply-helpers/tsconfig.json +9 -0
  291. package/workspace/node_modules/call-bound/.eslintrc +13 -0
  292. package/workspace/node_modules/call-bound/.github/FUNDING.yml +12 -0
  293. package/workspace/node_modules/call-bound/.nycrc +9 -0
  294. package/workspace/node_modules/call-bound/CHANGELOG.md +42 -0
  295. package/workspace/node_modules/call-bound/LICENSE +21 -0
  296. package/workspace/node_modules/call-bound/README.md +53 -0
  297. package/workspace/node_modules/call-bound/index.d.ts +94 -0
  298. package/workspace/node_modules/call-bound/index.js +19 -0
  299. package/workspace/node_modules/call-bound/package.json +99 -0
  300. package/workspace/node_modules/call-bound/test/index.js +61 -0
  301. package/workspace/node_modules/call-bound/tsconfig.json +10 -0
  302. package/workspace/node_modules/chownr/LICENSE +15 -0
  303. package/workspace/node_modules/chownr/README.md +3 -0
  304. package/workspace/node_modules/chownr/chownr.js +167 -0
  305. package/workspace/node_modules/chownr/package.json +29 -0
  306. package/workspace/node_modules/content-disposition/HISTORY.md +72 -0
  307. package/workspace/node_modules/content-disposition/LICENSE +22 -0
  308. package/workspace/node_modules/content-disposition/README.md +142 -0
  309. package/workspace/node_modules/content-disposition/index.js +458 -0
  310. package/workspace/node_modules/content-disposition/package.json +43 -0
  311. package/workspace/node_modules/content-type/HISTORY.md +29 -0
  312. package/workspace/node_modules/content-type/LICENSE +22 -0
  313. package/workspace/node_modules/content-type/README.md +94 -0
  314. package/workspace/node_modules/content-type/index.js +225 -0
  315. package/workspace/node_modules/content-type/package.json +42 -0
  316. package/workspace/node_modules/cookie/LICENSE +24 -0
  317. package/workspace/node_modules/cookie/README.md +317 -0
  318. package/workspace/node_modules/cookie/SECURITY.md +25 -0
  319. package/workspace/node_modules/cookie/index.js +335 -0
  320. package/workspace/node_modules/cookie/package.json +44 -0
  321. package/workspace/node_modules/cookie-signature/History.md +70 -0
  322. package/workspace/node_modules/cookie-signature/LICENSE +22 -0
  323. package/workspace/node_modules/cookie-signature/Readme.md +23 -0
  324. package/workspace/node_modules/cookie-signature/index.js +47 -0
  325. package/workspace/node_modules/cookie-signature/package.json +24 -0
  326. package/workspace/node_modules/debug/LICENSE +20 -0
  327. package/workspace/node_modules/debug/README.md +481 -0
  328. package/workspace/node_modules/debug/package.json +64 -0
  329. package/workspace/node_modules/debug/src/browser.js +272 -0
  330. package/workspace/node_modules/debug/src/common.js +292 -0
  331. package/workspace/node_modules/debug/src/index.js +10 -0
  332. package/workspace/node_modules/debug/src/node.js +263 -0
  333. package/workspace/node_modules/decompress-response/index.d.ts +22 -0
  334. package/workspace/node_modules/decompress-response/index.js +58 -0
  335. package/workspace/node_modules/decompress-response/license +9 -0
  336. package/workspace/node_modules/decompress-response/package.json +56 -0
  337. package/workspace/node_modules/decompress-response/readme.md +48 -0
  338. package/workspace/node_modules/deep-extend/CHANGELOG.md +46 -0
  339. package/workspace/node_modules/deep-extend/LICENSE +20 -0
  340. package/workspace/node_modules/deep-extend/README.md +91 -0
  341. package/workspace/node_modules/deep-extend/index.js +1 -0
  342. package/workspace/node_modules/deep-extend/lib/deep-extend.js +150 -0
  343. package/workspace/node_modules/deep-extend/package.json +62 -0
  344. package/workspace/node_modules/depd/History.md +103 -0
  345. package/workspace/node_modules/depd/LICENSE +22 -0
  346. package/workspace/node_modules/depd/Readme.md +280 -0
  347. package/workspace/node_modules/depd/index.js +538 -0
  348. package/workspace/node_modules/depd/lib/browser/index.js +77 -0
  349. package/workspace/node_modules/depd/package.json +45 -0
  350. package/workspace/node_modules/detect-libc/LICENSE +201 -0
  351. package/workspace/node_modules/detect-libc/README.md +163 -0
  352. package/workspace/node_modules/detect-libc/index.d.ts +14 -0
  353. package/workspace/node_modules/detect-libc/lib/detect-libc.js +313 -0
  354. package/workspace/node_modules/detect-libc/lib/elf.js +39 -0
  355. package/workspace/node_modules/detect-libc/lib/filesystem.js +51 -0
  356. package/workspace/node_modules/detect-libc/lib/process.js +24 -0
  357. package/workspace/node_modules/detect-libc/package.json +44 -0
  358. package/workspace/node_modules/dunder-proto/.eslintrc +5 -0
  359. package/workspace/node_modules/dunder-proto/.github/FUNDING.yml +12 -0
  360. package/workspace/node_modules/dunder-proto/.nycrc +13 -0
  361. package/workspace/node_modules/dunder-proto/CHANGELOG.md +24 -0
  362. package/workspace/node_modules/dunder-proto/LICENSE +21 -0
  363. package/workspace/node_modules/dunder-proto/README.md +54 -0
  364. package/workspace/node_modules/dunder-proto/get.d.ts +5 -0
  365. package/workspace/node_modules/dunder-proto/get.js +30 -0
  366. package/workspace/node_modules/dunder-proto/package.json +76 -0
  367. package/workspace/node_modules/dunder-proto/set.d.ts +5 -0
  368. package/workspace/node_modules/dunder-proto/set.js +35 -0
  369. package/workspace/node_modules/dunder-proto/test/get.js +34 -0
  370. package/workspace/node_modules/dunder-proto/test/index.js +4 -0
  371. package/workspace/node_modules/dunder-proto/test/set.js +50 -0
  372. package/workspace/node_modules/dunder-proto/tsconfig.json +9 -0
  373. package/workspace/node_modules/ee-first/LICENSE +22 -0
  374. package/workspace/node_modules/ee-first/README.md +80 -0
  375. package/workspace/node_modules/ee-first/index.js +95 -0
  376. package/workspace/node_modules/ee-first/package.json +29 -0
  377. package/workspace/node_modules/encodeurl/LICENSE +22 -0
  378. package/workspace/node_modules/encodeurl/README.md +109 -0
  379. package/workspace/node_modules/encodeurl/index.js +60 -0
  380. package/workspace/node_modules/encodeurl/package.json +40 -0
  381. package/workspace/node_modules/end-of-stream/LICENSE +21 -0
  382. package/workspace/node_modules/end-of-stream/README.md +54 -0
  383. package/workspace/node_modules/end-of-stream/index.js +96 -0
  384. package/workspace/node_modules/end-of-stream/package.json +37 -0
  385. package/workspace/node_modules/es-define-property/.eslintrc +13 -0
  386. package/workspace/node_modules/es-define-property/.github/FUNDING.yml +12 -0
  387. package/workspace/node_modules/es-define-property/.nycrc +9 -0
  388. package/workspace/node_modules/es-define-property/CHANGELOG.md +29 -0
  389. package/workspace/node_modules/es-define-property/LICENSE +21 -0
  390. package/workspace/node_modules/es-define-property/README.md +49 -0
  391. package/workspace/node_modules/es-define-property/index.d.ts +3 -0
  392. package/workspace/node_modules/es-define-property/index.js +14 -0
  393. package/workspace/node_modules/es-define-property/package.json +81 -0
  394. package/workspace/node_modules/es-define-property/test/index.js +56 -0
  395. package/workspace/node_modules/es-define-property/tsconfig.json +10 -0
  396. package/workspace/node_modules/es-errors/.eslintrc +5 -0
  397. package/workspace/node_modules/es-errors/.github/FUNDING.yml +12 -0
  398. package/workspace/node_modules/es-errors/CHANGELOG.md +40 -0
  399. package/workspace/node_modules/es-errors/LICENSE +21 -0
  400. package/workspace/node_modules/es-errors/README.md +55 -0
  401. package/workspace/node_modules/es-errors/eval.d.ts +3 -0
  402. package/workspace/node_modules/es-errors/eval.js +4 -0
  403. package/workspace/node_modules/es-errors/index.d.ts +3 -0
  404. package/workspace/node_modules/es-errors/index.js +4 -0
  405. package/workspace/node_modules/es-errors/package.json +80 -0
  406. package/workspace/node_modules/es-errors/range.d.ts +3 -0
  407. package/workspace/node_modules/es-errors/range.js +4 -0
  408. package/workspace/node_modules/es-errors/ref.d.ts +3 -0
  409. package/workspace/node_modules/es-errors/ref.js +4 -0
  410. package/workspace/node_modules/es-errors/syntax.d.ts +3 -0
  411. package/workspace/node_modules/es-errors/syntax.js +4 -0
  412. package/workspace/node_modules/es-errors/test/index.js +19 -0
  413. package/workspace/node_modules/es-errors/tsconfig.json +49 -0
  414. package/workspace/node_modules/es-errors/type.d.ts +3 -0
  415. package/workspace/node_modules/es-errors/type.js +4 -0
  416. package/workspace/node_modules/es-errors/uri.d.ts +3 -0
  417. package/workspace/node_modules/es-errors/uri.js +4 -0
  418. package/workspace/node_modules/es-object-atoms/.eslintrc +16 -0
  419. package/workspace/node_modules/es-object-atoms/.github/FUNDING.yml +12 -0
  420. package/workspace/node_modules/es-object-atoms/CHANGELOG.md +37 -0
  421. package/workspace/node_modules/es-object-atoms/LICENSE +21 -0
  422. package/workspace/node_modules/es-object-atoms/README.md +63 -0
  423. package/workspace/node_modules/es-object-atoms/RequireObjectCoercible.d.ts +3 -0
  424. package/workspace/node_modules/es-object-atoms/RequireObjectCoercible.js +11 -0
  425. package/workspace/node_modules/es-object-atoms/ToObject.d.ts +7 -0
  426. package/workspace/node_modules/es-object-atoms/ToObject.js +10 -0
  427. package/workspace/node_modules/es-object-atoms/index.d.ts +3 -0
  428. package/workspace/node_modules/es-object-atoms/index.js +4 -0
  429. package/workspace/node_modules/es-object-atoms/isObject.d.ts +3 -0
  430. package/workspace/node_modules/es-object-atoms/isObject.js +6 -0
  431. package/workspace/node_modules/es-object-atoms/package.json +80 -0
  432. package/workspace/node_modules/es-object-atoms/test/index.js +38 -0
  433. package/workspace/node_modules/es-object-atoms/tsconfig.json +6 -0
  434. package/workspace/node_modules/escape-html/LICENSE +24 -0
  435. package/workspace/node_modules/escape-html/Readme.md +43 -0
  436. package/workspace/node_modules/escape-html/index.js +78 -0
  437. package/workspace/node_modules/escape-html/package.json +24 -0
  438. package/workspace/node_modules/etag/HISTORY.md +83 -0
  439. package/workspace/node_modules/etag/LICENSE +22 -0
  440. package/workspace/node_modules/etag/README.md +159 -0
  441. package/workspace/node_modules/etag/index.js +131 -0
  442. package/workspace/node_modules/etag/package.json +47 -0
  443. package/workspace/node_modules/expand-template/.travis.yml +6 -0
  444. package/workspace/node_modules/expand-template/LICENSE +21 -0
  445. package/workspace/node_modules/expand-template/README.md +43 -0
  446. package/workspace/node_modules/expand-template/index.js +26 -0
  447. package/workspace/node_modules/expand-template/package.json +29 -0
  448. package/workspace/node_modules/expand-template/test.js +67 -0
  449. package/workspace/node_modules/express/LICENSE +24 -0
  450. package/workspace/node_modules/express/Readme.md +276 -0
  451. package/workspace/node_modules/express/index.js +11 -0
  452. package/workspace/node_modules/express/lib/application.js +631 -0
  453. package/workspace/node_modules/express/lib/express.js +81 -0
  454. package/workspace/node_modules/express/lib/request.js +514 -0
  455. package/workspace/node_modules/express/lib/response.js +1053 -0
  456. package/workspace/node_modules/express/lib/utils.js +271 -0
  457. package/workspace/node_modules/express/lib/view.js +205 -0
  458. package/workspace/node_modules/express/package.json +99 -0
  459. package/workspace/node_modules/file-uri-to-path/.travis.yml +30 -0
  460. package/workspace/node_modules/file-uri-to-path/History.md +21 -0
  461. package/workspace/node_modules/file-uri-to-path/LICENSE +20 -0
  462. package/workspace/node_modules/file-uri-to-path/README.md +74 -0
  463. package/workspace/node_modules/file-uri-to-path/index.d.ts +2 -0
  464. package/workspace/node_modules/file-uri-to-path/index.js +66 -0
  465. package/workspace/node_modules/file-uri-to-path/package.json +32 -0
  466. package/workspace/node_modules/file-uri-to-path/test/test.js +24 -0
  467. package/workspace/node_modules/file-uri-to-path/test/tests.json +13 -0
  468. package/workspace/node_modules/finalhandler/HISTORY.md +239 -0
  469. package/workspace/node_modules/finalhandler/LICENSE +22 -0
  470. package/workspace/node_modules/finalhandler/README.md +150 -0
  471. package/workspace/node_modules/finalhandler/index.js +293 -0
  472. package/workspace/node_modules/finalhandler/package.json +47 -0
  473. package/workspace/node_modules/forwarded/HISTORY.md +21 -0
  474. package/workspace/node_modules/forwarded/LICENSE +22 -0
  475. package/workspace/node_modules/forwarded/README.md +57 -0
  476. package/workspace/node_modules/forwarded/index.js +90 -0
  477. package/workspace/node_modules/forwarded/package.json +45 -0
  478. package/workspace/node_modules/fresh/HISTORY.md +80 -0
  479. package/workspace/node_modules/fresh/LICENSE +23 -0
  480. package/workspace/node_modules/fresh/README.md +117 -0
  481. package/workspace/node_modules/fresh/index.js +136 -0
  482. package/workspace/node_modules/fresh/package.json +46 -0
  483. package/workspace/node_modules/fs-constants/LICENSE +21 -0
  484. package/workspace/node_modules/fs-constants/README.md +26 -0
  485. package/workspace/node_modules/fs-constants/browser.js +1 -0
  486. package/workspace/node_modules/fs-constants/index.js +1 -0
  487. package/workspace/node_modules/fs-constants/package.json +19 -0
  488. package/workspace/node_modules/function-bind/.eslintrc +21 -0
  489. package/workspace/node_modules/function-bind/.github/FUNDING.yml +12 -0
  490. package/workspace/node_modules/function-bind/.github/SECURITY.md +3 -0
  491. package/workspace/node_modules/function-bind/.nycrc +13 -0
  492. package/workspace/node_modules/function-bind/CHANGELOG.md +136 -0
  493. package/workspace/node_modules/function-bind/LICENSE +20 -0
  494. package/workspace/node_modules/function-bind/README.md +46 -0
  495. package/workspace/node_modules/function-bind/implementation.js +84 -0
  496. package/workspace/node_modules/function-bind/index.js +5 -0
  497. package/workspace/node_modules/function-bind/package.json +87 -0
  498. package/workspace/node_modules/function-bind/test/.eslintrc +9 -0
  499. package/workspace/node_modules/function-bind/test/index.js +252 -0
  500. package/workspace/node_modules/get-intrinsic/.eslintrc +42 -0
  501. package/workspace/node_modules/get-intrinsic/.github/FUNDING.yml +12 -0
  502. package/workspace/node_modules/get-intrinsic/.nycrc +9 -0
  503. package/workspace/node_modules/get-intrinsic/CHANGELOG.md +186 -0
  504. package/workspace/node_modules/get-intrinsic/LICENSE +21 -0
  505. package/workspace/node_modules/get-intrinsic/README.md +71 -0
  506. package/workspace/node_modules/get-intrinsic/index.js +378 -0
  507. package/workspace/node_modules/get-intrinsic/package.json +97 -0
  508. package/workspace/node_modules/get-intrinsic/test/GetIntrinsic.js +274 -0
  509. package/workspace/node_modules/get-proto/.eslintrc +10 -0
  510. package/workspace/node_modules/get-proto/.github/FUNDING.yml +12 -0
  511. package/workspace/node_modules/get-proto/.nycrc +9 -0
  512. package/workspace/node_modules/get-proto/CHANGELOG.md +21 -0
  513. package/workspace/node_modules/get-proto/LICENSE +21 -0
  514. package/workspace/node_modules/get-proto/Object.getPrototypeOf.d.ts +5 -0
  515. package/workspace/node_modules/get-proto/Object.getPrototypeOf.js +6 -0
  516. package/workspace/node_modules/get-proto/README.md +50 -0
  517. package/workspace/node_modules/get-proto/Reflect.getPrototypeOf.d.ts +3 -0
  518. package/workspace/node_modules/get-proto/Reflect.getPrototypeOf.js +4 -0
  519. package/workspace/node_modules/get-proto/index.d.ts +5 -0
  520. package/workspace/node_modules/get-proto/index.js +27 -0
  521. package/workspace/node_modules/get-proto/package.json +81 -0
  522. package/workspace/node_modules/get-proto/test/index.js +68 -0
  523. package/workspace/node_modules/get-proto/tsconfig.json +9 -0
  524. package/workspace/node_modules/github-from-package/.travis.yml +4 -0
  525. package/workspace/node_modules/github-from-package/LICENSE +18 -0
  526. package/workspace/node_modules/github-from-package/example/package.json +8 -0
  527. package/workspace/node_modules/github-from-package/example/url.js +3 -0
  528. package/workspace/node_modules/github-from-package/index.js +17 -0
  529. package/workspace/node_modules/github-from-package/package.json +30 -0
  530. package/workspace/node_modules/github-from-package/readme.markdown +53 -0
  531. package/workspace/node_modules/github-from-package/test/a.json +8 -0
  532. package/workspace/node_modules/github-from-package/test/b.json +5 -0
  533. package/workspace/node_modules/github-from-package/test/c.json +5 -0
  534. package/workspace/node_modules/github-from-package/test/d.json +7 -0
  535. package/workspace/node_modules/github-from-package/test/e.json +5 -0
  536. package/workspace/node_modules/github-from-package/test/url.js +19 -0
  537. package/workspace/node_modules/gopd/.eslintrc +16 -0
  538. package/workspace/node_modules/gopd/.github/FUNDING.yml +12 -0
  539. package/workspace/node_modules/gopd/CHANGELOG.md +45 -0
  540. package/workspace/node_modules/gopd/LICENSE +21 -0
  541. package/workspace/node_modules/gopd/README.md +40 -0
  542. package/workspace/node_modules/gopd/gOPD.d.ts +1 -0
  543. package/workspace/node_modules/gopd/gOPD.js +4 -0
  544. package/workspace/node_modules/gopd/index.d.ts +5 -0
  545. package/workspace/node_modules/gopd/index.js +15 -0
  546. package/workspace/node_modules/gopd/package.json +77 -0
  547. package/workspace/node_modules/gopd/test/index.js +36 -0
  548. package/workspace/node_modules/gopd/tsconfig.json +9 -0
  549. package/workspace/node_modules/has-symbols/.eslintrc +11 -0
  550. package/workspace/node_modules/has-symbols/.github/FUNDING.yml +12 -0
  551. package/workspace/node_modules/has-symbols/.nycrc +9 -0
  552. package/workspace/node_modules/has-symbols/CHANGELOG.md +91 -0
  553. package/workspace/node_modules/has-symbols/LICENSE +21 -0
  554. package/workspace/node_modules/has-symbols/README.md +46 -0
  555. package/workspace/node_modules/has-symbols/index.d.ts +3 -0
  556. package/workspace/node_modules/has-symbols/index.js +14 -0
  557. package/workspace/node_modules/has-symbols/package.json +111 -0
  558. package/workspace/node_modules/has-symbols/shams.d.ts +3 -0
  559. package/workspace/node_modules/has-symbols/shams.js +45 -0
  560. package/workspace/node_modules/has-symbols/test/index.js +22 -0
  561. package/workspace/node_modules/has-symbols/test/shams/core-js.js +29 -0
  562. package/workspace/node_modules/has-symbols/test/shams/get-own-property-symbols.js +29 -0
  563. package/workspace/node_modules/has-symbols/test/tests.js +58 -0
  564. package/workspace/node_modules/has-symbols/tsconfig.json +10 -0
  565. package/workspace/node_modules/hasown/.eslintrc +5 -0
  566. package/workspace/node_modules/hasown/.github/FUNDING.yml +12 -0
  567. package/workspace/node_modules/hasown/.nycrc +13 -0
  568. package/workspace/node_modules/hasown/CHANGELOG.md +40 -0
  569. package/workspace/node_modules/hasown/LICENSE +21 -0
  570. package/workspace/node_modules/hasown/README.md +40 -0
  571. package/workspace/node_modules/hasown/index.d.ts +3 -0
  572. package/workspace/node_modules/hasown/index.js +8 -0
  573. package/workspace/node_modules/hasown/package.json +92 -0
  574. package/workspace/node_modules/hasown/tsconfig.json +6 -0
  575. package/workspace/node_modules/http-errors/HISTORY.md +186 -0
  576. package/workspace/node_modules/http-errors/LICENSE +23 -0
  577. package/workspace/node_modules/http-errors/README.md +169 -0
  578. package/workspace/node_modules/http-errors/index.js +290 -0
  579. package/workspace/node_modules/http-errors/package.json +54 -0
  580. package/workspace/node_modules/iconv-lite/LICENSE +21 -0
  581. package/workspace/node_modules/iconv-lite/README.md +138 -0
  582. package/workspace/node_modules/iconv-lite/encodings/dbcs-codec.js +532 -0
  583. package/workspace/node_modules/iconv-lite/encodings/dbcs-data.js +185 -0
  584. package/workspace/node_modules/iconv-lite/encodings/index.js +23 -0
  585. package/workspace/node_modules/iconv-lite/encodings/internal.js +218 -0
  586. package/workspace/node_modules/iconv-lite/encodings/sbcs-codec.js +75 -0
  587. package/workspace/node_modules/iconv-lite/encodings/sbcs-data-generated.js +451 -0
  588. package/workspace/node_modules/iconv-lite/encodings/sbcs-data.js +178 -0
  589. package/workspace/node_modules/iconv-lite/encodings/tables/big5-added.json +122 -0
  590. package/workspace/node_modules/iconv-lite/encodings/tables/cp936.json +264 -0
  591. package/workspace/node_modules/iconv-lite/encodings/tables/cp949.json +273 -0
  592. package/workspace/node_modules/iconv-lite/encodings/tables/cp950.json +177 -0
  593. package/workspace/node_modules/iconv-lite/encodings/tables/eucjp.json +182 -0
  594. package/workspace/node_modules/iconv-lite/encodings/tables/gb18030-ranges.json +1 -0
  595. package/workspace/node_modules/iconv-lite/encodings/tables/gbk-added.json +56 -0
  596. package/workspace/node_modules/iconv-lite/encodings/tables/shiftjis.json +125 -0
  597. package/workspace/node_modules/iconv-lite/encodings/utf16.js +187 -0
  598. package/workspace/node_modules/iconv-lite/encodings/utf32.js +307 -0
  599. package/workspace/node_modules/iconv-lite/encodings/utf7.js +283 -0
  600. package/workspace/node_modules/iconv-lite/lib/bom-handling.js +48 -0
  601. package/workspace/node_modules/iconv-lite/lib/helpers/merge-exports.js +13 -0
  602. package/workspace/node_modules/iconv-lite/lib/index.d.ts +129 -0
  603. package/workspace/node_modules/iconv-lite/lib/index.js +182 -0
  604. package/workspace/node_modules/iconv-lite/lib/streams.js +105 -0
  605. package/workspace/node_modules/iconv-lite/package.json +70 -0
  606. package/workspace/node_modules/iconv-lite/types/encodings.d.ts +423 -0
  607. package/workspace/node_modules/ieee754/LICENSE +11 -0
  608. package/workspace/node_modules/ieee754/README.md +51 -0
  609. package/workspace/node_modules/ieee754/index.d.ts +10 -0
  610. package/workspace/node_modules/ieee754/index.js +85 -0
  611. package/workspace/node_modules/ieee754/package.json +52 -0
  612. package/workspace/node_modules/inherits/LICENSE +16 -0
  613. package/workspace/node_modules/inherits/README.md +42 -0
  614. package/workspace/node_modules/inherits/inherits.js +9 -0
  615. package/workspace/node_modules/inherits/inherits_browser.js +27 -0
  616. package/workspace/node_modules/inherits/package.json +29 -0
  617. package/workspace/node_modules/ini/LICENSE +15 -0
  618. package/workspace/node_modules/ini/README.md +102 -0
  619. package/workspace/node_modules/ini/ini.js +206 -0
  620. package/workspace/node_modules/ini/package.json +33 -0
  621. package/workspace/node_modules/ipaddr.js/LICENSE +19 -0
  622. package/workspace/node_modules/ipaddr.js/README.md +233 -0
  623. package/workspace/node_modules/ipaddr.js/ipaddr.min.js +1 -0
  624. package/workspace/node_modules/ipaddr.js/lib/ipaddr.js +673 -0
  625. package/workspace/node_modules/ipaddr.js/lib/ipaddr.js.d.ts +68 -0
  626. package/workspace/node_modules/ipaddr.js/package.json +35 -0
  627. package/workspace/node_modules/is-promise/LICENSE +19 -0
  628. package/workspace/node_modules/is-promise/index.d.ts +2 -0
  629. package/workspace/node_modules/is-promise/index.js +6 -0
  630. package/workspace/node_modules/is-promise/index.mjs +3 -0
  631. package/workspace/node_modules/is-promise/package.json +30 -0
  632. package/workspace/node_modules/is-promise/readme.md +33 -0
  633. package/workspace/node_modules/math-intrinsics/.eslintrc +16 -0
  634. package/workspace/node_modules/math-intrinsics/.github/FUNDING.yml +12 -0
  635. package/workspace/node_modules/math-intrinsics/CHANGELOG.md +24 -0
  636. package/workspace/node_modules/math-intrinsics/LICENSE +21 -0
  637. package/workspace/node_modules/math-intrinsics/README.md +50 -0
  638. package/workspace/node_modules/math-intrinsics/abs.d.ts +1 -0
  639. package/workspace/node_modules/math-intrinsics/abs.js +4 -0
  640. package/workspace/node_modules/math-intrinsics/constants/maxArrayLength.d.ts +3 -0
  641. package/workspace/node_modules/math-intrinsics/constants/maxArrayLength.js +4 -0
  642. package/workspace/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts +3 -0
  643. package/workspace/node_modules/math-intrinsics/constants/maxSafeInteger.js +5 -0
  644. package/workspace/node_modules/math-intrinsics/constants/maxValue.d.ts +3 -0
  645. package/workspace/node_modules/math-intrinsics/constants/maxValue.js +5 -0
  646. package/workspace/node_modules/math-intrinsics/floor.d.ts +1 -0
  647. package/workspace/node_modules/math-intrinsics/floor.js +4 -0
  648. package/workspace/node_modules/math-intrinsics/isFinite.d.ts +3 -0
  649. package/workspace/node_modules/math-intrinsics/isFinite.js +12 -0
  650. package/workspace/node_modules/math-intrinsics/isInteger.d.ts +3 -0
  651. package/workspace/node_modules/math-intrinsics/isInteger.js +16 -0
  652. package/workspace/node_modules/math-intrinsics/isNaN.d.ts +1 -0
  653. package/workspace/node_modules/math-intrinsics/isNaN.js +6 -0
  654. package/workspace/node_modules/math-intrinsics/isNegativeZero.d.ts +3 -0
  655. package/workspace/node_modules/math-intrinsics/isNegativeZero.js +6 -0
  656. package/workspace/node_modules/math-intrinsics/max.d.ts +1 -0
  657. package/workspace/node_modules/math-intrinsics/max.js +4 -0
  658. package/workspace/node_modules/math-intrinsics/min.d.ts +1 -0
  659. package/workspace/node_modules/math-intrinsics/min.js +4 -0
  660. package/workspace/node_modules/math-intrinsics/mod.d.ts +3 -0
  661. package/workspace/node_modules/math-intrinsics/mod.js +9 -0
  662. package/workspace/node_modules/math-intrinsics/package.json +86 -0
  663. package/workspace/node_modules/math-intrinsics/pow.d.ts +1 -0
  664. package/workspace/node_modules/math-intrinsics/pow.js +4 -0
  665. package/workspace/node_modules/math-intrinsics/round.d.ts +1 -0
  666. package/workspace/node_modules/math-intrinsics/round.js +4 -0
  667. package/workspace/node_modules/math-intrinsics/sign.d.ts +3 -0
  668. package/workspace/node_modules/math-intrinsics/sign.js +11 -0
  669. package/workspace/node_modules/math-intrinsics/test/index.js +192 -0
  670. package/workspace/node_modules/math-intrinsics/tsconfig.json +3 -0
  671. package/workspace/node_modules/media-typer/HISTORY.md +50 -0
  672. package/workspace/node_modules/media-typer/LICENSE +22 -0
  673. package/workspace/node_modules/media-typer/README.md +93 -0
  674. package/workspace/node_modules/media-typer/index.js +143 -0
  675. package/workspace/node_modules/media-typer/package.json +33 -0
  676. package/workspace/node_modules/merge-descriptors/index.d.ts +11 -0
  677. package/workspace/node_modules/merge-descriptors/index.js +26 -0
  678. package/workspace/node_modules/merge-descriptors/license +11 -0
  679. package/workspace/node_modules/merge-descriptors/package.json +50 -0
  680. package/workspace/node_modules/merge-descriptors/readme.md +55 -0
  681. package/workspace/node_modules/mime-db/HISTORY.md +541 -0
  682. package/workspace/node_modules/mime-db/LICENSE +23 -0
  683. package/workspace/node_modules/mime-db/README.md +109 -0
  684. package/workspace/node_modules/mime-db/db.json +9342 -0
  685. package/workspace/node_modules/mime-db/index.js +12 -0
  686. package/workspace/node_modules/mime-db/package.json +56 -0
  687. package/workspace/node_modules/mime-types/HISTORY.md +428 -0
  688. package/workspace/node_modules/mime-types/LICENSE +23 -0
  689. package/workspace/node_modules/mime-types/README.md +126 -0
  690. package/workspace/node_modules/mime-types/index.js +211 -0
  691. package/workspace/node_modules/mime-types/mimeScore.js +57 -0
  692. package/workspace/node_modules/mime-types/package.json +49 -0
  693. package/workspace/node_modules/mimic-response/index.d.ts +17 -0
  694. package/workspace/node_modules/mimic-response/index.js +77 -0
  695. package/workspace/node_modules/mimic-response/license +9 -0
  696. package/workspace/node_modules/mimic-response/package.json +42 -0
  697. package/workspace/node_modules/mimic-response/readme.md +78 -0
  698. package/workspace/node_modules/minimist/.eslintrc +29 -0
  699. package/workspace/node_modules/minimist/.github/FUNDING.yml +12 -0
  700. package/workspace/node_modules/minimist/.nycrc +14 -0
  701. package/workspace/node_modules/minimist/CHANGELOG.md +298 -0
  702. package/workspace/node_modules/minimist/LICENSE +18 -0
  703. package/workspace/node_modules/minimist/README.md +121 -0
  704. package/workspace/node_modules/minimist/example/parse.js +4 -0
  705. package/workspace/node_modules/minimist/index.js +263 -0
  706. package/workspace/node_modules/minimist/package.json +75 -0
  707. package/workspace/node_modules/minimist/test/all_bool.js +34 -0
  708. package/workspace/node_modules/minimist/test/bool.js +177 -0
  709. package/workspace/node_modules/minimist/test/dash.js +43 -0
  710. package/workspace/node_modules/minimist/test/default_bool.js +37 -0
  711. package/workspace/node_modules/minimist/test/dotted.js +24 -0
  712. package/workspace/node_modules/minimist/test/kv_short.js +32 -0
  713. package/workspace/node_modules/minimist/test/long.js +33 -0
  714. package/workspace/node_modules/minimist/test/num.js +38 -0
  715. package/workspace/node_modules/minimist/test/parse.js +209 -0
  716. package/workspace/node_modules/minimist/test/parse_modified.js +11 -0
  717. package/workspace/node_modules/minimist/test/proto.js +64 -0
  718. package/workspace/node_modules/minimist/test/short.js +69 -0
  719. package/workspace/node_modules/minimist/test/stop_early.js +17 -0
  720. package/workspace/node_modules/minimist/test/unknown.js +104 -0
  721. package/workspace/node_modules/minimist/test/whitespace.js +10 -0
  722. package/workspace/node_modules/mkdirp-classic/LICENSE +21 -0
  723. package/workspace/node_modules/mkdirp-classic/README.md +18 -0
  724. package/workspace/node_modules/mkdirp-classic/index.js +98 -0
  725. package/workspace/node_modules/mkdirp-classic/package.json +18 -0
  726. package/workspace/node_modules/ms/index.js +162 -0
  727. package/workspace/node_modules/ms/license.md +21 -0
  728. package/workspace/node_modules/ms/package.json +38 -0
  729. package/workspace/node_modules/ms/readme.md +59 -0
  730. package/workspace/node_modules/napi-build-utils/.github/workflows/run-npm-tests.yml +31 -0
  731. package/workspace/node_modules/napi-build-utils/LICENSE +21 -0
  732. package/workspace/node_modules/napi-build-utils/README.md +52 -0
  733. package/workspace/node_modules/napi-build-utils/index.js +214 -0
  734. package/workspace/node_modules/napi-build-utils/index.md +0 -0
  735. package/workspace/node_modules/napi-build-utils/package.json +42 -0
  736. package/workspace/node_modules/negotiator/HISTORY.md +114 -0
  737. package/workspace/node_modules/negotiator/LICENSE +24 -0
  738. package/workspace/node_modules/negotiator/README.md +212 -0
  739. package/workspace/node_modules/negotiator/index.js +83 -0
  740. package/workspace/node_modules/negotiator/lib/charset.js +169 -0
  741. package/workspace/node_modules/negotiator/lib/encoding.js +205 -0
  742. package/workspace/node_modules/negotiator/lib/language.js +179 -0
  743. package/workspace/node_modules/negotiator/lib/mediaType.js +294 -0
  744. package/workspace/node_modules/negotiator/package.json +43 -0
  745. package/workspace/node_modules/node-abi/LICENSE +21 -0
  746. package/workspace/node_modules/node-abi/README.md +54 -0
  747. package/workspace/node_modules/node-abi/abi_registry.json +439 -0
  748. package/workspace/node_modules/node-abi/index.js +179 -0
  749. package/workspace/node_modules/node-abi/package.json +45 -0
  750. package/workspace/node_modules/object-inspect/.eslintrc +53 -0
  751. package/workspace/node_modules/object-inspect/.github/FUNDING.yml +12 -0
  752. package/workspace/node_modules/object-inspect/.nycrc +13 -0
  753. package/workspace/node_modules/object-inspect/CHANGELOG.md +424 -0
  754. package/workspace/node_modules/object-inspect/LICENSE +21 -0
  755. package/workspace/node_modules/object-inspect/example/all.js +23 -0
  756. package/workspace/node_modules/object-inspect/example/circular.js +6 -0
  757. package/workspace/node_modules/object-inspect/example/fn.js +5 -0
  758. package/workspace/node_modules/object-inspect/example/inspect.js +10 -0
  759. package/workspace/node_modules/object-inspect/index.js +544 -0
  760. package/workspace/node_modules/object-inspect/package-support.json +20 -0
  761. package/workspace/node_modules/object-inspect/package.json +105 -0
  762. package/workspace/node_modules/object-inspect/readme.markdown +84 -0
  763. package/workspace/node_modules/object-inspect/test/bigint.js +58 -0
  764. package/workspace/node_modules/object-inspect/test/browser/dom.js +15 -0
  765. package/workspace/node_modules/object-inspect/test/circular.js +16 -0
  766. package/workspace/node_modules/object-inspect/test/deep.js +12 -0
  767. package/workspace/node_modules/object-inspect/test/element.js +53 -0
  768. package/workspace/node_modules/object-inspect/test/err.js +48 -0
  769. package/workspace/node_modules/object-inspect/test/fakes.js +29 -0
  770. package/workspace/node_modules/object-inspect/test/fn.js +76 -0
  771. package/workspace/node_modules/object-inspect/test/global.js +17 -0
  772. package/workspace/node_modules/object-inspect/test/has.js +15 -0
  773. package/workspace/node_modules/object-inspect/test/holes.js +15 -0
  774. package/workspace/node_modules/object-inspect/test/indent-option.js +271 -0
  775. package/workspace/node_modules/object-inspect/test/inspect.js +139 -0
  776. package/workspace/node_modules/object-inspect/test/lowbyte.js +12 -0
  777. package/workspace/node_modules/object-inspect/test/number.js +58 -0
  778. package/workspace/node_modules/object-inspect/test/quoteStyle.js +26 -0
  779. package/workspace/node_modules/object-inspect/test/toStringTag.js +40 -0
  780. package/workspace/node_modules/object-inspect/test/undef.js +12 -0
  781. package/workspace/node_modules/object-inspect/test/values.js +261 -0
  782. package/workspace/node_modules/object-inspect/test-core-js.js +26 -0
  783. package/workspace/node_modules/object-inspect/util.inspect.js +1 -0
  784. package/workspace/node_modules/on-finished/HISTORY.md +98 -0
  785. package/workspace/node_modules/on-finished/LICENSE +23 -0
  786. package/workspace/node_modules/on-finished/README.md +162 -0
  787. package/workspace/node_modules/on-finished/index.js +234 -0
  788. package/workspace/node_modules/on-finished/package.json +39 -0
  789. package/workspace/node_modules/once/LICENSE +15 -0
  790. package/workspace/node_modules/once/README.md +79 -0
  791. package/workspace/node_modules/once/once.js +42 -0
  792. package/workspace/node_modules/once/package.json +33 -0
  793. package/workspace/node_modules/parseurl/HISTORY.md +58 -0
  794. package/workspace/node_modules/parseurl/LICENSE +24 -0
  795. package/workspace/node_modules/parseurl/README.md +133 -0
  796. package/workspace/node_modules/parseurl/index.js +158 -0
  797. package/workspace/node_modules/parseurl/package.json +40 -0
  798. package/workspace/node_modules/path-to-regexp/LICENSE +21 -0
  799. package/workspace/node_modules/path-to-regexp/Readme.md +224 -0
  800. package/workspace/node_modules/path-to-regexp/dist/index.d.ts +147 -0
  801. package/workspace/node_modules/path-to-regexp/dist/index.js +426 -0
  802. package/workspace/node_modules/path-to-regexp/dist/index.js.map +1 -0
  803. package/workspace/node_modules/path-to-regexp/package.json +64 -0
  804. package/workspace/node_modules/prebuild-install/CHANGELOG.md +131 -0
  805. package/workspace/node_modules/prebuild-install/CONTRIBUTING.md +6 -0
  806. package/workspace/node_modules/prebuild-install/LICENSE +21 -0
  807. package/workspace/node_modules/prebuild-install/README.md +163 -0
  808. package/workspace/node_modules/prebuild-install/asset.js +44 -0
  809. package/workspace/node_modules/prebuild-install/bin.js +78 -0
  810. package/workspace/node_modules/prebuild-install/download.js +142 -0
  811. package/workspace/node_modules/prebuild-install/error.js +14 -0
  812. package/workspace/node_modules/prebuild-install/help.txt +16 -0
  813. package/workspace/node_modules/prebuild-install/index.js +1 -0
  814. package/workspace/node_modules/prebuild-install/log.js +33 -0
  815. package/workspace/node_modules/prebuild-install/package.json +67 -0
  816. package/workspace/node_modules/prebuild-install/proxy.js +35 -0
  817. package/workspace/node_modules/prebuild-install/rc.js +64 -0
  818. package/workspace/node_modules/prebuild-install/util.js +143 -0
  819. package/workspace/node_modules/proxy-addr/HISTORY.md +161 -0
  820. package/workspace/node_modules/proxy-addr/LICENSE +22 -0
  821. package/workspace/node_modules/proxy-addr/README.md +139 -0
  822. package/workspace/node_modules/proxy-addr/index.js +327 -0
  823. package/workspace/node_modules/proxy-addr/package.json +47 -0
  824. package/workspace/node_modules/pump/.github/FUNDING.yml +2 -0
  825. package/workspace/node_modules/pump/.travis.yml +5 -0
  826. package/workspace/node_modules/pump/LICENSE +21 -0
  827. package/workspace/node_modules/pump/README.md +74 -0
  828. package/workspace/node_modules/pump/SECURITY.md +5 -0
  829. package/workspace/node_modules/pump/empty.js +1 -0
  830. package/workspace/node_modules/pump/index.js +86 -0
  831. package/workspace/node_modules/pump/package.json +30 -0
  832. package/workspace/node_modules/pump/test-browser.js +66 -0
  833. package/workspace/node_modules/pump/test-node.js +53 -0
  834. package/workspace/node_modules/qs/.editorconfig +46 -0
  835. package/workspace/node_modules/qs/.github/FUNDING.yml +12 -0
  836. package/workspace/node_modules/qs/.github/SECURITY.md +11 -0
  837. package/workspace/node_modules/qs/.github/THREAT_MODEL.md +78 -0
  838. package/workspace/node_modules/qs/.nycrc +13 -0
  839. package/workspace/node_modules/qs/CHANGELOG.md +806 -0
  840. package/workspace/node_modules/qs/LICENSE.md +29 -0
  841. package/workspace/node_modules/qs/README.md +758 -0
  842. package/workspace/node_modules/qs/dist/qs.js +141 -0
  843. package/workspace/node_modules/qs/eslint.config.mjs +56 -0
  844. package/workspace/node_modules/qs/lib/formats.js +23 -0
  845. package/workspace/node_modules/qs/lib/index.js +11 -0
  846. package/workspace/node_modules/qs/lib/parse.js +373 -0
  847. package/workspace/node_modules/qs/lib/stringify.js +356 -0
  848. package/workspace/node_modules/qs/lib/utils.js +342 -0
  849. package/workspace/node_modules/qs/package.json +94 -0
  850. package/workspace/node_modules/qs/test/empty-keys-cases.js +267 -0
  851. package/workspace/node_modules/qs/test/parse.js +1568 -0
  852. package/workspace/node_modules/qs/test/stringify.js +1310 -0
  853. package/workspace/node_modules/qs/test/utils.js +404 -0
  854. package/workspace/node_modules/range-parser/HISTORY.md +56 -0
  855. package/workspace/node_modules/range-parser/LICENSE +23 -0
  856. package/workspace/node_modules/range-parser/README.md +84 -0
  857. package/workspace/node_modules/range-parser/index.js +162 -0
  858. package/workspace/node_modules/range-parser/package.json +44 -0
  859. package/workspace/node_modules/raw-body/LICENSE +22 -0
  860. package/workspace/node_modules/raw-body/README.md +223 -0
  861. package/workspace/node_modules/raw-body/index.d.ts +85 -0
  862. package/workspace/node_modules/raw-body/index.js +336 -0
  863. package/workspace/node_modules/raw-body/package.json +46 -0
  864. package/workspace/node_modules/rc/LICENSE.APACHE2 +15 -0
  865. package/workspace/node_modules/rc/LICENSE.BSD +26 -0
  866. package/workspace/node_modules/rc/LICENSE.MIT +24 -0
  867. package/workspace/node_modules/rc/README.md +227 -0
  868. package/workspace/node_modules/rc/browser.js +7 -0
  869. package/workspace/node_modules/rc/cli.js +4 -0
  870. package/workspace/node_modules/rc/index.js +53 -0
  871. package/workspace/node_modules/rc/lib/utils.js +104 -0
  872. package/workspace/node_modules/rc/package.json +29 -0
  873. package/workspace/node_modules/rc/test/ini.js +16 -0
  874. package/workspace/node_modules/rc/test/nested-env-vars.js +50 -0
  875. package/workspace/node_modules/rc/test/test.js +59 -0
  876. package/workspace/node_modules/readable-stream/CONTRIBUTING.md +38 -0
  877. package/workspace/node_modules/readable-stream/GOVERNANCE.md +136 -0
  878. package/workspace/node_modules/readable-stream/LICENSE +47 -0
  879. package/workspace/node_modules/readable-stream/README.md +106 -0
  880. package/workspace/node_modules/readable-stream/errors-browser.js +127 -0
  881. package/workspace/node_modules/readable-stream/errors.js +116 -0
  882. package/workspace/node_modules/readable-stream/experimentalWarning.js +17 -0
  883. package/workspace/node_modules/readable-stream/lib/_stream_duplex.js +126 -0
  884. package/workspace/node_modules/readable-stream/lib/_stream_passthrough.js +37 -0
  885. package/workspace/node_modules/readable-stream/lib/_stream_readable.js +1027 -0
  886. package/workspace/node_modules/readable-stream/lib/_stream_transform.js +190 -0
  887. package/workspace/node_modules/readable-stream/lib/_stream_writable.js +641 -0
  888. package/workspace/node_modules/readable-stream/lib/internal/streams/async_iterator.js +180 -0
  889. package/workspace/node_modules/readable-stream/lib/internal/streams/buffer_list.js +183 -0
  890. package/workspace/node_modules/readable-stream/lib/internal/streams/destroy.js +96 -0
  891. package/workspace/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +86 -0
  892. package/workspace/node_modules/readable-stream/lib/internal/streams/from-browser.js +3 -0
  893. package/workspace/node_modules/readable-stream/lib/internal/streams/from.js +52 -0
  894. package/workspace/node_modules/readable-stream/lib/internal/streams/pipeline.js +86 -0
  895. package/workspace/node_modules/readable-stream/lib/internal/streams/state.js +22 -0
  896. package/workspace/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
  897. package/workspace/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
  898. package/workspace/node_modules/readable-stream/package.json +68 -0
  899. package/workspace/node_modules/readable-stream/readable-browser.js +9 -0
  900. package/workspace/node_modules/readable-stream/readable.js +16 -0
  901. package/workspace/node_modules/router/HISTORY.md +228 -0
  902. package/workspace/node_modules/router/LICENSE +23 -0
  903. package/workspace/node_modules/router/README.md +416 -0
  904. package/workspace/node_modules/router/index.js +748 -0
  905. package/workspace/node_modules/router/lib/layer.js +247 -0
  906. package/workspace/node_modules/router/lib/route.js +242 -0
  907. package/workspace/node_modules/router/package.json +44 -0
  908. package/workspace/node_modules/safe-buffer/LICENSE +21 -0
  909. package/workspace/node_modules/safe-buffer/README.md +584 -0
  910. package/workspace/node_modules/safe-buffer/index.d.ts +187 -0
  911. package/workspace/node_modules/safe-buffer/index.js +65 -0
  912. package/workspace/node_modules/safe-buffer/package.json +51 -0
  913. package/workspace/node_modules/safer-buffer/LICENSE +21 -0
  914. package/workspace/node_modules/safer-buffer/Porting-Buffer.md +268 -0
  915. package/workspace/node_modules/safer-buffer/Readme.md +156 -0
  916. package/workspace/node_modules/safer-buffer/dangerous.js +58 -0
  917. package/workspace/node_modules/safer-buffer/package.json +34 -0
  918. package/workspace/node_modules/safer-buffer/safer.js +77 -0
  919. package/workspace/node_modules/safer-buffer/tests.js +406 -0
  920. package/workspace/node_modules/semver/LICENSE +15 -0
  921. package/workspace/node_modules/semver/README.md +665 -0
  922. package/workspace/node_modules/semver/bin/semver.js +191 -0
  923. package/workspace/node_modules/semver/classes/comparator.js +143 -0
  924. package/workspace/node_modules/semver/classes/index.js +7 -0
  925. package/workspace/node_modules/semver/classes/range.js +557 -0
  926. package/workspace/node_modules/semver/classes/semver.js +333 -0
  927. package/workspace/node_modules/semver/functions/clean.js +8 -0
  928. package/workspace/node_modules/semver/functions/cmp.js +54 -0
  929. package/workspace/node_modules/semver/functions/coerce.js +62 -0
  930. package/workspace/node_modules/semver/functions/compare-build.js +9 -0
  931. package/workspace/node_modules/semver/functions/compare-loose.js +5 -0
  932. package/workspace/node_modules/semver/functions/compare.js +7 -0
  933. package/workspace/node_modules/semver/functions/diff.js +60 -0
  934. package/workspace/node_modules/semver/functions/eq.js +5 -0
  935. package/workspace/node_modules/semver/functions/gt.js +5 -0
  936. package/workspace/node_modules/semver/functions/gte.js +5 -0
  937. package/workspace/node_modules/semver/functions/inc.js +21 -0
  938. package/workspace/node_modules/semver/functions/lt.js +5 -0
  939. package/workspace/node_modules/semver/functions/lte.js +5 -0
  940. package/workspace/node_modules/semver/functions/major.js +5 -0
  941. package/workspace/node_modules/semver/functions/minor.js +5 -0
  942. package/workspace/node_modules/semver/functions/neq.js +5 -0
  943. package/workspace/node_modules/semver/functions/parse.js +18 -0
  944. package/workspace/node_modules/semver/functions/patch.js +5 -0
  945. package/workspace/node_modules/semver/functions/prerelease.js +8 -0
  946. package/workspace/node_modules/semver/functions/rcompare.js +5 -0
  947. package/workspace/node_modules/semver/functions/rsort.js +5 -0
  948. package/workspace/node_modules/semver/functions/satisfies.js +12 -0
  949. package/workspace/node_modules/semver/functions/sort.js +5 -0
  950. package/workspace/node_modules/semver/functions/valid.js +8 -0
  951. package/workspace/node_modules/semver/index.js +91 -0
  952. package/workspace/node_modules/semver/internal/constants.js +37 -0
  953. package/workspace/node_modules/semver/internal/debug.js +11 -0
  954. package/workspace/node_modules/semver/internal/identifiers.js +29 -0
  955. package/workspace/node_modules/semver/internal/lrucache.js +42 -0
  956. package/workspace/node_modules/semver/internal/parse-options.js +17 -0
  957. package/workspace/node_modules/semver/internal/re.js +223 -0
  958. package/workspace/node_modules/semver/package.json +78 -0
  959. package/workspace/node_modules/semver/preload.js +4 -0
  960. package/workspace/node_modules/semver/range.bnf +16 -0
  961. package/workspace/node_modules/semver/ranges/gtr.js +6 -0
  962. package/workspace/node_modules/semver/ranges/intersects.js +9 -0
  963. package/workspace/node_modules/semver/ranges/ltr.js +6 -0
  964. package/workspace/node_modules/semver/ranges/max-satisfying.js +27 -0
  965. package/workspace/node_modules/semver/ranges/min-satisfying.js +26 -0
  966. package/workspace/node_modules/semver/ranges/min-version.js +63 -0
  967. package/workspace/node_modules/semver/ranges/outside.js +82 -0
  968. package/workspace/node_modules/semver/ranges/simplify.js +49 -0
  969. package/workspace/node_modules/semver/ranges/subset.js +249 -0
  970. package/workspace/node_modules/semver/ranges/to-comparators.js +10 -0
  971. package/workspace/node_modules/semver/ranges/valid.js +13 -0
  972. package/workspace/node_modules/send/LICENSE +23 -0
  973. package/workspace/node_modules/send/README.md +317 -0
  974. package/workspace/node_modules/send/index.js +997 -0
  975. package/workspace/node_modules/send/package.json +63 -0
  976. package/workspace/node_modules/serve-static/LICENSE +25 -0
  977. package/workspace/node_modules/serve-static/README.md +253 -0
  978. package/workspace/node_modules/serve-static/index.js +208 -0
  979. package/workspace/node_modules/serve-static/package.json +44 -0
  980. package/workspace/node_modules/setprototypeof/LICENSE +13 -0
  981. package/workspace/node_modules/setprototypeof/README.md +31 -0
  982. package/workspace/node_modules/setprototypeof/index.d.ts +2 -0
  983. package/workspace/node_modules/setprototypeof/index.js +17 -0
  984. package/workspace/node_modules/setprototypeof/package.json +38 -0
  985. package/workspace/node_modules/setprototypeof/test/index.js +24 -0
  986. package/workspace/node_modules/side-channel/.editorconfig +9 -0
  987. package/workspace/node_modules/side-channel/.eslintrc +12 -0
  988. package/workspace/node_modules/side-channel/.github/FUNDING.yml +12 -0
  989. package/workspace/node_modules/side-channel/.nycrc +13 -0
  990. package/workspace/node_modules/side-channel/CHANGELOG.md +110 -0
  991. package/workspace/node_modules/side-channel/LICENSE +21 -0
  992. package/workspace/node_modules/side-channel/README.md +61 -0
  993. package/workspace/node_modules/side-channel/index.d.ts +14 -0
  994. package/workspace/node_modules/side-channel/index.js +43 -0
  995. package/workspace/node_modules/side-channel/package.json +85 -0
  996. package/workspace/node_modules/side-channel/test/index.js +104 -0
  997. package/workspace/node_modules/side-channel/tsconfig.json +9 -0
  998. package/workspace/node_modules/side-channel-list/.editorconfig +9 -0
  999. package/workspace/node_modules/side-channel-list/.eslintrc +11 -0
  1000. package/workspace/node_modules/side-channel-list/.github/FUNDING.yml +12 -0
  1001. package/workspace/node_modules/side-channel-list/.nycrc +13 -0
  1002. package/workspace/node_modules/side-channel-list/CHANGELOG.md +15 -0
  1003. package/workspace/node_modules/side-channel-list/LICENSE +21 -0
  1004. package/workspace/node_modules/side-channel-list/README.md +62 -0
  1005. package/workspace/node_modules/side-channel-list/index.d.ts +13 -0
  1006. package/workspace/node_modules/side-channel-list/index.js +113 -0
  1007. package/workspace/node_modules/side-channel-list/list.d.ts +14 -0
  1008. package/workspace/node_modules/side-channel-list/package.json +77 -0
  1009. package/workspace/node_modules/side-channel-list/test/index.js +104 -0
  1010. package/workspace/node_modules/side-channel-list/tsconfig.json +9 -0
  1011. package/workspace/node_modules/side-channel-map/.editorconfig +9 -0
  1012. package/workspace/node_modules/side-channel-map/.eslintrc +11 -0
  1013. package/workspace/node_modules/side-channel-map/.github/FUNDING.yml +12 -0
  1014. package/workspace/node_modules/side-channel-map/.nycrc +13 -0
  1015. package/workspace/node_modules/side-channel-map/CHANGELOG.md +22 -0
  1016. package/workspace/node_modules/side-channel-map/LICENSE +21 -0
  1017. package/workspace/node_modules/side-channel-map/README.md +62 -0
  1018. package/workspace/node_modules/side-channel-map/index.d.ts +15 -0
  1019. package/workspace/node_modules/side-channel-map/index.js +68 -0
  1020. package/workspace/node_modules/side-channel-map/package.json +80 -0
  1021. package/workspace/node_modules/side-channel-map/test/index.js +114 -0
  1022. package/workspace/node_modules/side-channel-map/tsconfig.json +9 -0
  1023. package/workspace/node_modules/side-channel-weakmap/.editorconfig +9 -0
  1024. package/workspace/node_modules/side-channel-weakmap/.eslintrc +12 -0
  1025. package/workspace/node_modules/side-channel-weakmap/.github/FUNDING.yml +12 -0
  1026. package/workspace/node_modules/side-channel-weakmap/.nycrc +13 -0
  1027. package/workspace/node_modules/side-channel-weakmap/CHANGELOG.md +28 -0
  1028. package/workspace/node_modules/side-channel-weakmap/LICENSE +21 -0
  1029. package/workspace/node_modules/side-channel-weakmap/README.md +62 -0
  1030. package/workspace/node_modules/side-channel-weakmap/index.d.ts +15 -0
  1031. package/workspace/node_modules/side-channel-weakmap/index.js +84 -0
  1032. package/workspace/node_modules/side-channel-weakmap/package.json +87 -0
  1033. package/workspace/node_modules/side-channel-weakmap/test/index.js +114 -0
  1034. package/workspace/node_modules/side-channel-weakmap/tsconfig.json +9 -0
  1035. package/workspace/node_modules/simple-concat/.travis.yml +3 -0
  1036. package/workspace/node_modules/simple-concat/LICENSE +20 -0
  1037. package/workspace/node_modules/simple-concat/README.md +44 -0
  1038. package/workspace/node_modules/simple-concat/index.js +15 -0
  1039. package/workspace/node_modules/simple-concat/package.json +47 -0
  1040. package/workspace/node_modules/simple-concat/test/basic.js +41 -0
  1041. package/workspace/node_modules/simple-get/.github/dependabot.yml +15 -0
  1042. package/workspace/node_modules/simple-get/.github/workflows/ci.yml +23 -0
  1043. package/workspace/node_modules/simple-get/LICENSE +20 -0
  1044. package/workspace/node_modules/simple-get/README.md +333 -0
  1045. package/workspace/node_modules/simple-get/index.js +108 -0
  1046. package/workspace/node_modules/simple-get/package.json +67 -0
  1047. package/workspace/node_modules/statuses/HISTORY.md +87 -0
  1048. package/workspace/node_modules/statuses/LICENSE +23 -0
  1049. package/workspace/node_modules/statuses/README.md +139 -0
  1050. package/workspace/node_modules/statuses/codes.json +65 -0
  1051. package/workspace/node_modules/statuses/index.js +146 -0
  1052. package/workspace/node_modules/statuses/package.json +49 -0
  1053. package/workspace/node_modules/string_decoder/LICENSE +48 -0
  1054. package/workspace/node_modules/string_decoder/README.md +47 -0
  1055. package/workspace/node_modules/string_decoder/lib/string_decoder.js +296 -0
  1056. package/workspace/node_modules/string_decoder/package.json +34 -0
  1057. package/workspace/node_modules/strip-json-comments/index.js +70 -0
  1058. package/workspace/node_modules/strip-json-comments/license +21 -0
  1059. package/workspace/node_modules/strip-json-comments/package.json +42 -0
  1060. package/workspace/node_modules/strip-json-comments/readme.md +64 -0
  1061. package/workspace/node_modules/tar-fs/.travis.yml +6 -0
  1062. package/workspace/node_modules/tar-fs/LICENSE +21 -0
  1063. package/workspace/node_modules/tar-fs/README.md +165 -0
  1064. package/workspace/node_modules/tar-fs/index.js +363 -0
  1065. package/workspace/node_modules/tar-fs/package.json +41 -0
  1066. package/workspace/node_modules/tar-fs/test/fixtures/a/hello.txt +1 -0
  1067. package/workspace/node_modules/tar-fs/test/fixtures/b/a/test.txt +1 -0
  1068. package/workspace/node_modules/tar-fs/test/fixtures/d/file1 +0 -0
  1069. package/workspace/node_modules/tar-fs/test/fixtures/d/file2 +0 -0
  1070. package/workspace/node_modules/tar-fs/test/fixtures/d/sub-dir/file5 +0 -0
  1071. package/workspace/node_modules/tar-fs/test/fixtures/d/sub-files/file3 +0 -0
  1072. package/workspace/node_modules/tar-fs/test/fixtures/d/sub-files/file4 +0 -0
  1073. package/workspace/node_modules/tar-fs/test/fixtures/e/directory/.ignore +0 -0
  1074. package/workspace/node_modules/tar-fs/test/fixtures/e/file +0 -0
  1075. package/workspace/node_modules/tar-fs/test/fixtures/invalid.tar +0 -0
  1076. package/workspace/node_modules/tar-fs/test/index.js +346 -0
  1077. package/workspace/node_modules/tar-stream/LICENSE +21 -0
  1078. package/workspace/node_modules/tar-stream/README.md +168 -0
  1079. package/workspace/node_modules/tar-stream/extract.js +257 -0
  1080. package/workspace/node_modules/tar-stream/headers.js +295 -0
  1081. package/workspace/node_modules/tar-stream/index.js +2 -0
  1082. package/workspace/node_modules/tar-stream/pack.js +255 -0
  1083. package/workspace/node_modules/tar-stream/package.json +58 -0
  1084. package/workspace/node_modules/tar-stream/sandbox.js +11 -0
  1085. package/workspace/node_modules/toidentifier/HISTORY.md +9 -0
  1086. package/workspace/node_modules/toidentifier/LICENSE +21 -0
  1087. package/workspace/node_modules/toidentifier/README.md +61 -0
  1088. package/workspace/node_modules/toidentifier/index.js +32 -0
  1089. package/workspace/node_modules/toidentifier/package.json +38 -0
  1090. package/workspace/node_modules/tunnel-agent/LICENSE +55 -0
  1091. package/workspace/node_modules/tunnel-agent/README.md +4 -0
  1092. package/workspace/node_modules/tunnel-agent/index.js +244 -0
  1093. package/workspace/node_modules/tunnel-agent/package.json +22 -0
  1094. package/workspace/node_modules/type-is/HISTORY.md +292 -0
  1095. package/workspace/node_modules/type-is/LICENSE +23 -0
  1096. package/workspace/node_modules/type-is/README.md +198 -0
  1097. package/workspace/node_modules/type-is/index.js +250 -0
  1098. package/workspace/node_modules/type-is/package.json +47 -0
  1099. package/workspace/node_modules/unpipe/HISTORY.md +4 -0
  1100. package/workspace/node_modules/unpipe/LICENSE +22 -0
  1101. package/workspace/node_modules/unpipe/README.md +43 -0
  1102. package/workspace/node_modules/unpipe/index.js +69 -0
  1103. package/workspace/node_modules/unpipe/package.json +27 -0
  1104. package/workspace/node_modules/util-deprecate/History.md +16 -0
  1105. package/workspace/node_modules/util-deprecate/LICENSE +24 -0
  1106. package/workspace/node_modules/util-deprecate/README.md +53 -0
  1107. package/workspace/node_modules/util-deprecate/browser.js +67 -0
  1108. package/workspace/node_modules/util-deprecate/node.js +6 -0
  1109. package/workspace/node_modules/util-deprecate/package.json +27 -0
  1110. package/workspace/node_modules/vary/HISTORY.md +39 -0
  1111. package/workspace/node_modules/vary/LICENSE +22 -0
  1112. package/workspace/node_modules/vary/README.md +101 -0
  1113. package/workspace/node_modules/vary/index.js +149 -0
  1114. package/workspace/node_modules/vary/package.json +43 -0
  1115. package/workspace/node_modules/wrappy/LICENSE +15 -0
  1116. package/workspace/node_modules/wrappy/README.md +36 -0
  1117. package/workspace/node_modules/wrappy/package.json +29 -0
  1118. package/workspace/node_modules/wrappy/wrappy.js +33 -0
  1119. package/workspace/package.json +9 -0
  1120. package/workspace/skills/backup skills/whatsapp/.claude-plugin/plugin.json +6 -0
  1121. package/workspace/skills/backup skills/whatsapp/SKILL.md +119 -0
  1122. package/workspace/skills/backup skills/whatsapp/skill.json +11 -0
  1123. package/workspace/skills/backup skills/whatsapp-clinic-secretary/.claude-plugin/plugin.json +6 -0
  1124. package/workspace/skills/backup skills/whatsapp-clinic-secretary/SCRIPT.md +154 -0
  1125. package/workspace/skills/backup skills/whatsapp-clinic-secretary/SKILL.md +132 -0
  1126. package/workspace/skills/backup skills/whatsapp-clinic-secretary/skill.json +12 -0
  1127. package/workspace/skills/backup skills/whatsapp-support/SCRIPT.md +172 -0
  1128. package/workspace/skills/backup skills/whatsapp-support/SKILL.md +21 -0
@@ -0,0 +1,2353 @@
1
+ import { useState, useRef, useEffect, type KeyboardEvent } from 'react';
2
+ import { ArrowRight, ArrowLeft, LoaderCircle, ExternalLink, ClipboardPaste, RefreshCw, Check, ChevronDown, Mic, Eye, EyeOff, Shield, ShieldCheck, ShieldOff, Copy, Smartphone, Globe, Wifi, WifiOff, TriangleAlert } from 'lucide-react';
3
+ import { motion, AnimatePresence } from 'framer-motion';
4
+
5
+ /* ── Access detection ── */
6
+
7
+ type AccessMethod = 'tailscale' | 'lan' | 'localhost' | 'tunnel' | 'relay' | 'custom-domain';
8
+
9
+ function detectAccessMethod(hostname: string): AccessMethod {
10
+ // Tailscale CGNAT range: 100.64.0.0 – 100.127.255.255
11
+ const tailscaleMatch = hostname.match(/^100\.(\d+)\./);
12
+ if (tailscaleMatch && +tailscaleMatch[1] >= 64 && +tailscaleMatch[1] <= 127) return 'tailscale';
13
+
14
+ // LAN ranges
15
+ if (/^192\.168\./.test(hostname) || /^10\./.test(hostname)) return 'lan';
16
+ const m172 = hostname.match(/^172\.(\d+)\./);
17
+ if (m172 && +m172[1] >= 16 && +m172[1] <= 31) return 'lan';
18
+
19
+ // Localhost
20
+ if (hostname === 'localhost' || hostname === '127.0.0.1') return 'localhost';
21
+
22
+ // Cloudflare quick tunnel
23
+ if (hostname.endsWith('.trycloudflare.com')) return 'tunnel';
24
+
25
+ // Relay domain
26
+ if (hostname.endsWith('.bloby.bot')) return 'relay';
27
+
28
+ // Anything else is a custom domain (named tunnel)
29
+ return 'custom-domain';
30
+ }
31
+
32
+ function isPrivateAccess(method: AccessMethod): boolean {
33
+ return method === 'tailscale' || method === 'lan' || method === 'localhost';
34
+ }
35
+
36
+ const ACCESS_LABELS: Record<AccessMethod, string> = {
37
+ tailscale: 'Tailscale',
38
+ lan: 'Local network',
39
+ localhost: 'Localhost',
40
+ tunnel: 'Cloudflare tunnel',
41
+ relay: 'Relay',
42
+ 'custom-domain': 'Custom domain',
43
+ };
44
+
45
+ /* ── Provider config ── */
46
+
47
+ const PROVIDERS = [
48
+ { id: 'anthropic', name: 'Claude', subtitle: 'by Anthropic', icon: '/icons/claude.png' },
49
+ { id: 'openai', name: 'OpenAI Codex', subtitle: 'ChatGPT Plus / Pro', icon: '/icons/codex.png' },
50
+ ] as const;
51
+
52
+ const MODELS: Record<string, { id: string; label: string }[]> = {
53
+ anthropic: [
54
+ { id: 'claude-opus-4-6', label: 'Opus 4.6' },
55
+ { id: 'claude-sonnet-4-6', label: 'Sonnet 4.6' },
56
+ { id: 'claude-haiku-4-5-20251001', label: 'Haiku 4.5' },
57
+ ],
58
+ openai: [
59
+ { id: 'gpt-5.2-codex:medium', label: 'GPT-5.2 Codex Medium' },
60
+ { id: 'gpt-5.2-codex:high', label: 'GPT-5.2 Codex High' },
61
+ { id: 'gpt-5.2-codex:xhigh', label: 'GPT-5.2 Codex Extra High' },
62
+ { id: 'gpt-5.3-codex:medium', label: 'GPT-5.3 Codex Medium (Pro)' },
63
+ { id: 'gpt-5.3-codex:high', label: 'GPT-5.3 Codex High (Pro)' },
64
+ { id: 'gpt-5.3-codex:xhigh', label: 'GPT-5.3 Codex Extra High (Pro)' },
65
+ ],
66
+ };
67
+
68
+ // TOTAL_STEPS is dynamic — set inside the component based on isInitialSetup
69
+
70
+ const HANDLES = [
71
+ { tier: 'at', prefix: 'my.bloby.bot/', label: (n: string) => `my.bloby.bot/${n}`, badge: 'Free', badgeCls: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20', highlight: false },
72
+ { tier: 'premium', prefix: 'bloby.bot/', label: (n: string) => `bloby.bot/${n}`, badge: '$5', badgeCls: 'bg-[#AF27E3]/15 text-[#AF27E3] border-[#AF27E3]/20', highlight: true },
73
+ ] as const;
74
+
75
+ /* ── Dropdown ── */
76
+
77
+ function ModelDropdown({ models, value, onChange }: { models: { id: string; label: string }[]; value: string; onChange: (id: string) => void }) {
78
+ const [open, setOpen] = useState(false);
79
+ const ref = useRef<HTMLDivElement>(null);
80
+
81
+ useEffect(() => {
82
+ if (!open) return;
83
+ const handler = (e: MouseEvent) => {
84
+ if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
85
+ };
86
+ document.addEventListener('mousedown', handler);
87
+ return () => document.removeEventListener('mousedown', handler);
88
+ }, [open]);
89
+
90
+ const selected = models.find((m) => m.id === value);
91
+
92
+ return (
93
+ <div className="relative" ref={ref}>
94
+ <button
95
+ type="button"
96
+ onClick={() => setOpen((o) => !o)}
97
+ className="w-full flex items-center justify-between bg-white/[0.03] border border-white/[0.08] text-white rounded-xl px-4 py-2.5 text-[13px] outline-none hover:border-white/15 focus:border-[#AF27E3]/30 transition-colors"
98
+ >
99
+ <span className={selected ? 'text-white' : 'text-white/20'}>
100
+ {selected ? selected.label : 'Choose a model...'}
101
+ </span>
102
+ <ChevronDown className={`h-4 w-4 text-white/30 transition-transform ${open ? 'rotate-180' : ''}`} />
103
+ </button>
104
+ {open && (
105
+ <div className="absolute left-0 right-0 top-full mt-1 bg-[#222] border border-white/[0.08] rounded-xl shadow-xl py-1 z-10 max-h-48 overflow-y-auto">
106
+ {models.map((m) => (
107
+ <button
108
+ key={m.id}
109
+ onClick={() => { onChange(m.id); setOpen(false); }}
110
+ className={`w-full text-left px-4 py-2 text-[13px] transition-colors ${
111
+ value === m.id
112
+ ? 'text-[#AF27E3] bg-[#AF27E3]/10'
113
+ : 'text-white/70 hover:bg-white/[0.04] hover:text-white'
114
+ }`}
115
+ >
116
+ {m.label}
117
+ </button>
118
+ ))}
119
+ </div>
120
+ )}
121
+ </div>
122
+ );
123
+ }
124
+
125
+ /* ── Component ── */
126
+
127
+ interface Props {
128
+ onComplete: () => void;
129
+ isInitialSetup?: boolean;
130
+ onSave?: (payload: any) => Promise<any>;
131
+ onTunnelSwitch?: (newMode: 'off' | 'quick') => Promise<any>;
132
+ }
133
+
134
+ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSave, onTunnelSwitch }: Props) {
135
+ const TOTAL_STEPS = isInitialSetup ? 7 : 6; // 0..5 normal, +step 6 "All Set" for initial
136
+
137
+ const [step, setStep] = useState(0);
138
+ const [userName, setUserName] = useState('');
139
+ const [provider, setProvider] = useState('anthropic');
140
+ const [model, setModel] = useState('');
141
+ const [saving, setSaving] = useState(false);
142
+
143
+ // Auth state per provider
144
+ const [authState, setAuthState] = useState<Record<string, 'idle' | 'authenticating' | 'connected'>>({
145
+ anthropic: 'idle',
146
+ openai: 'idle',
147
+ });
148
+
149
+ // Anthropic/Claude-specific
150
+ const [oauthStarted, setOauthStarted] = useState(false);
151
+ const [anthropicCode, setAnthropicCode] = useState('');
152
+ const [isExchanging, setIsExchanging] = useState(false);
153
+ const [anthropicError, setAnthropicError] = useState<string | undefined>();
154
+ const [anthropicChecking, setAnthropicChecking] = useState(false);
155
+
156
+ // OpenAI/Codex-specific
157
+ const [openaiWaiting, setOpenaiWaiting] = useState(false);
158
+ const [openaiError, setOpenaiError] = useState<string | undefined>();
159
+
160
+ // Bot name + Handle (step 2)
161
+ const [botName, setBotName] = useState('');
162
+ const [handleStatus, setHandleStatus] = useState<null | 'checking' | 'ready' | 'invalid'>(null);
163
+ const [handleError, setHandleError] = useState('');
164
+ const [tierAvailability, setTierAvailability] = useState<Record<string, boolean>>({});
165
+ const [selectedTier, setSelectedTier] = useState('');
166
+ const [registering, setRegistering] = useState(false);
167
+ const [registered, setRegistered] = useState(false);
168
+ const [registeredUrl, setRegisteredUrl] = useState('');
169
+ const handleDebounce = useRef<ReturnType<typeof setTimeout> | null>(null);
170
+
171
+ // Tunnel mode (step 2 branching)
172
+ const [tunnelMode, setTunnelMode] = useState<'quick' | 'named' | 'off'>('quick');
173
+ const [tunnelDomain, setTunnelDomain] = useState('');
174
+ const [tunnelUrl, setTunnelUrl] = useState('');
175
+ const [handleChoice, setHandleChoice] = useState<'tunnel' | 'relay'>('relay');
176
+
177
+ // Existing handle (for re-run / change flow)
178
+ const [existingHandle, setExistingHandle] = useState<{ username: string; tier: string; url: string } | null>(null);
179
+ const [showChangeConfirm, setShowChangeConfirm] = useState(false);
180
+ const [changingHandle, setChangingHandle] = useState(false);
181
+
182
+ // Reserved handle claim flow
183
+ const [tierReserved, setTierReserved] = useState<Record<string, boolean>>({});
184
+ const [showClaimInput, setShowClaimInput] = useState(false);
185
+ const [claimCode, setClaimCode] = useState('');
186
+ const [claimError, setClaimError] = useState('');
187
+ const [claiming, setClaiming] = useState(false);
188
+
189
+ // Access detection + Tailscale-only switch
190
+ const [accessMethod, setAccessMethod] = useState<AccessMethod>('tunnel');
191
+ const [showTailscaleSwitch, setShowTailscaleSwitch] = useState(false);
192
+ const [tailscaleConfirmText, setTailscaleConfirmText] = useState('');
193
+ const [tailscaleSwitching, setTailscaleSwitching] = useState(false);
194
+ const [tailscaleSwitchError, setTailscaleSwitchError] = useState('');
195
+ const [showReEnableTunnel, setShowReEnableTunnel] = useState(false);
196
+ const [reEnabling, setReEnabling] = useState(false);
197
+ const [reEnableError, setReEnableError] = useState('');
198
+
199
+ // Portal credentials (step 3)
200
+ const [portalUser, setPortalUser] = useState('admin');
201
+ const [portalPass, setPortalPass] = useState('');
202
+ const [portalPassConfirm, setPortalPassConfirm] = useState('');
203
+ const [portalCopied, setPortalCopied] = useState(false);
204
+ const [portalExists, setPortalExists] = useState(false);
205
+ const [showPassAllSet, setShowPassAllSet] = useState(false);
206
+
207
+ // Portal old password (for changing existing credentials)
208
+ const [portalOldPass, setPortalOldPass] = useState('');
209
+ const [portalOldPassError, setPortalOldPassError] = useState('');
210
+ const [portalOldPassVerified, setPortalOldPassVerified] = useState(false);
211
+ const [portalVerifying, setPortalVerifying] = useState(false);
212
+
213
+ // Whisper (step 5)
214
+ const [whisperEnabled, setWhisperEnabled] = useState(false);
215
+ const [whisperKey, setWhisperKey] = useState('');
216
+
217
+ // TOTP 2FA (step 3)
218
+ const [totpEnabled, setTotpEnabled] = useState(false);
219
+ const [totpSecret, setTotpSecret] = useState('');
220
+ const [totpQrUri, setTotpQrUri] = useState('');
221
+ const [totpOtpauthUri, setTotpOtpauthUri] = useState('');
222
+ const [totpCode, setTotpCode] = useState('');
223
+ const [totpVerified, setTotpVerified] = useState(false);
224
+ const [totpVerifying, setTotpVerifying] = useState(false);
225
+ const [totpError, setTotpError] = useState('');
226
+ const [recoveryCodes, setRecoveryCodes] = useState<string[]>([]);
227
+ const [recoveryCodesCopied, setRecoveryCodesCopied] = useState(false);
228
+ const [totpDisabling, setTotpDisabling] = useState(false);
229
+ const [totpDisableCode, setTotpDisableCode] = useState('');
230
+ const [totpDisableError, setTotpDisableError] = useState('');
231
+ const [isMobileDevice, setIsMobileDevice] = useState(false);
232
+ // Sub-phase within step 3: 'password' | 'totp-setup' | 'recovery'
233
+ const [step3Phase, setStep3Phase] = useState<'password' | 'totp-setup' | 'recovery'>('password');
234
+
235
+ // Clipboard feedback for TOTP secret copy
236
+ const [totpSecretCopied, setTotpSecretCopied] = useState(false);
237
+
238
+ // Pre-fill guard
239
+ const prefillDone = useRef(false);
240
+
241
+ const isConnected = authState[provider] === 'connected';
242
+
243
+ // Persist/restore TOTP setup state across page reloads (mobile: OS suspends PWA on app-switch)
244
+ const TOTP_STORAGE_KEY = 'bloby_totp_setup';
245
+
246
+ function saveTotpState() {
247
+ try {
248
+ sessionStorage.setItem(TOTP_STORAGE_KEY, JSON.stringify({
249
+ secret: totpSecret, qrUri: totpQrUri, otpauthUri: totpOtpauthUri, phase: step3Phase,
250
+ // Also persist password fields so user doesn't re-type after returning
251
+ portalPass, portalPassConfirm,
252
+ }));
253
+ } catch {}
254
+ }
255
+
256
+ function restoreTotpState() {
257
+ try {
258
+ const raw = sessionStorage.getItem(TOTP_STORAGE_KEY);
259
+ if (!raw) return false;
260
+ const saved = JSON.parse(raw);
261
+ if (saved.secret && saved.phase === 'totp-setup') {
262
+ setTotpSecret(saved.secret);
263
+ setTotpQrUri(saved.qrUri || '');
264
+ setTotpOtpauthUri(saved.otpauthUri || '');
265
+ setTotpEnabled(true);
266
+ setStep3Phase('totp-setup');
267
+ setStep(3);
268
+ if (saved.portalPass) { setPortalPass(saved.portalPass); setPortalPassConfirm(saved.portalPassConfirm || ''); }
269
+ return true;
270
+ }
271
+ } catch {}
272
+ return false;
273
+ }
274
+
275
+ function clearTotpStorage() {
276
+ try { sessionStorage.removeItem(TOTP_STORAGE_KEY); } catch {}
277
+ }
278
+
279
+ // Pre-fill from existing settings (re-run wizard)
280
+ useEffect(() => {
281
+ fetch('/api/onboard/status')
282
+ .then((r) => r.json())
283
+ .then((data) => {
284
+ if (data.userName) setUserName(data.userName);
285
+ if (data.handle) {
286
+ setBotName(data.handle.username);
287
+ setSelectedTier(data.handle.tier || 'at');
288
+ setExistingHandle({ username: data.handle.username, tier: data.handle.tier, url: data.handle.url });
289
+ setRegistered(true);
290
+ setRegisteredUrl(data.handle.url);
291
+ }
292
+ if (data.portalUser) setPortalUser(data.portalUser);
293
+ if (data.portalConfigured) setPortalExists(true);
294
+ if (data.provider) setProvider(data.provider);
295
+ if (data.model) setModel(data.model);
296
+ if (data.whisperEnabled) { setWhisperEnabled(true); setWhisperKey(data.whisperKey || ''); }
297
+ if (data.totpEnabled) { setTotpEnabled(true); setTotpVerified(true); }
298
+ if (data.tunnelMode) setTunnelMode(data.tunnelMode);
299
+ if (data.tunnelDomain) setTunnelDomain(data.tunnelDomain);
300
+ if (data.tunnelUrl) setTunnelUrl(data.tunnelUrl);
301
+ // If user has existing handle, default to 'relay'; otherwise default to 'tunnel'
302
+ if (!data.handle) setHandleChoice('tunnel');
303
+ prefillDone.current = true;
304
+
305
+ // Restore TOTP setup state if returning from authenticator app
306
+ if (!data.totpEnabled) restoreTotpState();
307
+ })
308
+ .catch(() => { prefillDone.current = true; });
309
+ }, []);
310
+
311
+ // Detect access method on mount
312
+ useEffect(() => {
313
+ setAccessMethod(detectAccessMethod(window.location.hostname));
314
+ }, []);
315
+
316
+ // Mobile detection for TOTP setup
317
+ useEffect(() => {
318
+ setIsMobileDevice(window.matchMedia('(max-width: 768px)').matches || 'ontouchstart' in window);
319
+ }, []);
320
+
321
+ // Check if Claude is already authenticated when selecting Anthropic
322
+ useEffect(() => {
323
+ if (provider !== 'anthropic' || authState.anthropic === 'connected') return;
324
+ fetch('/api/auth/claude/status')
325
+ .then((r) => r.json())
326
+ .then((data) => {
327
+ if (data.authenticated) setAuthState((s) => ({ ...s, anthropic: 'connected' }));
328
+ })
329
+ .catch(() => {});
330
+ }, [provider]);
331
+
332
+ // Check if Codex is already authenticated when selecting OpenAI
333
+ useEffect(() => {
334
+ if (provider !== 'openai' || authState.openai === 'connected') return;
335
+ fetch('/api/auth/codex/status')
336
+ .then((r) => r.json())
337
+ .then((data) => {
338
+ if (data.authenticated) setAuthState((s) => ({ ...s, openai: 'connected' }));
339
+ })
340
+ .catch(() => {});
341
+ }, [provider]);
342
+
343
+ // Poll for Codex auth completion while waiting
344
+ useEffect(() => {
345
+ if (!openaiWaiting) return;
346
+ const interval = setInterval(async () => {
347
+ try {
348
+ const res = await fetch('/api/auth/codex/status');
349
+ const data = await res.json();
350
+ if (data.authenticated) {
351
+ setOpenaiWaiting(false);
352
+ setAuthState((s) => ({ ...s, openai: 'connected' }));
353
+ }
354
+ } catch {}
355
+ }, 2000);
356
+ return () => clearInterval(interval);
357
+ }, [openaiWaiting]);
358
+
359
+ // Handle availability check (debounced, per-tier)
360
+ // Skip when the current botName matches the existing registered handle
361
+ useEffect(() => {
362
+ if (handleDebounce.current) clearTimeout(handleDebounce.current);
363
+
364
+ // Skip reset if this is the initial pre-fill setting the existing handle
365
+ if (!prefillDone.current) return;
366
+
367
+ // Private network mode — no handle registration needed
368
+ if (tunnelMode === 'off') return;
369
+
370
+ // Don't reset state if this is the already-registered handle
371
+ if (existingHandle && registered && botName === existingHandle.username) {
372
+ return;
373
+ }
374
+
375
+ setHandleStatus(null);
376
+ setHandleError('');
377
+ setTierAvailability({});
378
+ setTierReserved({});
379
+ setShowClaimInput(false);
380
+ setClaimCode('');
381
+ setClaimError('');
382
+ setRegistered(false);
383
+ setRegisteredUrl('');
384
+
385
+ const trimmed = botName.trim();
386
+ if (!trimmed) return;
387
+ if (trimmed.length < 3) {
388
+ setHandleStatus('invalid');
389
+ setHandleError('At least 3 characters');
390
+ return;
391
+ }
392
+
393
+ setHandleStatus('checking');
394
+ handleDebounce.current = setTimeout(async () => {
395
+ try {
396
+ const res = await fetch(`/api/handle/check/${encodeURIComponent(trimmed)}`);
397
+ const data = await res.json();
398
+ if (!data.valid) {
399
+ setHandleStatus('invalid');
400
+ setHandleError(data.error);
401
+ } else {
402
+ const avail: Record<string, boolean> = {};
403
+ const reserved: Record<string, boolean> = {};
404
+ for (const h of data.handles) {
405
+ avail[h.tier] = h.available;
406
+ if (h.reserved) reserved[h.tier] = true;
407
+ }
408
+ setTierAvailability(avail);
409
+ setTierReserved(reserved);
410
+ setHandleStatus('ready');
411
+ // Reset selection — user must explicitly choose
412
+ setSelectedTier('');
413
+ }
414
+ } catch {
415
+ setHandleStatus(null);
416
+ }
417
+ }, 400);
418
+
419
+ return () => { if (handleDebounce.current) clearTimeout(handleDebounce.current); };
420
+ }, [botName]);
421
+
422
+ // Re-check availability when user returns from external purchase page
423
+ useEffect(() => {
424
+ if (!botName || botName.length < 3 || handleStatus !== 'ready') return;
425
+ const onFocus = async () => {
426
+ try {
427
+ const res = await fetch(`/api/handle/check/${encodeURIComponent(botName.trim())}`);
428
+ const data = await res.json();
429
+ if (data.valid) {
430
+ const avail: Record<string, boolean> = {};
431
+ const reserved: Record<string, boolean> = {};
432
+ for (const h of data.handles) {
433
+ avail[h.tier] = h.available;
434
+ if (h.reserved) reserved[h.tier] = true;
435
+ }
436
+ setTierAvailability(avail);
437
+ setTierReserved(reserved);
438
+ }
439
+ } catch {}
440
+ };
441
+ window.addEventListener('focus', onFocus);
442
+ return () => window.removeEventListener('focus', onFocus);
443
+ }, [botName, handleStatus]);
444
+
445
+ const onBotNameInput = (val: string) => {
446
+ setBotName(val.toLowerCase().replace(/[^a-z0-9-]/g, ''));
447
+ };
448
+
449
+ const onClaimHandle = async () => {
450
+ if (!botName || handleStatus !== 'ready' || !tierAvailability[selectedTier]) return;
451
+ setRegistering(true);
452
+ try {
453
+ const res = await fetch('/api/handle/register', {
454
+ method: 'POST',
455
+ headers: { 'Content-Type': 'application/json' },
456
+ body: JSON.stringify({ username: botName, tier: selectedTier }),
457
+ });
458
+ const data = await res.json();
459
+ if (data.ok) {
460
+ setRegistered(true);
461
+ setRegisteredUrl(data.url);
462
+ } else {
463
+ setHandleError(data.error || 'Registration failed');
464
+ setHandleStatus('invalid');
465
+ }
466
+ } catch {
467
+ setHandleError('Could not reach server');
468
+ setHandleStatus('invalid');
469
+ } finally {
470
+ setRegistering(false);
471
+ }
472
+ };
473
+
474
+ const onChangeHandle = async () => {
475
+ if (!botName || handleStatus !== 'ready' || !tierAvailability[selectedTier]) return;
476
+ setChangingHandle(true);
477
+ try {
478
+ const res = await fetch('/api/handle/change', {
479
+ method: 'POST',
480
+ headers: { 'Content-Type': 'application/json' },
481
+ body: JSON.stringify({ username: botName, tier: selectedTier }),
482
+ });
483
+ const data = await res.json();
484
+ if (data.ok) {
485
+ setRegistered(true);
486
+ setRegisteredUrl(data.url);
487
+ setExistingHandle({ username: botName, tier: selectedTier, url: data.url });
488
+ setShowChangeConfirm(false);
489
+ } else {
490
+ setHandleError(data.error || 'Handle change failed');
491
+ setHandleStatus('invalid');
492
+ }
493
+ } catch {
494
+ setHandleError('Could not reach server');
495
+ setHandleStatus('invalid');
496
+ } finally {
497
+ setChangingHandle(false);
498
+ }
499
+ };
500
+
501
+ const onClaimReserved = async () => {
502
+ if (!botName || !claimCode) return;
503
+ setClaiming(true);
504
+ setClaimError('');
505
+ try {
506
+ const res = await fetch('/api/handle/claim-reserved', {
507
+ method: 'POST',
508
+ headers: { 'Content-Type': 'application/json' },
509
+ body: JSON.stringify({ handle: botName, hash: claimCode }),
510
+ });
511
+ const data = await res.json();
512
+ if (data.ok) {
513
+ setRegistered(true);
514
+ setRegisteredUrl(data.url);
515
+ setShowClaimInput(false);
516
+ setClaimCode('');
517
+ setSelectedTier('premium');
518
+ setHandleChoice('relay');
519
+ } else {
520
+ setClaimError(data.error || 'Invalid activation code');
521
+ }
522
+ } catch {
523
+ setClaimError('Could not reach server');
524
+ } finally {
525
+ setClaiming(false);
526
+ }
527
+ };
528
+
529
+ const handleProviderChange = (id: string) => {
530
+ if (provider === 'openai' && id !== 'openai' && openaiWaiting) {
531
+ fetch('/api/auth/codex/cancel', { method: 'POST' });
532
+ setOpenaiWaiting(false);
533
+ }
534
+ setProvider(id);
535
+ setModel('');
536
+ setOauthStarted(false);
537
+ setAnthropicCode('');
538
+ setAnthropicError(undefined);
539
+ setOpenaiWaiting(false);
540
+ setOpenaiError(undefined);
541
+ };
542
+
543
+ /* ── Auth handlers: Anthropic/Claude ── */
544
+
545
+ const openExternal = (url: string) => {
546
+ const isStandalone = window.matchMedia('(display-mode: standalone)').matches
547
+ || (navigator as any).standalone === true;
548
+ if (isStandalone) {
549
+ // iOS PWA standalone mode blocks window.open — use a temp anchor with target _blank
550
+ const a = document.createElement('a');
551
+ a.href = url;
552
+ a.target = '_blank';
553
+ a.rel = 'noopener noreferrer';
554
+ document.body.appendChild(a);
555
+ a.click();
556
+ document.body.removeChild(a);
557
+ } else {
558
+ window.open(url, '_blank', 'noopener,noreferrer');
559
+ }
560
+ };
561
+
562
+ const handleAnthropicAuth = async () => {
563
+ setAnthropicError(undefined);
564
+ try {
565
+ const res = await fetch('/api/auth/claude/start', { method: 'POST' });
566
+ const data = await res.json();
567
+ if (data.success && data.authUrl) {
568
+ openExternal(data.authUrl);
569
+ setOauthStarted(true);
570
+ } else {
571
+ setAnthropicError(data.error || 'Failed to start authentication');
572
+ }
573
+ } catch (err: any) {
574
+ setAnthropicError(err.message);
575
+ }
576
+ };
577
+
578
+ const handleAnthropicConnect = async () => {
579
+ if (!anthropicCode.trim()) return;
580
+ setIsExchanging(true);
581
+ setAnthropicError(undefined);
582
+ try {
583
+ const res = await fetch('/api/auth/claude/exchange', {
584
+ method: 'POST',
585
+ headers: { 'Content-Type': 'application/json' },
586
+ body: JSON.stringify({ code: anthropicCode.trim() }),
587
+ });
588
+ const data = await res.json();
589
+ if (data.success) {
590
+ setAuthState((s) => ({ ...s, anthropic: 'connected' }));
591
+ } else {
592
+ setAnthropicError(data.error || 'Code exchange failed');
593
+ }
594
+ } catch (err: any) {
595
+ setAnthropicError(err.message);
596
+ } finally {
597
+ setIsExchanging(false);
598
+ }
599
+ };
600
+
601
+ const handleAnthropicPaste = async () => {
602
+ try {
603
+ const text = await navigator.clipboard.readText();
604
+ if (text) setAnthropicCode(text.trim());
605
+ } catch { /* clipboard denied */ }
606
+ };
607
+
608
+ const handleAnthropicCheckAuth = async () => {
609
+ setAnthropicChecking(true);
610
+ setAnthropicError(undefined);
611
+ try {
612
+ const res = await fetch('/api/auth/claude/status');
613
+ const data = await res.json();
614
+ if (data.authenticated) {
615
+ setAuthState((s) => ({ ...s, anthropic: 'connected' }));
616
+ } else {
617
+ setAnthropicError('No active session found. Please authenticate first.');
618
+ }
619
+ } catch {} finally {
620
+ setAnthropicChecking(false);
621
+ }
622
+ };
623
+
624
+ /* ── Auth handlers: OpenAI/Codex ── */
625
+
626
+ const handleOpenAIAuth = async () => {
627
+ setOpenaiWaiting(true);
628
+ setOpenaiError(undefined);
629
+ try {
630
+ const res = await fetch('/api/auth/codex/start', { method: 'POST' });
631
+ const data = await res.json();
632
+ if (data.success && data.authUrl) {
633
+ openExternal(data.authUrl);
634
+ } else {
635
+ setOpenaiWaiting(false);
636
+ setOpenaiError(data.error || 'Failed to start authentication');
637
+ }
638
+ } catch (err: any) {
639
+ setOpenaiWaiting(false);
640
+ setOpenaiError(err.message);
641
+ }
642
+ };
643
+
644
+ const handleOpenAICancel = () => {
645
+ setOpenaiWaiting(false);
646
+ fetch('/api/auth/codex/cancel', { method: 'POST' });
647
+ };
648
+
649
+ /* ── Navigation ── */
650
+
651
+ // Steps: 0=Welcome, 1=Name, 2=Bot name + Handle, 3=Password, 4=Provider, 5=Whisper+Complete, 6=All Set (initial only)
652
+ const portalPassMatch = portalPass === portalPassConfirm;
653
+ const portalValid = portalPass.length >= 6 && portalPassMatch;
654
+ // When portal exists: can continue if no password fields touched, or old pass verified + new pass valid
655
+ const portalCanContinue = portalExists
656
+ ? (portalPass.length === 0 || (portalOldPassVerified && portalValid))
657
+ : portalValid;
658
+
659
+ const canNext = (() => {
660
+ switch (step) {
661
+ case 0: return true;
662
+ case 1: return userName.trim().length > 0;
663
+ case 2: {
664
+ if (showTailscaleSwitch || showReEnableTunnel) return false;
665
+ if (tunnelMode === 'off') return botName.trim().length >= 3;
666
+ if (tunnelMode === 'named') return botName.trim().length >= 3;
667
+ if (handleChoice === 'tunnel') return botName.trim().length >= 3;
668
+ return registered;
669
+ }
670
+ case 3: {
671
+ // Block "Continue" while in a TOTP sub-phase
672
+ if (step3Phase !== 'password') return false;
673
+ if (!portalCanContinue) return false;
674
+ if (totpEnabled && !totpVerified) return false;
675
+ return true;
676
+ }
677
+ case 4: return !!(provider && model && isConnected);
678
+ case 5: return true;
679
+ default: return false;
680
+ }
681
+ })();
682
+
683
+ const next = () => { if (canNext && step < TOTAL_STEPS - 1) setStep((s) => s + 1); };
684
+ const back = () => { if (step > 0) setStep((s) => s - 1); };
685
+
686
+ const handleKeyDown = (e: KeyboardEvent) => {
687
+ if (e.key === 'Enter' && canNext) next();
688
+ };
689
+
690
+ const handleComplete = async () => {
691
+ setSaving(true);
692
+ const payload = {
693
+ userName: userName.trim(),
694
+ agentName: botName.trim() || 'Bloby',
695
+ provider,
696
+ model,
697
+ apiKey: '',
698
+ whisperEnabled,
699
+ whisperKey: whisperEnabled ? whisperKey : '',
700
+ portalUser: portalUser.trim(),
701
+ portalPass,
702
+ };
703
+ try {
704
+ if (onSave) {
705
+ // Chat context: save via WebSocket (bypasses relay POST issues)
706
+ await onSave(payload);
707
+ } else {
708
+ // Initial onboard: direct POST
709
+ await fetch('/api/onboard', {
710
+ method: 'POST',
711
+ headers: { 'Content-Type': 'application/json' },
712
+ body: JSON.stringify(payload),
713
+ });
714
+ }
715
+ if (isInitialSetup) {
716
+ setSaving(false);
717
+ setStep(6);
718
+ } else {
719
+ onComplete();
720
+ }
721
+ } catch (err) {
722
+ console.error('[OnboardWizard] Onboard failed:', err);
723
+ setSaving(false);
724
+ }
725
+ };
726
+
727
+ /* ── Styles ── */
728
+
729
+ const inputCls =
730
+ 'w-full bg-white/[0.05] border border-white/[0.08] text-white rounded-xl px-4 py-3 text-base outline-none input-glow placeholder:text-white/20 transition-all';
731
+ const inputSmCls =
732
+ 'w-full bg-white/[0.03] border border-white/[0.08] text-white rounded-xl px-4 py-2.5 text-[13px] outline-none input-glow placeholder:text-white/20 transition-all';
733
+
734
+ return (
735
+ <div className="fixed inset-0 z-[200] flex items-center justify-center p-4">
736
+ <div className="absolute inset-0 bg-black/85 backdrop-blur-md" />
737
+
738
+ <motion.div
739
+ initial={{ opacity: 0, scale: 0.95 }}
740
+ animate={{ opacity: 1, scale: 1 }}
741
+ transition={{ duration: 0.3 }}
742
+ className="relative w-full max-w-[480px] bg-[#181818] border border-white/[0.06] rounded-[24px] shadow-2xl overflow-hidden"
743
+ >
744
+ {/* Step dots */}
745
+ <div className="flex justify-center gap-2 pt-6">
746
+ {Array.from({ length: TOTAL_STEPS }, (_, i) => (
747
+ <div
748
+ key={i}
749
+ className={`h-1.5 rounded-full transition-all duration-300 ${
750
+ i === step
751
+ ? 'w-7 bg-gradient-brand'
752
+ : i < step
753
+ ? 'w-1.5 bg-gradient-brand opacity-60'
754
+ : 'w-1.5 bg-white/10'
755
+ }`}
756
+ />
757
+ ))}
758
+ </div>
759
+
760
+ {/* Content */}
761
+ <AnimatePresence mode="wait">
762
+ <motion.div
763
+ key={step}
764
+ initial={{ opacity: 0, x: 30 }}
765
+ animate={{ opacity: 1, x: 0 }}
766
+ exit={{ opacity: 0, x: -30 }}
767
+ transition={{ duration: 0.2, ease: 'easeOut' }}
768
+ className="px-8 pt-6 pb-8"
769
+ >
770
+ {/* ── Step 0: Welcome ── */}
771
+ {step === 0 && (
772
+ <div className="flex flex-col items-center text-center">
773
+ <video autoPlay loop muted playsInline className="h-[180px] mb-4">
774
+ <source src="/bloby_say_hi.mov" type='video/mp4; codecs="hvc1"' />
775
+ <source src="/bloby_say_hi.webm" type="video/webm" />
776
+ </video>
777
+ <h1 className="text-2xl font-bold text-white tracking-tight">
778
+ Welcome to Bloby
779
+ </h1>
780
+ <p className="text-white/40 text-[14px] mt-2 leading-relaxed max-w-[320px]">
781
+ Let's set up your AI assistant in just a few steps.
782
+ </p>
783
+ <button
784
+ onClick={next}
785
+ className="mt-6 px-7 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center gap-2"
786
+ >
787
+ Get Started
788
+ <ArrowRight className="h-4 w-4" />
789
+ </button>
790
+ </div>
791
+ )}
792
+
793
+ {/* ── Step 1: Your name ── */}
794
+ {step === 1 && (
795
+ <div>
796
+ <h1 className="text-xl font-bold text-white tracking-tight">
797
+ What's your name?
798
+ </h1>
799
+ <p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
800
+ This is how your agent will address you.
801
+ </p>
802
+ <div className="mt-5 flex items-center gap-3">
803
+ <input
804
+ type="text"
805
+ value={userName}
806
+ onChange={(e) => setUserName(e.target.value)}
807
+ onKeyDown={handleKeyDown}
808
+ placeholder="Enter your name"
809
+ autoFocus
810
+ autoComplete="off"
811
+ data-1p-ignore
812
+ data-lpignore="true"
813
+ className={inputCls + ' flex-1'}
814
+ />
815
+ <button
816
+ onClick={next}
817
+ disabled={!canNext}
818
+ className="shrink-0 h-12 w-12 flex items-center justify-center rounded-full bg-gradient-brand hover:opacity-90 text-white transition-colors disabled:opacity-30"
819
+ >
820
+ <ArrowRight className="h-5 w-5" />
821
+ </button>
822
+ </div>
823
+ </div>
824
+ )}
825
+
826
+ {/* ── Step 2: Name your bot + Claim handle ── */}
827
+ {step === 2 && tunnelMode === 'off' && (
828
+ <div>
829
+ <h1 className="text-xl font-bold text-white tracking-tight">
830
+ Bot Name & Access
831
+ </h1>
832
+ <p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
833
+ Give your bot a name. This is used throughout the app as your bot's identity.
834
+ </p>
835
+
836
+ <div className="relative mt-5">
837
+ <input
838
+ type="text"
839
+ value={botName}
840
+ onChange={(e) => onBotNameInput(e.target.value)}
841
+ maxLength={30}
842
+ placeholder="your-bot-name"
843
+ spellCheck={false}
844
+ autoCapitalize="none"
845
+ autoCorrect="off"
846
+ autoComplete="off"
847
+ data-1p-ignore
848
+ data-lpignore="true"
849
+ autoFocus
850
+ className={inputCls + ' pr-10 font-mono'}
851
+ />
852
+ </div>
853
+
854
+ <div className="mt-4 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
855
+ <p className="text-white/40 text-[12px] leading-relaxed">
856
+ Private network mode — your bot is only accessible via your local network or VPN. No public URL will be created.
857
+ </p>
858
+ </div>
859
+
860
+ {/* Access badge */}
861
+ {!isInitialSetup && (
862
+ <div className="mt-3 flex items-center gap-2">
863
+ <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[11px] font-medium border ${
864
+ isPrivateAccess(accessMethod)
865
+ ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'
866
+ : 'bg-blue-500/10 text-blue-400 border-blue-500/20'
867
+ }`}>
868
+ {isPrivateAccess(accessMethod) ? <WifiOff className="h-3 w-3" /> : <Globe className="h-3 w-3" />}
869
+ Accessing via {ACCESS_LABELS[accessMethod]}
870
+ </span>
871
+ </div>
872
+ )}
873
+
874
+ {/* Re-enable tunnel option */}
875
+ {!isInitialSetup && !showReEnableTunnel && (
876
+ <button
877
+ onClick={() => { setShowReEnableTunnel(true); setReEnableError(''); }}
878
+ className="w-full mt-4 flex items-center justify-between px-4 py-3 rounded-xl border border-white/[0.06] bg-white/[0.02] hover:border-white/10 hover:bg-white/[0.04] transition-all text-left"
879
+ >
880
+ <div>
881
+ <p className="text-[13px] text-white/70 font-medium">Re-enable public tunnel access</p>
882
+ <p className="text-[11px] text-white/30 mt-0.5">Start a Cloudflare tunnel to make your bot accessible from anywhere</p>
883
+ </div>
884
+ <Globe className="h-4 w-4 text-white/30 shrink-0 ml-3" />
885
+ </button>
886
+ )}
887
+
888
+ {/* Re-enable confirmation */}
889
+ {showReEnableTunnel && (
890
+ <div className="mt-4 space-y-3">
891
+ <div className="bg-amber-500/8 border border-amber-500/20 rounded-xl px-4 py-3">
892
+ <div className="flex items-start gap-2">
893
+ <TriangleAlert className="h-4 w-4 text-amber-400 shrink-0 mt-0.5" />
894
+ <div>
895
+ <p className="text-amber-400/90 text-[13px] font-medium">Enable public access?</p>
896
+ <p className="text-amber-400/60 text-[12px] mt-1 leading-relaxed">
897
+ This will start a Cloudflare tunnel, making your bot reachable from the internet.
898
+ {existingHandle && ' Your handle will reconnect automatically.'}
899
+ </p>
900
+ </div>
901
+ </div>
902
+ </div>
903
+ {reEnableError && (
904
+ <p className="text-red-400/70 text-[12px]">{reEnableError}</p>
905
+ )}
906
+ <div className="flex gap-2">
907
+ <button
908
+ onClick={async () => {
909
+ if (!onTunnelSwitch) return;
910
+ setReEnabling(true);
911
+ setReEnableError('');
912
+ try {
913
+ await onTunnelSwitch('quick');
914
+ setTunnelMode('quick');
915
+ setShowReEnableTunnel(false);
916
+ } catch (err: any) {
917
+ setReEnableError(err.message || 'Failed to start tunnel');
918
+ } finally {
919
+ setReEnabling(false);
920
+ }
921
+ }}
922
+ disabled={reEnabling || !onTunnelSwitch}
923
+ className="flex-1 py-2.5 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
924
+ >
925
+ {reEnabling ? <><LoaderCircle className="h-4 w-4 animate-spin" />Starting tunnel...</> : 'Enable Tunnel'}
926
+ </button>
927
+ <button
928
+ onClick={() => { setShowReEnableTunnel(false); setReEnableError(''); }}
929
+ disabled={reEnabling}
930
+ className="px-5 py-2.5 bg-white/[0.04] hover:bg-white/[0.08] border border-white/[0.08] text-white/60 text-[13px] font-medium rounded-full transition-colors"
931
+ >
932
+ Cancel
933
+ </button>
934
+ </div>
935
+ </div>
936
+ )}
937
+
938
+ <button
939
+ onClick={next}
940
+ disabled={!canNext}
941
+ className="w-full mt-5 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
942
+ >
943
+ Continue
944
+ <ArrowRight className="h-4 w-4" />
945
+ </button>
946
+ </div>
947
+ )}
948
+
949
+ {step === 2 && tunnelMode === 'named' && (
950
+ <div>
951
+ <h1 className="text-xl font-bold text-white tracking-tight">
952
+ Bot Name & Access
953
+ </h1>
954
+ <p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
955
+ This is your bot's identity. Your named tunnel domain is already configured.
956
+ </p>
957
+
958
+ <div className="relative mt-5">
959
+ <input
960
+ type="text"
961
+ value={botName}
962
+ onChange={(e) => onBotNameInput(e.target.value)}
963
+ maxLength={30}
964
+ placeholder="your-bot-name"
965
+ spellCheck={false}
966
+ autoCapitalize="none"
967
+ autoCorrect="off"
968
+ autoComplete="off"
969
+ data-1p-ignore
970
+ data-lpignore="true"
971
+ autoFocus
972
+ className={inputCls + ' pr-10 font-mono'}
973
+ />
974
+ </div>
975
+
976
+ <div className="mt-4 flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
977
+ <span className="font-mono text-[13px] text-white/70 truncate flex-1 text-left">https://{tunnelDomain}</span>
978
+ <button
979
+ onClick={() => {
980
+ navigator.clipboard.writeText(`https://${tunnelDomain}`);
981
+ setPortalCopied(true);
982
+ setTimeout(() => setPortalCopied(false), 2000);
983
+ }}
984
+ className="shrink-0 text-white/30 hover:text-white/60 transition-colors"
985
+ >
986
+ {portalCopied ? (
987
+ <Check className="h-4 w-4 text-emerald-400" />
988
+ ) : (
989
+ <ClipboardPaste className="h-4 w-4" />
990
+ )}
991
+ </button>
992
+ </div>
993
+ </div>
994
+ )}
995
+
996
+ {step === 2 && tunnelMode === 'quick' && (
997
+ <div>
998
+ <h1 className="text-xl font-bold text-white tracking-tight">
999
+ Bot Name & Access
1000
+ </h1>
1001
+ <p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
1002
+ This is your bot's name and permanent handle — access it from anywhere.
1003
+ </p>
1004
+
1005
+ {/* Existing handle banner */}
1006
+ {existingHandle && registered && !showChangeConfirm && (
1007
+ <>
1008
+ <div className="mt-4 bg-emerald-500/8 border border-emerald-500/15 rounded-xl px-4 py-3">
1009
+ <div className="flex items-center gap-2">
1010
+ <Check className="h-4 w-4 text-emerald-400" />
1011
+ <p className="text-emerald-400/90 text-[13px] font-medium">Current handle</p>
1012
+ </div>
1013
+ <p className="text-emerald-400/60 text-[12px] mt-1 font-mono">{registeredUrl}</p>
1014
+ </div>
1015
+ <div className="flex gap-2 mt-4">
1016
+ <button
1017
+ onClick={next}
1018
+ className="flex-1 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2"
1019
+ >
1020
+ Continue
1021
+ <ArrowRight className="h-4 w-4" />
1022
+ </button>
1023
+ <button
1024
+ onClick={() => {
1025
+ setShowChangeConfirm(true);
1026
+ setRegistered(false);
1027
+ setBotName('');
1028
+ setHandleStatus(null);
1029
+ setTierAvailability({});
1030
+ setHandleChoice('relay');
1031
+ }}
1032
+ className="px-5 py-3 bg-white/[0.04] hover:bg-white/[0.08] border border-white/[0.08] text-white/60 text-[13px] font-medium rounded-full transition-colors"
1033
+ >
1034
+ Change
1035
+ </button>
1036
+ </div>
1037
+ </>
1038
+ )}
1039
+
1040
+ {/* Change confirmation alert */}
1041
+ {showChangeConfirm && !registered && (
1042
+ <div className="mt-4 bg-amber-500/8 border border-amber-500/20 rounded-xl px-4 py-3">
1043
+ <p className="text-amber-400/90 text-[13px] font-medium">Changing your handle</p>
1044
+ <p className="text-amber-400/60 text-[12px] mt-1">
1045
+ Your current handle <span className="font-mono">{existingHandle?.url}</span> will be released and become available for others.
1046
+ </p>
1047
+ </div>
1048
+ )}
1049
+
1050
+ {/* Input + handle flow — shown for new claim or change flow */}
1051
+ {(!existingHandle || showChangeConfirm || !registered) && !(existingHandle && registered && !showChangeConfirm) && (
1052
+ <>
1053
+ <div className="relative mt-5">
1054
+ <input
1055
+ type="text"
1056
+ value={botName}
1057
+ onChange={(e) => onBotNameInput(e.target.value)}
1058
+ maxLength={30}
1059
+ placeholder="your-bot-name"
1060
+ spellCheck={false}
1061
+ autoCapitalize="none"
1062
+ autoCorrect="off"
1063
+ autoComplete="off"
1064
+ data-1p-ignore
1065
+ data-lpignore="true"
1066
+ autoFocus
1067
+ disabled={registered}
1068
+ className={inputCls + ' pr-10 font-mono' + (registered ? ' opacity-50' : '')}
1069
+ />
1070
+ {handleStatus && botName.length > 0 && !registered && (
1071
+ <div className="absolute right-4 top-1/2 -translate-y-1/2">
1072
+ {handleStatus === 'checking' && (
1073
+ <div className="w-5 h-5 border-2 border-white/10 border-t-[#04D1FE] rounded-full animate-spin" />
1074
+ )}
1075
+ {handleStatus === 'invalid' && (
1076
+ <div className="w-6 h-6 rounded-full bg-amber-500/15 flex items-center justify-center">
1077
+ <svg className="w-3.5 h-3.5 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
1078
+ <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3m0 4h.01" />
1079
+ </svg>
1080
+ </div>
1081
+ )}
1082
+ </div>
1083
+ )}
1084
+ </div>
1085
+
1086
+ {/* Status messages */}
1087
+ {handleStatus === 'invalid' && handleError && (
1088
+ <p className="text-amber-400 text-[12px] mt-2">{handleError}</p>
1089
+ )}
1090
+
1091
+ {/* Handle tier options — shown when name is valid */}
1092
+ {handleStatus === 'ready' && botName.length > 0 && !registered && (
1093
+ <div className="space-y-3 mt-4">
1094
+ {/* ── Free tier block ── */}
1095
+ {(() => {
1096
+ const freeAvail = tierAvailability['at'];
1097
+ const freeTaken = freeAvail === false;
1098
+ const freeSelected = handleChoice === 'relay' && selectedTier === 'at';
1099
+ return (
1100
+ <button
1101
+ onClick={() => { if (!freeTaken) { setSelectedTier('at'); setHandleChoice('relay'); } }}
1102
+ disabled={freeTaken}
1103
+ className={`w-full rounded-xl border transition-all duration-200 text-left px-4 py-3 ${
1104
+ freeTaken
1105
+ ? 'border-white/[0.04] opacity-50 cursor-not-allowed'
1106
+ : freeSelected
1107
+ ? 'border-[#AF27E3]/30 bg-white/[0.04]'
1108
+ : 'border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
1109
+ }`}
1110
+ >
1111
+ <div className="flex items-center justify-between">
1112
+ <span className="text-[12px] font-semibold text-white/60 uppercase tracking-wider">Free</span>
1113
+ <span className={`text-[11px] font-medium px-2.5 py-0.5 rounded-full border ${
1114
+ freeTaken ? 'bg-red-500/10 text-red-400 border-red-500/20' : 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'
1115
+ }`}>
1116
+ {freeTaken ? 'Taken' : 'Available'}
1117
+ </span>
1118
+ </div>
1119
+ <p className={`font-mono text-[13px] mt-1.5 ${freeSelected ? 'text-white/80' : 'text-white/40'}`}>
1120
+ my.bloby.bot/{botName}
1121
+ </p>
1122
+ </button>
1123
+ );
1124
+ })()}
1125
+
1126
+ {/* ── Premium tier block ── */}
1127
+ {(() => {
1128
+ const premAvail = tierAvailability['premium'];
1129
+ const premTaken = premAvail === false;
1130
+ const premReserved = premTaken && tierReserved['premium'];
1131
+ return (
1132
+ <div className={`rounded-xl border transition-all duration-200 text-left px-4 py-3 ${
1133
+ premReserved
1134
+ ? 'border-[#AF27E3]/20'
1135
+ : 'border-white/[0.06]'
1136
+ }`}>
1137
+ <div className="flex items-center justify-between">
1138
+ <div className="flex items-center gap-2">
1139
+ <span className="text-[12px] font-semibold text-white/60 uppercase tracking-wider">Premium</span>
1140
+ <span className="text-[11px] font-medium px-2.5 py-0.5 rounded-full border bg-[#AF27E3]/15 text-[#AF27E3] border-[#AF27E3]/20">$5</span>
1141
+ </div>
1142
+ <span className={`text-[11px] font-medium px-2.5 py-0.5 rounded-full border ${
1143
+ premReserved
1144
+ ? 'bg-[#AF27E3]/10 text-[#AF27E3] border-[#AF27E3]/20'
1145
+ : premTaken
1146
+ ? 'bg-red-500/10 text-red-400 border-red-500/20'
1147
+ : 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'
1148
+ }`}>
1149
+ {premReserved ? 'Reserved' : premTaken ? 'Taken' : 'Available'}
1150
+ </span>
1151
+ </div>
1152
+ <p className="font-mono text-[13px] mt-1.5 text-white/40">
1153
+ bloby.bot/{botName}
1154
+ </p>
1155
+
1156
+ {/* Premium available — purchase on website */}
1157
+ {premAvail && (
1158
+ <div className="mt-3 pt-3 border-t border-white/[0.06] flex items-center justify-between">
1159
+ <p className="text-[12px] text-white/40">Purchase on bloby.bot</p>
1160
+ <a
1161
+ href="https://www.bloby.bot/#reserve"
1162
+ target="_blank"
1163
+ rel="noopener noreferrer"
1164
+ className="text-[12px] font-medium text-[#AF27E3] hover:text-[#c44df7] transition-colors"
1165
+ >
1166
+ Purchase
1167
+ </a>
1168
+ </div>
1169
+ )}
1170
+
1171
+ {/* Premium taken but reserved by user — activation */}
1172
+ {premReserved && (
1173
+ <div className="mt-3 pt-3 border-t border-[#AF27E3]/10">
1174
+ {!showClaimInput ? (
1175
+ <div className="flex items-center justify-between">
1176
+ <p className="text-[12px] text-white/40">Is that yours?</p>
1177
+ <button
1178
+ onClick={() => { setShowClaimInput(true); setClaimCode(''); setClaimError(''); }}
1179
+ className="text-[12px] font-medium text-[#AF27E3] hover:text-[#c44df7] transition-colors"
1180
+ >
1181
+ Activate
1182
+ </button>
1183
+ </div>
1184
+ ) : (
1185
+ <div className="space-y-2.5">
1186
+ <p className="text-[12px] text-white/40">
1187
+ Enter the 5-character code from your <span className="text-white/60 font-medium">bloby.bot</span> account
1188
+ </p>
1189
+ <div className="flex gap-2">
1190
+ <input
1191
+ type="text"
1192
+ value={claimCode}
1193
+ onChange={(e) => setClaimCode(e.target.value.trim())}
1194
+ maxLength={5}
1195
+ placeholder="e.g. a3Kx9"
1196
+ spellCheck={false}
1197
+ autoComplete="off"
1198
+ autoFocus
1199
+ className="flex-1 bg-white/[0.05] border border-white/[0.08] text-white rounded-lg px-3 py-2 text-[13px] font-mono outline-none focus:border-[#AF27E3]/30 transition-colors placeholder:text-white/20 tracking-widest text-center"
1200
+ />
1201
+ <button
1202
+ onClick={onClaimReserved}
1203
+ disabled={claiming || claimCode.length < 5}
1204
+ className="px-4 py-2 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-semibold rounded-lg transition-colors flex items-center gap-1.5 disabled:opacity-40"
1205
+ >
1206
+ {claiming ? <LoaderCircle className="h-3.5 w-3.5 animate-spin" /> : 'Activate'}
1207
+ </button>
1208
+ </div>
1209
+ {claimError && (
1210
+ <p className="text-red-400 text-[12px]">{claimError}</p>
1211
+ )}
1212
+ <button
1213
+ onClick={() => { setShowClaimInput(false); setClaimCode(''); setClaimError(''); }}
1214
+ className="text-[11px] text-white/25 hover:text-white/40 transition-colors"
1215
+ >
1216
+ Cancel
1217
+ </button>
1218
+ </div>
1219
+ )}
1220
+ </div>
1221
+ )}
1222
+ </div>
1223
+ );
1224
+ })()}
1225
+
1226
+ {/* No handle — use random tunnel URL */}
1227
+ <button
1228
+ onClick={() => { setHandleChoice('tunnel'); setSelectedTier('skip'); }}
1229
+ className={`w-full rounded-xl border transition-all duration-200 text-left px-4 py-3 ${
1230
+ selectedTier === 'skip'
1231
+ ? 'border-[#AF27E3]/30 bg-white/[0.04]'
1232
+ : 'border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
1233
+ }`}
1234
+ >
1235
+ <span className="text-[12px] font-semibold text-white/60 uppercase tracking-wider">No handle</span>
1236
+ <p className={`text-[13px] mt-1.5 ${selectedTier === 'skip' ? 'text-white/60' : 'text-white/30'}`}>
1237
+ I'll use the random tunnel URL
1238
+ </p>
1239
+ </button>
1240
+ </div>
1241
+ )}
1242
+
1243
+ {/* Registered success (after claiming) */}
1244
+ {registered && (
1245
+ <div className="mt-4 bg-emerald-500/8 border border-emerald-500/15 rounded-xl px-4 py-3">
1246
+ <div className="flex items-center gap-2">
1247
+ <Check className="h-4 w-4 text-emerald-400" />
1248
+ <p className="text-emerald-400/90 text-[13px] font-medium">Handle claimed!</p>
1249
+ </div>
1250
+ <p className="text-emerald-400/60 text-[12px] mt-1 font-mono">{registeredUrl}</p>
1251
+ </div>
1252
+ )}
1253
+
1254
+ {/* Action button — grayed out until user picks an option */}
1255
+ {handleStatus === 'ready' && botName.length > 0 && !registered && (
1256
+ <button
1257
+ onClick={
1258
+ selectedTier === 'skip' ? next
1259
+ : handleChoice === 'relay' && selectedTier === 'at' ? (showChangeConfirm ? onChangeHandle : onClaimHandle)
1260
+ : undefined
1261
+ }
1262
+ disabled={!selectedTier || registering || changingHandle || (selectedTier === 'at' && !tierAvailability['at'])}
1263
+ className="w-full mt-4 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40 disabled:cursor-not-allowed"
1264
+ >
1265
+ {(registering || changingHandle) ? (
1266
+ <><LoaderCircle className="h-4 w-4 animate-spin" />{showChangeConfirm ? 'Changing...' : 'Claiming...'}</>
1267
+ ) : !selectedTier ? (
1268
+ <>Select an option to continue</>
1269
+ ) : selectedTier === 'skip' ? (
1270
+ <>Continue<ArrowRight className="h-4 w-4" /></>
1271
+ ) : (
1272
+ <>{showChangeConfirm ? 'Change Handle' : 'Claim & Continue'}<ArrowRight className="h-4 w-4" /></>
1273
+ )}
1274
+ </button>
1275
+ )}
1276
+
1277
+ {/* Continue after claim */}
1278
+ {registered && (
1279
+ <button
1280
+ onClick={next}
1281
+ className="w-full mt-4 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2"
1282
+ >
1283
+ Continue
1284
+ <ArrowRight className="h-4 w-4" />
1285
+ </button>
1286
+ )}
1287
+
1288
+ {/* Cancel change */}
1289
+ {showChangeConfirm && !registered && (
1290
+ <button
1291
+ onClick={() => {
1292
+ setShowChangeConfirm(false);
1293
+ setBotName(existingHandle!.username);
1294
+ setRegistered(true);
1295
+ setRegisteredUrl(existingHandle!.url);
1296
+ setSelectedTier(existingHandle!.tier);
1297
+ setHandleChoice('relay');
1298
+ setHandleStatus(null);
1299
+ }}
1300
+ className="w-full mt-2 py-2 text-white/25 hover:text-white/40 text-[12px] transition-colors"
1301
+ >
1302
+ Cancel — keep current handle
1303
+ </button>
1304
+ )}
1305
+ </>
1306
+ )}
1307
+
1308
+ {/* ── Tailscale-only switch (only when re-running wizard) ── */}
1309
+ {!isInitialSetup && (
1310
+ <>
1311
+ {/* Access badge */}
1312
+ <div className="mt-5 flex items-center gap-2">
1313
+ <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[11px] font-medium border ${
1314
+ isPrivateAccess(accessMethod)
1315
+ ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20'
1316
+ : 'bg-blue-500/10 text-blue-400 border-blue-500/20'
1317
+ }`}>
1318
+ {isPrivateAccess(accessMethod) ? <Wifi className="h-3 w-3" /> : <Globe className="h-3 w-3" />}
1319
+ Accessing via {ACCESS_LABELS[accessMethod]}
1320
+ </span>
1321
+ </div>
1322
+
1323
+ {/* Switch to private network only */}
1324
+ {!showTailscaleSwitch && (
1325
+ <button
1326
+ onClick={() => {
1327
+ if (isPrivateAccess(accessMethod)) {
1328
+ setShowTailscaleSwitch(true);
1329
+ setTailscaleConfirmText('');
1330
+ setTailscaleSwitchError('');
1331
+ }
1332
+ }}
1333
+ disabled={!isPrivateAccess(accessMethod)}
1334
+ className={`w-full mt-3 flex items-center justify-between px-4 py-3 rounded-xl border transition-all text-left ${
1335
+ isPrivateAccess(accessMethod)
1336
+ ? 'border-white/[0.06] bg-white/[0.02] hover:border-white/10 hover:bg-white/[0.04] cursor-pointer'
1337
+ : 'border-white/[0.04] bg-transparent opacity-50 cursor-not-allowed'
1338
+ }`}
1339
+ >
1340
+ <div>
1341
+ <p className="text-[13px] text-white/70 font-medium">Switch to private network only</p>
1342
+ <p className="text-[11px] text-white/30 mt-0.5">
1343
+ {isPrivateAccess(accessMethod)
1344
+ ? 'Stop the tunnel and relay — access only via Tailscale or LAN'
1345
+ : 'Connect via Tailscale or private network to unlock'}
1346
+ </p>
1347
+ </div>
1348
+ <WifiOff className="h-4 w-4 text-white/30 shrink-0 ml-3" />
1349
+ </button>
1350
+ )}
1351
+
1352
+ {/* Confirmation flow */}
1353
+ {showTailscaleSwitch && (
1354
+ <div className="mt-3 space-y-3">
1355
+ <div className="bg-amber-500/8 border border-amber-500/20 rounded-xl px-4 py-3">
1356
+ <div className="flex items-start gap-2">
1357
+ <TriangleAlert className="h-4 w-4 text-amber-400 shrink-0 mt-0.5" />
1358
+ <div>
1359
+ <p className="text-amber-400/90 text-[13px] font-medium">Switch to private network only?</p>
1360
+ <p className="text-amber-400/60 text-[12px] mt-1 leading-relaxed">
1361
+ This will stop the Cloudflare tunnel and relay connection. Your bot will only be accessible via your private network.
1362
+ </p>
1363
+ {existingHandle && (
1364
+ <p className="text-amber-400/50 text-[12px] mt-1.5">
1365
+ Your handle will be preserved and can be re-activated later.
1366
+ </p>
1367
+ )}
1368
+ </div>
1369
+ </div>
1370
+ </div>
1371
+ <div>
1372
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">
1373
+ Type <span className="font-mono text-white/60">I confirm</span> to proceed
1374
+ </label>
1375
+ <input
1376
+ type="text"
1377
+ value={tailscaleConfirmText}
1378
+ onChange={(e) => setTailscaleConfirmText(e.target.value)}
1379
+ placeholder="I confirm"
1380
+ spellCheck={false}
1381
+ autoFocus
1382
+ className={inputSmCls}
1383
+ />
1384
+ </div>
1385
+ {tailscaleSwitchError && (
1386
+ <p className="text-red-400/70 text-[12px]">{tailscaleSwitchError}</p>
1387
+ )}
1388
+ <div className="flex gap-2">
1389
+ <button
1390
+ onClick={async () => {
1391
+ if (!onTunnelSwitch || tailscaleConfirmText.trim().toLowerCase() !== 'i confirm') return;
1392
+ setTailscaleSwitching(true);
1393
+ setTailscaleSwitchError('');
1394
+ try {
1395
+ await onTunnelSwitch('off');
1396
+ setTunnelMode('off');
1397
+ setShowTailscaleSwitch(false);
1398
+ } catch (err: any) {
1399
+ setTailscaleSwitchError(err.message || 'Failed to switch');
1400
+ } finally {
1401
+ setTailscaleSwitching(false);
1402
+ }
1403
+ }}
1404
+ disabled={tailscaleSwitching || tailscaleConfirmText.trim().toLowerCase() !== 'i confirm' || !onTunnelSwitch}
1405
+ className="flex-1 py-2.5 bg-amber-600 hover:bg-amber-500 text-white text-[13px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
1406
+ >
1407
+ {tailscaleSwitching ? <><LoaderCircle className="h-4 w-4 animate-spin" />Switching...</> : 'Confirm Switch'}
1408
+ </button>
1409
+ <button
1410
+ onClick={() => { setShowTailscaleSwitch(false); setTailscaleConfirmText(''); setTailscaleSwitchError(''); }}
1411
+ disabled={tailscaleSwitching}
1412
+ className="px-5 py-2.5 bg-white/[0.04] hover:bg-white/[0.08] border border-white/[0.08] text-white/60 text-[13px] font-medium rounded-full transition-colors"
1413
+ >
1414
+ Cancel
1415
+ </button>
1416
+ </div>
1417
+ </div>
1418
+ )}
1419
+ </>
1420
+ )}
1421
+ </div>
1422
+ )}
1423
+
1424
+ {/* ── Step 3: Password + 2FA (sub-phase flow) ── */}
1425
+ {step === 3 && (
1426
+ <div>
1427
+ <AnimatePresence mode="wait">
1428
+ {/* ── Phase: Password ── */}
1429
+ {step3Phase === 'password' && (
1430
+ <motion.div
1431
+ key="password"
1432
+ initial={{ opacity: 0, x: -20 }}
1433
+ animate={{ opacity: 1, x: 0 }}
1434
+ exit={{ opacity: 0, x: -20 }}
1435
+ transition={{ duration: 0.15 }}
1436
+ >
1437
+ <h1 className="text-xl font-bold text-white tracking-tight">
1438
+ Set a password
1439
+ </h1>
1440
+ <p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
1441
+ You'll need this password to access your agent's chat. Keep it safe — anyone with your URL will need it to log in.
1442
+ </p>
1443
+
1444
+ {portalExists && (
1445
+ <div className="mt-4 bg-white/[0.02] border border-white/[0.06] rounded-xl px-4 py-2.5">
1446
+ <p className="text-white/40 text-[12px]">Password already set. Leave fields empty to keep your current password, or enter your current password to change it.</p>
1447
+ </div>
1448
+ )}
1449
+
1450
+ {portalExists && (
1451
+ <div className="mt-5">
1452
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">Current password</label>
1453
+ <div className="flex items-center gap-2">
1454
+ <input
1455
+ type="password"
1456
+ value={portalOldPass}
1457
+ onChange={(e) => { setPortalOldPass(e.target.value); setPortalOldPassError(''); setPortalOldPassVerified(false); }}
1458
+ placeholder="Enter current password to change it"
1459
+ autoComplete="current-password"
1460
+ autoFocus
1461
+ className={inputCls + ' flex-1'}
1462
+ />
1463
+ {portalOldPass.length > 0 && !portalOldPassVerified && (
1464
+ <button
1465
+ onClick={async () => {
1466
+ setPortalVerifying(true);
1467
+ setPortalOldPassError('');
1468
+ try {
1469
+ const res = await fetch('/api/portal/verify-password', {
1470
+ method: 'POST',
1471
+ headers: { 'Content-Type': 'application/json' },
1472
+ body: JSON.stringify({ password: portalOldPass }),
1473
+ });
1474
+ const data = await res.json();
1475
+ if (data.valid) {
1476
+ setPortalOldPassVerified(true);
1477
+ } else {
1478
+ setPortalOldPassError('Incorrect password');
1479
+ }
1480
+ } catch {
1481
+ setPortalOldPassError('Could not verify');
1482
+ } finally {
1483
+ setPortalVerifying(false);
1484
+ }
1485
+ }}
1486
+ disabled={portalVerifying}
1487
+ className="shrink-0 px-4 py-3 bg-white/[0.06] hover:bg-white/[0.1] text-white/60 text-[13px] font-medium rounded-xl transition-colors disabled:opacity-40"
1488
+ >
1489
+ {portalVerifying ? <LoaderCircle className="h-4 w-4 animate-spin" /> : 'Verify'}
1490
+ </button>
1491
+ )}
1492
+ {portalOldPassVerified && (
1493
+ <div className="shrink-0 w-10 h-10 flex items-center justify-center">
1494
+ <Check className="h-4 w-4 text-emerald-400" />
1495
+ </div>
1496
+ )}
1497
+ </div>
1498
+ {portalOldPassError && (
1499
+ <p className="text-red-400/70 text-[11px] mt-1">{portalOldPassError}</p>
1500
+ )}
1501
+ </div>
1502
+ )}
1503
+
1504
+ {(!portalExists || portalOldPassVerified) && (
1505
+ <>
1506
+ <div className={portalExists ? 'mt-3' : 'mt-5'}>
1507
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">
1508
+ {portalExists ? 'New password' : 'Password'}
1509
+ </label>
1510
+ <input
1511
+ type="password"
1512
+ value={portalPass}
1513
+ onChange={(e) => setPortalPass(e.target.value)}
1514
+ placeholder="••••••••"
1515
+ autoComplete="new-password"
1516
+ autoFocus={!portalExists}
1517
+ onKeyDown={handleKeyDown}
1518
+ className={inputCls}
1519
+ />
1520
+ {portalPass.length > 0 && portalPass.length < 6 && (
1521
+ <p className="text-amber-400/70 text-[11px] mt-1">At least 6 characters</p>
1522
+ )}
1523
+ </div>
1524
+
1525
+ <div className="mt-3">
1526
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">
1527
+ {portalExists ? 'Confirm new password' : 'Confirm password'}
1528
+ </label>
1529
+ <input
1530
+ type="password"
1531
+ value={portalPassConfirm}
1532
+ onChange={(e) => setPortalPassConfirm(e.target.value)}
1533
+ placeholder="••••••••"
1534
+ autoComplete="new-password"
1535
+ onKeyDown={handleKeyDown}
1536
+ className={inputCls}
1537
+ />
1538
+ {portalPassConfirm.length > 0 && !portalPassMatch && (
1539
+ <p className="text-red-400/70 text-[11px] mt-1">Passwords don't match</p>
1540
+ )}
1541
+ </div>
1542
+ </>
1543
+ )}
1544
+
1545
+ {/* ── 2FA toggle card ── */}
1546
+ <div className="mt-5 border border-white/[0.06] rounded-xl overflow-hidden">
1547
+ <button
1548
+ onClick={async () => {
1549
+ if (totpEnabled && totpVerified) {
1550
+ setTotpDisabling(true);
1551
+ return;
1552
+ }
1553
+ if (!totpEnabled) {
1554
+ // Enable → go to setup phase
1555
+ setTotpEnabled(true);
1556
+ setTotpError('');
1557
+ setTotpCode('');
1558
+ // Fetch QR if not already loaded
1559
+ if (!totpQrUri) {
1560
+ try {
1561
+ const res = await fetch('/api/portal/totp/setup', {
1562
+ method: 'POST',
1563
+ headers: { 'Content-Type': 'application/json' },
1564
+ body: JSON.stringify({ password: portalExists ? portalOldPass : portalPass }),
1565
+ });
1566
+ const data = await res.json();
1567
+ if (res.ok) {
1568
+ setTotpSecret(data.secret);
1569
+ setTotpQrUri(data.qrDataUri);
1570
+ setTotpOtpauthUri(data.otpauthUri);
1571
+ setStep3Phase('totp-setup');
1572
+ } else {
1573
+ setTotpError(data.error || 'Setup failed');
1574
+ setTotpEnabled(false);
1575
+ }
1576
+ } catch {
1577
+ setTotpError('Could not reach server');
1578
+ setTotpEnabled(false);
1579
+ }
1580
+ } else {
1581
+ setStep3Phase('totp-setup');
1582
+ }
1583
+ } else {
1584
+ // Disable (setup incomplete)
1585
+ setTotpEnabled(false);
1586
+ setTotpQrUri('');
1587
+ setTotpSecret('');
1588
+ setTotpOtpauthUri('');
1589
+ }
1590
+ }}
1591
+ type="button"
1592
+ className="w-full flex items-center gap-3 px-4 py-3 text-left"
1593
+ >
1594
+ <div className={`w-9 h-9 rounded-lg flex items-center justify-center shrink-0 ${totpEnabled && totpVerified ? 'bg-emerald-500/10' : 'bg-white/[0.04]'}`}>
1595
+ {totpEnabled && totpVerified ? (
1596
+ <ShieldCheck className="h-[18px] w-[18px] text-emerald-400" />
1597
+ ) : (
1598
+ <Shield className="h-[18px] w-[18px] text-white/30" />
1599
+ )}
1600
+ </div>
1601
+ <div className="flex-1 min-w-0">
1602
+ <span className="text-[13px] font-medium text-white block">Two-Factor Auth</span>
1603
+ <p className="text-[11px] text-white/30 mt-0.5">
1604
+ {totpEnabled && totpVerified ? (
1605
+ <span className="text-emerald-400/70">Active — authenticator app required</span>
1606
+ ) : (tunnelMode === 'quick' || tunnelMode === 'named') ? (
1607
+ <span>Recommended for public bots</span>
1608
+ ) : (
1609
+ <span>Extra security with an authenticator app</span>
1610
+ )}
1611
+ </p>
1612
+ </div>
1613
+ <div className={`w-10 h-6 rounded-full transition-colors relative shrink-0 ${totpEnabled ? 'bg-emerald-500' : 'bg-white/10'}`}>
1614
+ <div className={`absolute top-1 w-4 h-4 rounded-full bg-white shadow transition-transform ${totpEnabled ? 'translate-x-5' : 'translate-x-1'}`} />
1615
+ </div>
1616
+ </button>
1617
+
1618
+ {/* Inline disable flow */}
1619
+ {totpDisabling && (
1620
+ <div className="px-4 pb-3 border-t border-white/[0.06]">
1621
+ <p className="text-[12px] text-white/40 mt-3 mb-2">Enter your current TOTP code to disable 2FA:</p>
1622
+ <div className="flex items-center gap-2">
1623
+ <input
1624
+ type="text"
1625
+ inputMode="numeric"
1626
+ autoComplete="one-time-code"
1627
+ maxLength={6}
1628
+ value={totpDisableCode}
1629
+ onChange={(e) => { setTotpDisableCode(e.target.value.replace(/\D/g, '')); setTotpDisableError(''); }}
1630
+ placeholder="000000"
1631
+ className={inputSmCls + ' flex-1 tracking-[0.3em] text-center font-mono'}
1632
+ />
1633
+ <button
1634
+ onClick={async () => {
1635
+ if (totpDisableCode.length !== 6) return;
1636
+ try {
1637
+ const res = await fetch('/api/portal/totp/disable', {
1638
+ method: 'POST',
1639
+ headers: { 'Content-Type': 'application/json' },
1640
+ body: JSON.stringify({ password: portalExists ? portalOldPass : portalPass, code: totpDisableCode }),
1641
+ });
1642
+ const data = await res.json();
1643
+ if (res.ok) {
1644
+ setTotpEnabled(false);
1645
+ setTotpVerified(false);
1646
+ setTotpSecret('');
1647
+ setTotpQrUri('');
1648
+ setTotpOtpauthUri('');
1649
+ setTotpDisabling(false);
1650
+ setTotpDisableCode('');
1651
+ setRecoveryCodes([]);
1652
+ setRecoveryCodesCopied(false);
1653
+ } else {
1654
+ setTotpDisableError(data.error || 'Failed to disable');
1655
+ }
1656
+ } catch {
1657
+ setTotpDisableError('Could not reach server');
1658
+ }
1659
+ }}
1660
+ disabled={totpDisableCode.length !== 6}
1661
+ className="shrink-0 px-4 py-2.5 bg-red-500/10 hover:bg-red-500/20 text-red-400 text-[13px] font-medium rounded-xl transition-colors disabled:opacity-40"
1662
+ >
1663
+ Disable
1664
+ </button>
1665
+ </div>
1666
+ {totpDisableError && <p className="text-red-400/70 text-[11px] mt-1">{totpDisableError}</p>}
1667
+ <button onClick={() => { setTotpDisabling(false); setTotpDisableCode(''); setTotpDisableError(''); }} className="text-[11px] text-white/30 hover:text-white/50 mt-2">Cancel</button>
1668
+ </div>
1669
+ )}
1670
+
1671
+ {totpError && step3Phase === 'password' && (
1672
+ <div className="mx-4 mb-3 bg-red-500/8 border border-red-500/15 rounded-xl px-3 py-2">
1673
+ <p className="text-red-400/90 text-[11px]">{totpError}</p>
1674
+ </div>
1675
+ )}
1676
+ </div>
1677
+
1678
+ <button
1679
+ onClick={next}
1680
+ disabled={!canNext}
1681
+ className="w-full mt-5 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
1682
+ >
1683
+ Continue
1684
+ <ArrowRight className="h-4 w-4" />
1685
+ </button>
1686
+ </motion.div>
1687
+ )}
1688
+
1689
+ {/* ── Phase: TOTP Setup (QR + verify) ── */}
1690
+ {step3Phase === 'totp-setup' && (
1691
+ <motion.div
1692
+ key="totp-setup"
1693
+ initial={{ opacity: 0, x: 20 }}
1694
+ animate={{ opacity: 1, x: 0 }}
1695
+ exit={{ opacity: 0, x: 20 }}
1696
+ transition={{ duration: 0.15 }}
1697
+ >
1698
+ <div className="flex items-center gap-3 mb-1">
1699
+ <button
1700
+ onClick={() => {
1701
+ setTotpEnabled(false);
1702
+ setTotpQrUri('');
1703
+ setTotpSecret('');
1704
+ setTotpOtpauthUri('');
1705
+ setTotpCode('');
1706
+ setTotpError('');
1707
+ setStep3Phase('password');
1708
+ clearTotpStorage();
1709
+ }}
1710
+ className="w-7 h-7 rounded-full bg-white/[0.04] flex items-center justify-center text-white/40 hover:text-white/70 transition-colors shrink-0"
1711
+ >
1712
+ <ArrowLeft className="h-4 w-4" />
1713
+ </button>
1714
+ <h1 className="text-xl font-bold text-white tracking-tight">Set up 2FA</h1>
1715
+ </div>
1716
+
1717
+ {totpError && (
1718
+ <div className="mt-3 bg-red-500/8 border border-red-500/15 rounded-xl px-3 py-2">
1719
+ <p className="text-red-400/90 text-[11px]">{totpError}</p>
1720
+ </div>
1721
+ )}
1722
+
1723
+ {totpQrUri && (
1724
+ <>
1725
+ {/* Desktop: horizontal QR + instructions | Mobile: authenticator link */}
1726
+ {isMobileDevice ? (
1727
+ <div className="mt-4">
1728
+ <p className="text-white/40 text-[13px] leading-relaxed mb-4">
1729
+ Add Bloby to your authenticator app, then enter the 6-digit code below to confirm.
1730
+ </p>
1731
+
1732
+ {/* Primary: copy secret key (doesn't leave the app) */}
1733
+ <button
1734
+ onClick={() => {
1735
+ navigator.clipboard.writeText(totpSecret);
1736
+ setTotpSecretCopied(true);
1737
+ setTimeout(() => setTotpSecretCopied(false), 3000);
1738
+ }}
1739
+ className={`w-full py-3 text-[14px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 ${
1740
+ totpSecretCopied
1741
+ ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'
1742
+ : 'bg-white/[0.05] text-white/70 hover:bg-white/[0.08] border border-white/[0.08]'
1743
+ }`}
1744
+ >
1745
+ {totpSecretCopied ? (
1746
+ <><Check className="h-4 w-4" />Secret key copied</>
1747
+ ) : (
1748
+ <><Copy className="h-4 w-4" />Copy secret key</>
1749
+ )}
1750
+ </button>
1751
+ <p className="text-[11px] text-white/20 text-center mt-2">
1752
+ Paste it in your authenticator app → Add account → Enter key
1753
+ </p>
1754
+
1755
+ {/* Secondary: deep link (saves state before leaving) */}
1756
+ <a
1757
+ href={totpOtpauthUri}
1758
+ onClick={() => saveTotpState()}
1759
+ className="w-full mt-3 py-2.5 text-[13px] text-white/30 hover:text-white/50 flex items-center justify-center gap-1.5 transition-colors"
1760
+ >
1761
+ <Smartphone className="h-3.5 w-3.5" />
1762
+ Or open directly in authenticator
1763
+ </a>
1764
+ </div>
1765
+ ) : (
1766
+ <div className="mt-4 flex gap-4 items-start">
1767
+ {/* QR Code — compact */}
1768
+ <div className="shrink-0">
1769
+ <div className="bg-white rounded-xl p-1.5">
1770
+ <img src={totpQrUri} alt="TOTP QR Code" className="w-[140px] h-[140px]" />
1771
+ </div>
1772
+ </div>
1773
+ {/* Instructions */}
1774
+ <div className="flex-1 min-w-0 pt-1">
1775
+ <p className="text-white/40 text-[13px] leading-relaxed">
1776
+ Scan this QR code with your authenticator app.
1777
+ </p>
1778
+ <p className="text-white/25 text-[11px] mt-2 leading-relaxed">
1779
+ Google Authenticator, Authy, 1Password, or any TOTP app.
1780
+ </p>
1781
+ <button
1782
+ onClick={() => { navigator.clipboard.writeText(totpSecret); }}
1783
+ className="mt-3 text-[11px] text-white/25 hover:text-white/40 flex items-center gap-1 transition-colors"
1784
+ >
1785
+ <Copy className="h-3 w-3" />
1786
+ Copy secret key
1787
+ </button>
1788
+ </div>
1789
+ </div>
1790
+ )}
1791
+
1792
+ {/* Verification input */}
1793
+ <div className="mt-5">
1794
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">Enter the 6-digit code from your app</label>
1795
+ <div className="flex items-center gap-2">
1796
+ <input
1797
+ type="text"
1798
+ inputMode="numeric"
1799
+ autoComplete="one-time-code"
1800
+ maxLength={6}
1801
+ value={totpCode}
1802
+ onChange={(e) => { setTotpCode(e.target.value.replace(/\D/g, '')); setTotpError(''); }}
1803
+ placeholder="000000"
1804
+ autoFocus
1805
+ className={inputCls + ' tracking-[0.3em] text-center font-mono flex-1'}
1806
+ />
1807
+ <button
1808
+ onClick={async () => {
1809
+ if (totpCode.length !== 6) return;
1810
+ setTotpVerifying(true);
1811
+ setTotpError('');
1812
+ try {
1813
+ const res = await fetch('/api/portal/totp/verify-setup', {
1814
+ method: 'POST',
1815
+ headers: { 'Content-Type': 'application/json' },
1816
+ body: JSON.stringify({ code: totpCode, password: portalExists ? portalOldPass : portalPass }),
1817
+ });
1818
+ const data = await res.json();
1819
+ if (res.ok && data.success) {
1820
+ setTotpVerified(true);
1821
+ setRecoveryCodes(data.recoveryCodes || []);
1822
+ setStep3Phase('recovery');
1823
+ clearTotpStorage();
1824
+ } else {
1825
+ setTotpError(data.error || 'Verification failed');
1826
+ }
1827
+ } catch {
1828
+ setTotpError('Could not reach server');
1829
+ } finally {
1830
+ setTotpVerifying(false);
1831
+ }
1832
+ }}
1833
+ disabled={totpCode.length !== 6 || totpVerifying}
1834
+ className="shrink-0 px-5 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-xl transition-colors flex items-center gap-2 disabled:opacity-40"
1835
+ >
1836
+ {totpVerifying ? <LoaderCircle className="h-4 w-4 animate-spin" /> : 'Verify'}
1837
+ </button>
1838
+ </div>
1839
+ </div>
1840
+ </>
1841
+ )}
1842
+ </motion.div>
1843
+ )}
1844
+
1845
+ {/* ── Phase: Recovery codes ── */}
1846
+ {step3Phase === 'recovery' && (
1847
+ <motion.div
1848
+ key="recovery"
1849
+ initial={{ opacity: 0, x: 20 }}
1850
+ animate={{ opacity: 1, x: 0 }}
1851
+ exit={{ opacity: 0, x: 20 }}
1852
+ transition={{ duration: 0.15 }}
1853
+ >
1854
+ <div className="flex items-center gap-3 mb-1">
1855
+ <div className="w-9 h-9 rounded-xl bg-emerald-500/10 flex items-center justify-center shrink-0">
1856
+ <ShieldCheck className="h-[18px] w-[18px] text-emerald-400" />
1857
+ </div>
1858
+ <div>
1859
+ <h1 className="text-xl font-bold text-white tracking-tight">2FA enabled</h1>
1860
+ <p className="text-emerald-400/70 text-[12px]">Save your recovery codes</p>
1861
+ </div>
1862
+ </div>
1863
+
1864
+ <p className="text-white/35 text-[13px] mt-3 leading-relaxed">
1865
+ If you lose your authenticator app, you can use one of these codes to sign in. Each code works once. Store them somewhere safe.
1866
+ </p>
1867
+
1868
+ <div className="mt-4 grid grid-cols-2 gap-1.5">
1869
+ {recoveryCodes.map((code, i) => (
1870
+ <div key={i} className="bg-white/[0.03] border border-white/[0.06] rounded-lg px-3 py-2 text-center">
1871
+ <code className="text-[13px] text-white/60 font-mono tracking-wider">{code}</code>
1872
+ </div>
1873
+ ))}
1874
+ </div>
1875
+
1876
+ <button
1877
+ onClick={() => {
1878
+ navigator.clipboard.writeText(recoveryCodes.join('\n'));
1879
+ setRecoveryCodesCopied(true);
1880
+ }}
1881
+ className={`w-full mt-4 py-3 text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 ${
1882
+ recoveryCodesCopied
1883
+ ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'
1884
+ : 'bg-white/[0.05] text-white/60 hover:bg-white/[0.08] border border-white/[0.08]'
1885
+ }`}
1886
+ >
1887
+ {recoveryCodesCopied ? (
1888
+ <><Check className="h-4 w-4" />Copied</>
1889
+ ) : (
1890
+ <><Copy className="h-4 w-4" />Copy recovery codes</>
1891
+ )}
1892
+ </button>
1893
+
1894
+ <button
1895
+ onClick={() => {
1896
+ setRecoveryCodes([]);
1897
+ setStep3Phase('password');
1898
+ clearTotpStorage();
1899
+ }}
1900
+ disabled={!recoveryCodesCopied}
1901
+ className="w-full mt-3 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
1902
+ >
1903
+ Done
1904
+ <ArrowRight className="h-4 w-4" />
1905
+ </button>
1906
+ </motion.div>
1907
+ )}
1908
+ </AnimatePresence>
1909
+ </div>
1910
+ )}
1911
+
1912
+ {/* ── Step 4: Provider + Auth + Model ── */}
1913
+ {step === 4 && (
1914
+ <div>
1915
+ <h1 className="text-xl font-bold text-white tracking-tight">
1916
+ Choose your AI provider
1917
+ </h1>
1918
+ <p className="text-white/40 text-[13px] mt-1.5 leading-relaxed">
1919
+ Pick one provider to power your bot, authenticate, and select a model.
1920
+ </p>
1921
+
1922
+ {/* Provider cards */}
1923
+ <div className="flex gap-2.5 mt-4">
1924
+ {PROVIDERS.map((p) => (
1925
+ <button
1926
+ key={p.id}
1927
+ onClick={() => handleProviderChange(p.id)}
1928
+ className={`flex-1 relative rounded-xl border transition-all duration-200 p-3 text-left ${
1929
+ provider === p.id
1930
+ ? 'bg-white/[0.04] border-[#AF27E3]/40'
1931
+ : 'bg-transparent border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
1932
+ }`}
1933
+ >
1934
+ <div className="flex flex-col items-center gap-1.5 py-0.5">
1935
+ {p.icon ? (
1936
+ <img src={p.icon} alt={p.name} className="w-8 h-8 rounded-lg" />
1937
+ ) : (
1938
+ <div className="w-8 h-8 rounded-lg bg-white/[0.06] flex items-center justify-center text-white/50 text-sm font-bold">
1939
+ O
1940
+ </div>
1941
+ )}
1942
+ <div className="text-center">
1943
+ <div className="text-[13px] font-medium text-white">{p.name}</div>
1944
+ <div className="text-[10px] text-white/30">{p.subtitle}</div>
1945
+ </div>
1946
+ </div>
1947
+ {authState[p.id] === 'connected' ? (
1948
+ <div className="absolute top-2 right-2 w-4 h-4 rounded-full bg-emerald-500/15 flex items-center justify-center">
1949
+ <Check className="h-2.5 w-2.5 text-emerald-400" />
1950
+ </div>
1951
+ ) : provider === p.id ? (
1952
+ <div className="absolute top-2 right-2 w-2 h-2 rounded-full bg-gradient-brand" />
1953
+ ) : null}
1954
+ </button>
1955
+ ))}
1956
+ </div>
1957
+
1958
+ <div className="border-t border-white/[0.06] mt-4 mb-3" />
1959
+
1960
+ {/* ── Auth flow: Anthropic ── */}
1961
+ {provider === 'anthropic' && (
1962
+ <div className="space-y-2.5">
1963
+ {isConnected && (
1964
+ <div className="space-y-2.5">
1965
+ <div className="bg-emerald-500/8 border border-emerald-500/15 rounded-lg px-3.5 py-2.5">
1966
+ <p className="text-emerald-400/90 text-[12px]">Connected — Anthropic subscription is active.</p>
1967
+ </div>
1968
+ <button
1969
+ onClick={() => { setAuthState((s) => ({ ...s, anthropic: 'idle' })); setOauthStarted(false); setAnthropicCode(''); setAnthropicError(''); }}
1970
+ className="w-full py-1.5 text-white/25 text-[11px] hover:text-white/40 transition-colors flex items-center justify-center gap-1.5"
1971
+ >
1972
+ <RefreshCw className="h-3 w-3" />
1973
+ Re-authenticate
1974
+ </button>
1975
+ </div>
1976
+ )}
1977
+
1978
+ {!isConnected && (
1979
+ <>
1980
+ {anthropicError && (
1981
+ <div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3.5 py-2.5">
1982
+ <p className="text-red-400/90 text-[12px]">{anthropicError}</p>
1983
+ </div>
1984
+ )}
1985
+
1986
+ <div className="space-y-1.5">
1987
+ {[
1988
+ 'Click the button below to open Anthropic\'s login page',
1989
+ 'Sign in with your Anthropic account — a code will be generated',
1990
+ 'Copy the code and paste it in the field below',
1991
+ ].map((text, i) => (
1992
+ <div key={i} className="flex items-start gap-2">
1993
+ <span className="flex-shrink-0 w-[18px] h-[18px] rounded-full bg-white/[0.06] text-white/30 text-[10px] font-medium flex items-center justify-center mt-px">{i + 1}</span>
1994
+ <p className="text-white/40 text-[12px] leading-relaxed">{text}</p>
1995
+ </div>
1996
+ ))}
1997
+ </div>
1998
+
1999
+ <button
2000
+ onClick={handleAnthropicAuth}
2001
+ className="w-full py-2.5 px-4 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2"
2002
+ >
2003
+ {oauthStarted ? (
2004
+ <><ExternalLink className="h-3.5 w-3.5 opacity-60" />Open authentication page again</>
2005
+ ) : (
2006
+ <>Authenticate with Anthropic<ArrowRight className="h-3.5 w-3.5 opacity-60" /></>
2007
+ )}
2008
+ </button>
2009
+
2010
+ <div className="relative">
2011
+ <input
2012
+ type="text"
2013
+ value={anthropicCode}
2014
+ onChange={(e) => setAnthropicCode(e.target.value)}
2015
+ onKeyDown={(e) => e.key === 'Enter' && handleAnthropicConnect()}
2016
+ placeholder="Paste your code here..."
2017
+ className={inputSmCls + ' pr-10 font-mono'}
2018
+ />
2019
+ <button
2020
+ onClick={handleAnthropicPaste}
2021
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-white/20 hover:text-white/50 transition-colors"
2022
+ >
2023
+ <ClipboardPaste className="h-3.5 w-3.5" />
2024
+ </button>
2025
+ </div>
2026
+
2027
+ <button
2028
+ onClick={handleAnthropicConnect}
2029
+ disabled={!anthropicCode.trim() || isExchanging}
2030
+ className="w-full py-2.5 px-4 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
2031
+ >
2032
+ {isExchanging ? (<><LoaderCircle className="h-3.5 w-3.5 animate-spin" />Verifying...</>) : 'Connect'}
2033
+ </button>
2034
+
2035
+ <button
2036
+ onClick={handleAnthropicCheckAuth}
2037
+ disabled={anthropicChecking}
2038
+ className="w-full py-1.5 text-white/25 text-[11px] hover:text-white/40 transition-colors flex items-center justify-center gap-1.5 disabled:opacity-50"
2039
+ >
2040
+ {anthropicChecking ? (
2041
+ <LoaderCircle className="h-3 w-3 animate-spin" />
2042
+ ) : (
2043
+ <RefreshCw className="h-3 w-3" />
2044
+ )}
2045
+ {anthropicChecking ? 'Checking...' : 'I\'m already authenticated'}
2046
+ </button>
2047
+ </>
2048
+ )}
2049
+ </div>
2050
+ )}
2051
+
2052
+ {/* ── Auth flow: OpenAI ── */}
2053
+ {provider === 'openai' && (
2054
+ <div className="space-y-2.5">
2055
+ {isConnected && (
2056
+ <div className="space-y-2.5">
2057
+ <div className="bg-emerald-500/8 border border-emerald-500/15 rounded-lg px-3.5 py-2.5">
2058
+ <p className="text-emerald-400/90 text-[12px]">Connected — ChatGPT subscription is active.</p>
2059
+ </div>
2060
+ <button
2061
+ onClick={() => { setAuthState((s) => ({ ...s, openai: 'idle' })); setOpenaiError(''); }}
2062
+ className="w-full py-1.5 text-white/25 text-[11px] hover:text-white/40 transition-colors flex items-center justify-center gap-1.5"
2063
+ >
2064
+ <RefreshCw className="h-3 w-3" />
2065
+ Re-authenticate
2066
+ </button>
2067
+ </div>
2068
+ )}
2069
+
2070
+ {!isConnected && (
2071
+ <>
2072
+ {openaiError && (
2073
+ <div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3.5 py-2.5">
2074
+ <p className="text-red-400/90 text-[12px]">{openaiError}</p>
2075
+ </div>
2076
+ )}
2077
+
2078
+ <div className="space-y-1.5">
2079
+ {[
2080
+ 'Click the button below — your browser will open for ChatGPT sign-in',
2081
+ 'Sign in with your ChatGPT Plus or Pro account',
2082
+ 'Authentication completes automatically — no code to copy',
2083
+ ].map((text, i) => (
2084
+ <div key={i} className="flex items-start gap-2">
2085
+ <span className="flex-shrink-0 w-[18px] h-[18px] rounded-full bg-white/[0.06] text-white/30 text-[10px] font-medium flex items-center justify-center mt-px">{i + 1}</span>
2086
+ <p className="text-white/40 text-[12px] leading-relaxed">{text}</p>
2087
+ </div>
2088
+ ))}
2089
+ </div>
2090
+
2091
+ <button
2092
+ onClick={handleOpenAIAuth}
2093
+ disabled={openaiWaiting}
2094
+ className="w-full py-2.5 px-4 bg-white/[0.06] hover:bg-white/[0.09] text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-60"
2095
+ >
2096
+ {openaiWaiting ? (
2097
+ <><LoaderCircle className="h-3.5 w-3.5 animate-spin opacity-60" />Waiting for sign-in...</>
2098
+ ) : (
2099
+ <>Authenticate with ChatGPT<ArrowRight className="h-3.5 w-3.5 opacity-60" /></>
2100
+ )}
2101
+ </button>
2102
+
2103
+ {openaiWaiting && (
2104
+ <button
2105
+ onClick={handleOpenAICancel}
2106
+ className="w-full py-1.5 text-white/25 text-[11px] hover:text-white/40 transition-colors"
2107
+ >
2108
+ Cancel
2109
+ </button>
2110
+ )}
2111
+ </>
2112
+ )}
2113
+ </div>
2114
+ )}
2115
+
2116
+ {/* ── Model dropdown (after auth) ── */}
2117
+ {isConnected && (
2118
+ <>
2119
+ <div className="border-t border-white/[0.06] mt-4 mb-3" />
2120
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">
2121
+ Select a model
2122
+ </label>
2123
+ <ModelDropdown
2124
+ models={MODELS[provider] || []}
2125
+ value={model}
2126
+ onChange={setModel}
2127
+ />
2128
+ </>
2129
+ )}
2130
+
2131
+ {isConnected && (
2132
+ <button
2133
+ onClick={next}
2134
+ disabled={!canNext}
2135
+ className="w-full mt-4 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
2136
+ >
2137
+ Continue
2138
+ <ArrowRight className="h-4 w-4" />
2139
+ </button>
2140
+ )}
2141
+ </div>
2142
+ )}
2143
+
2144
+ {/* ── Step 5: Voice Messages + Complete ── */}
2145
+ {step === 5 && (
2146
+ <div>
2147
+ <h1 className="text-xl font-bold text-white tracking-tight mb-1">
2148
+ Voice Messages
2149
+ </h1>
2150
+ <p className="text-white/40 text-[13px] mt-1 leading-relaxed">
2151
+ Voice input works out of the box using your browser's built-in speech recognition. For better accuracy, you can optionally enable OpenAI Whisper.
2152
+ </p>
2153
+
2154
+ {/* Browser Speech Recognition card */}
2155
+ <div className="w-full mt-5 rounded-xl border border-emerald-500/20 bg-emerald-500/[0.04] p-4">
2156
+ <div className="flex items-center gap-3.5">
2157
+ <div className="w-10 h-10 rounded-xl bg-emerald-500/10 flex items-center justify-center shrink-0">
2158
+ <Mic className="h-5 w-5 text-emerald-400" />
2159
+ </div>
2160
+ <div className="flex-1">
2161
+ <div className="text-[14px] font-medium text-white">Browser Speech Recognition</div>
2162
+ <div className="text-[12px] text-white/35 mt-0.5 leading-relaxed">
2163
+ Built-in voice input — no setup needed. Works in Chrome, Edge, and Safari.
2164
+ </div>
2165
+ </div>
2166
+ <div className="flex items-center justify-center w-6 h-6 rounded-full bg-emerald-500/20 shrink-0">
2167
+ <Check className="h-3.5 w-3.5 text-emerald-400" />
2168
+ </div>
2169
+ </div>
2170
+ </div>
2171
+
2172
+ {/* Whisper upgrade toggle */}
2173
+ <button
2174
+ onClick={() => setWhisperEnabled((v) => !v)}
2175
+ className={`w-full mt-3 rounded-xl border transition-all duration-200 p-4 text-left ${
2176
+ whisperEnabled
2177
+ ? 'bg-white/[0.04] border-[#AF27E3]/40'
2178
+ : 'bg-transparent border-white/[0.06] hover:border-white/10 hover:bg-white/[0.02]'
2179
+ }`}
2180
+ >
2181
+ <div className="flex items-center gap-3.5">
2182
+ <img src="/icons/openai.svg" alt="OpenAI" className="w-10 h-10 rounded-xl bg-white/[0.04] p-1.5" />
2183
+ <div className="flex-1">
2184
+ <div className="text-[14px] font-medium text-white">OpenAI Whisper</div>
2185
+ <div className="text-[12px] text-white/35 mt-0.5 leading-relaxed">
2186
+ Upgrade to more accurate transcription that works in all browsers including Firefox.
2187
+ </div>
2188
+ </div>
2189
+ <div className={`w-10 h-[22px] rounded-full transition-colors duration-200 flex items-center px-0.5 shrink-0 ${
2190
+ whisperEnabled ? 'bg-gradient-brand' : 'bg-white/[0.08]'
2191
+ }`}>
2192
+ <div className={`w-[18px] h-[18px] rounded-full bg-white shadow-sm transition-transform duration-200 ${
2193
+ whisperEnabled ? 'translate-x-[18px]' : 'translate-x-0'
2194
+ }`} />
2195
+ </div>
2196
+ </div>
2197
+ </button>
2198
+
2199
+ {whisperEnabled && (
2200
+ <div className="mt-3">
2201
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block">OpenAI API Key</label>
2202
+ <input
2203
+ type="password"
2204
+ value={whisperKey}
2205
+ onChange={(e) => setWhisperKey(e.target.value.trim())}
2206
+ placeholder="sk-..."
2207
+ autoComplete="off"
2208
+ className={inputCls + ' font-mono text-[13px]'}
2209
+ />
2210
+ {whisperKey.length > 0 && !whisperKey.startsWith('sk-') && (
2211
+ <p className="text-amber-400/70 text-[11px] mt-1">Key should start with sk-</p>
2212
+ )}
2213
+ {whisperKey.length > 0 && whisperKey.startsWith('sk-') && whisperKey.length < 20 && (
2214
+ <p className="text-amber-400/70 text-[11px] mt-1">Key looks too short</p>
2215
+ )}
2216
+ <div className="flex items-start gap-2.5 mt-3 bg-white/[0.02] border border-white/[0.06] rounded-xl px-4 py-3">
2217
+ <Mic className="h-4 w-4 text-[#AF27E3]/60 mt-0.5 shrink-0" />
2218
+ <p className="text-white/35 text-[12px] leading-relaxed">
2219
+ Whisper provides more accurate transcription and works in all browsers including Firefox.
2220
+ </p>
2221
+ </div>
2222
+ </div>
2223
+ )}
2224
+
2225
+ <button
2226
+ onClick={handleComplete}
2227
+ disabled={saving || (whisperEnabled && (!whisperKey.startsWith('sk-') || whisperKey.length < 20))}
2228
+ className="w-full mt-5 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
2229
+ >
2230
+ {saving ? (
2231
+ <><LoaderCircle className="h-4 w-4 animate-spin" />Setting up...</>
2232
+ ) : (
2233
+ <>Complete Setup<ArrowRight className="h-4 w-4" /></>
2234
+ )}
2235
+ </button>
2236
+
2237
+ {!whisperEnabled && (
2238
+ <p className="text-center text-white/20 text-[11px] mt-2.5">
2239
+ Voice input is active using your browser's built-in speech recognition.
2240
+ </p>
2241
+ )}
2242
+ </div>
2243
+ )}
2244
+
2245
+ {/* ── Step 6: All Set (initial onboard only) ── */}
2246
+ {step === 6 && isInitialSetup && (() => {
2247
+ const isPrivate = tunnelMode === 'off';
2248
+ const finalUrl = (() => {
2249
+ if (isPrivate) return window.location.origin;
2250
+ if (tunnelMode === 'named') return `https://${tunnelDomain}`;
2251
+ if (handleChoice === 'relay' && registeredUrl) return registeredUrl;
2252
+ return tunnelUrl || `http://localhost:${3000}`;
2253
+ })();
2254
+ const finalUrlFull = finalUrl.startsWith('http') ? finalUrl : `https://${finalUrl}`;
2255
+ const descriptionText = isPrivate
2256
+ ? 'Your agent is running on your private network. Access it from any device on your local network or VPN.'
2257
+ : tunnelMode === 'named'
2258
+ ? 'Access your agent at your custom domain.'
2259
+ : handleChoice === 'relay' && registeredUrl
2260
+ ? 'Your agent is live and ready. From now on, access it using your custom URL below.'
2261
+ : 'Your agent is live and ready. Your tunnel URL is shown below. Note: this URL changes on restart.';
2262
+ return (
2263
+ <div className="flex flex-col items-center text-center">
2264
+ <div className="w-16 h-16 rounded-full bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center mb-5">
2265
+ <Check className="h-8 w-8 text-emerald-400" />
2266
+ </div>
2267
+ <h1 className="text-2xl font-bold text-white tracking-tight">
2268
+ All Set!
2269
+ </h1>
2270
+ <p className="text-white/40 text-[13px] mt-2 leading-relaxed max-w-[340px]">
2271
+ {descriptionText}
2272
+ </p>
2273
+
2274
+ {/* URL */}
2275
+ <div className="w-full mt-6 flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
2276
+ <span className="font-mono text-[13px] text-white/70 truncate flex-1 text-left">{finalUrl}</span>
2277
+ <button
2278
+ onClick={() => {
2279
+ navigator.clipboard.writeText(finalUrlFull);
2280
+ setPortalCopied(true);
2281
+ setTimeout(() => setPortalCopied(false), 2000);
2282
+ }}
2283
+ className="shrink-0 text-white/30 hover:text-white/60 transition-colors"
2284
+ >
2285
+ {portalCopied ? (
2286
+ <Check className="h-4 w-4 text-emerald-400" />
2287
+ ) : (
2288
+ <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
2289
+ <rect x="9" y="9" width="13" height="13" rx="2" />
2290
+ <path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
2291
+ </svg>
2292
+ )}
2293
+ </button>
2294
+ </div>
2295
+
2296
+ {/* Password display with eye toggle */}
2297
+ <div className="w-full mt-3">
2298
+ <label className="text-[12px] text-white/40 font-medium mb-1.5 block text-left">Your password</label>
2299
+ <div className="flex items-center gap-2 bg-white/[0.03] border border-white/[0.06] rounded-xl px-4 py-3">
2300
+ <span className="font-mono text-[13px] text-white/70 truncate flex-1 text-left">
2301
+ {showPassAllSet ? portalPass : '\u2022'.repeat(Math.max(portalPass.length, 8))}
2302
+ </span>
2303
+ <button
2304
+ onClick={() => setShowPassAllSet((v) => !v)}
2305
+ className="shrink-0 text-white/30 hover:text-white/60 transition-colors"
2306
+ >
2307
+ {showPassAllSet ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
2308
+ </button>
2309
+ </div>
2310
+ </div>
2311
+
2312
+ {/* Redirect / done button */}
2313
+ <button
2314
+ onClick={() => {
2315
+ if (isPrivate) {
2316
+ (window.top || window).location.href = '/';
2317
+ } else {
2318
+ (window.top || window).location.href = finalUrlFull;
2319
+ }
2320
+ }}
2321
+ className="w-full mt-6 py-3 bg-gradient-brand hover:opacity-90 text-white text-[14px] font-semibold rounded-full transition-colors flex items-center justify-center gap-2"
2322
+ >
2323
+ {isPrivate ? 'Go to dashboard' : 'Go to your agent'}
2324
+ <ExternalLink className="h-4 w-4" />
2325
+ </button>
2326
+
2327
+ <p className="text-white/20 text-[11px] mt-3 leading-relaxed">
2328
+ {isPrivate
2329
+ ? 'Access from any device on your network using the URL above.'
2330
+ : `You'll be redirected to your ${tunnelMode === 'named' ? 'custom domain' : handleChoice === 'relay' ? 'custom URL' : 'tunnel URL'}.`
2331
+ }
2332
+ </p>
2333
+ </div>
2334
+ );
2335
+ })()}
2336
+ </motion.div>
2337
+ </AnimatePresence>
2338
+
2339
+ {/* Back button (hidden on All Set step) */}
2340
+ {step > 0 && step < TOTAL_STEPS - 1 && !(step === 6 && isInitialSetup) && (
2341
+ <div className="px-8 pb-5 -mt-3">
2342
+ <button
2343
+ onClick={back}
2344
+ className="text-white/25 hover:text-white/50 text-[12px] transition-colors"
2345
+ >
2346
+ &larr; Back
2347
+ </button>
2348
+ </div>
2349
+ )}
2350
+ </motion.div>
2351
+ </div>
2352
+ );
2353
+ }