nastechai-desktop 18.1.0

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 (546) hide show
  1. package/.prettierrc +11 -0
  2. package/DESIGN.md +167 -0
  3. package/README.md +141 -0
  4. package/assets/icon.icns +0 -0
  5. package/assets/icon.ico +0 -0
  6. package/assets/icon.png +0 -0
  7. package/components.json +21 -0
  8. package/electron/backend-env.cjs +112 -0
  9. package/electron/backend-env.test.cjs +111 -0
  10. package/electron/backend-probes.cjs +106 -0
  11. package/electron/backend-probes.test.cjs +82 -0
  12. package/electron/backend-ready.cjs +66 -0
  13. package/electron/bootstrap-platform.cjs +91 -0
  14. package/electron/bootstrap-platform.test.cjs +111 -0
  15. package/electron/bootstrap-runner.cjs +720 -0
  16. package/electron/bootstrap-runner.test.cjs +138 -0
  17. package/electron/connection-config.cjs +254 -0
  18. package/electron/connection-config.test.cjs +329 -0
  19. package/electron/dashboard-token.cjs +99 -0
  20. package/electron/dashboard-token.test.cjs +142 -0
  21. package/electron/desktop-uninstall.cjs +232 -0
  22. package/electron/desktop-uninstall.test.cjs +246 -0
  23. package/electron/entitlements.mac.inherit.plist +14 -0
  24. package/electron/entitlements.mac.plist +14 -0
  25. package/electron/fs-read-dir.cjs +109 -0
  26. package/electron/fs-read-dir.test.cjs +364 -0
  27. package/electron/gateway-ws-probe.cjs +188 -0
  28. package/electron/gateway-ws-probe.test.cjs +122 -0
  29. package/electron/git-root.cjs +54 -0
  30. package/electron/git-root.test.cjs +40 -0
  31. package/electron/git-worktrees.cjs +174 -0
  32. package/electron/hardening.cjs +184 -0
  33. package/electron/hardening.test.cjs +116 -0
  34. package/electron/main.cjs +5762 -0
  35. package/electron/oauth-net-request.cjs +20 -0
  36. package/electron/oauth-net-request.test.cjs +34 -0
  37. package/electron/preload.cjs +135 -0
  38. package/electron/session-windows.cjs +99 -0
  39. package/electron/session-windows.test.cjs +177 -0
  40. package/electron/update-remote.cjs +56 -0
  41. package/electron/update-remote.test.cjs +78 -0
  42. package/electron/vscode-marketplace.cjs +331 -0
  43. package/electron/vscode-marketplace.test.cjs +113 -0
  44. package/electron/windows-child-process.test.cjs +57 -0
  45. package/electron/windows-user-env.cjs +76 -0
  46. package/electron/windows-user-env.test.cjs +90 -0
  47. package/electron/workspace-cwd.cjs +38 -0
  48. package/electron/workspace-cwd.test.cjs +45 -0
  49. package/eslint.config.mjs +122 -0
  50. package/index.html +17 -0
  51. package/package.json +254 -0
  52. package/pr-assets/session-source-folders.png +0 -0
  53. package/preview-demo.html +65 -0
  54. package/public/apple-touch-icon.png +0 -0
  55. package/public/ds-assets/filler-bg0.jpg +0 -0
  56. package/public/nastech-frames/nastech-frame-0.png +0 -0
  57. package/public/nastech-frames/nastech-frame-1.png +0 -0
  58. package/public/nastech-frames/nastech-frame-2.png +0 -0
  59. package/public/nastech-frames/nastech-frame-3.png +0 -0
  60. package/public/nastech-frames/nastech-frame-4.png +0 -0
  61. package/public/nastech-frames/nastech-frame-5.png +0 -0
  62. package/public/nastech-frames/nastech-frame-6.png +0 -0
  63. package/public/nastech-frames/nastech-frame-7.png +0 -0
  64. package/public/nastech-girl.jpg +0 -0
  65. package/public/nastech-sprite.png +0 -0
  66. package/public/nastech.png +0 -0
  67. package/scripts/after-pack.cjs +41 -0
  68. package/scripts/assert-dist-built.cjs +70 -0
  69. package/scripts/assert-dist-built.test.cjs +84 -0
  70. package/scripts/assert-root-install.cjs +13 -0
  71. package/scripts/before-build.cjs +11 -0
  72. package/scripts/before-pack.cjs +78 -0
  73. package/scripts/before-pack.test.cjs +53 -0
  74. package/scripts/click-session.mjs +51 -0
  75. package/scripts/dev-no-hmr.mjs +22 -0
  76. package/scripts/diag-jump.mjs +115 -0
  77. package/scripts/diag-scroll-reset.mjs +229 -0
  78. package/scripts/eval.mjs +21 -0
  79. package/scripts/leak-typing.mjs +222 -0
  80. package/scripts/measure-jump.mjs +108 -0
  81. package/scripts/measure-latency.mjs +184 -0
  82. package/scripts/measure-real-stream.mjs +252 -0
  83. package/scripts/measure-submit.mjs +179 -0
  84. package/scripts/measure-synthetic-stream.mjs +322 -0
  85. package/scripts/notarize-artifact.cjs +77 -0
  86. package/scripts/notarize.cjs +100 -0
  87. package/scripts/patch-electron-builder-mac-binary.cjs +59 -0
  88. package/scripts/probe-renderer.mjs +38 -0
  89. package/scripts/probe-thread.mjs +40 -0
  90. package/scripts/profile-long-stream.mjs +191 -0
  91. package/scripts/profile-real-stream.mjs +137 -0
  92. package/scripts/profile-synth-stream.mjs +103 -0
  93. package/scripts/profile-typing-lag.md +381 -0
  94. package/scripts/profile-typing.mjs +260 -0
  95. package/scripts/reload-renderer.mjs +25 -0
  96. package/scripts/reload.mjs +36 -0
  97. package/scripts/set-exe-identity.cjs +94 -0
  98. package/scripts/stage-native-deps.cjs +159 -0
  99. package/scripts/test-desktop.mjs +425 -0
  100. package/scripts/write-build-stamp.cjs +126 -0
  101. package/src/app/agents/index.tsx +398 -0
  102. package/src/app/artifacts/index.test.ts +62 -0
  103. package/src/app/artifacts/index.tsx +906 -0
  104. package/src/app/chat/chat-drop-overlay.tsx +48 -0
  105. package/src/app/chat/chat-swap-overlay.tsx +47 -0
  106. package/src/app/chat/composer/attachments.tsx +114 -0
  107. package/src/app/chat/composer/completion-drawer.tsx +63 -0
  108. package/src/app/chat/composer/context-menu.tsx +172 -0
  109. package/src/app/chat/composer/controls.tsx +289 -0
  110. package/src/app/chat/composer/drop-affordance.ts +2 -0
  111. package/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
  112. package/src/app/chat/composer/focus.ts +134 -0
  113. package/src/app/chat/composer/help-hint.tsx +59 -0
  114. package/src/app/chat/composer/hooks/use-at-completions.ts +141 -0
  115. package/src/app/chat/composer/hooks/use-live-completion-adapter.ts +119 -0
  116. package/src/app/chat/composer/hooks/use-mic-recorder.ts +291 -0
  117. package/src/app/chat/composer/hooks/use-slash-completions.ts +114 -0
  118. package/src/app/chat/composer/hooks/use-voice-conversation.ts +390 -0
  119. package/src/app/chat/composer/hooks/use-voice-recorder.ts +116 -0
  120. package/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
  121. package/src/app/chat/composer/index.tsx +1611 -0
  122. package/src/app/chat/composer/inline-refs.ts +138 -0
  123. package/src/app/chat/composer/model-pill.tsx +86 -0
  124. package/src/app/chat/composer/queue-panel.tsx +130 -0
  125. package/src/app/chat/composer/rich-editor.test.ts +18 -0
  126. package/src/app/chat/composer/rich-editor.ts +165 -0
  127. package/src/app/chat/composer/skin-slash-popover.tsx +61 -0
  128. package/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
  129. package/src/app/chat/composer/status-stack/index.tsx +202 -0
  130. package/src/app/chat/composer/status-stack/status-row.tsx +155 -0
  131. package/src/app/chat/composer/text-utils.test.ts +77 -0
  132. package/src/app/chat/composer/text-utils.ts +107 -0
  133. package/src/app/chat/composer/trigger-popover.test.tsx +42 -0
  134. package/src/app/chat/composer/trigger-popover.tsx +116 -0
  135. package/src/app/chat/composer/types.ts +64 -0
  136. package/src/app/chat/composer/url-dialog.tsx +82 -0
  137. package/src/app/chat/composer/voice-activity.tsx +252 -0
  138. package/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
  139. package/src/app/chat/hooks/use-composer-actions.ts +525 -0
  140. package/src/app/chat/hooks/use-file-drop-zone.ts +118 -0
  141. package/src/app/chat/index.tsx +390 -0
  142. package/src/app/chat/perf-probe.tsx +269 -0
  143. package/src/app/chat/right-rail/index.ts +1 -0
  144. package/src/app/chat/right-rail/preview-console-state.ts +82 -0
  145. package/src/app/chat/right-rail/preview-console.tsx +290 -0
  146. package/src/app/chat/right-rail/preview-file.tsx +559 -0
  147. package/src/app/chat/right-rail/preview-pane.test.tsx +43 -0
  148. package/src/app/chat/right-rail/preview-pane.tsx +657 -0
  149. package/src/app/chat/right-rail/preview.tsx +171 -0
  150. package/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
  151. package/src/app/chat/scroll-to-bottom-button.tsx +74 -0
  152. package/src/app/chat/sidebar/cron-jobs-section.tsx +325 -0
  153. package/src/app/chat/sidebar/index.tsx +1219 -0
  154. package/src/app/chat/sidebar/load-more-row.tsx +30 -0
  155. package/src/app/chat/sidebar/order.test.ts +21 -0
  156. package/src/app/chat/sidebar/order.ts +17 -0
  157. package/src/app/chat/sidebar/profile-switcher.tsx +516 -0
  158. package/src/app/chat/sidebar/session-actions-menu.tsx +264 -0
  159. package/src/app/chat/sidebar/session-row.tsx +257 -0
  160. package/src/app/chat/sidebar/virtual-session-list.tsx +154 -0
  161. package/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
  162. package/src/app/chat/sidebar/workspace-groups.ts +326 -0
  163. package/src/app/chat/thread-loading.test.ts +34 -0
  164. package/src/app/chat/thread-loading.ts +26 -0
  165. package/src/app/command-center/index.tsx +654 -0
  166. package/src/app/command-palette/index.tsx +513 -0
  167. package/src/app/command-palette/marketplace-theme-page.tsx +157 -0
  168. package/src/app/cron/index.tsx +942 -0
  169. package/src/app/cron/job-state.ts +29 -0
  170. package/src/app/desktop-controller.tsx +938 -0
  171. package/src/app/floating-hud.ts +22 -0
  172. package/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
  173. package/src/app/gateway/hooks/use-gateway-boot.ts +387 -0
  174. package/src/app/gateway/hooks/use-gateway-request.ts +138 -0
  175. package/src/app/hooks/use-keybinds.ts +186 -0
  176. package/src/app/hooks/use-refresh-hotkey.ts +45 -0
  177. package/src/app/hooks/use-route-enum-param.ts +38 -0
  178. package/src/app/index.tsx +1 -0
  179. package/src/app/layout-constants.ts +13 -0
  180. package/src/app/messaging/index.tsx +648 -0
  181. package/src/app/messaging/platform-icon.tsx +93 -0
  182. package/src/app/model-picker-overlay.tsx +42 -0
  183. package/src/app/model-visibility-overlay.tsx +31 -0
  184. package/src/app/overlays/overlay-chrome.tsx +66 -0
  185. package/src/app/overlays/overlay-search-input.tsx +33 -0
  186. package/src/app/overlays/overlay-split-layout.tsx +130 -0
  187. package/src/app/overlays/overlay-view.tsx +91 -0
  188. package/src/app/page-search-shell.tsx +75 -0
  189. package/src/app/profiles/create-profile-dialog.tsx +154 -0
  190. package/src/app/profiles/delete-profile-dialog.tsx +65 -0
  191. package/src/app/profiles/index.tsx +671 -0
  192. package/src/app/profiles/rename-profile-dialog.tsx +125 -0
  193. package/src/app/right-sidebar/files/dnd-manager.ts +27 -0
  194. package/src/app/right-sidebar/files/ipc.test.ts +100 -0
  195. package/src/app/right-sidebar/files/ipc.ts +161 -0
  196. package/src/app/right-sidebar/files/remote-picker.tsx +177 -0
  197. package/src/app/right-sidebar/files/tree.tsx +224 -0
  198. package/src/app/right-sidebar/files/use-project-tree.test.ts +190 -0
  199. package/src/app/right-sidebar/files/use-project-tree.ts +268 -0
  200. package/src/app/right-sidebar/index.test.tsx +75 -0
  201. package/src/app/right-sidebar/index.tsx +395 -0
  202. package/src/app/right-sidebar/store.ts +15 -0
  203. package/src/app/right-sidebar/terminal/buffer.ts +65 -0
  204. package/src/app/right-sidebar/terminal/index.tsx +98 -0
  205. package/src/app/right-sidebar/terminal/persistent.tsx +122 -0
  206. package/src/app/right-sidebar/terminal/selection.ts +75 -0
  207. package/src/app/right-sidebar/terminal/use-terminal-session.ts +504 -0
  208. package/src/app/routes.ts +88 -0
  209. package/src/app/session/hooks/use-context-suggestions.ts +58 -0
  210. package/src/app/session/hooks/use-cwd-actions.ts +109 -0
  211. package/src/app/session/hooks/use-message-stream.ts +957 -0
  212. package/src/app/session/hooks/use-model-controls.test.tsx +198 -0
  213. package/src/app/session/hooks/use-model-controls.ts +106 -0
  214. package/src/app/session/hooks/use-nastech-config.ts +74 -0
  215. package/src/app/session/hooks/use-preview-routing.test.tsx +168 -0
  216. package/src/app/session/hooks/use-preview-routing.ts +223 -0
  217. package/src/app/session/hooks/use-prompt-actions.test.tsx +316 -0
  218. package/src/app/session/hooks/use-prompt-actions.ts +1030 -0
  219. package/src/app/session/hooks/use-route-resume.test.tsx +136 -0
  220. package/src/app/session/hooks/use-route-resume.ts +115 -0
  221. package/src/app/session/hooks/use-session-actions.test.tsx +119 -0
  222. package/src/app/session/hooks/use-session-actions.ts +885 -0
  223. package/src/app/session/hooks/use-session-state-cache.test.tsx +118 -0
  224. package/src/app/session/hooks/use-session-state-cache.ts +191 -0
  225. package/src/app/session-picker-overlay.tsx +32 -0
  226. package/src/app/session-switcher.tsx +107 -0
  227. package/src/app/settings/about-settings.tsx +173 -0
  228. package/src/app/settings/appearance-settings.tsx +162 -0
  229. package/src/app/settings/config-settings.tsx +384 -0
  230. package/src/app/settings/constants.ts +545 -0
  231. package/src/app/settings/credential-key-ui.tsx +373 -0
  232. package/src/app/settings/env-credentials.tsx +198 -0
  233. package/src/app/settings/env-var-actions-menu.tsx +136 -0
  234. package/src/app/settings/field-copy.ts +56 -0
  235. package/src/app/settings/gateway-settings.tsx +620 -0
  236. package/src/app/settings/helpers.test.ts +138 -0
  237. package/src/app/settings/helpers.ts +151 -0
  238. package/src/app/settings/index.tsx +237 -0
  239. package/src/app/settings/keys-settings.tsx +96 -0
  240. package/src/app/settings/mcp-settings.tsx +271 -0
  241. package/src/app/settings/model-settings.test.tsx +157 -0
  242. package/src/app/settings/model-settings.tsx +559 -0
  243. package/src/app/settings/notifications-settings.tsx +150 -0
  244. package/src/app/settings/primitives.tsx +115 -0
  245. package/src/app/settings/providers-settings.test.tsx +100 -0
  246. package/src/app/settings/providers-settings.tsx +258 -0
  247. package/src/app/settings/sessions-settings.tsx +276 -0
  248. package/src/app/settings/toolset-config-panel.test.tsx +289 -0
  249. package/src/app/settings/toolset-config-panel.tsx +449 -0
  250. package/src/app/settings/types.ts +42 -0
  251. package/src/app/settings/uninstall-section.tsx +185 -0
  252. package/src/app/settings/use-deep-link-highlight.ts +60 -0
  253. package/src/app/shell/app-shell.tsx +167 -0
  254. package/src/app/shell/gateway-menu-panel.tsx +150 -0
  255. package/src/app/shell/hooks/use-overlay-routing.ts +71 -0
  256. package/src/app/shell/hooks/use-status-snapshot.ts +57 -0
  257. package/src/app/shell/hooks/use-statusbar-items.tsx +403 -0
  258. package/src/app/shell/keybind-panel.tsx +220 -0
  259. package/src/app/shell/model-edit-submenu.test.tsx +84 -0
  260. package/src/app/shell/model-edit-submenu.tsx +245 -0
  261. package/src/app/shell/model-menu-panel.tsx +295 -0
  262. package/src/app/shell/sidebar-label.tsx +22 -0
  263. package/src/app/shell/statusbar-controls.tsx +185 -0
  264. package/src/app/shell/titlebar-controls.tsx +244 -0
  265. package/src/app/shell/titlebar.test.ts +26 -0
  266. package/src/app/shell/titlebar.ts +45 -0
  267. package/src/app/shell/use-group-registry.ts +39 -0
  268. package/src/app/skills/index.test.tsx +103 -0
  269. package/src/app/skills/index.tsx +371 -0
  270. package/src/app/types.ts +99 -0
  271. package/src/app/updates-overlay.tsx +369 -0
  272. package/src/components/Backdrop.tsx +114 -0
  273. package/src/components/assistant-ui/ansi-text.tsx +34 -0
  274. package/src/components/assistant-ui/clarify-tool.tsx +281 -0
  275. package/src/components/assistant-ui/directive-text.test.ts +39 -0
  276. package/src/components/assistant-ui/directive-text.tsx +389 -0
  277. package/src/components/assistant-ui/markdown-text.test.ts +204 -0
  278. package/src/components/assistant-ui/markdown-text.tsx +497 -0
  279. package/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
  280. package/src/components/assistant-ui/message-render-boundary.tsx +48 -0
  281. package/src/components/assistant-ui/streaming.test.tsx +739 -0
  282. package/src/components/assistant-ui/thread-list.tsx +307 -0
  283. package/src/components/assistant-ui/thread-virtualizer.tsx +512 -0
  284. package/src/components/assistant-ui/thread.tsx +1474 -0
  285. package/src/components/assistant-ui/todo-tool.tsx +109 -0
  286. package/src/components/assistant-ui/tool-approval-group.test.tsx +158 -0
  287. package/src/components/assistant-ui/tool-approval.test.tsx +81 -0
  288. package/src/components/assistant-ui/tool-approval.tsx +209 -0
  289. package/src/components/assistant-ui/tool-fallback-model.test.ts +66 -0
  290. package/src/components/assistant-ui/tool-fallback-model.ts +1368 -0
  291. package/src/components/assistant-ui/tool-fallback.tsx +466 -0
  292. package/src/components/assistant-ui/tooltip-icon-button.tsx +33 -0
  293. package/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
  294. package/src/components/assistant-ui/user-message-text.tsx +150 -0
  295. package/src/components/boot-failure-overlay.tsx +246 -0
  296. package/src/components/boot-failure-reauth.test.ts +100 -0
  297. package/src/components/boot-failure-reauth.ts +81 -0
  298. package/src/components/brand-mark.tsx +19 -0
  299. package/src/components/chat/activity-timer-text.tsx +24 -0
  300. package/src/components/chat/activity-timer.test.tsx +43 -0
  301. package/src/components/chat/activity-timer.ts +64 -0
  302. package/src/components/chat/code-card.tsx +78 -0
  303. package/src/components/chat/compact-markdown.tsx +113 -0
  304. package/src/components/chat/composer-dock.ts +31 -0
  305. package/src/components/chat/diff-lines.tsx +54 -0
  306. package/src/components/chat/disclosure-row.tsx +63 -0
  307. package/src/components/chat/generated-image-context.tsx +19 -0
  308. package/src/components/chat/generated-image-result.tsx +174 -0
  309. package/src/components/chat/image-generation-placeholder.tsx +279 -0
  310. package/src/components/chat/intro-copy.jsonl +75 -0
  311. package/src/components/chat/intro.tsx +182 -0
  312. package/src/components/chat/preview-attachment.tsx +125 -0
  313. package/src/components/chat/shiki-highlighter.tsx +107 -0
  314. package/src/components/chat/status-row.tsx +70 -0
  315. package/src/components/chat/status-section.tsx +42 -0
  316. package/src/components/chat/terminal-output.tsx +50 -0
  317. package/src/components/chat/zoomable-image.tsx +177 -0
  318. package/src/components/desktop-install-overlay.tsx +595 -0
  319. package/src/components/desktop-onboarding-overlay.test.tsx +100 -0
  320. package/src/components/desktop-onboarding-overlay.tsx +1286 -0
  321. package/src/components/error-boundary.tsx +77 -0
  322. package/src/components/gateway-connecting-overlay.test.tsx +143 -0
  323. package/src/components/gateway-connecting-overlay.tsx +183 -0
  324. package/src/components/haptics-provider.tsx +19 -0
  325. package/src/components/language-switcher.test.tsx +53 -0
  326. package/src/components/language-switcher.tsx +175 -0
  327. package/src/components/model-picker.tsx +340 -0
  328. package/src/components/model-visibility-dialog.tsx +155 -0
  329. package/src/components/notifications.tsx +196 -0
  330. package/src/components/page-loader.tsx +34 -0
  331. package/src/components/pane-shell/context.ts +14 -0
  332. package/src/components/pane-shell/index.ts +4 -0
  333. package/src/components/pane-shell/pane-shell.test.tsx +333 -0
  334. package/src/components/pane-shell/pane-shell.tsx +330 -0
  335. package/src/components/prompt-overlays.tsx +234 -0
  336. package/src/components/session-picker.tsx +108 -0
  337. package/src/components/status-dot.tsx +26 -0
  338. package/src/components/ui/action-status.tsx +25 -0
  339. package/src/components/ui/alert.tsx +53 -0
  340. package/src/components/ui/badge.tsx +35 -0
  341. package/src/components/ui/braille-spinner.tsx +61 -0
  342. package/src/components/ui/button.tsx +81 -0
  343. package/src/components/ui/checkbox.tsx +27 -0
  344. package/src/components/ui/codicon.tsx +20 -0
  345. package/src/components/ui/command.tsx +111 -0
  346. package/src/components/ui/confirm-dialog.tsx +109 -0
  347. package/src/components/ui/context-menu.tsx +141 -0
  348. package/src/components/ui/control.ts +25 -0
  349. package/src/components/ui/copy-button.test.tsx +36 -0
  350. package/src/components/ui/copy-button.tsx +229 -0
  351. package/src/components/ui/dialog.tsx +152 -0
  352. package/src/components/ui/disclosure-caret.tsx +20 -0
  353. package/src/components/ui/dropdown-menu.tsx +291 -0
  354. package/src/components/ui/error-state.tsx +50 -0
  355. package/src/components/ui/fade-text.tsx +110 -0
  356. package/src/components/ui/glyph-spinner.tsx +63 -0
  357. package/src/components/ui/input.tsx +22 -0
  358. package/src/components/ui/kbd.tsx +37 -0
  359. package/src/components/ui/loader.tsx +558 -0
  360. package/src/components/ui/log-view.tsx +17 -0
  361. package/src/components/ui/pagination.tsx +114 -0
  362. package/src/components/ui/popover.tsx +44 -0
  363. package/src/components/ui/scroll-area.tsx +43 -0
  364. package/src/components/ui/search-field.tsx +80 -0
  365. package/src/components/ui/segmented-control.tsx +51 -0
  366. package/src/components/ui/select.tsx +92 -0
  367. package/src/components/ui/separator.tsx +26 -0
  368. package/src/components/ui/sheet.tsx +116 -0
  369. package/src/components/ui/sidebar.tsx +674 -0
  370. package/src/components/ui/skeleton.tsx +7 -0
  371. package/src/components/ui/switch.tsx +49 -0
  372. package/src/components/ui/tabs.tsx +36 -0
  373. package/src/components/ui/text-tab.tsx +43 -0
  374. package/src/components/ui/textarea.tsx +11 -0
  375. package/src/components/ui/tool-icon.tsx +65 -0
  376. package/src/components/ui/tooltip.tsx +69 -0
  377. package/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
  378. package/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
  379. package/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
  380. package/src/global.d.ts +457 -0
  381. package/src/hooks/use-image-download.ts +85 -0
  382. package/src/hooks/use-media-query.ts +24 -0
  383. package/src/hooks/use-mobile.ts +3 -0
  384. package/src/hooks/use-resize-observer.ts +38 -0
  385. package/src/hooks/use-worktree-info.ts +68 -0
  386. package/src/i18n/catalog.ts +12 -0
  387. package/src/i18n/context.test.tsx +232 -0
  388. package/src/i18n/context.tsx +183 -0
  389. package/src/i18n/define-locale.ts +41 -0
  390. package/src/i18n/en.ts +1779 -0
  391. package/src/i18n/index.ts +20 -0
  392. package/src/i18n/ja.ts +1890 -0
  393. package/src/i18n/languages.test.ts +43 -0
  394. package/src/i18n/languages.ts +86 -0
  395. package/src/i18n/runtime.test.ts +75 -0
  396. package/src/i18n/runtime.ts +53 -0
  397. package/src/i18n/types.ts +1452 -0
  398. package/src/i18n/zh-hant.ts +1849 -0
  399. package/src/i18n/zh.ts +1923 -0
  400. package/src/lib/ansi.test.ts +123 -0
  401. package/src/lib/ansi.ts +175 -0
  402. package/src/lib/chat-messages.test.ts +708 -0
  403. package/src/lib/chat-messages.ts +885 -0
  404. package/src/lib/chat-runtime.test.ts +18 -0
  405. package/src/lib/chat-runtime.ts +335 -0
  406. package/src/lib/clipboard.ts +28 -0
  407. package/src/lib/commit-changelog.test.ts +114 -0
  408. package/src/lib/commit-changelog.ts +177 -0
  409. package/src/lib/completion-sound.ts +519 -0
  410. package/src/lib/desktop-fs.test.ts +116 -0
  411. package/src/lib/desktop-fs.ts +113 -0
  412. package/src/lib/desktop-slash-commands.test.ts +126 -0
  413. package/src/lib/desktop-slash-commands.ts +286 -0
  414. package/src/lib/embedded-images.test.ts +35 -0
  415. package/src/lib/embedded-images.ts +60 -0
  416. package/src/lib/external-link.test.tsx +168 -0
  417. package/src/lib/external-link.tsx +303 -0
  418. package/src/lib/gateway-events.test.ts +27 -0
  419. package/src/lib/gateway-events.ts +49 -0
  420. package/src/lib/gateway-ws-url.test.ts +78 -0
  421. package/src/lib/gateway-ws-url.ts +91 -0
  422. package/src/lib/generated-images.test.ts +97 -0
  423. package/src/lib/generated-images.ts +116 -0
  424. package/src/lib/haptics.ts +129 -0
  425. package/src/lib/icons.ts +203 -0
  426. package/src/lib/incremental-external-store-runtime.ts +188 -0
  427. package/src/lib/katex-memo.ts +260 -0
  428. package/src/lib/keybinds/actions.ts +125 -0
  429. package/src/lib/keybinds/combo.test.ts +86 -0
  430. package/src/lib/keybinds/combo.ts +169 -0
  431. package/src/lib/local-preview.ts +126 -0
  432. package/src/lib/markdown-code.test.ts +23 -0
  433. package/src/lib/markdown-code.ts +195 -0
  434. package/src/lib/markdown-preprocess.ts +386 -0
  435. package/src/lib/media.remote.test.ts +58 -0
  436. package/src/lib/media.ts +111 -0
  437. package/src/lib/model-status-label.test.ts +31 -0
  438. package/src/lib/model-status-label.ts +103 -0
  439. package/src/lib/mutable-ref.ts +6 -0
  440. package/src/lib/preview-targets.test.ts +27 -0
  441. package/src/lib/preview-targets.ts +63 -0
  442. package/src/lib/profile-color.ts +58 -0
  443. package/src/lib/provider-setup-errors.test.ts +26 -0
  444. package/src/lib/provider-setup-errors.ts +12 -0
  445. package/src/lib/query-client.ts +13 -0
  446. package/src/lib/remend-tail.test.ts +105 -0
  447. package/src/lib/remend-tail.ts +108 -0
  448. package/src/lib/runtime-readiness.test.ts +65 -0
  449. package/src/lib/runtime-readiness.ts +147 -0
  450. package/src/lib/session-export.ts +57 -0
  451. package/src/lib/session-search.test.ts +58 -0
  452. package/src/lib/session-search.ts +19 -0
  453. package/src/lib/session-source.ts +62 -0
  454. package/src/lib/speech-text.ts +35 -0
  455. package/src/lib/statusbar.ts +91 -0
  456. package/src/lib/storage.test.ts +25 -0
  457. package/src/lib/storage.ts +107 -0
  458. package/src/lib/todos.test.ts +35 -0
  459. package/src/lib/todos.ts +51 -0
  460. package/src/lib/tool-result-summary.test.ts +106 -0
  461. package/src/lib/tool-result-summary.ts +467 -0
  462. package/src/lib/update-copy.test.ts +38 -0
  463. package/src/lib/update-copy.ts +44 -0
  464. package/src/lib/use-enter-animation.ts +100 -0
  465. package/src/lib/utils.ts +6 -0
  466. package/src/lib/voice-playback.ts +128 -0
  467. package/src/lib/yolo-session.ts +26 -0
  468. package/src/main.tsx +43 -0
  469. package/src/nastech.test.ts +49 -0
  470. package/src/nastech.ts +718 -0
  471. package/src/store/activity.ts +100 -0
  472. package/src/store/boot.ts +91 -0
  473. package/src/store/clarify.test.ts +81 -0
  474. package/src/store/clarify.ts +69 -0
  475. package/src/store/command-palette.ts +20 -0
  476. package/src/store/compaction.test.ts +53 -0
  477. package/src/store/compaction.ts +38 -0
  478. package/src/store/completion-sound.ts +32 -0
  479. package/src/store/composer-input-history.test.ts +147 -0
  480. package/src/store/composer-input-history.ts +158 -0
  481. package/src/store/composer-queue.test.ts +148 -0
  482. package/src/store/composer-queue.ts +239 -0
  483. package/src/store/composer-status.test.ts +99 -0
  484. package/src/store/composer-status.ts +277 -0
  485. package/src/store/composer.test.ts +106 -0
  486. package/src/store/composer.ts +184 -0
  487. package/src/store/cron.ts +19 -0
  488. package/src/store/gateway.ts +290 -0
  489. package/src/store/haptics.ts +17 -0
  490. package/src/store/keybinds.ts +139 -0
  491. package/src/store/layout.ts +176 -0
  492. package/src/store/model-presets.test.ts +51 -0
  493. package/src/store/model-presets.ts +86 -0
  494. package/src/store/model-visibility.test.ts +37 -0
  495. package/src/store/model-visibility.ts +108 -0
  496. package/src/store/native-notifications.test.ts +192 -0
  497. package/src/store/native-notifications.ts +203 -0
  498. package/src/store/notifications.ts +165 -0
  499. package/src/store/onboarding.test.ts +372 -0
  500. package/src/store/onboarding.ts +866 -0
  501. package/src/store/panes.test.ts +146 -0
  502. package/src/store/panes.ts +145 -0
  503. package/src/store/preview.test.ts +135 -0
  504. package/src/store/preview.ts +466 -0
  505. package/src/store/profile.test.ts +89 -0
  506. package/src/store/profile.ts +365 -0
  507. package/src/store/prompts.test.ts +121 -0
  508. package/src/store/prompts.ts +115 -0
  509. package/src/store/session-switcher.test.ts +115 -0
  510. package/src/store/session-switcher.ts +128 -0
  511. package/src/store/session-sync.ts +25 -0
  512. package/src/store/session.test.ts +131 -0
  513. package/src/store/session.ts +255 -0
  514. package/src/store/subagents.test.ts +111 -0
  515. package/src/store/subagents.ts +260 -0
  516. package/src/store/thread-scroll.ts +46 -0
  517. package/src/store/todos.test.ts +47 -0
  518. package/src/store/todos.ts +64 -0
  519. package/src/store/tool-diffs.ts +23 -0
  520. package/src/store/tool-dismiss.ts +45 -0
  521. package/src/store/tool-view.ts +91 -0
  522. package/src/store/translucency.ts +38 -0
  523. package/src/store/updates.test.ts +77 -0
  524. package/src/store/updates.ts +315 -0
  525. package/src/store/voice-playback.ts +24 -0
  526. package/src/store/windows.test.ts +143 -0
  527. package/src/store/windows.ts +77 -0
  528. package/src/styles.css +1235 -0
  529. package/src/themes/color.ts +142 -0
  530. package/src/themes/context.tsx +339 -0
  531. package/src/themes/index.ts +3 -0
  532. package/src/themes/install.test.ts +119 -0
  533. package/src/themes/install.ts +95 -0
  534. package/src/themes/presets.test.ts +33 -0
  535. package/src/themes/presets.ts +293 -0
  536. package/src/themes/profile-theme.test.ts +41 -0
  537. package/src/themes/types.ts +66 -0
  538. package/src/themes/use-skin-command.ts +60 -0
  539. package/src/themes/user-themes.test.ts +63 -0
  540. package/src/themes/user-themes.ts +122 -0
  541. package/src/themes/vscode.test.ts +171 -0
  542. package/src/themes/vscode.ts +343 -0
  543. package/src/types/nastech.ts +646 -0
  544. package/src/vite-env.d.ts +1 -0
  545. package/tsconfig.json +25 -0
  546. package/vite.config.ts +56 -0
@@ -0,0 +1,138 @@
1
+ const assert = require('node:assert/strict')
2
+ const test = require('node:test')
3
+ const fs = require('node:fs')
4
+ const os = require('node:os')
5
+ const path = require('node:path')
6
+
7
+ const {
8
+ runBootstrap,
9
+ resolveInstallScript,
10
+ installedAgentInstallScript,
11
+ cachedScriptPath
12
+ } = require('./bootstrap-runner.cjs')
13
+
14
+ const SCRIPT_NAME = process.platform === 'win32' ? 'install.ps1' : 'install.sh'
15
+
16
+ function mkTmpHome() {
17
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'nastech-bootstrap-test-'))
18
+ }
19
+
20
+ test('runBootstrap bails immediately when the signal is already aborted', async () => {
21
+ const controller = new AbortController()
22
+ controller.abort()
23
+
24
+ const events = []
25
+ const result = await runBootstrap({
26
+ installStamp: null,
27
+ activeRoot: '/tmp/nastech-runner-test',
28
+ sourceRepoRoot: null,
29
+ nastechHome: '/tmp/nastech-runner-test',
30
+ logRoot: '/tmp/nastech-runner-test',
31
+ onEvent: ev => events.push(ev),
32
+ abortSignal: controller.signal
33
+ })
34
+
35
+ // Cancelled before any install script is spawned.
36
+ assert.deepEqual(result, { ok: false, cancelled: true })
37
+ assert.ok(
38
+ events.some(ev => ev.type === 'failed' && /cancelled/i.test(ev.error)),
39
+ 'should emit a cancelled failure event'
40
+ )
41
+ })
42
+
43
+ test('installedAgentInstallScript resolves the installer in the agent checkout', () => {
44
+ const home = mkTmpHome()
45
+ try {
46
+ assert.equal(installedAgentInstallScript(home), null, 'absent before the checkout exists')
47
+
48
+ const scriptsDir = path.join(home, 'nastech-agent', 'scripts')
49
+ fs.mkdirSync(scriptsDir, { recursive: true })
50
+ const scriptPath = path.join(scriptsDir, SCRIPT_NAME)
51
+ fs.writeFileSync(scriptPath, '#!/bin/sh\necho hi\n')
52
+
53
+ assert.equal(installedAgentInstallScript(home), scriptPath)
54
+ assert.equal(installedAgentInstallScript(null), null, 'null home -> null')
55
+ } finally {
56
+ fs.rmSync(home, { recursive: true, force: true })
57
+ }
58
+ })
59
+
60
+ test('resolveInstallScript prefers a cached script without touching the network', async () => {
61
+ const home = mkTmpHome()
62
+ try {
63
+ const commit = 'a'.repeat(40)
64
+ const cached = cachedScriptPath(home, commit)
65
+ fs.mkdirSync(path.dirname(cached), { recursive: true })
66
+ fs.writeFileSync(cached, '#!/bin/sh\necho cached\n')
67
+
68
+ const logs = []
69
+ const result = await resolveInstallScript({
70
+ installStamp: { commit },
71
+ sourceRepoRoot: null,
72
+ nastechHome: home,
73
+ emit: ev => logs.push(ev)
74
+ })
75
+
76
+ assert.equal(result.source, 'cache')
77
+ assert.equal(result.path, cached)
78
+ } finally {
79
+ fs.rmSync(home, { recursive: true, force: true })
80
+ }
81
+ })
82
+
83
+ test('resolveInstallScript falls back to the installed agent checkout on a 404', async () => {
84
+ const home = mkTmpHome()
85
+ try {
86
+ const commit = 'a'.repeat(40)
87
+ // Seed the installed agent checkout so the fallback has something to resolve.
88
+ const scriptsDir = path.join(home, 'nastech-agent', 'scripts')
89
+ fs.mkdirSync(scriptsDir, { recursive: true })
90
+ const installed = path.join(scriptsDir, SCRIPT_NAME)
91
+ fs.writeFileSync(installed, '#!/bin/sh\necho fallback\n')
92
+
93
+ const logs = []
94
+ const result = await resolveInstallScript({
95
+ installStamp: { commit },
96
+ sourceRepoRoot: null,
97
+ nastechHome: home,
98
+ emit: ev => logs.push(ev),
99
+ // Simulate GitHub returning a 404 for the pinned commit.
100
+ _download: async () => {
101
+ throw new Error('Failed to download install.sh: HTTP 404')
102
+ }
103
+ })
104
+
105
+ assert.equal(result.source, 'installed-agent')
106
+ // It should have copied the installer into the bootstrap cache.
107
+ assert.equal(result.path, cachedScriptPath(home, commit))
108
+ assert.ok(fs.existsSync(result.path), 'fallback script copied into cache')
109
+ assert.ok(
110
+ logs.some(ev => /falling back to installed agent/.test(ev.line || '')),
111
+ 'emits a fallback log line'
112
+ )
113
+ } finally {
114
+ fs.rmSync(home, { recursive: true, force: true })
115
+ }
116
+ })
117
+
118
+ test('resolveInstallScript rethrows when the 404 fallback is unavailable', async () => {
119
+ const home = mkTmpHome()
120
+ try {
121
+ const commit = 'a'.repeat(40)
122
+ // No installed agent checkout seeded -> nothing to fall back to.
123
+ await assert.rejects(
124
+ resolveInstallScript({
125
+ installStamp: { commit },
126
+ sourceRepoRoot: null,
127
+ nastechHome: home,
128
+ emit: () => {},
129
+ _download: async () => {
130
+ throw new Error('Failed to download install.sh: HTTP 404')
131
+ }
132
+ }),
133
+ /HTTP 404|Failed to download/
134
+ )
135
+ } finally {
136
+ fs.rmSync(home, { recursive: true, force: true })
137
+ }
138
+ })
@@ -0,0 +1,254 @@
1
+ /**
2
+ * connection-config.cjs
3
+ *
4
+ * Pure, electron-free helpers for the desktop's remote-gateway connection
5
+ * config: URL normalization, WS-URL construction (token vs OAuth ticket),
6
+ * auth-mode classification, and the auth-mode coercion rules.
7
+ *
8
+ * Kept standalone (no `require('electron')`) so it can be unit-tested with
9
+ * `node --test` — same pattern as backend-probes.cjs / bootstrap-platform.cjs.
10
+ * main.cjs requires these and wires them into the electron-coupled IPC layer.
11
+ *
12
+ * Background on the two auth models a remote gateway can use:
13
+ * - 'token': legacy static dashboard session token. REST uses an
14
+ * `X-NasTech-Session-Token` header; WS uses `?token=`.
15
+ * - 'oauth': hosted gateways gate behind an OAuth provider. REST is authed
16
+ * by an HttpOnly session cookie; WS upgrades require a single-use
17
+ * `?ticket=` minted at POST /api/auth/ws-ticket. The gateway advertises
18
+ * this via the public `/api/status` field `auth_required: true`.
19
+ */
20
+
21
+ // Bare + prefixed variants of the session cookies the gateway may set,
22
+ // depending on its deploy shape (HTTPS direct → __Host-, behind a path prefix
23
+ // → __Secure-, loopback HTTP → bare). Mirrors
24
+ // nastech_cli/dashboard_auth/cookies.py.
25
+ //
26
+ // Two cookies are in play (see that module):
27
+ // - nastech_session_at: the OAuth access token. Short-lived (~15 min); its
28
+ // Max-Age tracks the access-token TTL, so the cookie jar drops it the
29
+ // instant the AT expires.
30
+ // - nastech_session_rt: the OAuth refresh token. Long-lived (24h rotating,
31
+ // reuse-detected — Portal NAS #293 / nastech #37247). When the AT cookie
32
+ // has lapsed but the RT cookie is still present, the gateway middleware
33
+ // transparently rotates a fresh AT on the next authenticated request
34
+ // (POST /api/auth/ws-ticket), so the session is still LIVE even with no
35
+ // AT cookie. A liveness check that looked only at the AT cookie would
36
+ // force a needless full re-login every ~15 min — hence cookiesHaveLiveSession.
37
+ const AT_COOKIE_VARIANTS = ['__Host-nastech_session_at', '__Secure-nastech_session_at', 'nastech_session_at']
38
+ const RT_COOKIE_VARIANTS = ['__Host-nastech_session_rt', '__Secure-nastech_session_rt', 'nastech_session_rt']
39
+
40
+ function normalizeRemoteBaseUrl(rawUrl) {
41
+ const value = String(rawUrl || '').trim()
42
+
43
+ if (!value) {
44
+ throw new Error('Remote gateway URL is required.')
45
+ }
46
+
47
+ let parsed
48
+ try {
49
+ parsed = new URL(value)
50
+ } catch (error) {
51
+ throw new Error(`Remote gateway URL is not valid: ${error.message}`)
52
+ }
53
+
54
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
55
+ throw new Error(`Remote gateway URL must be http:// or https://, got ${parsed.protocol}`)
56
+ }
57
+
58
+ parsed.hash = ''
59
+ parsed.search = ''
60
+ parsed.pathname = parsed.pathname.replace(/\/+$/, '')
61
+
62
+ return parsed.toString().replace(/\/+$/, '')
63
+ }
64
+
65
+ function buildGatewayWsUrl(baseUrl, token) {
66
+ const parsed = new URL(baseUrl)
67
+ const wsScheme = parsed.protocol === 'https:' ? 'wss' : 'ws'
68
+ const prefix = parsed.pathname.replace(/\/+$/, '')
69
+
70
+ return `${wsScheme}://${parsed.host}${prefix}/api/ws?token=${encodeURIComponent(token)}`
71
+ }
72
+
73
+ function buildGatewayWsUrlWithTicket(baseUrl, ticket) {
74
+ const parsed = new URL(baseUrl)
75
+ const wsScheme = parsed.protocol === 'https:' ? 'wss' : 'ws'
76
+ const prefix = parsed.pathname.replace(/\/+$/, '')
77
+
78
+ return `${wsScheme}://${parsed.host}${prefix}/api/ws?ticket=${encodeURIComponent(ticket)}`
79
+ }
80
+
81
+ /**
82
+ * Build the WS URL the renderer would connect with, so the connection test can
83
+ * exercise the same transport the app actually uses.
84
+ *
85
+ * The OAuth ticket-minter is injected (`mintTicket(baseUrl) -> Promise<ticket>`)
86
+ * so this stays electron-free and unit-testable; main.cjs passes the real
87
+ * `mintGatewayWsTicket`.
88
+ *
89
+ * Return semantics:
90
+ * - token mode + token → ws(s)://…/api/ws?token=…
91
+ * - token mode, no token → null (genuine skip; nothing to authenticate with)
92
+ * - oauth, mint ok → ws(s)://…/api/ws?ticket=…
93
+ * - oauth, mint fails → THROWS (NOT a skip)
94
+ *
95
+ * The oauth-mint-failure throw is the important case: the real boot path
96
+ * (resolveRemoteBackend in main.cjs) treats a mint failure as a hard
97
+ * "session expired" auth error and refuses to connect. Swallowing it here
98
+ * would re-introduce the exact false-positive this test exists to catch —
99
+ * HTTP /api/status passes, the test reports "reachable", then the renderer
100
+ * can't authenticate /api/ws and boot dies with "Could not connect".
101
+ *
102
+ * @param {string} baseUrl
103
+ * @param {'token'|'oauth'} authMode
104
+ * @param {string|null} token
105
+ * @param {{ mintTicket: (baseUrl: string) => Promise<string> }} deps
106
+ * @returns {Promise<string|null>}
107
+ */
108
+ async function resolveTestWsUrl(baseUrl, authMode, token, deps = {}) {
109
+ if (authMode === 'oauth') {
110
+ const mintTicket = deps.mintTicket
111
+ if (typeof mintTicket !== 'function') {
112
+ throw new Error('resolveTestWsUrl: a mintTicket function is required in OAuth mode.')
113
+ }
114
+ let ticket
115
+ try {
116
+ ticket = await mintTicket(baseUrl)
117
+ } catch (error) {
118
+ const err = new Error(
119
+ 'Reached the gateway over HTTP, but could not mint a WebSocket ticket for the OAuth session ' +
120
+ '(it may have expired). Open Settings → Gateway and sign in again.'
121
+ )
122
+ err.needsOauthLogin = true
123
+ err.cause = error
124
+ throw err
125
+ }
126
+ return buildGatewayWsUrlWithTicket(baseUrl, ticket)
127
+ }
128
+ if (!token) {
129
+ return null
130
+ }
131
+ return buildGatewayWsUrl(baseUrl, token)
132
+ }
133
+
134
+ // Normalize a profile name to a connection scope key, or null for the global
135
+ // (default) connection. Shared by the resolver and the IPC layer.
136
+ function connectionScopeKey(profile) {
137
+ return String(profile ?? '').trim() || null
138
+ }
139
+
140
+ // Coerce a remote auth mode to one of the two supported values ('token' default).
141
+ function normAuthMode(mode) {
142
+ return mode === 'oauth' ? 'oauth' : 'token'
143
+ }
144
+
145
+ /**
146
+ * Select a profile's explicit remote override from a connection config, or null
147
+ * when it has none (so the caller falls back to env → global remote → local).
148
+ *
149
+ * The config may carry a `profiles` map keyed by name; an entry counts as an
150
+ * override only with `mode === 'remote'` and a non-empty `url`. Pure: `token`
151
+ * is the raw stored secret; main.cjs decrypts it. Returns
152
+ * `{ url, authMode, token } | null`.
153
+ */
154
+ function profileRemoteOverride(config, profile) {
155
+ const key = connectionScopeKey(profile)
156
+ const entry = key ? config?.profiles?.[key] : null
157
+ if (!entry || typeof entry !== 'object' || entry.mode !== 'remote') {
158
+ return null
159
+ }
160
+
161
+ const url = String(entry.url || '').trim()
162
+ if (!url) {
163
+ return null
164
+ }
165
+
166
+ return { url, authMode: normAuthMode(entry.authMode), token: entry.token }
167
+ }
168
+
169
+ function tokenPreview(value) {
170
+ const raw = String(value || '')
171
+
172
+ if (!raw) {
173
+ return null
174
+ }
175
+
176
+ return raw.length <= 8 ? 'set' : `...${raw.slice(-6)}`
177
+ }
178
+
179
+ /**
180
+ * Classify a gateway's auth mode from its public /api/status body.
181
+ * `auth_required: true` → OAuth gate engaged; otherwise legacy token auth.
182
+ * Returns 'oauth' | 'token'.
183
+ */
184
+ function authModeFromStatus(statusBody) {
185
+ return statusBody && statusBody.auth_required ? 'oauth' : 'token'
186
+ }
187
+
188
+ /**
189
+ * Resolve the effective auth mode for a coerce/save operation.
190
+ * Explicit input wins; otherwise inherit the saved value; default 'token'.
191
+ * Returns 'oauth' | 'token'.
192
+ */
193
+ function resolveAuthMode(inputAuthMode, existingAuthMode) {
194
+ if (inputAuthMode === 'oauth') return 'oauth'
195
+ if (inputAuthMode === 'token') return 'token'
196
+ if (existingAuthMode === 'oauth') return 'oauth'
197
+ return 'token'
198
+ }
199
+
200
+ /**
201
+ * True if any cookie in `cookies` is a nastech session ACCESS-token cookie
202
+ * with a non-empty value. `cookies` is an array of {name, value} (the shape
203
+ * Electron's session.cookies.get returns).
204
+ *
205
+ * Note: this is AT-only. A session whose AT cookie has lapsed but whose RT
206
+ * cookie is still alive is STILL connectable (the gateway refreshes the AT on
207
+ * the next request) — use `cookiesHaveLiveSession` for a connectivity/display
208
+ * check. `cookiesHaveSession` remains exported for callers that specifically
209
+ * need to know whether an unexpired access token is present right now.
210
+ */
211
+ function cookiesHaveSession(cookies) {
212
+ if (!Array.isArray(cookies)) return false
213
+ return cookies.some(c => c && AT_COOKIE_VARIANTS.includes(c.name) && c.value)
214
+ }
215
+
216
+ /**
217
+ * True if the cookie jar holds a credential that can yield an authenticated
218
+ * request — EITHER a live access-token cookie OR a refresh-token cookie. The
219
+ * RT cookie outlives the AT cookie (24h vs ~15min), and the gateway middleware
220
+ * transparently rotates a fresh AT from the RT on the next authenticated
221
+ * request. Gating connectivity on the AT alone would force a full IDP
222
+ * re-login every ~15 min even though a valid 24h RT is sitting in the jar.
223
+ *
224
+ * This answers "should we even attempt to connect / show as signed in?", not
225
+ * "is the access token unexpired?". The authoritative liveness check is still
226
+ * the actual ws-ticket mint at connect time (which surfaces a true 401 when
227
+ * the RT is also dead/revoked).
228
+ */
229
+ function cookiesHaveLiveSession(cookies) {
230
+ if (!Array.isArray(cookies)) return false
231
+ return cookies.some(
232
+ c =>
233
+ c &&
234
+ c.value &&
235
+ (AT_COOKIE_VARIANTS.includes(c.name) || RT_COOKIE_VARIANTS.includes(c.name))
236
+ )
237
+ }
238
+
239
+ module.exports = {
240
+ AT_COOKIE_VARIANTS,
241
+ RT_COOKIE_VARIANTS,
242
+ authModeFromStatus,
243
+ buildGatewayWsUrl,
244
+ buildGatewayWsUrlWithTicket,
245
+ connectionScopeKey,
246
+ cookiesHaveSession,
247
+ cookiesHaveLiveSession,
248
+ normAuthMode,
249
+ normalizeRemoteBaseUrl,
250
+ profileRemoteOverride,
251
+ resolveAuthMode,
252
+ resolveTestWsUrl,
253
+ tokenPreview
254
+ }