@deepseek-harness-tui/dsh-tui 0.6.0 → 0.7.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/README.md +74 -21
- package/bin/dsh-tui.js +115 -25
- package/cordis.patch.yml +69 -17
- package/cordis.yml +50 -12
- package/lib/types/activityPrefs.d.ts +1 -1
- package/lib/types/activityPrefs.js +3 -3
- package/lib/types/cc/figures.d.ts +27 -20
- package/lib/types/cc/figures.d.ts.map +1 -1
- package/lib/types/cc/figures.js +43 -34
- package/lib/types/cc/format.d.ts +18 -9
- package/lib/types/cc/format.d.ts.map +1 -1
- package/lib/types/cc/format.js +26 -18
- package/lib/types/cc/markdown.d.ts +31 -16
- package/lib/types/cc/markdown.d.ts.map +1 -1
- package/lib/types/cc/markdown.js +283 -260
- package/lib/types/cc/terminal.d.ts +10 -7
- package/lib/types/cc/terminal.d.ts.map +1 -1
- package/lib/types/cc/terminal.js +62 -50
- package/lib/types/command-trees.d.ts +3 -0
- package/lib/types/command-trees.d.ts.map +1 -0
- package/lib/types/command-trees.js +4 -0
- package/lib/types/commands.d.ts +37 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +68 -3
- package/lib/types/components/ActivityLine.d.ts +1 -1
- package/lib/types/components/ActivityLine.d.ts.map +1 -1
- package/lib/types/components/ActivityPicker.d.ts.map +1 -1
- package/lib/types/components/ActivityPicker.js +3 -4
- package/lib/types/components/BtwPanel.d.ts +16 -0
- package/lib/types/components/BtwPanel.d.ts.map +1 -0
- package/lib/types/components/BtwPanel.js +47 -0
- package/lib/types/components/CommandSuggestions.d.ts +4 -2
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +2 -2
- package/lib/types/components/EffortSlider.d.ts +15 -0
- package/lib/types/components/EffortSlider.d.ts.map +1 -0
- package/lib/types/components/EffortSlider.js +17 -0
- package/lib/types/components/GoalTodoPanel.d.ts +1 -1
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.d.ts +5 -2
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +8 -3
- package/lib/types/components/HistorySearchDialog.d.ts +1 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +21 -10
- package/lib/types/components/InterruptedByUser.d.ts +2 -2
- package/lib/types/components/InterruptedByUser.d.ts.map +1 -1
- package/lib/types/components/InterruptedByUser.js +4 -3
- package/lib/types/components/LoadedContextPanel.d.ts +1 -1
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +2 -1
- package/lib/types/components/Markdown.d.ts +11 -11
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +63 -57
- package/lib/types/components/MarkdownTable.d.ts +1 -1
- package/lib/types/components/MarkdownTable.js +2 -2
- package/lib/types/components/MessageList.d.ts +10 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +17 -10
- package/lib/types/components/ModelPicker.d.ts +4 -1
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +16 -4
- package/lib/types/components/OverlayAbove.d.ts +20 -0
- package/lib/types/components/OverlayAbove.d.ts.map +1 -0
- package/lib/types/components/OverlayAbove.js +18 -0
- package/lib/types/components/PresetPicker.d.ts +1 -1
- package/lib/types/components/PresetPicker.d.ts.map +1 -1
- package/lib/types/components/PresetPicker.js +2 -3
- package/lib/types/components/PromptInput.d.ts +5 -4
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +186 -39
- package/lib/types/components/ResumePicker.d.ts +15 -4
- package/lib/types/components/ResumePicker.d.ts.map +1 -1
- package/lib/types/components/ResumePicker.js +25 -9
- package/lib/types/components/RewindPicker.d.ts +1 -1
- package/lib/types/components/RewindPicker.d.ts.map +1 -1
- package/lib/types/components/RewindPicker.js +14 -5
- package/lib/types/components/SearchBox.d.ts +1 -1
- package/lib/types/components/SearchBox.js +1 -1
- package/lib/types/components/Spinner/GlimmerMessage.d.ts +1 -2
- package/lib/types/components/Spinner/GlimmerMessage.d.ts.map +1 -1
- package/lib/types/components/Spinner/GlimmerMessage.js +1 -2
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +1 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.js +2 -2
- package/lib/types/components/Spinner/SpinnerGlyph.d.ts +1 -1
- package/lib/types/components/Spinner/SpinnerGlyph.js +1 -1
- package/lib/types/components/Spinner/spinnerUtils.d.ts +13 -18
- package/lib/types/components/Spinner/spinnerUtils.d.ts.map +1 -1
- package/lib/types/components/Spinner/spinnerUtils.js +67 -71
- package/lib/types/components/Spinner/useStalledAnimation.d.ts +1 -2
- package/lib/types/components/Spinner/useStalledAnimation.d.ts.map +1 -1
- package/lib/types/components/Spinner/useStalledAnimation.js +1 -2
- package/lib/types/components/StreamingMarkdown.d.ts +1 -1
- package/lib/types/components/StreamingMarkdown.js +1 -1
- package/lib/types/components/ThemePicker.d.ts +13 -9
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +21 -15
- package/lib/types/components/ThinkingToggle.d.ts +1 -1
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +8 -8
- package/lib/types/components/ToolUseLoader.d.ts +1 -1
- package/lib/types/components/ToolUseLoader.js +1 -1
- package/lib/types/components/WorkingSpinner.d.ts +2 -2
- package/lib/types/components/WorkingSpinner.js +2 -2
- package/lib/types/components/WorkspaceFlowPicker.d.ts +15 -0
- package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -0
- package/lib/types/components/WorkspaceFlowPicker.js +19 -0
- package/lib/types/components/WorkspacePicker.d.ts +9 -0
- package/lib/types/components/WorkspacePicker.d.ts.map +1 -0
- package/lib/types/components/WorkspacePicker.js +13 -0
- package/lib/types/components/approvals/ApprovalPanel.d.ts +1 -1
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
- package/lib/types/components/approvals/ApprovalPanel.js +2 -1
- package/lib/types/components/design-system/Byline.d.ts +1 -1
- package/lib/types/components/design-system/Byline.js +1 -1
- package/lib/types/components/design-system/Divider.d.ts +1 -1
- package/lib/types/components/design-system/Divider.js +1 -1
- package/lib/types/components/design-system/HintLine.d.ts +12 -0
- package/lib/types/components/design-system/HintLine.d.ts.map +1 -0
- package/lib/types/components/design-system/HintLine.js +15 -0
- package/lib/types/components/design-system/KeyboardShortcutHint.d.ts +1 -1
- package/lib/types/components/design-system/KeyboardShortcutHint.js +1 -1
- package/lib/types/components/design-system/ListItem.d.ts +2 -2
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +35 -3
- package/lib/types/components/design-system/LoadingState.d.ts +1 -2
- package/lib/types/components/design-system/LoadingState.d.ts.map +1 -1
- package/lib/types/components/design-system/LoadingState.js +1 -2
- package/lib/types/components/design-system/Pane.d.ts +2 -2
- package/lib/types/components/design-system/Pane.js +2 -2
- package/lib/types/components/design-system/ProgressBar.d.ts +1 -2
- package/lib/types/components/design-system/ProgressBar.d.ts.map +1 -1
- package/lib/types/components/design-system/ProgressBar.js +1 -2
- package/lib/types/components/design-system/StatusIcon.d.ts +2 -2
- package/lib/types/components/design-system/StatusIcon.js +2 -2
- package/lib/types/components/design-system/ThemeProvider.d.ts +1 -1
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemeProvider.js +87 -26
- package/lib/types/components/design-system/ThemedBox.d.ts +1 -1
- package/lib/types/components/design-system/ThemedBox.js +1 -1
- package/lib/types/components/design-system/ThemedText.d.ts +2 -2
- package/lib/types/components/design-system/ThemedText.js +2 -2
- package/lib/types/components/listWindow.d.ts +26 -0
- package/lib/types/components/listWindow.d.ts.map +1 -0
- package/lib/types/components/listWindow.js +47 -0
- package/lib/types/components/messages/AssistantTextMessage.d.ts +1 -2
- package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantTextMessage.js +1 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +6 -6
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +12 -4
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +9 -5
- package/lib/types/components/messages/MessageMetadata.d.ts +1 -1
- package/lib/types/components/messages/MessageMetadata.js +1 -1
- package/lib/types/components/messages/UserPromptMessage.d.ts +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +5 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +11 -9
- package/lib/types/components/questions/PlanReviewPanel.d.ts +1 -1
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +3 -2
- package/lib/types/components/trajectory/HotspotView.d.ts +14 -0
- package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -0
- package/lib/types/components/trajectory/HotspotView.js +83 -0
- package/lib/types/components/trajectory/Inspector.d.ts +31 -0
- package/lib/types/components/trajectory/Inspector.d.ts.map +1 -0
- package/lib/types/components/trajectory/Inspector.js +67 -0
- package/lib/types/components/trajectory/Ledger.d.ts +20 -0
- package/lib/types/components/trajectory/Ledger.d.ts.map +1 -0
- package/lib/types/components/trajectory/Ledger.js +112 -0
- package/lib/types/components/trajectory/MiniWake.d.ts +24 -0
- package/lib/types/components/trajectory/MiniWake.d.ts.map +1 -0
- package/lib/types/components/trajectory/MiniWake.js +99 -0
- package/lib/types/components/trajectory/WaveBand.d.ts +33 -0
- package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -0
- package/lib/types/components/trajectory/WaveBand.js +145 -0
- package/lib/types/customTheme.d.ts +6 -6
- package/lib/types/customTheme.d.ts.map +1 -1
- package/lib/types/customTheme.js +15 -12
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -0
- package/lib/types/{channel.d.ts → dsh-adapter/channel.d.ts} +184 -43
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -0
- package/lib/types/{channel.js → dsh-adapter/channel.js} +972 -201
- package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -0
- package/lib/types/{childStderr.js → dsh-adapter/childStderr.js} +1 -1
- package/lib/types/dsh-adapter/command-trees.d.ts +27 -0
- package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -0
- package/lib/types/dsh-adapter/command-trees.js +42 -0
- package/lib/types/dsh-adapter/compat/index.d.ts +22 -0
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -0
- package/lib/types/{compat → dsh-adapter/compat}/index.js +7 -4
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +86 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/sessionLog.js +347 -0
- package/lib/types/dsh-adapter/contract.d.ts +20 -0
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -0
- package/lib/types/dsh-adapter/contract.js +109 -0
- package/lib/types/dsh-adapter/index.d.ts +80 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/index.js +45 -0
- package/lib/types/dsh-adapter/invariant.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-skills.d.ts.map +1 -0
- package/lib/types/{plugin.d.ts → dsh-adapter/plugin.d.ts} +17 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -0
- package/lib/types/{plugin.js → dsh-adapter/plugin.js} +280 -80
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts +91 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/providerWizard.js +332 -0
- package/lib/types/{questions.d.ts → dsh-adapter/questions.d.ts} +7 -2
- package/lib/types/dsh-adapter/questions.d.ts.map +1 -0
- package/lib/types/{questions.js → dsh-adapter/questions.js} +15 -7
- package/lib/types/dsh-adapter/sideQuestion.d.ts +38 -0
- package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sideQuestion.js +69 -0
- package/lib/types/dsh-adapter/trajectory/aggregate.d.ts +46 -0
- package/lib/types/dsh-adapter/trajectory/aggregate.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/aggregate.js +207 -0
- package/lib/types/dsh-adapter/trajectory/guards.d.ts +177 -0
- package/lib/types/dsh-adapter/trajectory/guards.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/guards.js +235 -0
- package/lib/types/dsh-adapter/trajectory/index.d.ts +16 -0
- package/lib/types/dsh-adapter/trajectory/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/index.js +15 -0
- package/lib/types/dsh-adapter/trajectory/inspect.d.ts +40 -0
- package/lib/types/dsh-adapter/trajectory/inspect.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/inspect.js +160 -0
- package/lib/types/dsh-adapter/trajectory/projection.d.ts +128 -0
- package/lib/types/dsh-adapter/trajectory/projection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/projection.js +653 -0
- package/lib/types/dsh-adapter/trajectory/types.d.ts +232 -0
- package/lib/types/dsh-adapter/trajectory/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/types.js +70 -0
- package/lib/types/dsh-adapter/trajectory/wave.d.ts +45 -0
- package/lib/types/dsh-adapter/trajectory/wave.d.ts.map +1 -0
- package/lib/types/dsh-adapter/trajectory/wave.js +210 -0
- package/lib/types/dsh-adapter/types.d.ts +23 -0
- package/lib/types/dsh-adapter/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/types.js +1 -0
- package/lib/types/dsh-adapter/workspace.d.ts +22 -0
- package/lib/types/dsh-adapter/workspace.d.ts.map +1 -0
- package/lib/types/dsh-adapter/workspace.js +16 -0
- package/lib/types/dsh-adapter/workspaces.d.ts +118 -0
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -0
- package/lib/types/dsh-adapter/workspaces.js +290 -0
- package/lib/types/effortPrefs.d.ts +1 -1
- package/lib/types/effortPrefs.js +3 -3
- package/lib/types/history.js +2 -2
- package/lib/types/hooks/useBlink.d.ts +1 -1
- package/lib/types/hooks/useBlink.js +1 -1
- package/lib/types/i18n.d.ts +977 -36
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +307 -30
- package/lib/types/index.d.ts +1 -69
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +3 -31
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +33 -19
- package/lib/types/ink/dom.d.ts +15 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/ink/dom.js +32 -0
- package/lib/types/ink/hooks/use-input.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-input.js +8 -0
- package/lib/types/ink/hooks/use-terminal-size.d.ts +1 -1
- package/lib/types/ink/hooks/use-terminal-size.js +1 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +109 -28
- package/lib/types/ink/input-suppression.d.ts +20 -0
- package/lib/types/ink/input-suppression.d.ts.map +1 -0
- package/lib/types/ink/input-suppression.js +26 -0
- package/lib/types/ink/log-update.js +21 -6
- package/lib/types/ink/parse-keypress.d.ts +31 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +442 -8
- package/lib/types/ink/terminal-querier.d.ts +5 -1
- package/lib/types/ink/terminal-querier.d.ts.map +1 -1
- package/lib/types/ink/terminal-querier.js +27 -3
- package/lib/types/ink/terminal.d.ts +18 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +32 -4
- package/lib/types/ink/termio/csi.d.ts +10 -0
- package/lib/types/ink/termio/csi.d.ts.map +1 -1
- package/lib/types/ink/termio/csi.js +20 -0
- package/lib/types/ink/termio/tokenize.js +10 -0
- package/lib/types/modelPrefs.d.ts +1 -1
- package/lib/types/modelPrefs.js +3 -3
- package/lib/types/presetPrefs.d.ts +1 -1
- package/lib/types/presetPrefs.js +3 -3
- package/lib/types/screens/Chat.d.ts +20 -4
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +655 -55
- package/lib/types/screens/StatusLine.d.ts +17 -2
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +12 -4
- package/lib/types/screens/TrajectoryScene.d.ts +16 -0
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -0
- package/lib/types/screens/TrajectoryScene.js +305 -0
- package/lib/types/sessionHistory.d.ts +9 -1
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessionHistory.js +56 -16
- package/lib/types/sessionModes.d.ts +29 -0
- package/lib/types/sessionModes.d.ts.map +1 -0
- package/lib/types/sessionModes.js +45 -0
- package/lib/types/theme.d.ts +27 -3
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +48 -4
- package/lib/types/themePrefs.d.ts +3 -3
- package/lib/types/themePrefs.js +5 -5
- package/lib/types/trajectory/format.d.ts +100 -0
- package/lib/types/trajectory/format.d.ts.map +1 -0
- package/lib/types/trajectory/format.js +213 -0
- package/lib/types/trajectory/motion.d.ts +99 -0
- package/lib/types/trajectory/motion.d.ts.map +1 -0
- package/lib/types/trajectory/motion.js +143 -0
- package/lib/types/trajectory/query.d.ts +83 -0
- package/lib/types/trajectory/query.d.ts.map +1 -0
- package/lib/types/trajectory/query.js +187 -0
- package/lib/types/trajectoryPrefs.d.ts +36 -0
- package/lib/types/trajectoryPrefs.d.ts.map +1 -0
- package/lib/types/trajectoryPrefs.js +65 -0
- package/lib/types/update.d.ts +3 -3
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +18 -7
- package/lib/types/utils/clipboard.d.ts +101 -29
- package/lib/types/utils/clipboard.d.ts.map +1 -1
- package/lib/types/utils/clipboard.js +533 -55
- package/lib/types/utils/debug.d.ts +1 -1
- package/lib/types/utils/debug.js +2 -2
- package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
- package/lib/types/utils/execFileNoThrow.js +16 -6
- package/lib/types/utils/externalEditor.d.ts +115 -0
- package/lib/types/utils/externalEditor.d.ts.map +1 -0
- package/lib/types/utils/externalEditor.js +270 -0
- package/lib/types/utils/fullscreen.d.ts +2 -2
- package/lib/types/utils/fullscreen.js +3 -3
- package/lib/types/utils/loaded-context.d.ts +1 -1
- package/lib/types/utils/loaded-context.d.ts.map +1 -1
- package/lib/types/utils/modifiers.d.ts +24 -0
- package/lib/types/utils/modifiers.d.ts.map +1 -1
- package/lib/types/utils/modifiers.js +27 -0
- package/lib/types/utils/paths.d.ts +45 -0
- package/lib/types/utils/paths.d.ts.map +1 -0
- package/lib/types/utils/paths.js +69 -0
- package/lib/types/utils/shellQuote.d.ts +14 -0
- package/lib/types/utils/shellQuote.d.ts.map +1 -0
- package/lib/types/utils/shellQuote.js +39 -0
- package/lib/types/utils/workspaceRoot.d.ts +21 -0
- package/lib/types/utils/workspaceRoot.d.ts.map +1 -0
- package/lib/types/utils/workspaceRoot.js +47 -0
- package/lib/types/workspaces.d.ts +3 -0
- package/lib/types/workspaces.d.ts.map +1 -0
- package/lib/types/workspaces.js +4 -0
- package/package.json +26 -8
- package/lib/types/approvals.d.ts.map +0 -1
- package/lib/types/channel.d.ts.map +0 -1
- package/lib/types/childStderr.d.ts.map +0 -1
- package/lib/types/compat/index.d.ts +0 -20
- package/lib/types/compat/index.d.ts.map +0 -1
- package/lib/types/compat/sessionLog.d.ts +0 -40
- package/lib/types/compat/sessionLog.d.ts.map +0 -1
- package/lib/types/compat/sessionLog.js +0 -226
- package/lib/types/invariant.d.ts.map +0 -1
- package/lib/types/packaged-skills.d.ts.map +0 -1
- package/lib/types/plugin.d.ts.map +0 -1
- package/lib/types/presets.d.ts.map +0 -1
- package/lib/types/questions.d.ts.map +0 -1
- /package/lib/types/{approvals.d.ts → dsh-adapter/approvals.d.ts} +0 -0
- /package/lib/types/{approvals.js → dsh-adapter/approvals.js} +0 -0
- /package/lib/types/{childStderr.d.ts → dsh-adapter/childStderr.d.ts} +0 -0
- /package/lib/types/{invariant.d.ts → dsh-adapter/invariant.d.ts} +0 -0
- /package/lib/types/{invariant.js → dsh-adapter/invariant.js} +0 -0
- /package/lib/types/{packaged-skills.d.ts → dsh-adapter/packaged-skills.d.ts} +0 -0
- /package/lib/types/{packaged-skills.js → dsh-adapter/packaged-skills.js} +0 -0
- /package/lib/types/{presets.d.ts → dsh-adapter/presets.d.ts} +0 -0
- /package/lib/types/{presets.js → dsh-adapter/presets.js} +0 -0
|
@@ -1,73 +1,544 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* the
|
|
4
|
-
* the app and the clipboard is read here
|
|
5
|
-
* zero-dependency route: file drops (Explorer copy) come back as a
|
|
6
|
-
* FileDropList (the user pastes a file → insert its path), anything else
|
|
7
|
-
* comes back as text. Text is base64-encoded on the PowerShell side so the
|
|
8
|
-
* line-oriented stdout parse survives multi-line clipboard content (a raw
|
|
9
|
-
* write would put every line on its own output line and drop all but the
|
|
10
|
-
* first); CJK survives because base64 is pure ASCII.
|
|
11
|
-
*/
|
|
12
|
-
import { execFile } from 'node:child_process';
|
|
13
|
-
const PS_SCRIPT = [
|
|
14
|
-
"$ErrorActionPreference='SilentlyContinue'",
|
|
15
|
-
'[Console]::OutputEncoding=[System.Text.Encoding]::UTF8',
|
|
16
|
-
'$files=$null',
|
|
17
|
-
'try { $files = Get-Clipboard -Format FileDropList -ErrorAction Stop } catch {}',
|
|
18
|
-
'if($files){foreach($f in $files){Write-Output ("FILE:"+$f.FullName)}}',
|
|
19
|
-
'if(-not $files){$t=Get-Clipboard -Raw; if($null -ne $t){Write-Output ("TEXT64:"+[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($t)))}}',
|
|
20
|
-
].join('; ');
|
|
21
|
-
/**
|
|
22
|
-
* Read the Windows clipboard: file paths when Explorer copied files,
|
|
23
|
-
* otherwise the plain text. Returns null when the clipboard holds neither.
|
|
24
|
-
* Clipboard access is retried — another process (e.g. Explorer) briefly
|
|
25
|
-
* holding the clipboard open makes OpenClipboard fail with a transient
|
|
26
|
-
* error.
|
|
2
|
+
* Cross-platform clipboard access for Ctrl+V paste. The TUI runs in raw
|
|
3
|
+
* mode, so the terminal never performs its own paste for Ctrl+V — the key
|
|
4
|
+
* arrives at the app and the clipboard is read here, per platform:
|
|
27
5
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
6
|
+
* - **Windows**: PowerShell `Get-Clipboard` (zero-dependency). File drops
|
|
7
|
+
* (Explorer copy) come back as a FileDropList, a raw image (screenshot)
|
|
8
|
+
* is saved as PNG via System.Drawing, anything else as text.
|
|
9
|
+
* - **macOS**: `osascript` for files («class furl») and images («class
|
|
10
|
+
* PNGf»/«class TIFF», written to a temp file), `pbpaste` for text.
|
|
11
|
+
* - **Linux/other**: the paste tools `wl-paste` (Wayland), `xclip` (X11)
|
|
12
|
+
* and `xsel` (X11, text only) are tried in session order until one
|
|
13
|
+
* connects — an installed tool whose session is unreachable (stale
|
|
14
|
+
* WAYLAND_DISPLAY/DISPLAY) falls through to the next candidate.
|
|
15
|
+
* `text/uri-list` offers become file paths, `image/*` offers are
|
|
16
|
+
* exported to a temp file whose path is inserted.
|
|
17
|
+
*
|
|
18
|
+
* Priority is always files → image → text (a screenshot copy offers only an
|
|
19
|
+
* image; a file-manager copy offers a file list; everything else falls
|
|
20
|
+
* through to text). Exported images go into a per-process private
|
|
21
|
+
* directory (mkdtemp, mode 0700) under the OS temp dir and are created
|
|
22
|
+
* with mode 0600 — clipboard screenshots routinely contain sensitive
|
|
23
|
+
* content and must not be world-readable in a shared /tmp. The directory
|
|
24
|
+
* lives until the OS cleans temp; files are referenced by the prompt as
|
|
25
|
+
* paths, so they must outlive the read itself. When the directory cannot
|
|
26
|
+
* be created (bad TMPDIR, permissions), an image offer degrades to the
|
|
27
|
+
* text branch instead of failing the read — and the failure is not
|
|
28
|
+
* cached, so the next paste retries.
|
|
29
|
+
*
|
|
30
|
+
* Text is base64-encoded on the PowerShell side so the line-oriented
|
|
31
|
+
* stdout parse survives multi-line clipboard content (a raw write would
|
|
32
|
+
* put every line on its own output line and drop all but the first); CJK
|
|
33
|
+
* survives because base64 is pure ASCII. The Linux/macOS tools write raw
|
|
34
|
+
* UTF-8 to stdout, which Node decodes directly.
|
|
30
35
|
*/
|
|
31
|
-
|
|
36
|
+
import { execFile, spawn } from 'node:child_process';
|
|
37
|
+
import { existsSync } from 'node:fs';
|
|
38
|
+
import { chmod, mkdtemp, writeFile } from 'node:fs/promises';
|
|
39
|
+
import { tmpdir } from 'node:os';
|
|
40
|
+
import { join } from 'node:path';
|
|
41
|
+
import { fileURLToPath } from 'node:url';
|
|
42
|
+
import { execFileNoThrow } from './execFileNoThrow.js';
|
|
43
|
+
/** Timeout for every clipboard helper invocation (ms). */
|
|
44
|
+
const CLIPBOARD_TIMEOUT = 3000;
|
|
45
|
+
/**
|
|
46
|
+
* Parse `text/uri-list` clipboard content into local file paths. Lines are
|
|
47
|
+
* parsed as real URLs: comment lines (`#…`), non-`file:` URIs and remote
|
|
48
|
+
* authorities (`file://server/share` — a network share must not silently
|
|
49
|
+
* become a local `/share` path) are skipped; only an empty authority or
|
|
50
|
+
* `localhost` is accepted. Percent-escapes decode via fileURLToPath, the
|
|
51
|
+
* single-slash form `file:/path` works, and query/fragment parts never
|
|
52
|
+
* leak into the file name. Entries whose escapes are malformed keep their
|
|
53
|
+
* undecoded path rather than being dropped. The first line of GNOME/KDE's
|
|
54
|
+
* `x-special/gnome-copied-files` (`copy`/`cut`) is not a URL and is
|
|
55
|
+
* skipped by the same filter, so that format can be fed through unchanged.
|
|
56
|
+
* @param uriList - Raw `text/uri-list` payload (CRLF or LF separated).
|
|
57
|
+
* @returns The decoded local paths, in offer order.
|
|
58
|
+
*/
|
|
59
|
+
export function parseUriList(uriList) {
|
|
60
|
+
const paths = [];
|
|
61
|
+
for (const raw of uriList.split(/\r?\n/)) {
|
|
62
|
+
const line = raw.trim();
|
|
63
|
+
if (!line || line.startsWith('#'))
|
|
64
|
+
continue;
|
|
65
|
+
let url;
|
|
66
|
+
try {
|
|
67
|
+
url = new URL(line);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (url.protocol !== 'file:')
|
|
73
|
+
continue;
|
|
74
|
+
const host = url.hostname.toLowerCase();
|
|
75
|
+
if (host !== '' && host !== 'localhost')
|
|
76
|
+
continue;
|
|
77
|
+
try {
|
|
78
|
+
paths.push(fileURLToPath(url));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// A POSIX file URI has no drive letter, so fileURLToPath() rejects it
|
|
82
|
+
// when this pure parser is exercised on Windows. Decode that pathname
|
|
83
|
+
// ourselves, while preserving encoded separators and malformed escapes.
|
|
84
|
+
try {
|
|
85
|
+
if (/%(?:2f|5c)/iu.test(url.pathname))
|
|
86
|
+
throw new URIError('encoded path separator');
|
|
87
|
+
paths.push(decodeURIComponent(url.pathname));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
paths.push(url.pathname);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return paths;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Pick the image MIME type to capture from the offered clipboard target
|
|
98
|
+
* list: `image/png` when offered (screenshots are lossless PNG), otherwise
|
|
99
|
+
* the first `image/*` offer.
|
|
100
|
+
* @param targets - MIME types advertised by the clipboard owner.
|
|
101
|
+
* @returns The chosen MIME type, or null when no image is offered.
|
|
102
|
+
*/
|
|
103
|
+
export function pickImageMime(targets) {
|
|
104
|
+
const images = targets.map(t => t.trim()).filter(t => /^image\//i.test(t));
|
|
105
|
+
if (images.length === 0)
|
|
106
|
+
return null;
|
|
107
|
+
return images.find(t => t.toLowerCase() === 'image/png') ?? images[0];
|
|
108
|
+
}
|
|
109
|
+
/** Text MIME types in preference order (UTF-8 variants first). */
|
|
110
|
+
const TEXT_MIME_PRIORITY = [
|
|
111
|
+
'text/plain;charset=utf-8',
|
|
112
|
+
'utf8_string',
|
|
113
|
+
'text/plain',
|
|
114
|
+
'text',
|
|
115
|
+
'string',
|
|
116
|
+
];
|
|
117
|
+
/**
|
|
118
|
+
* Pick the text MIME type to read from the offered clipboard target list:
|
|
119
|
+
* the first match of {@link TEXT_MIME_PRIORITY}, otherwise any other
|
|
120
|
+
* `text/*` offer except `text/uri-list` (already consumed by the files
|
|
121
|
+
* branch — falling back to it would insert raw URIs as text).
|
|
122
|
+
* @param targets - MIME types advertised by the clipboard owner.
|
|
123
|
+
* @returns The chosen MIME type, or null when no text is offered.
|
|
124
|
+
*/
|
|
125
|
+
export function pickTextMime(targets) {
|
|
126
|
+
const lowered = targets.map(t => t.trim().toLowerCase()).filter(Boolean);
|
|
127
|
+
for (const want of TEXT_MIME_PRIORITY) {
|
|
128
|
+
if (lowered.includes(want)) {
|
|
129
|
+
return targets[lowered.indexOf(want)].trim();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const anyText = targets.find(t => {
|
|
133
|
+
const mime = t.trim().toLowerCase();
|
|
134
|
+
return /^text\//.test(mime) && mime !== 'text/uri-list';
|
|
135
|
+
});
|
|
136
|
+
return anyText ? anyText.trim() : null;
|
|
137
|
+
}
|
|
138
|
+
/** File extension per image MIME type for the exported temp file. */
|
|
139
|
+
const IMAGE_EXTENSIONS = {
|
|
140
|
+
'image/png': '.png',
|
|
141
|
+
'image/jpeg': '.jpg',
|
|
142
|
+
'image/gif': '.gif',
|
|
143
|
+
'image/webp': '.webp',
|
|
144
|
+
'image/bmp': '.bmp',
|
|
145
|
+
'image/tiff': '.tiff',
|
|
146
|
+
'image/svg+xml': '.svg',
|
|
147
|
+
};
|
|
148
|
+
// Per-process private directory for exported clipboard images, created
|
|
149
|
+
// lazily on the first image paste. mkdtemp guarantees a fresh,
|
|
150
|
+
// unpredictably-named directory; mode 0700 keeps other local users out of
|
|
151
|
+
// whatever a screenshot contains. Only SUCCESS is cached — a failed
|
|
152
|
+
// creation (bad TMPDIR, permissions) must not disable image pastes for
|
|
153
|
+
// the rest of the process, and never rejects: callers degrade to their
|
|
154
|
+
// text branch instead.
|
|
155
|
+
let imageDir;
|
|
156
|
+
/**
|
|
157
|
+
* Lazily create (once per process) and return the private directory that
|
|
158
|
+
* holds exported clipboard images.
|
|
159
|
+
* @returns Absolute path of a mode-0700 directory inside the OS temp dir,
|
|
160
|
+
* or null when it cannot be created this time (not cached — the next
|
|
161
|
+
* paste retries).
|
|
162
|
+
*/
|
|
163
|
+
async function ensureImageDir() {
|
|
164
|
+
if (imageDir !== undefined)
|
|
165
|
+
return imageDir;
|
|
166
|
+
let dir;
|
|
167
|
+
try {
|
|
168
|
+
dir = await mkdtemp(join(tmpdir(), 'dsh-tui-paste-'));
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
// mkdtemp(3) already creates 0700; enforce it where the platform's
|
|
175
|
+
// temp-dir inheritance could widen it. Windows ACLs ignore mode —
|
|
176
|
+
// %TEMP% is per-user there, so the chmod is best-effort.
|
|
177
|
+
await chmod(dir, 0o700);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Best-effort hardening only.
|
|
181
|
+
}
|
|
182
|
+
imageDir = dir;
|
|
183
|
+
return dir;
|
|
184
|
+
}
|
|
185
|
+
/** Per-process counter keeping exported image names unique. */
|
|
186
|
+
let imageCounter = 0;
|
|
187
|
+
/**
|
|
188
|
+
* Build a unique path for an exported clipboard image inside the private
|
|
189
|
+
* image directory (created on first use).
|
|
190
|
+
* @param mime - The image MIME type being exported.
|
|
191
|
+
* @returns An absolute, not-yet-existing path for the export, or null
|
|
192
|
+
* when the private directory is unavailable (see {@link ensureImageDir}).
|
|
193
|
+
*/
|
|
194
|
+
async function imageTempPath(mime) {
|
|
195
|
+
const dir = await ensureImageDir();
|
|
196
|
+
if (dir === null)
|
|
197
|
+
return null;
|
|
198
|
+
const ext = IMAGE_EXTENSIONS[mime.toLowerCase()] ??
|
|
199
|
+
`.${mime.split('/')[1]?.replace(/[^a-z0-9]/gi, '').toLowerCase() || 'img'}`;
|
|
200
|
+
imageCounter += 1;
|
|
201
|
+
return join(dir, `paste-${imageCounter}${ext}`);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Run a command and capture stdout as a raw Buffer (binary-safe, unlike
|
|
205
|
+
* {@link execFileNoThrow} which string-decodes). Never rejects: spawn
|
|
206
|
+
* failure, non-zero exit and timeout all resolve with the partial buffer
|
|
207
|
+
* and a non-zero/null code.
|
|
208
|
+
* @param file - The executable to spawn.
|
|
209
|
+
* @param args - Command-line arguments.
|
|
210
|
+
* @returns Exit code and captured stdout bytes.
|
|
211
|
+
*/
|
|
212
|
+
function spawnForBuffer(file, args) {
|
|
213
|
+
return new Promise(resolve => {
|
|
214
|
+
const child = spawn(file, [...args], { timeout: CLIPBOARD_TIMEOUT });
|
|
215
|
+
const chunks = [];
|
|
216
|
+
child.stdout.on('data', (chunk) => chunks.push(chunk));
|
|
217
|
+
child.on('error', () => resolve({ code: 1, stdout: Buffer.concat(chunks) }));
|
|
218
|
+
child.on('close', code => resolve({ code, stdout: Buffer.concat(chunks) }));
|
|
219
|
+
child.stdin.end();
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Capture an offered clipboard image to the private image directory via a
|
|
224
|
+
* Linux paste tool. The file is created exclusively (`wx`, mode 0600) —
|
|
225
|
+
* never following a pre-existing path.
|
|
226
|
+
* @param tool - The paste tool to read with.
|
|
227
|
+
* @param mime - The image MIME type to request.
|
|
228
|
+
* @returns The written temp path, or null when the capture failed.
|
|
229
|
+
*/
|
|
230
|
+
async function captureLinuxImage(tool, mime) {
|
|
231
|
+
const args = tool === 'wl-paste'
|
|
232
|
+
? ['--type', mime]
|
|
233
|
+
: ['-selection', 'clipboard', '-o', '-t', mime];
|
|
234
|
+
const result = await spawnForBuffer(tool, args);
|
|
235
|
+
if (result.code !== 0 || result.stdout.length === 0)
|
|
236
|
+
return null;
|
|
237
|
+
const path = await imageTempPath(mime);
|
|
238
|
+
if (path === null)
|
|
239
|
+
return null;
|
|
240
|
+
try {
|
|
241
|
+
await writeFile(path, result.stdout, { flag: 'wx', mode: 0o600 });
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
return path;
|
|
247
|
+
}
|
|
248
|
+
// Linux paste tool cache: undefined = not yet established, null = nothing
|
|
249
|
+
// installed/connectable last probe. Only a tool that successfully listed
|
|
250
|
+
// the clipboard (or proved its session by reporting it empty) is cached;
|
|
251
|
+
// a tool that fails to connect is evicted so the next read re-probes.
|
|
252
|
+
let linuxPaste;
|
|
253
|
+
/**
|
|
254
|
+
* Order the Linux paste candidates by session: Wayland prefers wl-paste,
|
|
255
|
+
* X11 prefers xclip/xsel; the other side's tools stay as fallback since
|
|
256
|
+
* mixed setups (XWayland-only apps, forwarded sessions) exist.
|
|
257
|
+
* @returns Candidate tools, most likely usable first.
|
|
258
|
+
*/
|
|
259
|
+
function linuxCandidateOrder() {
|
|
260
|
+
const wayland = Boolean(process.env.WAYLAND_DISPLAY);
|
|
261
|
+
const x11 = Boolean(process.env.DISPLAY);
|
|
262
|
+
if (wayland && !x11)
|
|
263
|
+
return ['wl-paste', 'xclip', 'xsel'];
|
|
264
|
+
if (x11 && !wayland)
|
|
265
|
+
return ['xclip', 'xsel', 'wl-paste'];
|
|
266
|
+
return ['wl-paste', 'xclip', 'xsel'];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Check whether a paste tool is installed (its version query exits 0).
|
|
270
|
+
* @param tool - The tool to probe.
|
|
271
|
+
* @returns True when the binary exists and runs.
|
|
272
|
+
*/
|
|
273
|
+
async function isPasteToolInstalled(tool) {
|
|
274
|
+
const versionArg = tool === 'xclip' ? '-version' : '--version';
|
|
275
|
+
const r = await execFileNoThrow(tool, [versionArg], { timeout: 2000 });
|
|
276
|
+
return r.code === 0;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Recognize the KNOWN "selection is empty" failures of each paste tool.
|
|
280
|
+
* Anything else that goes wrong — dead display session, missing
|
|
281
|
+
* permissions, protocol errors, kill-by-timeout (exit code null), the
|
|
282
|
+
* binary vanishing mid-session — is a backend failure and the next
|
|
283
|
+
* candidate tool gets its turn; treating those as "empty" would both
|
|
284
|
+
* misreport to the user and pin a broken tool in the cache.
|
|
285
|
+
* @param tool - The tool that produced the stderr.
|
|
286
|
+
* @param stderr - The captured stderr text.
|
|
287
|
+
* @returns True only for the tools' stable empty-selection phrasings.
|
|
288
|
+
*/
|
|
289
|
+
function isKnownEmptySelection(tool, stderr) {
|
|
290
|
+
// wl-paste with an empty clipboard: "Nothing is copied" (both the C
|
|
291
|
+
// original and wl-clipboard-rs phrase it this way).
|
|
292
|
+
if (tool === 'wl-paste')
|
|
293
|
+
return /nothing is copied|clipboard is empty/i.test(stderr);
|
|
294
|
+
// xclip with no selection owner: "Error: target TARGETS not available";
|
|
295
|
+
// xsel: "xsel: No selection" on some builds.
|
|
296
|
+
return /target \S+ not available|no selection/i.test(stderr);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Read the clipboard with one specific Linux paste tool: `text/uri-list`
|
|
300
|
+
* (or GNOME/KDE's `x-special/gnome-copied-files`) becomes file paths, an
|
|
301
|
+
* `image/*` offer is exported to the private image directory, and text
|
|
302
|
+
* offers come back as text.
|
|
303
|
+
* @param tool - The tool to read with.
|
|
304
|
+
* @returns The clipboard content, null when the selection is empty or
|
|
305
|
+
* holds nothing usable, or 'backend-error' when the tool itself failed
|
|
306
|
+
* (see {@link isKnownEmptySelection} for the split).
|
|
307
|
+
*/
|
|
308
|
+
async function readWithLinuxTool(tool) {
|
|
309
|
+
const opts = { timeout: CLIPBOARD_TIMEOUT };
|
|
310
|
+
// xsel has no target negotiation in common builds — text only.
|
|
311
|
+
if (tool === 'xsel') {
|
|
312
|
+
const text = await execFileNoThrow('xsel', ['--clipboard', '--output'], opts);
|
|
313
|
+
if (text.code !== 0) {
|
|
314
|
+
return isKnownEmptySelection(tool, text.stderr) ? null : 'backend-error';
|
|
315
|
+
}
|
|
316
|
+
return text.stdout.length > 0 ? { kind: 'text', text: text.stdout } : null;
|
|
317
|
+
}
|
|
318
|
+
const listArgs = tool === 'wl-paste'
|
|
319
|
+
? ['--list-types']
|
|
320
|
+
: ['-selection', 'clipboard', '-o', '-t', 'TARGETS'];
|
|
321
|
+
const offered = await execFileNoThrow(tool, listArgs, opts);
|
|
322
|
+
// Non-zero exit (or null after a timeout kill) splits into the known
|
|
323
|
+
// empty-selection phrasings (→ null) and every other failure (→ try the
|
|
324
|
+
// next candidate tool).
|
|
325
|
+
if (offered.code !== 0) {
|
|
326
|
+
return isKnownEmptySelection(tool, offered.stderr) ? null : 'backend-error';
|
|
327
|
+
}
|
|
328
|
+
const targets = offered.stdout.split(/\r?\n/).filter(t => t.trim().length > 0);
|
|
329
|
+
// Files first: a file-manager copy offers a URI list.
|
|
330
|
+
const uriMime = targets.some(t => t.trim().toLowerCase() === 'text/uri-list')
|
|
331
|
+
? 'text/uri-list'
|
|
332
|
+
: targets.some(t => t.trim().toLowerCase() === 'x-special/gnome-copied-files')
|
|
333
|
+
? 'x-special/gnome-copied-files'
|
|
334
|
+
: null;
|
|
335
|
+
if (uriMime !== null) {
|
|
336
|
+
const args = tool === 'wl-paste'
|
|
337
|
+
? ['--type', uriMime]
|
|
338
|
+
: ['-selection', 'clipboard', '-o', '-t', uriMime];
|
|
339
|
+
const uriList = await execFileNoThrow(tool, args, opts);
|
|
340
|
+
if (uriList.code === 0) {
|
|
341
|
+
const paths = parseUriList(uriList.stdout);
|
|
342
|
+
if (paths.length > 0)
|
|
343
|
+
return { kind: 'files', paths };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Then a raw image (screenshot) — export to a temp file, insert its path.
|
|
347
|
+
const imageMime = pickImageMime(targets);
|
|
348
|
+
if (imageMime !== null) {
|
|
349
|
+
const path = await captureLinuxImage(tool, imageMime);
|
|
350
|
+
if (path !== null)
|
|
351
|
+
return { kind: 'image', path };
|
|
352
|
+
}
|
|
353
|
+
// Finally plain text.
|
|
354
|
+
const textMime = pickTextMime(targets);
|
|
355
|
+
if (textMime !== null) {
|
|
356
|
+
const args = tool === 'wl-paste'
|
|
357
|
+
? ['--no-newline', '--type', textMime]
|
|
358
|
+
: ['-selection', 'clipboard', '-o', '-t', textMime];
|
|
359
|
+
const text = await execFileNoThrow(tool, args, opts);
|
|
360
|
+
if (text.code === 0 && text.stdout.length > 0) {
|
|
361
|
+
return { kind: 'text', text: text.stdout };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Read the clipboard on Linux/Unix: try each candidate paste tool in
|
|
368
|
+
* session order, skipping ones that are not installed and falling through
|
|
369
|
+
* ones that fail for any reason other than a genuinely empty selection.
|
|
370
|
+
* @returns The clipboard content, null when empty/unreadable, or
|
|
371
|
+
* 'unavailable' when every installed tool failed.
|
|
372
|
+
*/
|
|
373
|
+
async function readClipboardLinux() {
|
|
374
|
+
const order = linuxCandidateOrder();
|
|
375
|
+
// The cached winner goes first; a backend error evicts it below and the
|
|
376
|
+
// remaining candidates re-probe.
|
|
377
|
+
const candidates = linuxPaste != null && order.includes(linuxPaste)
|
|
378
|
+
? [linuxPaste, ...order.filter(t => t !== linuxPaste)]
|
|
379
|
+
: order;
|
|
380
|
+
for (const tool of candidates) {
|
|
381
|
+
if (tool !== linuxPaste && !(await isPasteToolInstalled(tool)))
|
|
382
|
+
continue;
|
|
383
|
+
const outcome = await readWithLinuxTool(tool);
|
|
384
|
+
if (outcome === 'backend-error') {
|
|
385
|
+
if (tool === linuxPaste)
|
|
386
|
+
linuxPaste = undefined;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
linuxPaste = tool;
|
|
390
|
+
return outcome;
|
|
391
|
+
}
|
|
392
|
+
// Nothing installed at all, or every installed tool failed — both read
|
|
393
|
+
// as "no usable clipboard" to the caller.
|
|
394
|
+
return { kind: 'unavailable' };
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Read the clipboard on macOS: files via the «class furl» coercion (a
|
|
398
|
+
* Finder copy of a single file; multi-file copies have no portable
|
|
399
|
+
* AppleScript coercion and fall through to image/text), images via
|
|
400
|
+
* «class PNGf»/«class TIFF» written to the private image directory, and
|
|
401
|
+
* text via pbpaste.
|
|
402
|
+
* @returns The clipboard content, or null when empty/unreadable.
|
|
403
|
+
*/
|
|
404
|
+
async function readClipboardDarwin() {
|
|
405
|
+
const opts = { timeout: CLIPBOARD_TIMEOUT };
|
|
406
|
+
const furl = await execFileNoThrow('osascript', ['-e', 'POSIX path of (the clipboard as «class furl»)'], opts);
|
|
407
|
+
if (furl.code === 0 && furl.stdout.trim().length > 0) {
|
|
408
|
+
return { kind: 'files', paths: [furl.stdout.trim()] };
|
|
409
|
+
}
|
|
410
|
+
const info = await execFileNoThrow('osascript', ['-e', 'clipboard info'], opts);
|
|
411
|
+
const imageClass = /«class (PNGf|TIFF)»/.exec(info.stdout)?.[1];
|
|
412
|
+
if (info.code === 0 && imageClass !== undefined) {
|
|
413
|
+
const path = await imageTempPath(imageClass === 'PNGf' ? 'image/png' : 'image/tiff');
|
|
414
|
+
if (path !== null) {
|
|
415
|
+
const escaped = path.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
416
|
+
const written = await execFileNoThrow('osascript', [
|
|
417
|
+
'-e',
|
|
418
|
+
[
|
|
419
|
+
`set imageData to the clipboard as «class ${imageClass}»`,
|
|
420
|
+
`set fp to open for access (POSIX file "${escaped}") with write permission`,
|
|
421
|
+
'write imageData to fp',
|
|
422
|
+
'close access fp',
|
|
423
|
+
].join('\n'),
|
|
424
|
+
], opts);
|
|
425
|
+
if (written.code === 0)
|
|
426
|
+
return { kind: 'image', path };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const text = await execFileNoThrow('pbpaste', [], opts);
|
|
430
|
+
if (text.code === 0 && text.stdout.length > 0) {
|
|
431
|
+
return { kind: 'text', text: text.stdout };
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Build the PowerShell read script for Windows, with the target image
|
|
437
|
+
* export path baked in as a single-quoted literal (only `''` needs
|
|
438
|
+
* escaping there — `-Command` treats trailing argv as more command text,
|
|
439
|
+
* so `$args` passing is unreliable). A null path disables the image
|
|
440
|
+
* branch: file and text reads must survive an unavailable temp directory.
|
|
441
|
+
* @param imagePath - Where a clipboard image should be saved as PNG, or
|
|
442
|
+
* null to skip the image attempt.
|
|
443
|
+
* @returns The complete -Command script.
|
|
444
|
+
*/
|
|
445
|
+
function buildPsScript(imagePath) {
|
|
446
|
+
const imageLiteral = imagePath === null ? "''" : `'${imagePath.replace(/'/g, "''")}'`;
|
|
447
|
+
return [
|
|
448
|
+
"$ErrorActionPreference='SilentlyContinue'",
|
|
449
|
+
'[Console]::OutputEncoding=[System.Text.Encoding]::UTF8',
|
|
450
|
+
`$imagePath=${imageLiteral}`,
|
|
451
|
+
'$files=$null',
|
|
452
|
+
'try { $files = Get-Clipboard -Format FileDropList -ErrorAction Stop } catch {}',
|
|
453
|
+
'if($files){foreach($f in $files){Write-Output ("FILE:"+$f.FullName)}}',
|
|
454
|
+
'$saved=$false',
|
|
455
|
+
"if(-not $files -and $imagePath -ne ''){try { Add-Type -AssemblyName System.Drawing; $img=Get-Clipboard -Format Image -ErrorAction Stop; if($img){$img.Save($imagePath,[System.Drawing.Imaging.ImageFormat]::Png); $img.Dispose(); $saved=$true} } catch {}}",
|
|
456
|
+
'if($saved){Write-Output ("IMAGE:"+$imagePath)}',
|
|
457
|
+
'if(-not $files -and -not $saved){$t=Get-Clipboard -Raw; if($null -ne $t){Write-Output ("TEXT64:"+[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($t)))}}',
|
|
458
|
+
].join('; ');
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Read the Windows clipboard: file paths when Explorer copied files, a
|
|
462
|
+
* raw image (screenshot) saved as PNG into the private image directory,
|
|
463
|
+
* otherwise the plain text. Clipboard access is retried — another process
|
|
464
|
+
* (e.g. Explorer) briefly holding the clipboard open makes OpenClipboard
|
|
465
|
+
* fail with a transient error.
|
|
466
|
+
* @returns The clipboard content, or null when empty/blocked.
|
|
467
|
+
*/
|
|
468
|
+
function readClipboardWindows() {
|
|
32
469
|
return new Promise(resolve => {
|
|
33
470
|
let attempts = 0;
|
|
34
471
|
const attempt = () => {
|
|
35
472
|
attempts += 1;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
473
|
+
void imageTempPath('image/png').then(imagePath => {
|
|
474
|
+
const child = execFile('powershell', ['-NoProfile', '-NonInteractive', '-Command', buildPsScript(imagePath)], { encoding: 'utf8', windowsHide: true, timeout: CLIPBOARD_TIMEOUT }, (error, stdout) => {
|
|
475
|
+
if (error) {
|
|
476
|
+
if (attempts < 3) {
|
|
477
|
+
// Transient clipboard lock — retry shortly.
|
|
478
|
+
setTimeout(attempt, 150);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
resolve(null);
|
|
41
482
|
return;
|
|
42
483
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
484
|
+
const files = [];
|
|
485
|
+
const texts = [];
|
|
486
|
+
let image = null;
|
|
487
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
488
|
+
if (line.startsWith('FILE:'))
|
|
489
|
+
files.push(line.slice(5));
|
|
490
|
+
else if (line.startsWith('IMAGE:'))
|
|
491
|
+
image = line.slice(6);
|
|
492
|
+
else if (line.startsWith('TEXT64:')) {
|
|
493
|
+
texts.push(Buffer.from(line.slice(7), 'base64').toString('utf8'));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (files.length > 0)
|
|
497
|
+
resolve({ kind: 'files', paths: files });
|
|
498
|
+
else if (image !== null && existsSync(image)) {
|
|
499
|
+
resolve({ kind: 'image', path: image });
|
|
500
|
+
}
|
|
501
|
+
else if (texts.length > 0) {
|
|
502
|
+
resolve({ kind: 'text', text: texts.join('\n') });
|
|
53
503
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
else
|
|
60
|
-
resolve(null);
|
|
504
|
+
else
|
|
505
|
+
resolve(null);
|
|
506
|
+
});
|
|
507
|
+
// The app must never be held hostage by a stuck PowerShell.
|
|
508
|
+
child.unref();
|
|
61
509
|
});
|
|
62
|
-
// The app must never be held hostage by a stuck PowerShell.
|
|
63
|
-
child.unref();
|
|
64
510
|
};
|
|
65
511
|
attempt();
|
|
66
512
|
});
|
|
67
513
|
}
|
|
68
514
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
515
|
+
* Read the system clipboard for Ctrl+V paste, dispatching on platform:
|
|
516
|
+
* PowerShell on Windows, osascript/pbpaste on macOS, wl-paste/xclip/xsel
|
|
517
|
+
* on Linux and other Unixes.
|
|
518
|
+
* @returns The clipboard content, null when empty/unreadable, or
|
|
519
|
+
* 'unavailable' when no clipboard backend can be reached.
|
|
520
|
+
*/
|
|
521
|
+
export function readClipboard() {
|
|
522
|
+
switch (process.platform) {
|
|
523
|
+
case 'win32':
|
|
524
|
+
return readClipboardWindows();
|
|
525
|
+
case 'darwin':
|
|
526
|
+
return readClipboardDarwin();
|
|
527
|
+
default:
|
|
528
|
+
return readClipboardLinux();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Reset the cached Linux paste tool, forcing the next read to re-probe.
|
|
533
|
+
* @internal test-only
|
|
534
|
+
*/
|
|
535
|
+
export function _resetLinuxPasteCache() {
|
|
536
|
+
linuxPaste = undefined;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Render pasted clipboard content for insertion into the prompt. Image files
|
|
540
|
+
* become `@` references so the send pipeline can attach their bytes; ordinary
|
|
541
|
+
* files remain quoted paths and text has normalized line endings.
|
|
71
542
|
* @param content - Clipboard content as read by {@link readClipboard}.
|
|
72
543
|
* @returns The prompt-ready text: quoted, space-joined paths, or the text
|
|
73
544
|
* with line endings normalized.
|
|
@@ -75,8 +546,15 @@ export function readClipboard() {
|
|
|
75
546
|
export function formatClipboardInsert(content) {
|
|
76
547
|
if (content.kind === 'files') {
|
|
77
548
|
return content.paths
|
|
78
|
-
.map(path =>
|
|
549
|
+
.map(path => {
|
|
550
|
+
const rendered = /\s/.test(path) ? `"${path}"` : path;
|
|
551
|
+
return /\.(?:png|jpe?g|webp|gif)$/iu.test(path) ? `@${rendered}` : rendered;
|
|
552
|
+
})
|
|
79
553
|
.join(' ');
|
|
80
554
|
}
|
|
555
|
+
if (content.kind === 'image') {
|
|
556
|
+
const rendered = /\s/.test(content.path) ? `"${content.path}"` : content.path;
|
|
557
|
+
return `@${rendered}`;
|
|
558
|
+
}
|
|
81
559
|
return content.text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
82
560
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Debug logger for the ported Ink core. Writes to stderr only when
|
|
3
|
-
* `
|
|
3
|
+
* `DSH_TUI_DEBUG` is set, so normal runs stay quiet.
|
|
4
4
|
* @param message - The message to log.
|
|
5
5
|
* @param fields - Optional JSON-serialized fields appended to the line.
|
|
6
6
|
*/
|
package/lib/types/utils/debug.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Debug logger for the ported Ink core. Writes to stderr only when
|
|
3
|
-
* `
|
|
3
|
+
* `DSH_TUI_DEBUG` is set, so normal runs stay quiet.
|
|
4
4
|
* @param message - The message to log.
|
|
5
5
|
* @param fields - Optional JSON-serialized fields appended to the line.
|
|
6
6
|
*/
|
|
7
7
|
export function logForDebugging(message, fields) {
|
|
8
|
-
if (!process.env.
|
|
8
|
+
if (!process.env.DSH_TUI_DEBUG)
|
|
9
9
|
return;
|
|
10
10
|
const suffix = fields === undefined ? '' : ` ${JSON.stringify(fields)}`;
|
|
11
11
|
process.stderr.write(`[dsh-tui] ${message}${suffix}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execFileNoThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/execFileNoThrow.ts"],"names":[],"mappings":"AAEA,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"execFileNoThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/execFileNoThrow.ts"],"names":[],"mappings":"AAEA,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CA8BhC"}
|