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
package/.prettierrc ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "arrowParens": "avoid",
3
+ "bracketSpacing": true,
4
+ "endOfLine": "auto",
5
+ "printWidth": 120,
6
+ "semi": false,
7
+ "singleQuote": true,
8
+ "tabWidth": 2,
9
+ "trailingComma": "none",
10
+ "useTabs": false
11
+ }
package/DESIGN.md ADDED
@@ -0,0 +1,167 @@
1
+ # Desktop Design System
2
+
3
+ Conventions for the Electron desktop app (`apps/desktop`). Read this before
4
+ adding a component, overlay, or style. The rule of thumb: **one source per
5
+ concern, tokens over literals, flat over boxed.** If you reach for a raw color,
6
+ a one-off shadow, a bespoke button, or a hardcoded `px-*` on a control — stop,
7
+ there's already a primitive for it.
8
+
9
+ ## Principles
10
+
11
+ 1. **Flat, not boxed.** No card-in-card, no divider borders inside a panel.
12
+ Group with whitespace and a single hairline, never nested rounded boxes.
13
+ 2. **Borderless + shadow for elevation.** Overlays float on `shadow-nastech` + a
14
+ `--stroke-nastech` hairline, not hard borders.
15
+ 3. **One primitive per concern.** One `Button`, one set of control variants,
16
+ one `SearchField`, one `Loader`, one `ErrorState`. Migrate onto them; don't
17
+ fork.
18
+ 4. **Tokens, not literals.** Reference CSS vars (`--ui-*`, `--shadow-nastech`,
19
+ `--theme-*`), never raw hex / ad-hoc rgba in components.
20
+ 5. **Style lives in the primitive.** Variants and sizes own padding, radius,
21
+ color, chrome. Call sites pass a `variant`/`size`, not `className` overrides
22
+ that re-specify those.
23
+
24
+ ## Surfaces & elevation
25
+
26
+ Every overlay / dialog / toast (boot-failure, install, notifications,
27
+ model-picker, onboarding, prompt-overlays, updates, base `Dialog`) uses:
28
+
29
+ ```
30
+ shadow-nastech /* downward-weighted, layered contact→ambient falloff */
31
+ border-(--stroke-nastech) /* currentColor hairline, theme-adaptive */
32
+ ```
33
+
34
+ Both are CSS vars in `src/styles.css` — tune in one place, everything inherits.
35
+ Don't add per-overlay `shadow-[…]` or `border-(--ui-stroke-secondary)`
36
+ one-offs; if elevation needs to change, change the token.
37
+
38
+ ## Stroke & color tokens
39
+
40
+ | Token | Use |
41
+ | --- | --- |
42
+ | `--ui-stroke-primary…quaternary` | hairlines, in descending strength |
43
+ | `--ui-stroke-tertiary` | the default in-panel divider / list hairline |
44
+ | `--stroke-nastech` | the overlay hairline (pairs with `shadow-nastech`) |
45
+ | `--ui-text-primary / -secondary / -tertiary` | text hierarchy |
46
+ | `--ui-bg-quaternary` | soft control fill (secondary button) |
47
+ | `--chrome-action-hover` | hover fill for quiet controls |
48
+ | `--theme-primary`, `--ui-accent` | brand/accent |
49
+
50
+ Never hardcode `border-gray-*`, `bg-white`, `text-black`, etc. The white tile in
51
+ `BrandMark` is the one sanctioned literal (the mark needs a fixed backdrop).
52
+
53
+ ## Buttons — one component
54
+
55
+ `src/components/ui/button.tsx` is the single source. Pick a `variant` + `size`;
56
+ do **not** pass `h-*`, `px-*`, `py-*`, or icon-size overrides.
57
+
58
+ **Variants:** `default` (primary), `destructive`, `secondary` (soft fill —
59
+ the default non-primary look), `outline` (transparent + 1px inset ring, no
60
+ fill/shadow), `ghost`, `link`, `text` (boxless quiet inline — "Cancel",
61
+ "Clear"), `textStrong` (bold underlined inline affordance — "Change",
62
+ "Open logs").
63
+
64
+ **Sizes:** `default`, `xs`, `sm`, `lg`, `inline` (flush, zero box — for buttons
65
+ that sit inside a heading/sentence; replaces `h-auto px-0 py-0`), and the icon
66
+ family `icon` / `icon-xs` / `icon-sm` / `icon-lg` / `icon-titlebar`.
67
+
68
+ Notes:
69
+ - Text buttons are square (no radius) and sized by padding + line-height (no
70
+ fixed heights). Only icon buttons carry the shared 4px radius.
71
+ - SVGs inherit `size-3.5` (`size-3` at `xs`). Don't re-set icon size.
72
+ - Polymorph with `asChild` when the button must render as a link/Slot.
73
+
74
+ ## Form controls
75
+
76
+ - **`controlVariants`** (`src/components/ui/control.ts`) is the shared shape for
77
+ `Input` / `Textarea` / `SelectTrigger`. New text-entry controls compose it.
78
+ - **`SearchField`** — borderless, underline-on-focus, auto-width. The only
79
+ search input. Don't build boxed search bars; don't wrap it in a bordered tile.
80
+ Empty lists hide their search field.
81
+ - **`SegmentedControl`** — the choice control for small mutually-exclusive sets
82
+ (color mode, tool-call display, usage period). Replaces radio piles and
83
+ pill rows.
84
+ - **`Switch`** (`size="xs"`) — bare, with `aria-label`. No bordered text wrapper.
85
+
86
+ ## Layout
87
+
88
+ - **Gutters:** `PAGE_INSET_X` (`src/app/layout-constants.ts`) for page side
89
+ padding; `PAGE_INSET_NEG_X` to bleed a child to the edge. Don't hardcode
90
+ `px-6`/`px-8` on pages.
91
+ - **Master/detail overlays:** `OverlaySplitLayout` + `OverlaySidebar` /
92
+ `OverlayMain`. Cron, profiles, etc. ride this — don't rebuild a titlebar
93
+ shell.
94
+ - **Rows:** `ListRow` (settings `primitives.tsx`) for label/description/action
95
+ rows. Flat, flush-left; no per-row indentation that fights flush headers.
96
+ - **No dividers between rows** unless the list genuinely needs them; prefer
97
+ spacing. When you do need one, it's a single `--ui-stroke-tertiary` hairline.
98
+
99
+ ## Feedback & empty/error/loading states
100
+
101
+ - **Loading:** `Loader` (`src/components/ui/loader.tsx`) — animated math/ascii
102
+ curves (`lemniscate-bloom` for long ops). Never ship the literal text
103
+ "Loading…".
104
+ - **Errors:** `ErrorState` + the canonical `ErrorIcon` (no bg chip). One look
105
+ for the React boundary, in-dialog errors, and the boot-failure banner. Pass
106
+ nodes for title/description so Radix `DialogTitle`/`Description` can flow
107
+ through for a11y.
108
+ - **Logs:** `LogView` — no bg, hairline border, tight padding, small mono.
109
+ Every place we surface raw logs uses it.
110
+ - **Empty:** `EmptyState` / `EmptyPanel` — don't hand-roll centered empties.
111
+
112
+ ## Iconography & brand
113
+
114
+ - **`Codicon`** is the icon set. No mixing icon libraries inline.
115
+ - **`BrandMark`** (`src/components/brand-mark.tsx`) is the brand glyph — the
116
+ `nastech-girl` mark on a white tile, softly rounded, identical in light/dark.
117
+ It replaced scattered Sparkles glyphs in updates / onboarding / about. Use it
118
+ for hero/brand moments; don't reintroduce decorative star/sparkle icons.
119
+
120
+ ## Motion
121
+
122
+ - Quick, functional transitions (~100ms on controls). Respect
123
+ `prefers-reduced-motion` for anything beyond a fade.
124
+ - Choreographed exits (e.g. onboarding's "matrix" fade-down) stagger per-element
125
+ then settle the surface — the outer container's fade is *delayed* so it
126
+ doesn't swallow the inner animation. Don't let a global fade race the detail.
127
+
128
+ ## i18n
129
+
130
+ - Every user-facing string goes through `useI18n()` (`src/i18n/context.tsx`).
131
+ No literals in JSX.
132
+ - **Update all locales together** — `en`, `ja`, `zh`, `zh-hant`. A string change
133
+ in `en.ts` that skips the others is a regression (drifted punctuation,
134
+ stale labels). Keep trailing-punctuation and tone consistent across all four.
135
+
136
+ ## State (TypeScript)
137
+
138
+ Mirrors the repo TS style (see root `AGENTS.md`):
139
+
140
+ - Shared/cross-component state → small **nanostores**, not prop-drilling.
141
+ Each feature owns its atoms; shared atoms live in `src/store`.
142
+ - Rendering components subscribe with `useStore`; non-render actions read with
143
+ `$atom.get()`.
144
+ - Colocated action modules over god hooks. A hook owns one narrow job.
145
+ - Keep persistence beside the atom that owns it. Route roots stay thin.
146
+ - Prefer `interface` for public props; extend React primitives
147
+ (`React.ComponentProps<'button'>`, `Omit<…>`).
148
+
149
+ ## Affordances
150
+
151
+ - `cursor-pointer` at the primitive level (Button, dropdown/select) — don't
152
+ hardcode it per call site.
153
+ - Global focus-ring reset; titlebar actions have no active-background state.
154
+ - `Esc` closes every dismissable overlay/dialog (install/onboarding excluded);
155
+ close is an x-icon, not the word "Close".
156
+
157
+ ## Before you add something — checklist
158
+
159
+ - [ ] Reuse a primitive (`Button`, `SearchField`, `SegmentedControl`,
160
+ `ListRow`, `Loader`, `ErrorState`, `LogView`) instead of forking one?
161
+ - [ ] Tokens (`--ui-*`, `shadow-nastech`, `--stroke-nastech`) — zero raw colors /
162
+ one-off shadows?
163
+ - [ ] No `className` overriding a primitive's padding / size / radius / chrome?
164
+ - [ ] Overlay uses `shadow-nastech` + `border-(--stroke-nastech)`, no hard border?
165
+ - [ ] Flat — no card-in-card, no gratuitous row dividers?
166
+ - [ ] All four locales updated for any new/changed string?
167
+ - [ ] `cursor-pointer`, focus ring, and `Esc`-to-close behave?
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # NasTech Desktop ☤
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/nastech-ai/NasTech-Agent/releases"><img src="https://img.shields.io/badge/Download-macOS%20%C2%B7%20Windows%20%C2%B7%20Linux-FFD700?style=for-the-badge" alt="Download"></a>
5
+ <a href="https://nastech-agent.nastech.ai/docs/"><img src="https://img.shields.io/badge/Docs-NASTECH--agent.nastech.ai-FFD700?style=for-the-badge" alt="Documentation"></a>
6
+ <a href="https://discord.gg/nastech"><img src="https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord"></a>
7
+ <a href="https://github.com/nastech-ai/NasTech-Agent/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" alt="License: MIT"></a>
8
+ </p>
9
+
10
+ **The native desktop app for [NasTech Agent](../../README.md) — the self-improving AI agent from [NasTech](https://nastech.ai).** Same agent, same skills, same memory as the CLI and gateway, in a polished native window — chat with streaming tool output, side-by-side previews, a file browser, voice, and settings, no terminal required. Available for **macOS, Windows, and Linux**.
11
+
12
+ <table>
13
+ <tr><td><b>Chat with the full agent</b></td><td>Streaming responses, live tool activity, structured tool summaries, and the same conversation history as every other NasTech surface.</td></tr>
14
+ <tr><td><b>Side-by-side previews</b></td><td>Render web pages, files, and tool outputs in a right-hand pane while you keep chatting.</td></tr>
15
+ <tr><td><b>File browser</b></td><td>Explore and preview the working directory without leaving the app.</td></tr>
16
+ <tr><td><b>Voice</b></td><td>Talk to NasTech and hear it back.</td></tr>
17
+ <tr><td><b>Settings & onboarding</b></td><td>Manage providers, models, tools, and credentials from a real UI. First-run setup gets you to your first message in seconds.</td></tr>
18
+ <tr><td><b>Stays current</b></td><td>Built-in updates pull the latest agent and rebuild the app in place.</td></tr>
19
+ </table>
20
+
21
+ ---
22
+
23
+ ## Install
24
+
25
+ ### Install with NasTech (recommended)
26
+
27
+ Already have the NasTech CLI? Just run:
28
+
29
+ ```bash
30
+ NASTECH desktop
31
+ ```
32
+
33
+ It builds and launches the GUI against your existing install — same config, keys, sessions, and skills. On first launch NasTech walks you through picking a provider and model; nothing else to configure.
34
+
35
+ ### Prebuilt installers
36
+
37
+ Prebuilt installers are built and distributed via [the NasTech Desktop website.](https://nastech-agent.nastech.ai/desktop).
38
+
39
+ ---
40
+
41
+ ## Updating
42
+
43
+ The app checks for updates in the background and offers a one-click update when one is ready. You can also update any time from the CLI:
44
+
45
+ ```bash
46
+ NASTECH update
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Requirements
52
+
53
+ The installer handles everything for you (Python 3.11+, a portable Git, ripgrep).
54
+
55
+ ---
56
+
57
+ ## Development
58
+
59
+ Want to hack on the app itself? Install workspace deps from the repo root once, then run the dev server from this directory:
60
+
61
+ ```bash
62
+ npm install # from repo root — links apps/desktop, web, apps/shared
63
+ cd apps/desktop
64
+ npm run dev # Vite renderer + Electron, which boots the Python backend
65
+ ```
66
+
67
+ Point the app at a specific source checkout, or sandbox it away from your real config:
68
+
69
+ ```bash
70
+ NASTECH_DESKTOP_NASTECH_ROOT=/path/to/clone npm run dev
71
+ NASTECH_HOME=/tmp/throwaway npm run dev
72
+ npm run dev:fake-boot # exercise the startup overlay with deterministic delays
73
+ ```
74
+
75
+ ### Building installers
76
+
77
+ ```bash
78
+ npm run dist:mac # DMG + zip
79
+ npm run dist:win # NSIS + MSI
80
+ npm run dist:linux # AppImage + deb + rpm
81
+ npm run pack # unpacked app under release/ (no installer)
82
+ ```
83
+
84
+ Installers are built and uploaded to GitHub Releases manually. macOS/Windows signing & notarization happen automatically when the relevant credentials are present in the environment (`CSC_LINK` / `CSC_KEY_PASSWORD` / `APPLE_*` for macOS, `WIN_CSC_*` for Windows).
85
+
86
+ ### How it works
87
+
88
+ The packaged app ships only the Electron shell. On first launch it installs the NasTech Agent runtime into `NASTECH_HOME` (`~/.NASTECH`, or `%LOCALAPPDATA%\NASTECH` on Windows) — the **same layout a CLI install uses**, so the two are interchangeable. The renderer (React, in `src/`) talks to a `NASTECH dashboard` backend over the standard gateway APIs and reuses the embedded TUI rather than reimplementing chat. The install, backend-resolution, and self-update logic all live in `electron/main.cjs`.
89
+
90
+ ### Verification
91
+
92
+ Run before opening a PR (lint may surface pre-existing warnings but must exit cleanly):
93
+
94
+ ```bash
95
+ npm run fix
96
+ npm run type-check
97
+ npm run lint
98
+ npm run test:desktop:all
99
+ ```
100
+
101
+ ### Troubleshooting
102
+
103
+ Boot logs land in `NASTECH_HOME/logs/desktop.log` (includes backend output and recent Python tracebacks) — check it first if the app reports a boot failure.
104
+
105
+ **macOS / Linux:**
106
+
107
+ ```bash
108
+ # Force a clean first-launch setup
109
+ rm "$HOME/.NASTECH/nastech-agent/.NASTECH-bootstrap-complete"
110
+ # Rebuild a broken Python venv
111
+ rm -rf "$HOME/.NASTECH/nastech-agent/venv"
112
+ # Reset a stuck macOS microphone prompt (macOS only)
113
+ tccutil reset Microphone com.nastech.NASTECH
114
+ ```
115
+
116
+ **Windows (PowerShell):**
117
+
118
+ ```powershell
119
+ # Force a clean first-launch setup
120
+ Remove-Item "$env:LOCALAPPDATA\NASTECH\nastech-agent\.NASTECH-bootstrap-complete"
121
+ # Rebuild a broken Python venv
122
+ Remove-Item -Recurse -Force "$env:LOCALAPPDATA\NASTECH\nastech-agent\venv"
123
+ ```
124
+
125
+ > The default NasTech home on Windows is `%LOCALAPPDATA%\NASTECH`. Set the `NASTECH_HOME` env var if you've relocated it.
126
+
127
+ ---
128
+
129
+ ## Community
130
+
131
+ - 💬 [Discord](https://discord.gg/nastech)
132
+ - 📖 [Documentation](https://nastech-agent.nastech.ai/docs/)
133
+ - 🐛 [Issues](https://github.com/nastech-ai/NasTech-Agent/issues)
134
+
135
+ ---
136
+
137
+ ## License
138
+
139
+ MIT — see [LICENSE](../../LICENSE).
140
+
141
+ Built by [NasTech](https://nastech.ai).
Binary file
Binary file
Binary file
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/styles.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
@@ -0,0 +1,112 @@
1
+ const path = require('node:path')
2
+
3
+ // Match the POSIX fallback surface used by the Python terminal environment.
4
+ // macOS apps launched from Finder/Dock often inherit only /usr/bin:/bin:/usr/sbin:/sbin,
5
+ // which misses Apple Silicon Homebrew and user-installed CLI tools such as codex.
6
+ const POSIX_SANE_PATH_ENTRIES = Object.freeze([
7
+ '/opt/homebrew/bin',
8
+ '/opt/homebrew/sbin',
9
+ '/usr/local/sbin',
10
+ '/usr/local/bin',
11
+ '/usr/sbin',
12
+ '/usr/bin',
13
+ '/sbin',
14
+ '/bin'
15
+ ])
16
+
17
+ function delimiterForPlatform(platform = process.platform) {
18
+ return platform === 'win32' ? ';' : ':'
19
+ }
20
+
21
+ function pathModuleForPlatform(platform = process.platform) {
22
+ return platform === 'win32' ? path.win32 : path.posix
23
+ }
24
+
25
+ function pathEnvKey(env = process.env, platform = process.platform) {
26
+ if (platform !== 'win32') return 'PATH'
27
+ return Object.keys(env || {}).find(key => key.toUpperCase() === 'PATH') || 'PATH'
28
+ }
29
+
30
+ function currentPathValue(env = process.env, platform = process.platform) {
31
+ const key = pathEnvKey(env, platform)
32
+ return env?.[key] || ''
33
+ }
34
+
35
+ function appendUniquePathEntries(entries, { delimiter = path.delimiter } = {}) {
36
+ const seen = new Set()
37
+ const ordered = []
38
+
39
+ for (const entry of entries) {
40
+ if (!entry) continue
41
+ const parts = Array.isArray(entry) ? entry : String(entry).split(delimiter)
42
+ for (const part of parts) {
43
+ if (!part || seen.has(part)) continue
44
+ seen.add(part)
45
+ ordered.push(part)
46
+ }
47
+ }
48
+
49
+ return ordered.join(delimiter)
50
+ }
51
+
52
+ function buildDesktopBackendPath({
53
+ nastechHome,
54
+ venvRoot,
55
+ currentPath = '',
56
+ platform = process.platform,
57
+ pathModule = pathModuleForPlatform(platform)
58
+ } = {}) {
59
+ const delimiter = delimiterForPlatform(platform)
60
+ const nastechNodeBin = nastechHome ? pathModule.join(nastechHome, 'node', 'bin') : null
61
+ const venvBin = venvRoot ? pathModule.join(venvRoot, platform === 'win32' ? 'Scripts' : 'bin') : null
62
+ const saneEntries = platform === 'win32' ? [] : POSIX_SANE_PATH_ENTRIES
63
+
64
+ return appendUniquePathEntries(
65
+ [nastechNodeBin, venvBin, currentPath, saneEntries],
66
+ { delimiter }
67
+ )
68
+ }
69
+
70
+ function normalizeNasTechHomeRoot(nastechHome, { pathModule = pathModuleForPlatform(process.platform) } = {}) {
71
+ if (!nastechHome) return nastechHome
72
+ const resolved = pathModule.resolve(String(nastechHome))
73
+ const parent = pathModule.dirname(resolved)
74
+ if (pathModule.basename(parent).toLowerCase() === 'profiles') {
75
+ return pathModule.dirname(parent)
76
+ }
77
+ return resolved
78
+ }
79
+
80
+ function buildDesktopBackendEnv({
81
+ nastechHome,
82
+ pythonPathEntries = [],
83
+ venvRoot,
84
+ currentEnv = process.env,
85
+ platform = process.platform,
86
+ pathModule = pathModuleForPlatform(platform)
87
+ } = {}) {
88
+ const delimiter = delimiterForPlatform(platform)
89
+ const currentPythonPath = currentEnv?.PYTHONPATH || ''
90
+ const key = pathEnvKey(currentEnv, platform)
91
+
92
+ return {
93
+ PYTHONPATH: appendUniquePathEntries([...pythonPathEntries, currentPythonPath], { delimiter }),
94
+ [key]: buildDesktopBackendPath({
95
+ nastechHome,
96
+ venvRoot,
97
+ currentPath: currentPathValue(currentEnv, platform),
98
+ platform,
99
+ pathModule
100
+ })
101
+ }
102
+ }
103
+
104
+ module.exports = {
105
+ POSIX_SANE_PATH_ENTRIES,
106
+ appendUniquePathEntries,
107
+ buildDesktopBackendEnv,
108
+ buildDesktopBackendPath,
109
+ delimiterForPlatform,
110
+ normalizeNasTechHomeRoot,
111
+ pathEnvKey
112
+ }
@@ -0,0 +1,111 @@
1
+ const test = require('node:test')
2
+ const assert = require('node:assert/strict')
3
+ const path = require('node:path')
4
+
5
+ const {
6
+ POSIX_SANE_PATH_ENTRIES,
7
+ appendUniquePathEntries,
8
+ buildDesktopBackendEnv,
9
+ buildDesktopBackendPath,
10
+ normalizeNasTechHomeRoot,
11
+ pathEnvKey
12
+ } = require('./backend-env.cjs')
13
+
14
+ test('desktop backend PATH adds NasTech-managed bins and missing POSIX sane entries', () => {
15
+ const result = buildDesktopBackendPath({
16
+ nastechHome: '/Users/test/.nastech',
17
+ venvRoot: '/Users/test/.nastech/nastech-agent/venv',
18
+ currentPath: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
19
+ platform: 'darwin',
20
+ pathModule: path.posix
21
+ })
22
+
23
+ const entries = result.split(':')
24
+ assert.equal(entries[0], '/Users/test/.nastech/node/bin')
25
+ assert.equal(entries[1], '/Users/test/.nastech/nastech-agent/venv/bin')
26
+ assert.ok(entries.includes('/opt/homebrew/bin'), 'Apple Silicon Homebrew bin is added')
27
+ assert.ok(entries.includes('/opt/homebrew/sbin'), 'Apple Silicon Homebrew sbin is added')
28
+ assert.ok(entries.includes('/usr/local/sbin'), 'missing standard sbin is added')
29
+
30
+ for (const expected of POSIX_SANE_PATH_ENTRIES) {
31
+ assert.ok(entries.includes(expected), `${expected} should be present`)
32
+ }
33
+ })
34
+
35
+ test('desktop backend PATH preserves first occurrence and avoids duplicates', () => {
36
+ const result = buildDesktopBackendPath({
37
+ nastechHome: '/Users/test/.nastech',
38
+ venvRoot: '/Users/test/.nastech/nastech-agent/venv',
39
+ currentPath: '/opt/homebrew/bin:/usr/bin:/opt/homebrew/bin:/bin',
40
+ platform: 'darwin',
41
+ pathModule: path.posix
42
+ })
43
+
44
+ const entries = result.split(':')
45
+ assert.equal(entries.filter(entry => entry === '/opt/homebrew/bin').length, 1)
46
+ assert.ok(
47
+ entries.indexOf('/opt/homebrew/bin') < entries.indexOf('/opt/homebrew/sbin'),
48
+ 'existing Homebrew bin keeps its precedence over appended missing sane entries'
49
+ )
50
+ })
51
+
52
+ test('buildDesktopBackendEnv extends PYTHONPATH and backend PATH together', () => {
53
+ const env = buildDesktopBackendEnv({
54
+ nastechHome: '/Users/test/.nastech',
55
+ pythonPathEntries: ['/repo/nastech-agent'],
56
+ venvRoot: '/Users/test/.nastech/nastech-agent/venv',
57
+ currentEnv: {
58
+ PATH: '/usr/bin:/bin',
59
+ PYTHONPATH: '/existing/pythonpath'
60
+ },
61
+ platform: 'darwin',
62
+ pathModule: path.posix
63
+ })
64
+
65
+ assert.equal(env.PYTHONPATH, '/repo/nastech-agent:/existing/pythonpath')
66
+ assert.ok(env.PATH.startsWith('/Users/test/.nastech/node/bin:/Users/test/.nastech/nastech-agent/venv/bin:'))
67
+ assert.ok(env.PATH.includes('/opt/homebrew/bin'))
68
+ })
69
+
70
+ test('normalizeNasTechHomeRoot maps profile homes back to the global NasTech root', () => {
71
+ assert.equal(
72
+ normalizeNasTechHomeRoot('/Users/test/.nastech/profiles/oracle', { pathModule: path.posix }),
73
+ '/Users/test/.nastech'
74
+ )
75
+ assert.equal(
76
+ normalizeNasTechHomeRoot('C:\\Users\\test\\AppData\\Local\\nastech\\profiles\\oracle', { pathModule: path.win32 }),
77
+ 'C:\\Users\\test\\AppData\\Local\\nastech'
78
+ )
79
+ assert.equal(
80
+ normalizeNasTechHomeRoot('/Users/test/.nastech', { pathModule: path.posix }),
81
+ '/Users/test/.nastech'
82
+ )
83
+ })
84
+
85
+ test('Windows PATH casing and delimiter are preserved without POSIX sane entries', () => {
86
+ const env = buildDesktopBackendEnv({
87
+ nastechHome: 'C:\\Users\\test\\AppData\\Local\\nastech',
88
+ pythonPathEntries: ['C:\\repo\\nastech-agent'],
89
+ venvRoot: 'C:\\Users\\test\\AppData\\Local\\nastech\\nastech-agent\\venv',
90
+ currentEnv: {
91
+ Path: 'C:\\Windows\\System32;C:\\Windows',
92
+ PYTHONPATH: 'C:\\existing\\pythonpath'
93
+ },
94
+ platform: 'win32',
95
+ pathModule: path.win32
96
+ })
97
+
98
+ assert.equal(pathEnvKey({ Path: 'x' }, 'win32'), 'Path')
99
+ assert.equal(env.PATH, undefined)
100
+ assert.ok(env.Path.startsWith('C:\\Users\\test\\AppData\\Local\\nastech\\node\\bin;'))
101
+ assert.ok(env.Path.includes('\\venv\\Scripts;'))
102
+ assert.ok(env.Path.includes(';C:\\Windows\\System32;C:\\Windows'))
103
+ assert.equal(env.Path.includes('/opt/homebrew/bin'), false)
104
+ })
105
+
106
+ test('appendUniquePathEntries drops empty entries and keeps first occurrence', () => {
107
+ assert.equal(
108
+ appendUniquePathEntries([':/a::/b', ['/a', '/c']], { delimiter: ':' }),
109
+ '/a:/b:/c'
110
+ )
111
+ })
@@ -0,0 +1,106 @@
1
+ /**
2
+ * backend-probes.cjs
3
+ *
4
+ * Cheap "does this candidate backend actually work" checks used by
5
+ * resolveNastechBackend (main.cjs). The resolver walks a ladder of
6
+ * candidates -- bootstrap marker, `nastech` on PATH, system Python with
7
+ * nastech_cli installed -- and historically returned the first candidate
8
+ * whose binary existed on disk. That assumption breaks when a user has
9
+ * a pre-installed Python 3.11-3.13 (so findSystemPython() returns a
10
+ * path) but no nastech_cli in its site-packages: the resolver hands back
11
+ * a backend the spawn step can't actually run, and the user gets a
12
+ * dead-on-arrival "ModuleNotFoundError: No module named 'nastech_cli'"
13
+ * instead of the first-launch installer.
14
+ *
15
+ * These probes give the resolver a way to verify a candidate before
16
+ * trusting it. Failure (non-zero exit, exception, timeout) means "skip
17
+ * this rung, try the next one"; success means "spawn this for real."
18
+ * Falling off the bottom of the ladder lands on the bootstrap-needed
19
+ * sentinel, which is exactly what we want when nothing pre-existing
20
+ * actually works.
21
+ *
22
+ * Both probes are deliberately fast and forgiving:
23
+ * - 5s timeout (a hung interpreter beats forever, but we still give
24
+ * slow disks / cold caches room to breathe)
25
+ * - stdio ignored (we only care about exit code; stdout/stderr are
26
+ * not surfaced to the user, just to recentNastechLog for forensics
27
+ * via the caller's catch block if it chooses)
28
+ * - any throw -> false (never propagate -- resolver wants a boolean)
29
+ *
30
+ * Kept in a standalone cjs module so it can be unit-tested with
31
+ * `node --test` without dragging in the electron runtime (same pattern
32
+ * as bootstrap-platform.cjs and hardening.cjs).
33
+ */
34
+
35
+ const { execFileSync } = require('node:child_process')
36
+
37
+ const PROBE_TIMEOUT_MS = 5000
38
+
39
+ /**
40
+ * Return true iff `python -c "import nastech_cli"` exits 0.
41
+ *
42
+ * Used to gate the "fallback to system Python with nastech_cli installed"
43
+ * rung of resolveNastechBackend. Without this, a system Python 3.11-3.13
44
+ * registered in PEP 514 makes findSystemPython() succeed regardless of
45
+ * whether nastech_cli has actually been pip-installed into its
46
+ * site-packages -- and the resolver returns a backend that immediately
47
+ * dies on spawn.
48
+ *
49
+ * @param {string} pythonPath - Absolute path to a python.exe / python.
50
+ * @returns {boolean}
51
+ */
52
+ function canImportNastechCli(pythonPath) {
53
+ if (!pythonPath) return false
54
+ try {
55
+ execFileSync(pythonPath, ['-c', 'import nastech_cli'], {
56
+ stdio: 'ignore',
57
+ timeout: PROBE_TIMEOUT_MS,
58
+ windowsHide: true
59
+ })
60
+ return true
61
+ } catch {
62
+ return false
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Return true iff `<nastechCommand> --version` exits 0.
68
+ *
69
+ * Used to gate the "existing `nastech` on PATH" rung. Without this, a
70
+ * stale nastech.cmd shim left behind by an uninstalled pip install (or
71
+ * a half-built venv whose `nastech` entry-point points at a deleted
72
+ * Python) survives findOnPath() and gets selected as the backend.
73
+ *
74
+ * We intentionally avoid invoking the command with the dashboard args
75
+ * here -- `--version` is the cheapest "is this binary alive" smoke
76
+ * test that every nastech_cli entry-point has supported since 0.1.
77
+ *
78
+ * @param {string} nastechCommand - Resolved absolute path to a nastech
79
+ * executable (or an interpreter+script wrapper).
80
+ * @param {object} [opts]
81
+ * @param {boolean} [opts.shell] - Whether to run through a shell. For
82
+ * .cmd/.bat shims on Windows execFileSync needs shell:true to find
83
+ * the cmd interpreter; mirrors the same flag isCommandScript() drives
84
+ * in resolveNastechBackend.
85
+ * @returns {boolean}
86
+ */
87
+ function verifyNastechCli(nastechCommand, opts = {}) {
88
+ if (!nastechCommand) return false
89
+ try {
90
+ execFileSync(nastechCommand, ['--version'], {
91
+ stdio: 'ignore',
92
+ timeout: PROBE_TIMEOUT_MS,
93
+ shell: Boolean(opts.shell),
94
+ windowsHide: true
95
+ })
96
+ return true
97
+ } catch {
98
+ return false
99
+ }
100
+ }
101
+
102
+ module.exports = {
103
+ canImportNastechCli,
104
+ verifyNastechCli,
105
+ PROBE_TIMEOUT_MS
106
+ }