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