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.
- package/.prettierrc +11 -0
- package/DESIGN.md +167 -0
- package/README.md +141 -0
- package/assets/icon.icns +0 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/components.json +21 -0
- package/electron/backend-env.cjs +112 -0
- package/electron/backend-env.test.cjs +111 -0
- package/electron/backend-probes.cjs +106 -0
- package/electron/backend-probes.test.cjs +82 -0
- package/electron/backend-ready.cjs +66 -0
- package/electron/bootstrap-platform.cjs +91 -0
- package/electron/bootstrap-platform.test.cjs +111 -0
- package/electron/bootstrap-runner.cjs +720 -0
- package/electron/bootstrap-runner.test.cjs +138 -0
- package/electron/connection-config.cjs +254 -0
- package/electron/connection-config.test.cjs +329 -0
- package/electron/dashboard-token.cjs +99 -0
- package/electron/dashboard-token.test.cjs +142 -0
- package/electron/desktop-uninstall.cjs +232 -0
- package/electron/desktop-uninstall.test.cjs +246 -0
- package/electron/entitlements.mac.inherit.plist +14 -0
- package/electron/entitlements.mac.plist +14 -0
- package/electron/fs-read-dir.cjs +109 -0
- package/electron/fs-read-dir.test.cjs +364 -0
- package/electron/gateway-ws-probe.cjs +188 -0
- package/electron/gateway-ws-probe.test.cjs +122 -0
- package/electron/git-root.cjs +54 -0
- package/electron/git-root.test.cjs +40 -0
- package/electron/git-worktrees.cjs +174 -0
- package/electron/hardening.cjs +184 -0
- package/electron/hardening.test.cjs +116 -0
- package/electron/main.cjs +5762 -0
- package/electron/oauth-net-request.cjs +20 -0
- package/electron/oauth-net-request.test.cjs +34 -0
- package/electron/preload.cjs +135 -0
- package/electron/session-windows.cjs +99 -0
- package/electron/session-windows.test.cjs +177 -0
- package/electron/update-remote.cjs +56 -0
- package/electron/update-remote.test.cjs +78 -0
- package/electron/vscode-marketplace.cjs +331 -0
- package/electron/vscode-marketplace.test.cjs +113 -0
- package/electron/windows-child-process.test.cjs +57 -0
- package/electron/windows-user-env.cjs +76 -0
- package/electron/windows-user-env.test.cjs +90 -0
- package/electron/workspace-cwd.cjs +38 -0
- package/electron/workspace-cwd.test.cjs +45 -0
- package/eslint.config.mjs +122 -0
- package/index.html +17 -0
- package/package.json +254 -0
- package/pr-assets/session-source-folders.png +0 -0
- package/preview-demo.html +65 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/ds-assets/filler-bg0.jpg +0 -0
- package/public/nastech-frames/nastech-frame-0.png +0 -0
- package/public/nastech-frames/nastech-frame-1.png +0 -0
- package/public/nastech-frames/nastech-frame-2.png +0 -0
- package/public/nastech-frames/nastech-frame-3.png +0 -0
- package/public/nastech-frames/nastech-frame-4.png +0 -0
- package/public/nastech-frames/nastech-frame-5.png +0 -0
- package/public/nastech-frames/nastech-frame-6.png +0 -0
- package/public/nastech-frames/nastech-frame-7.png +0 -0
- package/public/nastech-girl.jpg +0 -0
- package/public/nastech-sprite.png +0 -0
- package/public/nastech.png +0 -0
- package/scripts/after-pack.cjs +41 -0
- package/scripts/assert-dist-built.cjs +70 -0
- package/scripts/assert-dist-built.test.cjs +84 -0
- package/scripts/assert-root-install.cjs +13 -0
- package/scripts/before-build.cjs +11 -0
- package/scripts/before-pack.cjs +78 -0
- package/scripts/before-pack.test.cjs +53 -0
- package/scripts/click-session.mjs +51 -0
- package/scripts/dev-no-hmr.mjs +22 -0
- package/scripts/diag-jump.mjs +115 -0
- package/scripts/diag-scroll-reset.mjs +229 -0
- package/scripts/eval.mjs +21 -0
- package/scripts/leak-typing.mjs +222 -0
- package/scripts/measure-jump.mjs +108 -0
- package/scripts/measure-latency.mjs +184 -0
- package/scripts/measure-real-stream.mjs +252 -0
- package/scripts/measure-submit.mjs +179 -0
- package/scripts/measure-synthetic-stream.mjs +322 -0
- package/scripts/notarize-artifact.cjs +77 -0
- package/scripts/notarize.cjs +100 -0
- package/scripts/patch-electron-builder-mac-binary.cjs +59 -0
- package/scripts/probe-renderer.mjs +38 -0
- package/scripts/probe-thread.mjs +40 -0
- package/scripts/profile-long-stream.mjs +191 -0
- package/scripts/profile-real-stream.mjs +137 -0
- package/scripts/profile-synth-stream.mjs +103 -0
- package/scripts/profile-typing-lag.md +381 -0
- package/scripts/profile-typing.mjs +260 -0
- package/scripts/reload-renderer.mjs +25 -0
- package/scripts/reload.mjs +36 -0
- package/scripts/set-exe-identity.cjs +94 -0
- package/scripts/stage-native-deps.cjs +159 -0
- package/scripts/test-desktop.mjs +425 -0
- package/scripts/write-build-stamp.cjs +126 -0
- package/src/app/agents/index.tsx +398 -0
- package/src/app/artifacts/index.test.ts +62 -0
- package/src/app/artifacts/index.tsx +906 -0
- package/src/app/chat/chat-drop-overlay.tsx +48 -0
- package/src/app/chat/chat-swap-overlay.tsx +47 -0
- package/src/app/chat/composer/attachments.tsx +114 -0
- package/src/app/chat/composer/completion-drawer.tsx +63 -0
- package/src/app/chat/composer/context-menu.tsx +172 -0
- package/src/app/chat/composer/controls.tsx +289 -0
- package/src/app/chat/composer/drop-affordance.ts +2 -0
- package/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
- package/src/app/chat/composer/focus.ts +134 -0
- package/src/app/chat/composer/help-hint.tsx +59 -0
- package/src/app/chat/composer/hooks/use-at-completions.ts +141 -0
- package/src/app/chat/composer/hooks/use-live-completion-adapter.ts +119 -0
- package/src/app/chat/composer/hooks/use-mic-recorder.ts +291 -0
- package/src/app/chat/composer/hooks/use-slash-completions.ts +114 -0
- package/src/app/chat/composer/hooks/use-voice-conversation.ts +390 -0
- package/src/app/chat/composer/hooks/use-voice-recorder.ts +116 -0
- package/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
- package/src/app/chat/composer/index.tsx +1611 -0
- package/src/app/chat/composer/inline-refs.ts +138 -0
- package/src/app/chat/composer/model-pill.tsx +86 -0
- package/src/app/chat/composer/queue-panel.tsx +130 -0
- package/src/app/chat/composer/rich-editor.test.ts +18 -0
- package/src/app/chat/composer/rich-editor.ts +165 -0
- package/src/app/chat/composer/skin-slash-popover.tsx +61 -0
- package/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
- package/src/app/chat/composer/status-stack/index.tsx +202 -0
- package/src/app/chat/composer/status-stack/status-row.tsx +155 -0
- package/src/app/chat/composer/text-utils.test.ts +77 -0
- package/src/app/chat/composer/text-utils.ts +107 -0
- package/src/app/chat/composer/trigger-popover.test.tsx +42 -0
- package/src/app/chat/composer/trigger-popover.tsx +116 -0
- package/src/app/chat/composer/types.ts +64 -0
- package/src/app/chat/composer/url-dialog.tsx +82 -0
- package/src/app/chat/composer/voice-activity.tsx +252 -0
- package/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
- package/src/app/chat/hooks/use-composer-actions.ts +525 -0
- package/src/app/chat/hooks/use-file-drop-zone.ts +118 -0
- package/src/app/chat/index.tsx +390 -0
- package/src/app/chat/perf-probe.tsx +269 -0
- package/src/app/chat/right-rail/index.ts +1 -0
- package/src/app/chat/right-rail/preview-console-state.ts +82 -0
- package/src/app/chat/right-rail/preview-console.tsx +290 -0
- package/src/app/chat/right-rail/preview-file.tsx +559 -0
- package/src/app/chat/right-rail/preview-pane.test.tsx +43 -0
- package/src/app/chat/right-rail/preview-pane.tsx +657 -0
- package/src/app/chat/right-rail/preview.tsx +171 -0
- package/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
- package/src/app/chat/scroll-to-bottom-button.tsx +74 -0
- package/src/app/chat/sidebar/cron-jobs-section.tsx +325 -0
- package/src/app/chat/sidebar/index.tsx +1219 -0
- package/src/app/chat/sidebar/load-more-row.tsx +30 -0
- package/src/app/chat/sidebar/order.test.ts +21 -0
- package/src/app/chat/sidebar/order.ts +17 -0
- package/src/app/chat/sidebar/profile-switcher.tsx +516 -0
- package/src/app/chat/sidebar/session-actions-menu.tsx +264 -0
- package/src/app/chat/sidebar/session-row.tsx +257 -0
- package/src/app/chat/sidebar/virtual-session-list.tsx +154 -0
- package/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
- package/src/app/chat/sidebar/workspace-groups.ts +326 -0
- package/src/app/chat/thread-loading.test.ts +34 -0
- package/src/app/chat/thread-loading.ts +26 -0
- package/src/app/command-center/index.tsx +654 -0
- package/src/app/command-palette/index.tsx +513 -0
- package/src/app/command-palette/marketplace-theme-page.tsx +157 -0
- package/src/app/cron/index.tsx +942 -0
- package/src/app/cron/job-state.ts +29 -0
- package/src/app/desktop-controller.tsx +938 -0
- package/src/app/floating-hud.ts +22 -0
- package/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
- package/src/app/gateway/hooks/use-gateway-boot.ts +387 -0
- package/src/app/gateway/hooks/use-gateway-request.ts +138 -0
- package/src/app/hooks/use-keybinds.ts +186 -0
- package/src/app/hooks/use-refresh-hotkey.ts +45 -0
- package/src/app/hooks/use-route-enum-param.ts +38 -0
- package/src/app/index.tsx +1 -0
- package/src/app/layout-constants.ts +13 -0
- package/src/app/messaging/index.tsx +648 -0
- package/src/app/messaging/platform-icon.tsx +93 -0
- package/src/app/model-picker-overlay.tsx +42 -0
- package/src/app/model-visibility-overlay.tsx +31 -0
- package/src/app/overlays/overlay-chrome.tsx +66 -0
- package/src/app/overlays/overlay-search-input.tsx +33 -0
- package/src/app/overlays/overlay-split-layout.tsx +130 -0
- package/src/app/overlays/overlay-view.tsx +91 -0
- package/src/app/page-search-shell.tsx +75 -0
- package/src/app/profiles/create-profile-dialog.tsx +154 -0
- package/src/app/profiles/delete-profile-dialog.tsx +65 -0
- package/src/app/profiles/index.tsx +671 -0
- package/src/app/profiles/rename-profile-dialog.tsx +125 -0
- package/src/app/right-sidebar/files/dnd-manager.ts +27 -0
- package/src/app/right-sidebar/files/ipc.test.ts +100 -0
- package/src/app/right-sidebar/files/ipc.ts +161 -0
- package/src/app/right-sidebar/files/remote-picker.tsx +177 -0
- package/src/app/right-sidebar/files/tree.tsx +224 -0
- package/src/app/right-sidebar/files/use-project-tree.test.ts +190 -0
- package/src/app/right-sidebar/files/use-project-tree.ts +268 -0
- package/src/app/right-sidebar/index.test.tsx +75 -0
- package/src/app/right-sidebar/index.tsx +395 -0
- package/src/app/right-sidebar/store.ts +15 -0
- package/src/app/right-sidebar/terminal/buffer.ts +65 -0
- package/src/app/right-sidebar/terminal/index.tsx +98 -0
- package/src/app/right-sidebar/terminal/persistent.tsx +122 -0
- package/src/app/right-sidebar/terminal/selection.ts +75 -0
- package/src/app/right-sidebar/terminal/use-terminal-session.ts +504 -0
- package/src/app/routes.ts +88 -0
- package/src/app/session/hooks/use-context-suggestions.ts +58 -0
- package/src/app/session/hooks/use-cwd-actions.ts +109 -0
- package/src/app/session/hooks/use-message-stream.ts +957 -0
- package/src/app/session/hooks/use-model-controls.test.tsx +198 -0
- package/src/app/session/hooks/use-model-controls.ts +106 -0
- package/src/app/session/hooks/use-nastech-config.ts +74 -0
- package/src/app/session/hooks/use-preview-routing.test.tsx +168 -0
- package/src/app/session/hooks/use-preview-routing.ts +223 -0
- package/src/app/session/hooks/use-prompt-actions.test.tsx +316 -0
- package/src/app/session/hooks/use-prompt-actions.ts +1030 -0
- package/src/app/session/hooks/use-route-resume.test.tsx +136 -0
- package/src/app/session/hooks/use-route-resume.ts +115 -0
- package/src/app/session/hooks/use-session-actions.test.tsx +119 -0
- package/src/app/session/hooks/use-session-actions.ts +885 -0
- package/src/app/session/hooks/use-session-state-cache.test.tsx +118 -0
- package/src/app/session/hooks/use-session-state-cache.ts +191 -0
- package/src/app/session-picker-overlay.tsx +32 -0
- package/src/app/session-switcher.tsx +107 -0
- package/src/app/settings/about-settings.tsx +173 -0
- package/src/app/settings/appearance-settings.tsx +162 -0
- package/src/app/settings/config-settings.tsx +384 -0
- package/src/app/settings/constants.ts +545 -0
- package/src/app/settings/credential-key-ui.tsx +373 -0
- package/src/app/settings/env-credentials.tsx +198 -0
- package/src/app/settings/env-var-actions-menu.tsx +136 -0
- package/src/app/settings/field-copy.ts +56 -0
- package/src/app/settings/gateway-settings.tsx +620 -0
- package/src/app/settings/helpers.test.ts +138 -0
- package/src/app/settings/helpers.ts +151 -0
- package/src/app/settings/index.tsx +237 -0
- package/src/app/settings/keys-settings.tsx +96 -0
- package/src/app/settings/mcp-settings.tsx +271 -0
- package/src/app/settings/model-settings.test.tsx +157 -0
- package/src/app/settings/model-settings.tsx +559 -0
- package/src/app/settings/notifications-settings.tsx +150 -0
- package/src/app/settings/primitives.tsx +115 -0
- package/src/app/settings/providers-settings.test.tsx +100 -0
- package/src/app/settings/providers-settings.tsx +258 -0
- package/src/app/settings/sessions-settings.tsx +276 -0
- package/src/app/settings/toolset-config-panel.test.tsx +289 -0
- package/src/app/settings/toolset-config-panel.tsx +449 -0
- package/src/app/settings/types.ts +42 -0
- package/src/app/settings/uninstall-section.tsx +185 -0
- package/src/app/settings/use-deep-link-highlight.ts +60 -0
- package/src/app/shell/app-shell.tsx +167 -0
- package/src/app/shell/gateway-menu-panel.tsx +150 -0
- package/src/app/shell/hooks/use-overlay-routing.ts +71 -0
- package/src/app/shell/hooks/use-status-snapshot.ts +57 -0
- package/src/app/shell/hooks/use-statusbar-items.tsx +403 -0
- package/src/app/shell/keybind-panel.tsx +220 -0
- package/src/app/shell/model-edit-submenu.test.tsx +84 -0
- package/src/app/shell/model-edit-submenu.tsx +245 -0
- package/src/app/shell/model-menu-panel.tsx +295 -0
- package/src/app/shell/sidebar-label.tsx +22 -0
- package/src/app/shell/statusbar-controls.tsx +185 -0
- package/src/app/shell/titlebar-controls.tsx +244 -0
- package/src/app/shell/titlebar.test.ts +26 -0
- package/src/app/shell/titlebar.ts +45 -0
- package/src/app/shell/use-group-registry.ts +39 -0
- package/src/app/skills/index.test.tsx +103 -0
- package/src/app/skills/index.tsx +371 -0
- package/src/app/types.ts +99 -0
- package/src/app/updates-overlay.tsx +369 -0
- package/src/components/Backdrop.tsx +114 -0
- package/src/components/assistant-ui/ansi-text.tsx +34 -0
- package/src/components/assistant-ui/clarify-tool.tsx +281 -0
- package/src/components/assistant-ui/directive-text.test.ts +39 -0
- package/src/components/assistant-ui/directive-text.tsx +389 -0
- package/src/components/assistant-ui/markdown-text.test.ts +204 -0
- package/src/components/assistant-ui/markdown-text.tsx +497 -0
- package/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
- package/src/components/assistant-ui/message-render-boundary.tsx +48 -0
- package/src/components/assistant-ui/streaming.test.tsx +739 -0
- package/src/components/assistant-ui/thread-list.tsx +307 -0
- package/src/components/assistant-ui/thread-virtualizer.tsx +512 -0
- package/src/components/assistant-ui/thread.tsx +1474 -0
- package/src/components/assistant-ui/todo-tool.tsx +109 -0
- package/src/components/assistant-ui/tool-approval-group.test.tsx +158 -0
- package/src/components/assistant-ui/tool-approval.test.tsx +81 -0
- package/src/components/assistant-ui/tool-approval.tsx +209 -0
- package/src/components/assistant-ui/tool-fallback-model.test.ts +66 -0
- package/src/components/assistant-ui/tool-fallback-model.ts +1368 -0
- package/src/components/assistant-ui/tool-fallback.tsx +466 -0
- package/src/components/assistant-ui/tooltip-icon-button.tsx +33 -0
- package/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
- package/src/components/assistant-ui/user-message-text.tsx +150 -0
- package/src/components/boot-failure-overlay.tsx +246 -0
- package/src/components/boot-failure-reauth.test.ts +100 -0
- package/src/components/boot-failure-reauth.ts +81 -0
- package/src/components/brand-mark.tsx +19 -0
- package/src/components/chat/activity-timer-text.tsx +24 -0
- package/src/components/chat/activity-timer.test.tsx +43 -0
- package/src/components/chat/activity-timer.ts +64 -0
- package/src/components/chat/code-card.tsx +78 -0
- package/src/components/chat/compact-markdown.tsx +113 -0
- package/src/components/chat/composer-dock.ts +31 -0
- package/src/components/chat/diff-lines.tsx +54 -0
- package/src/components/chat/disclosure-row.tsx +63 -0
- package/src/components/chat/generated-image-context.tsx +19 -0
- package/src/components/chat/generated-image-result.tsx +174 -0
- package/src/components/chat/image-generation-placeholder.tsx +279 -0
- package/src/components/chat/intro-copy.jsonl +75 -0
- package/src/components/chat/intro.tsx +182 -0
- package/src/components/chat/preview-attachment.tsx +125 -0
- package/src/components/chat/shiki-highlighter.tsx +107 -0
- package/src/components/chat/status-row.tsx +70 -0
- package/src/components/chat/status-section.tsx +42 -0
- package/src/components/chat/terminal-output.tsx +50 -0
- package/src/components/chat/zoomable-image.tsx +177 -0
- package/src/components/desktop-install-overlay.tsx +595 -0
- package/src/components/desktop-onboarding-overlay.test.tsx +100 -0
- package/src/components/desktop-onboarding-overlay.tsx +1286 -0
- package/src/components/error-boundary.tsx +77 -0
- package/src/components/gateway-connecting-overlay.test.tsx +143 -0
- package/src/components/gateway-connecting-overlay.tsx +183 -0
- package/src/components/haptics-provider.tsx +19 -0
- package/src/components/language-switcher.test.tsx +53 -0
- package/src/components/language-switcher.tsx +175 -0
- package/src/components/model-picker.tsx +340 -0
- package/src/components/model-visibility-dialog.tsx +155 -0
- package/src/components/notifications.tsx +196 -0
- package/src/components/page-loader.tsx +34 -0
- package/src/components/pane-shell/context.ts +14 -0
- package/src/components/pane-shell/index.ts +4 -0
- package/src/components/pane-shell/pane-shell.test.tsx +333 -0
- package/src/components/pane-shell/pane-shell.tsx +330 -0
- package/src/components/prompt-overlays.tsx +234 -0
- package/src/components/session-picker.tsx +108 -0
- package/src/components/status-dot.tsx +26 -0
- package/src/components/ui/action-status.tsx +25 -0
- package/src/components/ui/alert.tsx +53 -0
- package/src/components/ui/badge.tsx +35 -0
- package/src/components/ui/braille-spinner.tsx +61 -0
- package/src/components/ui/button.tsx +81 -0
- package/src/components/ui/checkbox.tsx +27 -0
- package/src/components/ui/codicon.tsx +20 -0
- package/src/components/ui/command.tsx +111 -0
- package/src/components/ui/confirm-dialog.tsx +109 -0
- package/src/components/ui/context-menu.tsx +141 -0
- package/src/components/ui/control.ts +25 -0
- package/src/components/ui/copy-button.test.tsx +36 -0
- package/src/components/ui/copy-button.tsx +229 -0
- package/src/components/ui/dialog.tsx +152 -0
- package/src/components/ui/disclosure-caret.tsx +20 -0
- package/src/components/ui/dropdown-menu.tsx +291 -0
- package/src/components/ui/error-state.tsx +50 -0
- package/src/components/ui/fade-text.tsx +110 -0
- package/src/components/ui/glyph-spinner.tsx +63 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/kbd.tsx +37 -0
- package/src/components/ui/loader.tsx +558 -0
- package/src/components/ui/log-view.tsx +17 -0
- package/src/components/ui/pagination.tsx +114 -0
- package/src/components/ui/popover.tsx +44 -0
- package/src/components/ui/scroll-area.tsx +43 -0
- package/src/components/ui/search-field.tsx +80 -0
- package/src/components/ui/segmented-control.tsx +51 -0
- package/src/components/ui/select.tsx +92 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +116 -0
- package/src/components/ui/sidebar.tsx +674 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/switch.tsx +49 -0
- package/src/components/ui/tabs.tsx +36 -0
- package/src/components/ui/text-tab.tsx +43 -0
- package/src/components/ui/textarea.tsx +11 -0
- package/src/components/ui/tool-icon.tsx +65 -0
- package/src/components/ui/tooltip.tsx +69 -0
- package/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
- package/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
- package/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/src/global.d.ts +457 -0
- package/src/hooks/use-image-download.ts +85 -0
- package/src/hooks/use-media-query.ts +24 -0
- package/src/hooks/use-mobile.ts +3 -0
- package/src/hooks/use-resize-observer.ts +38 -0
- package/src/hooks/use-worktree-info.ts +68 -0
- package/src/i18n/catalog.ts +12 -0
- package/src/i18n/context.test.tsx +232 -0
- package/src/i18n/context.tsx +183 -0
- package/src/i18n/define-locale.ts +41 -0
- package/src/i18n/en.ts +1779 -0
- package/src/i18n/index.ts +20 -0
- package/src/i18n/ja.ts +1890 -0
- package/src/i18n/languages.test.ts +43 -0
- package/src/i18n/languages.ts +86 -0
- package/src/i18n/runtime.test.ts +75 -0
- package/src/i18n/runtime.ts +53 -0
- package/src/i18n/types.ts +1452 -0
- package/src/i18n/zh-hant.ts +1849 -0
- package/src/i18n/zh.ts +1923 -0
- package/src/lib/ansi.test.ts +123 -0
- package/src/lib/ansi.ts +175 -0
- package/src/lib/chat-messages.test.ts +708 -0
- package/src/lib/chat-messages.ts +885 -0
- package/src/lib/chat-runtime.test.ts +18 -0
- package/src/lib/chat-runtime.ts +335 -0
- package/src/lib/clipboard.ts +28 -0
- package/src/lib/commit-changelog.test.ts +114 -0
- package/src/lib/commit-changelog.ts +177 -0
- package/src/lib/completion-sound.ts +519 -0
- package/src/lib/desktop-fs.test.ts +116 -0
- package/src/lib/desktop-fs.ts +113 -0
- package/src/lib/desktop-slash-commands.test.ts +126 -0
- package/src/lib/desktop-slash-commands.ts +286 -0
- package/src/lib/embedded-images.test.ts +35 -0
- package/src/lib/embedded-images.ts +60 -0
- package/src/lib/external-link.test.tsx +168 -0
- package/src/lib/external-link.tsx +303 -0
- package/src/lib/gateway-events.test.ts +27 -0
- package/src/lib/gateway-events.ts +49 -0
- package/src/lib/gateway-ws-url.test.ts +78 -0
- package/src/lib/gateway-ws-url.ts +91 -0
- package/src/lib/generated-images.test.ts +97 -0
- package/src/lib/generated-images.ts +116 -0
- package/src/lib/haptics.ts +129 -0
- package/src/lib/icons.ts +203 -0
- package/src/lib/incremental-external-store-runtime.ts +188 -0
- package/src/lib/katex-memo.ts +260 -0
- package/src/lib/keybinds/actions.ts +125 -0
- package/src/lib/keybinds/combo.test.ts +86 -0
- package/src/lib/keybinds/combo.ts +169 -0
- package/src/lib/local-preview.ts +126 -0
- package/src/lib/markdown-code.test.ts +23 -0
- package/src/lib/markdown-code.ts +195 -0
- package/src/lib/markdown-preprocess.ts +386 -0
- package/src/lib/media.remote.test.ts +58 -0
- package/src/lib/media.ts +111 -0
- package/src/lib/model-status-label.test.ts +31 -0
- package/src/lib/model-status-label.ts +103 -0
- package/src/lib/mutable-ref.ts +6 -0
- package/src/lib/preview-targets.test.ts +27 -0
- package/src/lib/preview-targets.ts +63 -0
- package/src/lib/profile-color.ts +58 -0
- package/src/lib/provider-setup-errors.test.ts +26 -0
- package/src/lib/provider-setup-errors.ts +12 -0
- package/src/lib/query-client.ts +13 -0
- package/src/lib/remend-tail.test.ts +105 -0
- package/src/lib/remend-tail.ts +108 -0
- package/src/lib/runtime-readiness.test.ts +65 -0
- package/src/lib/runtime-readiness.ts +147 -0
- package/src/lib/session-export.ts +57 -0
- package/src/lib/session-search.test.ts +58 -0
- package/src/lib/session-search.ts +19 -0
- package/src/lib/session-source.ts +62 -0
- package/src/lib/speech-text.ts +35 -0
- package/src/lib/statusbar.ts +91 -0
- package/src/lib/storage.test.ts +25 -0
- package/src/lib/storage.ts +107 -0
- package/src/lib/todos.test.ts +35 -0
- package/src/lib/todos.ts +51 -0
- package/src/lib/tool-result-summary.test.ts +106 -0
- package/src/lib/tool-result-summary.ts +467 -0
- package/src/lib/update-copy.test.ts +38 -0
- package/src/lib/update-copy.ts +44 -0
- package/src/lib/use-enter-animation.ts +100 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/voice-playback.ts +128 -0
- package/src/lib/yolo-session.ts +26 -0
- package/src/main.tsx +43 -0
- package/src/nastech.test.ts +49 -0
- package/src/nastech.ts +718 -0
- package/src/store/activity.ts +100 -0
- package/src/store/boot.ts +91 -0
- package/src/store/clarify.test.ts +81 -0
- package/src/store/clarify.ts +69 -0
- package/src/store/command-palette.ts +20 -0
- package/src/store/compaction.test.ts +53 -0
- package/src/store/compaction.ts +38 -0
- package/src/store/completion-sound.ts +32 -0
- package/src/store/composer-input-history.test.ts +147 -0
- package/src/store/composer-input-history.ts +158 -0
- package/src/store/composer-queue.test.ts +148 -0
- package/src/store/composer-queue.ts +239 -0
- package/src/store/composer-status.test.ts +99 -0
- package/src/store/composer-status.ts +277 -0
- package/src/store/composer.test.ts +106 -0
- package/src/store/composer.ts +184 -0
- package/src/store/cron.ts +19 -0
- package/src/store/gateway.ts +290 -0
- package/src/store/haptics.ts +17 -0
- package/src/store/keybinds.ts +139 -0
- package/src/store/layout.ts +176 -0
- package/src/store/model-presets.test.ts +51 -0
- package/src/store/model-presets.ts +86 -0
- package/src/store/model-visibility.test.ts +37 -0
- package/src/store/model-visibility.ts +108 -0
- package/src/store/native-notifications.test.ts +192 -0
- package/src/store/native-notifications.ts +203 -0
- package/src/store/notifications.ts +165 -0
- package/src/store/onboarding.test.ts +372 -0
- package/src/store/onboarding.ts +866 -0
- package/src/store/panes.test.ts +146 -0
- package/src/store/panes.ts +145 -0
- package/src/store/preview.test.ts +135 -0
- package/src/store/preview.ts +466 -0
- package/src/store/profile.test.ts +89 -0
- package/src/store/profile.ts +365 -0
- package/src/store/prompts.test.ts +121 -0
- package/src/store/prompts.ts +115 -0
- package/src/store/session-switcher.test.ts +115 -0
- package/src/store/session-switcher.ts +128 -0
- package/src/store/session-sync.ts +25 -0
- package/src/store/session.test.ts +131 -0
- package/src/store/session.ts +255 -0
- package/src/store/subagents.test.ts +111 -0
- package/src/store/subagents.ts +260 -0
- package/src/store/thread-scroll.ts +46 -0
- package/src/store/todos.test.ts +47 -0
- package/src/store/todos.ts +64 -0
- package/src/store/tool-diffs.ts +23 -0
- package/src/store/tool-dismiss.ts +45 -0
- package/src/store/tool-view.ts +91 -0
- package/src/store/translucency.ts +38 -0
- package/src/store/updates.test.ts +77 -0
- package/src/store/updates.ts +315 -0
- package/src/store/voice-playback.ts +24 -0
- package/src/store/windows.test.ts +143 -0
- package/src/store/windows.ts +77 -0
- package/src/styles.css +1235 -0
- package/src/themes/color.ts +142 -0
- package/src/themes/context.tsx +339 -0
- package/src/themes/index.ts +3 -0
- package/src/themes/install.test.ts +119 -0
- package/src/themes/install.ts +95 -0
- package/src/themes/presets.test.ts +33 -0
- package/src/themes/presets.ts +293 -0
- package/src/themes/profile-theme.test.ts +41 -0
- package/src/themes/types.ts +66 -0
- package/src/themes/use-skin-command.ts +60 -0
- package/src/themes/user-themes.test.ts +63 -0
- package/src/themes/user-themes.ts +122 -0
- package/src/themes/vscode.test.ts +171 -0
- package/src/themes/vscode.ts +343 -0
- package/src/types/nastech.ts +646 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +25 -0
- 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()
|