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,138 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const test = require('node:test')
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
runBootstrap,
|
|
9
|
+
resolveInstallScript,
|
|
10
|
+
installedAgentInstallScript,
|
|
11
|
+
cachedScriptPath
|
|
12
|
+
} = require('./bootstrap-runner.cjs')
|
|
13
|
+
|
|
14
|
+
const SCRIPT_NAME = process.platform === 'win32' ? 'install.ps1' : 'install.sh'
|
|
15
|
+
|
|
16
|
+
function mkTmpHome() {
|
|
17
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'nastech-bootstrap-test-'))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test('runBootstrap bails immediately when the signal is already aborted', async () => {
|
|
21
|
+
const controller = new AbortController()
|
|
22
|
+
controller.abort()
|
|
23
|
+
|
|
24
|
+
const events = []
|
|
25
|
+
const result = await runBootstrap({
|
|
26
|
+
installStamp: null,
|
|
27
|
+
activeRoot: '/tmp/nastech-runner-test',
|
|
28
|
+
sourceRepoRoot: null,
|
|
29
|
+
nastechHome: '/tmp/nastech-runner-test',
|
|
30
|
+
logRoot: '/tmp/nastech-runner-test',
|
|
31
|
+
onEvent: ev => events.push(ev),
|
|
32
|
+
abortSignal: controller.signal
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Cancelled before any install script is spawned.
|
|
36
|
+
assert.deepEqual(result, { ok: false, cancelled: true })
|
|
37
|
+
assert.ok(
|
|
38
|
+
events.some(ev => ev.type === 'failed' && /cancelled/i.test(ev.error)),
|
|
39
|
+
'should emit a cancelled failure event'
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('installedAgentInstallScript resolves the installer in the agent checkout', () => {
|
|
44
|
+
const home = mkTmpHome()
|
|
45
|
+
try {
|
|
46
|
+
assert.equal(installedAgentInstallScript(home), null, 'absent before the checkout exists')
|
|
47
|
+
|
|
48
|
+
const scriptsDir = path.join(home, 'nastech-agent', 'scripts')
|
|
49
|
+
fs.mkdirSync(scriptsDir, { recursive: true })
|
|
50
|
+
const scriptPath = path.join(scriptsDir, SCRIPT_NAME)
|
|
51
|
+
fs.writeFileSync(scriptPath, '#!/bin/sh\necho hi\n')
|
|
52
|
+
|
|
53
|
+
assert.equal(installedAgentInstallScript(home), scriptPath)
|
|
54
|
+
assert.equal(installedAgentInstallScript(null), null, 'null home -> null')
|
|
55
|
+
} finally {
|
|
56
|
+
fs.rmSync(home, { recursive: true, force: true })
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('resolveInstallScript prefers a cached script without touching the network', async () => {
|
|
61
|
+
const home = mkTmpHome()
|
|
62
|
+
try {
|
|
63
|
+
const commit = 'a'.repeat(40)
|
|
64
|
+
const cached = cachedScriptPath(home, commit)
|
|
65
|
+
fs.mkdirSync(path.dirname(cached), { recursive: true })
|
|
66
|
+
fs.writeFileSync(cached, '#!/bin/sh\necho cached\n')
|
|
67
|
+
|
|
68
|
+
const logs = []
|
|
69
|
+
const result = await resolveInstallScript({
|
|
70
|
+
installStamp: { commit },
|
|
71
|
+
sourceRepoRoot: null,
|
|
72
|
+
nastechHome: home,
|
|
73
|
+
emit: ev => logs.push(ev)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
assert.equal(result.source, 'cache')
|
|
77
|
+
assert.equal(result.path, cached)
|
|
78
|
+
} finally {
|
|
79
|
+
fs.rmSync(home, { recursive: true, force: true })
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('resolveInstallScript falls back to the installed agent checkout on a 404', async () => {
|
|
84
|
+
const home = mkTmpHome()
|
|
85
|
+
try {
|
|
86
|
+
const commit = 'a'.repeat(40)
|
|
87
|
+
// Seed the installed agent checkout so the fallback has something to resolve.
|
|
88
|
+
const scriptsDir = path.join(home, 'nastech-agent', 'scripts')
|
|
89
|
+
fs.mkdirSync(scriptsDir, { recursive: true })
|
|
90
|
+
const installed = path.join(scriptsDir, SCRIPT_NAME)
|
|
91
|
+
fs.writeFileSync(installed, '#!/bin/sh\necho fallback\n')
|
|
92
|
+
|
|
93
|
+
const logs = []
|
|
94
|
+
const result = await resolveInstallScript({
|
|
95
|
+
installStamp: { commit },
|
|
96
|
+
sourceRepoRoot: null,
|
|
97
|
+
nastechHome: home,
|
|
98
|
+
emit: ev => logs.push(ev),
|
|
99
|
+
// Simulate GitHub returning a 404 for the pinned commit.
|
|
100
|
+
_download: async () => {
|
|
101
|
+
throw new Error('Failed to download install.sh: HTTP 404')
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
assert.equal(result.source, 'installed-agent')
|
|
106
|
+
// It should have copied the installer into the bootstrap cache.
|
|
107
|
+
assert.equal(result.path, cachedScriptPath(home, commit))
|
|
108
|
+
assert.ok(fs.existsSync(result.path), 'fallback script copied into cache')
|
|
109
|
+
assert.ok(
|
|
110
|
+
logs.some(ev => /falling back to installed agent/.test(ev.line || '')),
|
|
111
|
+
'emits a fallback log line'
|
|
112
|
+
)
|
|
113
|
+
} finally {
|
|
114
|
+
fs.rmSync(home, { recursive: true, force: true })
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('resolveInstallScript rethrows when the 404 fallback is unavailable', async () => {
|
|
119
|
+
const home = mkTmpHome()
|
|
120
|
+
try {
|
|
121
|
+
const commit = 'a'.repeat(40)
|
|
122
|
+
// No installed agent checkout seeded -> nothing to fall back to.
|
|
123
|
+
await assert.rejects(
|
|
124
|
+
resolveInstallScript({
|
|
125
|
+
installStamp: { commit },
|
|
126
|
+
sourceRepoRoot: null,
|
|
127
|
+
nastechHome: home,
|
|
128
|
+
emit: () => {},
|
|
129
|
+
_download: async () => {
|
|
130
|
+
throw new Error('Failed to download install.sh: HTTP 404')
|
|
131
|
+
}
|
|
132
|
+
}),
|
|
133
|
+
/HTTP 404|Failed to download/
|
|
134
|
+
)
|
|
135
|
+
} finally {
|
|
136
|
+
fs.rmSync(home, { recursive: true, force: true })
|
|
137
|
+
}
|
|
138
|
+
})
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connection-config.cjs
|
|
3
|
+
*
|
|
4
|
+
* Pure, electron-free helpers for the desktop's remote-gateway connection
|
|
5
|
+
* config: URL normalization, WS-URL construction (token vs OAuth ticket),
|
|
6
|
+
* auth-mode classification, and the auth-mode coercion rules.
|
|
7
|
+
*
|
|
8
|
+
* Kept standalone (no `require('electron')`) so it can be unit-tested with
|
|
9
|
+
* `node --test` — same pattern as backend-probes.cjs / bootstrap-platform.cjs.
|
|
10
|
+
* main.cjs requires these and wires them into the electron-coupled IPC layer.
|
|
11
|
+
*
|
|
12
|
+
* Background on the two auth models a remote gateway can use:
|
|
13
|
+
* - 'token': legacy static dashboard session token. REST uses an
|
|
14
|
+
* `X-NasTech-Session-Token` header; WS uses `?token=`.
|
|
15
|
+
* - 'oauth': hosted gateways gate behind an OAuth provider. REST is authed
|
|
16
|
+
* by an HttpOnly session cookie; WS upgrades require a single-use
|
|
17
|
+
* `?ticket=` minted at POST /api/auth/ws-ticket. The gateway advertises
|
|
18
|
+
* this via the public `/api/status` field `auth_required: true`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Bare + prefixed variants of the session cookies the gateway may set,
|
|
22
|
+
// depending on its deploy shape (HTTPS direct → __Host-, behind a path prefix
|
|
23
|
+
// → __Secure-, loopback HTTP → bare). Mirrors
|
|
24
|
+
// nastech_cli/dashboard_auth/cookies.py.
|
|
25
|
+
//
|
|
26
|
+
// Two cookies are in play (see that module):
|
|
27
|
+
// - nastech_session_at: the OAuth access token. Short-lived (~15 min); its
|
|
28
|
+
// Max-Age tracks the access-token TTL, so the cookie jar drops it the
|
|
29
|
+
// instant the AT expires.
|
|
30
|
+
// - nastech_session_rt: the OAuth refresh token. Long-lived (24h rotating,
|
|
31
|
+
// reuse-detected — Portal NAS #293 / nastech #37247). When the AT cookie
|
|
32
|
+
// has lapsed but the RT cookie is still present, the gateway middleware
|
|
33
|
+
// transparently rotates a fresh AT on the next authenticated request
|
|
34
|
+
// (POST /api/auth/ws-ticket), so the session is still LIVE even with no
|
|
35
|
+
// AT cookie. A liveness check that looked only at the AT cookie would
|
|
36
|
+
// force a needless full re-login every ~15 min — hence cookiesHaveLiveSession.
|
|
37
|
+
const AT_COOKIE_VARIANTS = ['__Host-nastech_session_at', '__Secure-nastech_session_at', 'nastech_session_at']
|
|
38
|
+
const RT_COOKIE_VARIANTS = ['__Host-nastech_session_rt', '__Secure-nastech_session_rt', 'nastech_session_rt']
|
|
39
|
+
|
|
40
|
+
function normalizeRemoteBaseUrl(rawUrl) {
|
|
41
|
+
const value = String(rawUrl || '').trim()
|
|
42
|
+
|
|
43
|
+
if (!value) {
|
|
44
|
+
throw new Error('Remote gateway URL is required.')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let parsed
|
|
48
|
+
try {
|
|
49
|
+
parsed = new URL(value)
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(`Remote gateway URL is not valid: ${error.message}`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
55
|
+
throw new Error(`Remote gateway URL must be http:// or https://, got ${parsed.protocol}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
parsed.hash = ''
|
|
59
|
+
parsed.search = ''
|
|
60
|
+
parsed.pathname = parsed.pathname.replace(/\/+$/, '')
|
|
61
|
+
|
|
62
|
+
return parsed.toString().replace(/\/+$/, '')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildGatewayWsUrl(baseUrl, token) {
|
|
66
|
+
const parsed = new URL(baseUrl)
|
|
67
|
+
const wsScheme = parsed.protocol === 'https:' ? 'wss' : 'ws'
|
|
68
|
+
const prefix = parsed.pathname.replace(/\/+$/, '')
|
|
69
|
+
|
|
70
|
+
return `${wsScheme}://${parsed.host}${prefix}/api/ws?token=${encodeURIComponent(token)}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildGatewayWsUrlWithTicket(baseUrl, ticket) {
|
|
74
|
+
const parsed = new URL(baseUrl)
|
|
75
|
+
const wsScheme = parsed.protocol === 'https:' ? 'wss' : 'ws'
|
|
76
|
+
const prefix = parsed.pathname.replace(/\/+$/, '')
|
|
77
|
+
|
|
78
|
+
return `${wsScheme}://${parsed.host}${prefix}/api/ws?ticket=${encodeURIComponent(ticket)}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build the WS URL the renderer would connect with, so the connection test can
|
|
83
|
+
* exercise the same transport the app actually uses.
|
|
84
|
+
*
|
|
85
|
+
* The OAuth ticket-minter is injected (`mintTicket(baseUrl) -> Promise<ticket>`)
|
|
86
|
+
* so this stays electron-free and unit-testable; main.cjs passes the real
|
|
87
|
+
* `mintGatewayWsTicket`.
|
|
88
|
+
*
|
|
89
|
+
* Return semantics:
|
|
90
|
+
* - token mode + token → ws(s)://…/api/ws?token=…
|
|
91
|
+
* - token mode, no token → null (genuine skip; nothing to authenticate with)
|
|
92
|
+
* - oauth, mint ok → ws(s)://…/api/ws?ticket=…
|
|
93
|
+
* - oauth, mint fails → THROWS (NOT a skip)
|
|
94
|
+
*
|
|
95
|
+
* The oauth-mint-failure throw is the important case: the real boot path
|
|
96
|
+
* (resolveRemoteBackend in main.cjs) treats a mint failure as a hard
|
|
97
|
+
* "session expired" auth error and refuses to connect. Swallowing it here
|
|
98
|
+
* would re-introduce the exact false-positive this test exists to catch —
|
|
99
|
+
* HTTP /api/status passes, the test reports "reachable", then the renderer
|
|
100
|
+
* can't authenticate /api/ws and boot dies with "Could not connect".
|
|
101
|
+
*
|
|
102
|
+
* @param {string} baseUrl
|
|
103
|
+
* @param {'token'|'oauth'} authMode
|
|
104
|
+
* @param {string|null} token
|
|
105
|
+
* @param {{ mintTicket: (baseUrl: string) => Promise<string> }} deps
|
|
106
|
+
* @returns {Promise<string|null>}
|
|
107
|
+
*/
|
|
108
|
+
async function resolveTestWsUrl(baseUrl, authMode, token, deps = {}) {
|
|
109
|
+
if (authMode === 'oauth') {
|
|
110
|
+
const mintTicket = deps.mintTicket
|
|
111
|
+
if (typeof mintTicket !== 'function') {
|
|
112
|
+
throw new Error('resolveTestWsUrl: a mintTicket function is required in OAuth mode.')
|
|
113
|
+
}
|
|
114
|
+
let ticket
|
|
115
|
+
try {
|
|
116
|
+
ticket = await mintTicket(baseUrl)
|
|
117
|
+
} catch (error) {
|
|
118
|
+
const err = new Error(
|
|
119
|
+
'Reached the gateway over HTTP, but could not mint a WebSocket ticket for the OAuth session ' +
|
|
120
|
+
'(it may have expired). Open Settings → Gateway and sign in again.'
|
|
121
|
+
)
|
|
122
|
+
err.needsOauthLogin = true
|
|
123
|
+
err.cause = error
|
|
124
|
+
throw err
|
|
125
|
+
}
|
|
126
|
+
return buildGatewayWsUrlWithTicket(baseUrl, ticket)
|
|
127
|
+
}
|
|
128
|
+
if (!token) {
|
|
129
|
+
return null
|
|
130
|
+
}
|
|
131
|
+
return buildGatewayWsUrl(baseUrl, token)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Normalize a profile name to a connection scope key, or null for the global
|
|
135
|
+
// (default) connection. Shared by the resolver and the IPC layer.
|
|
136
|
+
function connectionScopeKey(profile) {
|
|
137
|
+
return String(profile ?? '').trim() || null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Coerce a remote auth mode to one of the two supported values ('token' default).
|
|
141
|
+
function normAuthMode(mode) {
|
|
142
|
+
return mode === 'oauth' ? 'oauth' : 'token'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Select a profile's explicit remote override from a connection config, or null
|
|
147
|
+
* when it has none (so the caller falls back to env → global remote → local).
|
|
148
|
+
*
|
|
149
|
+
* The config may carry a `profiles` map keyed by name; an entry counts as an
|
|
150
|
+
* override only with `mode === 'remote'` and a non-empty `url`. Pure: `token`
|
|
151
|
+
* is the raw stored secret; main.cjs decrypts it. Returns
|
|
152
|
+
* `{ url, authMode, token } | null`.
|
|
153
|
+
*/
|
|
154
|
+
function profileRemoteOverride(config, profile) {
|
|
155
|
+
const key = connectionScopeKey(profile)
|
|
156
|
+
const entry = key ? config?.profiles?.[key] : null
|
|
157
|
+
if (!entry || typeof entry !== 'object' || entry.mode !== 'remote') {
|
|
158
|
+
return null
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const url = String(entry.url || '').trim()
|
|
162
|
+
if (!url) {
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { url, authMode: normAuthMode(entry.authMode), token: entry.token }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function tokenPreview(value) {
|
|
170
|
+
const raw = String(value || '')
|
|
171
|
+
|
|
172
|
+
if (!raw) {
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return raw.length <= 8 ? 'set' : `...${raw.slice(-6)}`
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Classify a gateway's auth mode from its public /api/status body.
|
|
181
|
+
* `auth_required: true` → OAuth gate engaged; otherwise legacy token auth.
|
|
182
|
+
* Returns 'oauth' | 'token'.
|
|
183
|
+
*/
|
|
184
|
+
function authModeFromStatus(statusBody) {
|
|
185
|
+
return statusBody && statusBody.auth_required ? 'oauth' : 'token'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Resolve the effective auth mode for a coerce/save operation.
|
|
190
|
+
* Explicit input wins; otherwise inherit the saved value; default 'token'.
|
|
191
|
+
* Returns 'oauth' | 'token'.
|
|
192
|
+
*/
|
|
193
|
+
function resolveAuthMode(inputAuthMode, existingAuthMode) {
|
|
194
|
+
if (inputAuthMode === 'oauth') return 'oauth'
|
|
195
|
+
if (inputAuthMode === 'token') return 'token'
|
|
196
|
+
if (existingAuthMode === 'oauth') return 'oauth'
|
|
197
|
+
return 'token'
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* True if any cookie in `cookies` is a nastech session ACCESS-token cookie
|
|
202
|
+
* with a non-empty value. `cookies` is an array of {name, value} (the shape
|
|
203
|
+
* Electron's session.cookies.get returns).
|
|
204
|
+
*
|
|
205
|
+
* Note: this is AT-only. A session whose AT cookie has lapsed but whose RT
|
|
206
|
+
* cookie is still alive is STILL connectable (the gateway refreshes the AT on
|
|
207
|
+
* the next request) — use `cookiesHaveLiveSession` for a connectivity/display
|
|
208
|
+
* check. `cookiesHaveSession` remains exported for callers that specifically
|
|
209
|
+
* need to know whether an unexpired access token is present right now.
|
|
210
|
+
*/
|
|
211
|
+
function cookiesHaveSession(cookies) {
|
|
212
|
+
if (!Array.isArray(cookies)) return false
|
|
213
|
+
return cookies.some(c => c && AT_COOKIE_VARIANTS.includes(c.name) && c.value)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* True if the cookie jar holds a credential that can yield an authenticated
|
|
218
|
+
* request — EITHER a live access-token cookie OR a refresh-token cookie. The
|
|
219
|
+
* RT cookie outlives the AT cookie (24h vs ~15min), and the gateway middleware
|
|
220
|
+
* transparently rotates a fresh AT from the RT on the next authenticated
|
|
221
|
+
* request. Gating connectivity on the AT alone would force a full IDP
|
|
222
|
+
* re-login every ~15 min even though a valid 24h RT is sitting in the jar.
|
|
223
|
+
*
|
|
224
|
+
* This answers "should we even attempt to connect / show as signed in?", not
|
|
225
|
+
* "is the access token unexpired?". The authoritative liveness check is still
|
|
226
|
+
* the actual ws-ticket mint at connect time (which surfaces a true 401 when
|
|
227
|
+
* the RT is also dead/revoked).
|
|
228
|
+
*/
|
|
229
|
+
function cookiesHaveLiveSession(cookies) {
|
|
230
|
+
if (!Array.isArray(cookies)) return false
|
|
231
|
+
return cookies.some(
|
|
232
|
+
c =>
|
|
233
|
+
c &&
|
|
234
|
+
c.value &&
|
|
235
|
+
(AT_COOKIE_VARIANTS.includes(c.name) || RT_COOKIE_VARIANTS.includes(c.name))
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
module.exports = {
|
|
240
|
+
AT_COOKIE_VARIANTS,
|
|
241
|
+
RT_COOKIE_VARIANTS,
|
|
242
|
+
authModeFromStatus,
|
|
243
|
+
buildGatewayWsUrl,
|
|
244
|
+
buildGatewayWsUrlWithTicket,
|
|
245
|
+
connectionScopeKey,
|
|
246
|
+
cookiesHaveSession,
|
|
247
|
+
cookiesHaveLiveSession,
|
|
248
|
+
normAuthMode,
|
|
249
|
+
normalizeRemoteBaseUrl,
|
|
250
|
+
profileRemoteOverride,
|
|
251
|
+
resolveAuthMode,
|
|
252
|
+
resolveTestWsUrl,
|
|
253
|
+
tokenPreview
|
|
254
|
+
}
|