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,425 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { spawn, spawnSync } from 'node:child_process'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { listPackage } from '@electron/asar'
7
+
8
+ const DESKTOP_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
9
+ const PACKAGE_JSON = JSON.parse(fs.readFileSync(path.join(DESKTOP_ROOT, 'package.json'), 'utf8'))
10
+ const MODE = process.argv[2] || 'help'
11
+ const ARCH = process.arch === 'arm64' ? 'arm64' : 'x64'
12
+ const RELEASE_ROOT = path.join(DESKTOP_ROOT, 'release')
13
+ const PLATFORM = process.platform
14
+
15
+ // Platform-specific packaged-app layout. The thin installer ships an Electron
16
+ // app shell plus extraResources (install-stamp.json + native-deps/) -- it
17
+ // no longer bundles the NasTech Agent Python payload (that's fetched at first
18
+ // launch via install.ps1 / install.sh, per the Phase 1 thin-installer flow).
19
+ const APP = (() => {
20
+ if (PLATFORM === 'darwin') {
21
+ const appPath = path.join(RELEASE_ROOT, `mac-${ARCH}`, 'NasTech.app')
22
+ return {
23
+ appPath,
24
+ binary: path.join(appPath, 'Contents', 'MacOS', 'NasTech'),
25
+ resourcesPath: path.join(appPath, 'Contents', 'Resources'),
26
+ asarPath: path.join(appPath, 'Contents', 'Resources', 'app.asar'),
27
+ unpackedDistIndex: path.join(appPath, 'Contents', 'Resources', 'app.asar.unpacked', 'dist', 'index.html')
28
+ }
29
+ }
30
+ if (PLATFORM === 'win32') {
31
+ const unpacked = path.join(RELEASE_ROOT, 'win-unpacked')
32
+ return {
33
+ appPath: unpacked,
34
+ binary: path.join(unpacked, 'NasTech.exe'),
35
+ resourcesPath: path.join(unpacked, 'resources'),
36
+ asarPath: path.join(unpacked, 'resources', 'app.asar'),
37
+ unpackedDistIndex: path.join(unpacked, 'resources', 'app.asar.unpacked', 'dist', 'index.html')
38
+ }
39
+ }
40
+ // linux unpacked layout matches windows but with different binary name
41
+ const unpacked = path.join(RELEASE_ROOT, 'linux-unpacked')
42
+ return {
43
+ appPath: unpacked,
44
+ binary: path.join(unpacked, 'nastech'),
45
+ resourcesPath: path.join(unpacked, 'resources'),
46
+ asarPath: path.join(unpacked, 'resources', 'app.asar'),
47
+ unpackedDistIndex: path.join(unpacked, 'resources', 'app.asar.unpacked', 'dist', 'index.html')
48
+ }
49
+ })()
50
+
51
+ // Default NASTECH_HOME for non-sandboxed runs -- matches main.cjs's
52
+ // resolveNastechHome(). On Windows it's %LOCALAPPDATA%\nastech; elsewhere
53
+ // it's ~/.nastech. The fresh-install sandbox launchFresh() sets its own
54
+ // NASTECH_HOME and never touches this.
55
+ const DEFAULT_NASTECH_HOME = (() => {
56
+ if (PLATFORM === 'win32' && process.env.LOCALAPPDATA) {
57
+ return path.join(process.env.LOCALAPPDATA, 'nastech')
58
+ }
59
+ return path.join(os.homedir(), '.nastech')
60
+ })()
61
+ const VENV_ROOT = path.join(DEFAULT_NASTECH_HOME, 'nastech-agent', 'venv')
62
+ const FRESH_SANDBOX_ROOT = path.join(os.tmpdir(), 'nastech-desktop-fresh-install')
63
+
64
+ function die(message) {
65
+ console.error(`\n${message}`)
66
+ process.exit(1)
67
+ }
68
+
69
+ function run(command, args, options = {}) {
70
+ const result = spawnSync(command, args, {
71
+ cwd: options.cwd || DESKTOP_ROOT,
72
+ env: options.env || process.env,
73
+ shell: Boolean(options.shell) || PLATFORM === 'win32',
74
+ stdio: 'inherit'
75
+ })
76
+
77
+ if (result.status !== 0) {
78
+ die(`${command} ${args.join(' ')} failed`)
79
+ }
80
+ }
81
+
82
+ function exists(target) {
83
+ return fs.existsSync(target)
84
+ }
85
+
86
+ // Match nodepty native binding location to what main.cjs's resolver fallback
87
+ // expects (apps/desktop/electron/main.cjs, packaged-build branch). Upstream
88
+ // node-pty 1.x is N-API based and ships per-arch prebuilts under
89
+ // prebuilds/<platform>-<arch>/ instead of build/Release/. We check the
90
+ // per-arch dir since that's what stage-native-deps actually copies.
91
+ function expectedNativeDepPaths() {
92
+ const root = path.join(APP.resourcesPath, 'native-deps', 'node-pty')
93
+ const prebuildsDir = path.join(root, 'prebuilds', `${PLATFORM}-${ARCH}`)
94
+ return {
95
+ packageJson: path.join(root, 'package.json'),
96
+ prebuildsDir,
97
+ libIndex: path.join(root, 'lib', 'index.js')
98
+ }
99
+ }
100
+
101
+ function ensurePlatformBuilds() {
102
+ if (PLATFORM === 'darwin') return
103
+ if (PLATFORM === 'win32') return
104
+ die(
105
+ `Desktop bundle validation is only wired for darwin / win32 today; platform=${PLATFORM} ` +
106
+ `is not yet supported. The thin-installer story for Linux ships in Phase 2 alongside ` +
107
+ `install.sh's stage protocol.`
108
+ )
109
+ }
110
+
111
+ function ensurePackagedApp() {
112
+ if (process.env.NASTECH_DESKTOP_SKIP_BUILD === '1' && exists(APP.binary)) {
113
+ return
114
+ }
115
+
116
+ run('npm', ['run', 'pack'])
117
+ }
118
+
119
+ function resolveDmgPath() {
120
+ if (!exists(RELEASE_ROOT)) {
121
+ return path.join(RELEASE_ROOT, `NasTech-${PACKAGE_JSON.version}-${ARCH}.dmg`)
122
+ }
123
+
124
+ const prefix = `NasTech-${PACKAGE_JSON.version}`
125
+ const candidates = fs
126
+ .readdirSync(RELEASE_ROOT)
127
+ .filter(name => name.endsWith('.dmg'))
128
+ .filter(name => name.startsWith(prefix))
129
+ .filter(name => name.includes(ARCH))
130
+ .sort((a, b) => {
131
+ const aMtime = fs.statSync(path.join(RELEASE_ROOT, a)).mtimeMs
132
+ const bMtime = fs.statSync(path.join(RELEASE_ROOT, b)).mtimeMs
133
+ return bMtime - aMtime
134
+ })
135
+
136
+ return candidates.length > 0
137
+ ? path.join(RELEASE_ROOT, candidates[0])
138
+ : path.join(RELEASE_ROOT, `NasTech-${PACKAGE_JSON.version}-${ARCH}.dmg`)
139
+ }
140
+
141
+ function resolveNsisPath() {
142
+ // electron-builder NSIS artifactName template is 'NasTech-${version}-${os}-${arch}.${ext}'
143
+ if (!exists(RELEASE_ROOT)) return null
144
+ const candidates = fs
145
+ .readdirSync(RELEASE_ROOT)
146
+ .filter(name => /\.exe$/i.test(name) && /win/i.test(name))
147
+ .sort((a, b) => {
148
+ const aMtime = fs.statSync(path.join(RELEASE_ROOT, a)).mtimeMs
149
+ const bMtime = fs.statSync(path.join(RELEASE_ROOT, b)).mtimeMs
150
+ return bMtime - aMtime
151
+ })
152
+ return candidates.length > 0 ? path.join(RELEASE_ROOT, candidates[0]) : null
153
+ }
154
+
155
+ function ensureDmg() {
156
+ if (PLATFORM !== 'darwin') {
157
+ die('DMG mode is macOS-only; on Windows use the `nsis` mode instead.')
158
+ }
159
+ if (process.env.NASTECH_DESKTOP_SKIP_BUILD === '1' && exists(resolveDmgPath())) {
160
+ return
161
+ }
162
+ run('npm', ['run', 'dist:mac:dmg'])
163
+ }
164
+
165
+ function ensureNsis() {
166
+ if (PLATFORM !== 'win32') {
167
+ die('NSIS mode is win32-only; on macOS use the `dmg` mode instead.')
168
+ }
169
+ if (process.env.NASTECH_DESKTOP_SKIP_BUILD === '1' && resolveNsisPath()) {
170
+ return
171
+ }
172
+ run('npm', ['run', 'dist:win:nsis'])
173
+ }
174
+
175
+ function openApp() {
176
+ if (!exists(APP.binary)) {
177
+ die(`Missing packaged app: ${APP.binary}`)
178
+ }
179
+
180
+ if (PLATFORM === 'darwin') {
181
+ run('open', ['-n', APP.appPath])
182
+ } else if (PLATFORM === 'win32') {
183
+ // Spawn detached so the test script exits while the app keeps running.
184
+ spawn(APP.binary, [], { detached: true, stdio: 'ignore' }).unref()
185
+ } else {
186
+ spawn(APP.binary, [], { detached: true, stdio: 'ignore' }).unref()
187
+ }
188
+ }
189
+
190
+ function openDmg() {
191
+ if (PLATFORM !== 'darwin') {
192
+ die('DMG mode is macOS-only.')
193
+ }
194
+ const dmgPath = resolveDmgPath()
195
+ if (!exists(dmgPath)) {
196
+ die(`Missing DMG: ${dmgPath}`)
197
+ }
198
+ run('open', [dmgPath])
199
+ }
200
+
201
+ const CREDENTIAL_ENV_SUFFIXES = [
202
+ '_API_KEY',
203
+ '_TOKEN',
204
+ '_SECRET',
205
+ '_PASSWORD',
206
+ '_CREDENTIALS',
207
+ '_ACCESS_KEY',
208
+ '_PRIVATE_KEY',
209
+ '_OAUTH_TOKEN'
210
+ ]
211
+
212
+ const CREDENTIAL_ENV_NAMES = new Set([
213
+ 'ANTHROPIC_BASE_URL',
214
+ 'ANTHROPIC_TOKEN',
215
+ 'AWS_ACCESS_KEY_ID',
216
+ 'AWS_SECRET_ACCESS_KEY',
217
+ 'AWS_SESSION_TOKEN',
218
+ 'CUSTOM_API_KEY',
219
+ 'GEMINI_BASE_URL',
220
+ 'OPENAI_BASE_URL',
221
+ 'OPENROUTER_BASE_URL',
222
+ 'OLLAMA_BASE_URL',
223
+ 'GROQ_BASE_URL',
224
+ 'XAI_BASE_URL'
225
+ ])
226
+
227
+ function isCredentialEnvVar(name) {
228
+ if (CREDENTIAL_ENV_NAMES.has(name)) return true
229
+ return CREDENTIAL_ENV_SUFFIXES.some(suffix => name.endsWith(suffix))
230
+ }
231
+
232
+ function launchFresh() {
233
+ if (!exists(APP.binary)) {
234
+ die(`Missing app executable: ${APP.binary}`)
235
+ }
236
+
237
+ const sandbox = fs.mkdtempSync(`${FRESH_SANDBOX_ROOT}-`)
238
+ const userDataDir = path.join(sandbox, 'electron-user-data')
239
+ const nastechHome = path.join(sandbox, 'nastech-home')
240
+ const cwd = path.join(sandbox, 'workspace')
241
+
242
+ fs.mkdirSync(userDataDir, { recursive: true })
243
+ fs.mkdirSync(nastechHome, { recursive: true })
244
+ fs.mkdirSync(cwd, { recursive: true })
245
+
246
+ // Strip every credential-shaped env var so the sandbox is actually fresh.
247
+ const env = {}
248
+ for (const [key, value] of Object.entries(process.env)) {
249
+ if (isCredentialEnvVar(key)) continue
250
+ env[key] = value
251
+ }
252
+
253
+ env.NASTECH_DESKTOP_CWD = cwd
254
+ env.NASTECH_DESKTOP_IGNORE_EXISTING = '1'
255
+ env.NASTECH_DESKTOP_TEST_MODE = 'fresh-install'
256
+ env.NASTECH_DESKTOP_USER_DATA_DIR = userDataDir
257
+ env.NASTECH_HOME = nastechHome
258
+ delete env.NASTECH_DESKTOP_NASTECH
259
+ delete env.NASTECH_DESKTOP_NASTECH_ROOT
260
+
261
+ const child = spawn(APP.binary, [], {
262
+ cwd: os.homedir(),
263
+ detached: true,
264
+ env,
265
+ stdio: 'ignore'
266
+ })
267
+ child.unref()
268
+
269
+ console.log('\nFresh install sandbox:')
270
+ console.log(` root: ${sandbox}`)
271
+ console.log(` electron userData: ${userDataDir}`)
272
+ console.log(` NASTECH_HOME: ${nastechHome}`)
273
+ console.log(` cwd: ${cwd}`)
274
+
275
+ return { runtimeRoot: path.join(nastechHome, 'nastech-agent', 'venv') }
276
+ }
277
+
278
+ // Validate the packaged bundle matches the thin-installer architecture:
279
+ // - The NasTech Agent Python payload is NOT shipped (it's fetched at first
280
+ // launch via install.ps1's stage protocol).
281
+ // - install-stamp.json IS shipped in resources/ with a valid commit + branch.
282
+ // - native-deps/@homebridge/node-pty-prebuilt-multiarch/ IS shipped with
283
+ // the package.json + lib/ + at least one .node binary (the renderer's
284
+ // integrated terminal needs this; see Phase 1F.6).
285
+ // - The renderer's dist/index.html is reachable (either unpacked or
286
+ // inside app.asar).
287
+ function validateBundle() {
288
+ if (!exists(APP.binary)) {
289
+ die(`Missing packaged app binary: ${APP.binary}`)
290
+ }
291
+
292
+ // Negative assertion: the OLD fat-installer factory payload must NOT be
293
+ // present anymore. If a stray ship of nastech_cli sneaks back in we want
294
+ // to fail loudly rather than re-introduce the 400MB delta we just removed.
295
+ const staleFactoryMarker = path.join(APP.resourcesPath, 'nastech-agent', 'nastech_cli', 'main.py')
296
+ if (exists(staleFactoryMarker)) {
297
+ die(
298
+ `Thin-installer regression: factory-payload file should NOT be in the package: ${staleFactoryMarker}`
299
+ )
300
+ }
301
+
302
+ // Positive assertion: install-stamp.json carries a sane commit + branch
303
+ const stampPath = path.join(APP.resourcesPath, 'install-stamp.json')
304
+ if (!exists(stampPath)) {
305
+ die(`Missing install-stamp.json (required for first-launch bootstrap pinning): ${stampPath}`)
306
+ }
307
+ let stamp
308
+ try {
309
+ stamp = JSON.parse(fs.readFileSync(stampPath, 'utf8'))
310
+ } catch (err) {
311
+ die(`install-stamp.json is not valid JSON: ${err.message}`)
312
+ }
313
+ if (!stamp.commit || typeof stamp.commit !== 'string' || stamp.commit.length < 7) {
314
+ die(`install-stamp.json is missing a usable commit field: ${JSON.stringify(stamp)}`)
315
+ }
316
+ if (!stamp.branch || typeof stamp.branch !== 'string') {
317
+ die(`install-stamp.json is missing the branch field: ${JSON.stringify(stamp)}`)
318
+ }
319
+
320
+ // Positive assertion: node-pty native deps shipped
321
+ const native = expectedNativeDepPaths()
322
+ if (!exists(native.packageJson)) {
323
+ die(`Missing node-pty package.json in resources/native-deps: ${native.packageJson}`)
324
+ }
325
+ if (!exists(native.libIndex)) {
326
+ die(`Missing node-pty lib/index.js in resources/native-deps: ${native.libIndex}`)
327
+ }
328
+ if (!exists(native.prebuildsDir)) {
329
+ die(`Missing node-pty prebuilds dir for ${PLATFORM}-${ARCH}: ${native.prebuildsDir}`)
330
+ }
331
+ const nodeBinaries = fs.readdirSync(native.prebuildsDir).filter(name => name.endsWith('.node'))
332
+ if (nodeBinaries.length === 0) {
333
+ die(`No .node native binaries found in: ${native.prebuildsDir}`)
334
+ }
335
+ // Darwin requires a runtime-execed spawn-helper alongside pty.node; missing
336
+ // it manifests as "ENOENT: spawn-helper" on first pty.spawn() call.
337
+ if (PLATFORM === 'darwin') {
338
+ const spawnHelper = path.join(native.prebuildsDir, 'spawn-helper')
339
+ if (!exists(spawnHelper)) {
340
+ die(`Missing node-pty spawn-helper (required on darwin): ${spawnHelper}`)
341
+ }
342
+ }
343
+
344
+ // Renderer payload check (either unpacked or in the asar)
345
+ if (exists(APP.unpackedDistIndex)) {
346
+ return { stamp, nodeBinaries }
347
+ }
348
+ if (!exists(APP.asarPath)) {
349
+ die(`Missing renderer payload: neither ${APP.unpackedDistIndex} nor ${APP.asarPath} exists`)
350
+ }
351
+ const files = listPackage(APP.asarPath)
352
+ // Normalize separators because @electron/asar's listPackage returns
353
+ // backslash-prefixed entries on Windows ('\\dist\\index.html') and
354
+ // forward-slash on Unix.
355
+ const normalized = files.map(f => f.replace(/\\/g, '/').replace(/^\/+/, ''))
356
+ if (!normalized.includes('dist/index.html')) {
357
+ die(`Missing renderer payload file in app.asar: ${APP.asarPath} (expected dist/index.html)`)
358
+ }
359
+ return { stamp, nodeBinaries }
360
+ }
361
+
362
+ function printArtifacts(options = {}) {
363
+ const runtimeRoot = options.runtimeRoot || VENV_ROOT
364
+ const stamp = options.stamp
365
+
366
+ console.log('\nDesktop artifacts:')
367
+ console.log(` app: ${APP.appPath}`)
368
+ if (PLATFORM === 'darwin') {
369
+ console.log(` dmg: ${resolveDmgPath()}`)
370
+ } else if (PLATFORM === 'win32') {
371
+ const exe = resolveNsisPath()
372
+ if (exe) console.log(` installer: ${exe}`)
373
+ }
374
+ console.log(` runtime: ${runtimeRoot}`)
375
+ if (stamp) {
376
+ console.log(` install-stamp: ${stamp.commit.slice(0, 12)} on ${stamp.branch}`)
377
+ }
378
+ if (options.nodeBinaries && options.nodeBinaries.length > 0) {
379
+ console.log(` node-pty binaries: ${options.nodeBinaries.join(', ')}`)
380
+ }
381
+ }
382
+
383
+ function help() {
384
+ console.log(`Usage:
385
+ npm run test:desktop:existing # build packaged app, launch with normal PATH/existing NasTech
386
+ npm run test:desktop:fresh # build packaged app, launch with temp userData + NASTECH_HOME
387
+ npm run test:desktop:dmg # (macOS only) build DMG and open it
388
+ npm run test:desktop:nsis # (win32 only) build NSIS installer
389
+ npm run test:desktop:all # build installer, validate app payload, print paths
390
+
391
+ Fast rerun (skip rebuild if the packaged app already exists):
392
+ NASTECH_DESKTOP_SKIP_BUILD=1 npm run test:desktop:fresh
393
+ `)
394
+ }
395
+
396
+ ensurePlatformBuilds()
397
+
398
+ if (MODE === 'existing') {
399
+ ensurePackagedApp()
400
+ const result = validateBundle()
401
+ openApp()
402
+ printArtifacts(result)
403
+ } else if (MODE === 'fresh') {
404
+ ensurePackagedApp()
405
+ const result = validateBundle()
406
+ printArtifacts({ ...launchFresh(), ...result })
407
+ } else if (MODE === 'dmg') {
408
+ ensureDmg()
409
+ openDmg()
410
+ printArtifacts()
411
+ } else if (MODE === 'nsis') {
412
+ ensureNsis()
413
+ printArtifacts(validateBundle())
414
+ } else if (MODE === 'all') {
415
+ if (PLATFORM === 'darwin') {
416
+ ensureDmg()
417
+ } else if (PLATFORM === 'win32') {
418
+ ensureNsis()
419
+ } else {
420
+ ensurePackagedApp()
421
+ }
422
+ printArtifacts(validateBundle())
423
+ } else {
424
+ help()
425
+ }
@@ -0,0 +1,126 @@
1
+ "use strict"
2
+
3
+ /**
4
+ * Writes apps/desktop/build/install-stamp.json with the git ref the desktop
5
+ * .exe should pin to at first-launch bootstrap time. This file ships inside
6
+ * the packaged app via electron-builder's extraResources entry and is read
7
+ * by electron/main.cjs to drive the install.ps1 stage bootstrap flow.
8
+ *
9
+ * Schema (subject to bump via STAMP_SCHEMA_VERSION):
10
+ * {
11
+ * "schemaVersion": 1,
12
+ * "commit": "<40-char SHA>",
13
+ * "branch": "<branch name>",
14
+ * "builtAt": "<ISO 8601 UTC timestamp>",
15
+ * "dirty": true|false,
16
+ * "source": "ci" | "local"
17
+ * }
18
+ *
19
+ * Source preference order:
20
+ * 1. CI env vars ($GITHUB_SHA / $GITHUB_REF_NAME) -- avoid edge cases with
21
+ * shallow clones, detached HEADs, etc. in CI.
22
+ * 2. Local `git rev-parse` against the parent repo (../..).
23
+ *
24
+ * Dev / out-of-repo builds without git produce an explicit error rather than
25
+ * silently writing an unstamped manifest -- the packaged app refuses to
26
+ * bootstrap without a stamp.
27
+ */
28
+
29
+ const fs = require("fs")
30
+ const path = require("path")
31
+ const { execSync } = require("child_process")
32
+
33
+ const STAMP_SCHEMA_VERSION = 1
34
+
35
+ const DESKTOP_ROOT = path.resolve(__dirname, "..")
36
+ const REPO_ROOT = path.resolve(DESKTOP_ROOT, "..", "..")
37
+ const OUT_DIR = path.join(DESKTOP_ROOT, "build")
38
+ const OUT_FILE = path.join(OUT_DIR, "install-stamp.json")
39
+
40
+ function tryExec(cmd, opts) {
41
+ try {
42
+ return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], ...opts }).trim()
43
+ } catch {
44
+ return null
45
+ }
46
+ }
47
+
48
+ function fromCI() {
49
+ const sha = process.env.GITHUB_SHA
50
+ if (!sha) return null
51
+ const branch = process.env.GITHUB_REF_NAME || process.env.GITHUB_HEAD_REF || null
52
+ return {
53
+ commit: sha,
54
+ branch: branch,
55
+ dirty: false, // CI builds from a checkout-of-ref by definition
56
+ source: "ci"
57
+ }
58
+ }
59
+
60
+ function fromLocalGit() {
61
+ const sha = tryExec("git rev-parse HEAD", { cwd: REPO_ROOT })
62
+ if (!sha) return null
63
+ const branch = tryExec("git rev-parse --abbrev-ref HEAD", { cwd: REPO_ROOT })
64
+ // `git status --porcelain -uno` is empty iff tracked files match HEAD.
65
+ // We exclude untracked files (-uno) intentionally: a developer who's
66
+ // checked out an installer scratch dir alongside the repo shouldn't
67
+ // poison every local build with a [DIRTY] stamp. We DO care about
68
+ // tracked-but-modified files because those mean the .exe content
69
+ // differs from the commit being pinned.
70
+ const status = tryExec("git status --porcelain -uno", { cwd: REPO_ROOT })
71
+ const dirty = status !== null && status.length > 0
72
+ return {
73
+ commit: sha,
74
+ branch: branch === "HEAD" ? null : branch, // detached HEAD -> null
75
+ dirty: dirty,
76
+ source: "local"
77
+ }
78
+ }
79
+
80
+ function main() {
81
+ const stamp = fromCI() || fromLocalGit()
82
+ if (!stamp || !stamp.commit) {
83
+ console.error(
84
+ "[write-build-stamp] ERROR: could not determine git commit.\n" +
85
+ " - $GITHUB_SHA not set\n" +
86
+ " - `git rev-parse HEAD` failed at " +
87
+ REPO_ROOT +
88
+ "\n" +
89
+ "Packaged builds require a git ref to pin first-launch install.ps1\n" +
90
+ "against. Run from a git checkout or set $GITHUB_SHA explicitly."
91
+ )
92
+ process.exit(1)
93
+ }
94
+
95
+ if (stamp.dirty) {
96
+ console.warn(
97
+ "[write-build-stamp] WARNING: working tree is dirty.\n" +
98
+ " Pinning to " +
99
+ stamp.commit.slice(0, 12) +
100
+ " but the packaged code may differ from that commit.\n" +
101
+ " Commit your changes before publishing this build."
102
+ )
103
+ }
104
+
105
+ const payload = {
106
+ schemaVersion: STAMP_SCHEMA_VERSION,
107
+ commit: stamp.commit,
108
+ branch: stamp.branch,
109
+ builtAt: new Date().toISOString(),
110
+ dirty: stamp.dirty,
111
+ source: stamp.source
112
+ }
113
+
114
+ fs.mkdirSync(OUT_DIR, { recursive: true })
115
+ fs.writeFileSync(OUT_FILE, JSON.stringify(payload, null, 2) + "\n", "utf8")
116
+ console.log(
117
+ "[write-build-stamp] wrote " +
118
+ path.relative(REPO_ROOT, OUT_FILE) +
119
+ " -> " +
120
+ stamp.commit.slice(0, 12) +
121
+ (stamp.branch ? " (" + stamp.branch + ")" : "") +
122
+ (stamp.dirty ? " [DIRTY]" : "")
123
+ )
124
+ }
125
+
126
+ main()